-wpa_supplicant and hostapd v0.6.x
----------------------------------
+wpa_supplicant and hostapd
+--------------------------
-Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi> and contributors
+Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi> and contributors
All Rights Reserved.
-These program is dual-licensed under both the GPL version 2 and BSD
-license. Either license may be used at your option.
+These programs are dual-licensed under both the GPL version 2 and BSD
+license (the one with advertisement clause removed). Either license
+may be used at your option.
+
+If you are submitting changes to the project, please see CONTRIBUTIONS
+file for more instructions.
This package may include either wpa_supplicant, hostapd, or both. See
README file respective subdirectories (wpa_supplicant/README or
hostapd/README) for more details.
-Source code files have been moved around in v0.6.x releases and
-compared to earlier releases, the programs are now build by first
-going to a subdirectory (wpa_supplicant or hostapd) and creating
-build configuration (.config) and running 'make' there (for
-Linux/BSD/cygwin builds).
+Source code files were moved around in v0.6.x releases and compared to
+earlier releases, the programs are now built by first going to a
+subdirectory (wpa_supplicant or hostapd) and creating build
+configuration (.config) and running 'make' there (for Linux/BSD/cygwin
+builds).
+
+
+License
+-------
+
+GPL v2:
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License version 2 as
+published by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+(this copy of the license is in COPYING file)
+
+
+Alternatively, this software may be distributed, used, and modified
+under the terms of BSD license:
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+3. Neither the name(s) of the above-listed copyright holder(s) nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+* Fri Jun 8 2012 Arron <arron.wang@intel.com> - 1.0
+- Merge with wpa_supplicant 1.0 stable release
+
* Wed May 30 2012 Junfeng Dong <junfeng.dong@intel.com>
- Clean up spec file for packaging.
Name: wpa_supplicant
Summary: WPA/WPA2/IEEE 802.1X Supplicant
-Version: 0.7.3
+Version: 1.0
Release: 1
Group: System/Base
License: BSD
-SUBDIRS=ap common crypto drivers eapol_auth eapol_supp eap_common eap_peer eap_server l2_packet radius rsn_supp tls utils wps
+SUBDIRS=ap common crypto drivers eapol_auth eapol_supp eap_common eap_peer eap_server l2_packet p2p radius rsn_supp tls utils wps
all:
for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d; done
#include "ieee802_1x.h"
#include "ap_config.h"
#include "sta_info.h"
+#include "ap_drv_ops.h"
#include "accounting.h"
struct sta_info *sta,
struct hostap_sta_driver_data *data)
{
- if (hapd->drv.read_sta_data(hapd, data, sta->addr))
+ if (hostapd_drv_read_sta_data(hapd, data, sta->addr))
return -1;
if (sta->last_rx_bytes > data->rx_bytes)
void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta)
{
struct radius_msg *msg;
+ struct os_time t;
int interval;
if (sta->acct_session_started)
"starting accounting session %08X-%08X",
sta->acct_session_id_hi, sta->acct_session_id_lo);
- time(&sta->acct_session_start);
+ os_get_time(&t);
+ sta->acct_session_start = t.sec;
sta->last_rx_bytes = sta->last_tx_bytes = 0;
sta->acct_input_gigawords = sta->acct_output_gigawords = 0;
- hapd->drv.sta_clear_stats(hapd, sta->addr);
+ hostapd_drv_sta_clear_stats(hapd, sta->addr);
if (!hapd->conf->radius->acct_server)
return;
hapd, sta);
msg = accounting_msg(hapd, sta, RADIUS_ACCT_STATUS_TYPE_START);
- if (msg)
- radius_client_send(hapd->radius, msg, RADIUS_ACCT, sta->addr);
+ if (msg &&
+ radius_client_send(hapd->radius, msg, RADIUS_ACCT, sta->addr) < 0)
+ radius_msg_free(msg);
sta->acct_session_started = 1;
}
struct radius_msg *msg;
int cause = sta->acct_terminate_cause;
struct hostap_sta_driver_data data;
+ struct os_time now;
u32 gigawords;
if (!hapd->conf->radius->acct_server)
return;
}
+ os_get_time(&now);
if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_SESSION_TIME,
- time(NULL) - sta->acct_session_start)) {
+ now.sec - sta->acct_session_start)) {
printf("Could not add Acct-Session-Time\n");
goto fail;
}
}
if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP,
- time(NULL))) {
+ now.sec)) {
printf("Could not add Event-Timestamp\n");
goto fail;
}
goto fail;
}
- radius_client_send(hapd->radius, msg,
- stop ? RADIUS_ACCT : RADIUS_ACCT_INTERIM,
- sta->addr);
+ if (radius_client_send(hapd->radius, msg,
+ stop ? RADIUS_ACCT : RADIUS_ACCT_INTERIM,
+ sta->addr) < 0)
+ goto fail;
return;
fail:
return;
}
- radius_client_send(hapd->radius, msg, RADIUS_ACCT, NULL);
+ if (radius_client_send(hapd->radius, msg, RADIUS_ACCT, NULL) < 0)
+ radius_msg_free(msg);
}
*/
int accounting_init(struct hostapd_data *hapd)
{
+ struct os_time now;
+
/* Acct-Session-Id should be unique over reboots. If reliable clock is
* not available, this could be replaced with reboot counter, etc. */
- hapd->acct_session_id_hi = time(NULL);
+ os_get_time(&now);
+ hapd->acct_session_id_hi = now.sec;
if (radius_client_register(hapd->radius, RADIUS_ACCT,
accounting_receive, hapd))
bss->max_listen_interval = 65535;
+ bss->pwd_group = 19; /* ECC: GF(p=256) */
+
#ifdef CONFIG_IEEE80211W
bss->assoc_sa_query_max_timeout = 1000;
bss->assoc_sa_query_retry_timeout = 201;
bss->pac_key_lifetime = 7 * 24 * 60 * 60;
bss->pac_key_refresh_time = 1 * 24 * 60 * 60;
#endif /* EAP_SERVER_FAST */
+
+ /* Set to -1 as defaults depends on HT in setup */
+ bss->wmm_enabled = -1;
+
+#ifdef CONFIG_IEEE80211R
+ bss->ft_over_ds = 1;
+#endif /* CONFIG_IEEE80211R */
}
struct hostapd_config * hostapd_config_defaults(void)
{
+#define ecw2cw(ecw) ((1 << (ecw)) - 1)
+
struct hostapd_config *conf;
struct hostapd_bss_config *bss;
- int i;
const int aCWmin = 4, aCWmax = 10;
const struct hostapd_wmm_ac_params ac_bk =
{ aCWmin, aCWmax, 7, 0, 0 }; /* background traffic */
{ aCWmin - 1, aCWmin, 2, 3000 / 32, 1 };
const struct hostapd_wmm_ac_params ac_vo = /* voice traffic */
{ aCWmin - 2, aCWmin - 1, 2, 1500 / 32, 1 };
+ const struct hostapd_tx_queue_params txq_bk =
+ { 7, ecw2cw(aCWmin), ecw2cw(aCWmax), 0 };
+ const struct hostapd_tx_queue_params txq_be =
+ { 3, ecw2cw(aCWmin), 4 * (ecw2cw(aCWmin) + 1) - 1, 0};
+ const struct hostapd_tx_queue_params txq_vi =
+ { 1, (ecw2cw(aCWmin) + 1) / 2 - 1, ecw2cw(aCWmin), 30};
+ const struct hostapd_tx_queue_params txq_vo =
+ { 1, (ecw2cw(aCWmin) + 1) / 4 - 1,
+ (ecw2cw(aCWmin) + 1) / 2 - 1, 15};
+
+#undef ecw2cw
conf = os_zalloc(sizeof(*conf));
bss = os_zalloc(sizeof(*bss));
conf->fragm_threshold = -1; /* user driver default: 2346 */
conf->send_probe_response = 1;
- for (i = 0; i < NUM_TX_QUEUES; i++)
- conf->tx_queue[i].aifs = -1; /* use hw default */
-
conf->wmm_ac_params[0] = ac_be;
conf->wmm_ac_params[1] = ac_bk;
conf->wmm_ac_params[2] = ac_vi;
conf->wmm_ac_params[3] = ac_vo;
+ conf->tx_queue[0] = txq_vo;
+ conf->tx_queue[1] = txq_vi;
+ conf->tx_queue[2] = txq_be;
+ conf->tx_queue[3] = txq_bk;
+
conf->ht_capab = HT_CAP_INFO_SMPS_DISABLED;
return conf;
ssid->dyn_vlan_keys = NULL;
}
+ os_free(conf->time_zone);
+
#ifdef CONFIG_IEEE80211R
{
struct ft_remote_r0kh *r0kh, *r0kh_prev;
os_free(conf->model_name);
os_free(conf->model_number);
os_free(conf->serial_number);
- os_free(conf->device_type);
os_free(conf->config_methods);
os_free(conf->ap_pin);
os_free(conf->extra_cred);
os_free(conf->model_url);
os_free(conf->upc);
#endif /* CONFIG_WPS */
+
+ os_free(conf->roaming_consortium);
}
#include "common/defs.h"
#include "ip_addr.h"
#include "common/wpa_common.h"
+#include "wps/wps.h"
#define MAX_STA_COUNT 2007
#define MAX_VLAN_ID 4094
};
-#define NUM_TX_QUEUES 8
+#define NUM_TX_QUEUES 4
struct hostapd_tx_queue_params {
int aifs;
int cwmin;
int cwmax;
int burst; /* maximum burst time in 0.1 ms, i.e., 10 = 1 ms */
- int configured;
};
struct hostapd_wmm_ac_params {
};
+#define MAX_ROAMING_CONSORTIUM_LEN 15
+
+struct hostapd_roaming_consortium {
+ u8 len;
+ u8 oi[MAX_ROAMING_CONSORTIUM_LEN];
+};
+
/**
* struct hostapd_bss_config - Per-BSS configuration
*/
struct hostapd_bss_config {
char iface[IFNAMSIZ + 1];
char bridge[IFNAMSIZ + 1];
+ char wds_bridge[IFNAMSIZ + 1];
enum hostapd_logger_level logger_syslog_level, logger_stdout_level;
struct mac_acl_entry *deny_mac;
int num_deny_mac;
int wds_sta;
+ int isolate;
int auth_algs; /* bitfield of allowed IEEE 802.11 authentication
* algorithms, WPA_AUTH_ALG_{OPEN,SHARED,LEAP} */
struct ft_remote_r0kh *r0kh_list;
struct ft_remote_r1kh *r1kh_list;
int pmk_r1_push;
+ int ft_over_ds;
#endif /* CONFIG_IEEE80211R */
char *ctrl_interface; /* directory for UNIX domain sockets */
int pac_key_refresh_time;
int eap_sim_aka_result_ind;
int tnc;
+ int fragment_size;
+ u16 pwd_group;
char *radius_server_clients;
int radius_server_auth_port;
*/
u16 max_listen_interval;
+ int disable_pmksa_caching;
int okc; /* Opportunistic Key Caching */
int wps_state;
char *model_name;
char *model_number;
char *serial_number;
- char *device_type;
+ u8 device_type[WPS_DEV_TYPE_LEN];
char *config_methods;
u8 os_version[4];
char *ap_pin;
char *model_description;
char *model_url;
char *upc;
+ struct wpabuf *wps_vendor_ext[MAX_WPS_VENDOR_EXTENSIONS];
#endif /* CONFIG_WPS */
+ int pbc_in_m1;
+
+#define P2P_ENABLED BIT(0)
+#define P2P_GROUP_OWNER BIT(1)
+#define P2P_GROUP_FORMATION BIT(2)
+#define P2P_MANAGE BIT(3)
+#define P2P_ALLOW_CROSS_CONNECTION BIT(4)
+ int p2p;
+
+ int disassoc_low_ack;
+
+#define TDLS_PROHIBIT BIT(0)
+#define TDLS_PROHIBIT_CHAN_SWITCH BIT(1)
+ int tdls;
+ int disable_11n;
+
+ /* IEEE 802.11v */
+ int time_advertisement;
+ char *time_zone;
+
+ /* IEEE 802.11u - Interworking */
+ int interworking;
+ int access_network_type;
+ int internet;
+ int asra;
+ int esr;
+ int uesa;
+ int venue_info_set;
+ u8 venue_group;
+ u8 venue_type;
+ u8 hessid[ETH_ALEN];
+
+ /* IEEE 802.11u - Roaming Consortium list */
+ unsigned int roaming_consortium_count;
+ struct hostapd_roaming_consortium *roaming_consortium;
};
LONG_PREAMBLE = 0,
SHORT_PREAMBLE = 1
} preamble;
- enum {
- CTS_PROTECTION_AUTOMATIC = 0,
- CTS_PROTECTION_FORCE_ENABLED = 1,
- CTS_PROTECTION_FORCE_DISABLED = 2,
- CTS_PROTECTION_AUTOMATIC_NO_OLBC = 3,
- } cts_protection_type;
int *supported_rates;
int *basic_rates;
u16 ht_capab;
int ieee80211n;
int secondary_channel;
+ int require_ht;
};
#include "utils/common.h"
#include "drivers/driver.h"
#include "common/ieee802_11_defs.h"
+#include "wps/wps.h"
#include "hostapd.h"
#include "ieee802_11.h"
#include "sta_info.h"
#include "ap_config.h"
+#include "p2p_hostapd.h"
#include "ap_drv_ops.h"
-static int hostapd_sta_flags_to_drv(int flags)
+u32 hostapd_sta_flags_to_drv(u32 flags)
{
int res = 0;
if (flags & WLAN_STA_AUTHORIZED)
}
-static int hostapd_set_ap_wps_ie(struct hostapd_data *hapd)
+int hostapd_build_ap_extra_ies(struct hostapd_data *hapd,
+ struct wpabuf **beacon_ret,
+ struct wpabuf **proberesp_ret,
+ struct wpabuf **assocresp_ret)
{
- struct wpabuf *beacon, *proberesp;
- int ret;
+ struct wpabuf *beacon = NULL, *proberesp = NULL, *assocresp = NULL;
+ u8 buf[200], *pos;
- if (hapd->driver == NULL || hapd->driver->set_ap_wps_ie == NULL)
- return 0;
+ *beacon_ret = *proberesp_ret = *assocresp_ret = NULL;
- beacon = hapd->wps_beacon_ie;
- proberesp = hapd->wps_probe_resp_ie;
+ pos = buf;
+ pos = hostapd_eid_time_adv(hapd, pos);
+ if (pos != buf) {
+ if (wpabuf_resize(&beacon, pos - buf) != 0)
+ goto fail;
+ wpabuf_put_data(beacon, buf, pos - buf);
+ }
+ pos = hostapd_eid_time_zone(hapd, pos);
+ if (pos != buf) {
+ if (wpabuf_resize(&proberesp, pos - buf) != 0)
+ goto fail;
+ wpabuf_put_data(proberesp, buf, pos - buf);
+ }
- ret = hapd->driver->set_ap_wps_ie(hapd->drv_priv, beacon, proberesp);
+ pos = buf;
+ pos = hostapd_eid_ext_capab(hapd, pos);
+ if (pos != buf) {
+ if (wpabuf_resize(&assocresp, pos - buf) != 0)
+ goto fail;
+ wpabuf_put_data(assocresp, buf, pos - buf);
+ }
+ pos = hostapd_eid_interworking(hapd, pos);
+ pos = hostapd_eid_adv_proto(hapd, pos);
+ pos = hostapd_eid_roaming_consortium(hapd, pos);
+ if (pos != buf) {
+ if (wpabuf_resize(&beacon, pos - buf) != 0)
+ goto fail;
+ wpabuf_put_data(beacon, buf, pos - buf);
+
+ if (wpabuf_resize(&proberesp, pos - buf) != 0)
+ goto fail;
+ wpabuf_put_data(proberesp, buf, pos - buf);
+ }
- return ret;
+ if (hapd->wps_beacon_ie) {
+ if (wpabuf_resize(&beacon, wpabuf_len(hapd->wps_beacon_ie)) <
+ 0)
+ goto fail;
+ wpabuf_put_buf(beacon, hapd->wps_beacon_ie);
+ }
+
+ if (hapd->wps_probe_resp_ie) {
+ if (wpabuf_resize(&proberesp,
+ wpabuf_len(hapd->wps_probe_resp_ie)) < 0)
+ goto fail;
+ wpabuf_put_buf(proberesp, hapd->wps_probe_resp_ie);
+ }
+
+#ifdef CONFIG_P2P
+ if (hapd->p2p_beacon_ie) {
+ if (wpabuf_resize(&beacon, wpabuf_len(hapd->p2p_beacon_ie)) <
+ 0)
+ goto fail;
+ wpabuf_put_buf(beacon, hapd->p2p_beacon_ie);
+ }
+
+ if (hapd->p2p_probe_resp_ie) {
+ if (wpabuf_resize(&proberesp,
+ wpabuf_len(hapd->p2p_probe_resp_ie)) < 0)
+ goto fail;
+ wpabuf_put_buf(proberesp, hapd->p2p_probe_resp_ie);
+ }
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_P2P_MANAGER
+ if (hapd->conf->p2p & P2P_MANAGE) {
+ if (wpabuf_resize(&beacon, 100) == 0) {
+ u8 *start, *p;
+ start = wpabuf_put(beacon, 0);
+ p = hostapd_eid_p2p_manage(hapd, start);
+ wpabuf_put(beacon, p - start);
+ }
+
+ if (wpabuf_resize(&proberesp, 100) == 0) {
+ u8 *start, *p;
+ start = wpabuf_put(proberesp, 0);
+ p = hostapd_eid_p2p_manage(hapd, start);
+ wpabuf_put(proberesp, p - start);
+ }
+ }
+#endif /* CONFIG_P2P_MANAGER */
+
+#ifdef CONFIG_WPS2
+ if (hapd->conf->wps_state) {
+ struct wpabuf *a = wps_build_assoc_resp_ie();
+ if (a && wpabuf_resize(&assocresp, wpabuf_len(a)) == 0)
+ wpabuf_put_buf(assocresp, a);
+ wpabuf_free(a);
+ }
+#endif /* CONFIG_WPS2 */
+
+#ifdef CONFIG_P2P_MANAGER
+ if (hapd->conf->p2p & P2P_MANAGE) {
+ if (wpabuf_resize(&assocresp, 100) == 0) {
+ u8 *start, *p;
+ start = wpabuf_put(assocresp, 0);
+ p = hostapd_eid_p2p_manage(hapd, start);
+ wpabuf_put(assocresp, p - start);
+ }
+ }
+#endif /* CONFIG_P2P_MANAGER */
+
+ *beacon_ret = beacon;
+ *proberesp_ret = proberesp;
+ *assocresp_ret = assocresp;
+
+ return 0;
+
+fail:
+ wpabuf_free(beacon);
+ wpabuf_free(proberesp);
+ wpabuf_free(assocresp);
+ return -1;
}
-static int hostapd_send_mgmt_frame(struct hostapd_data *hapd, const void *msg,
- size_t len)
+void hostapd_free_ap_extra_ies(struct hostapd_data *hapd,
+ struct wpabuf *beacon,
+ struct wpabuf *proberesp,
+ struct wpabuf *assocresp)
{
- if (hapd->driver == NULL || hapd->driver->send_mlme == NULL)
- return 0;
- return hapd->driver->send_mlme(hapd->drv_priv, msg, len);
+ wpabuf_free(beacon);
+ wpabuf_free(proberesp);
+ wpabuf_free(assocresp);
}
-static int hostapd_send_eapol(struct hostapd_data *hapd, const u8 *addr,
- const u8 *data, size_t data_len, int encrypt)
+int hostapd_set_ap_wps_ie(struct hostapd_data *hapd)
{
- if (hapd->driver == NULL || hapd->driver->hapd_send_eapol == NULL)
+ struct wpabuf *beacon, *proberesp, *assocresp;
+ int ret;
+
+ if (hapd->driver == NULL || hapd->driver->set_ap_wps_ie == NULL)
return 0;
- return hapd->driver->hapd_send_eapol(hapd->drv_priv, addr, data,
- data_len, encrypt,
- hapd->own_addr);
+
+ if (hostapd_build_ap_extra_ies(hapd, &beacon, &proberesp, &assocresp) <
+ 0)
+ return -1;
+
+ ret = hapd->driver->set_ap_wps_ie(hapd->drv_priv, beacon, proberesp,
+ assocresp);
+
+ hostapd_free_ap_extra_ies(hapd, beacon, proberesp, assocresp);
+
+ return ret;
}
-static int hostapd_set_authorized(struct hostapd_data *hapd,
- struct sta_info *sta, int authorized)
+int hostapd_set_authorized(struct hostapd_data *hapd,
+ struct sta_info *sta, int authorized)
{
if (authorized) {
return hostapd_sta_set_flags(hapd, sta->addr,
}
-static int hostapd_set_key(const char *ifname, struct hostapd_data *hapd,
- enum wpa_alg alg, const u8 *addr, int key_idx,
- int set_tx, const u8 *seq, size_t seq_len,
- const u8 *key, size_t key_len)
-{
- if (hapd->driver == NULL || hapd->driver->set_key == NULL)
- return 0;
- return hapd->driver->set_key(ifname, hapd->drv_priv, alg, addr,
- key_idx, set_tx, seq, seq_len, key,
- key_len);
-}
-
-
-static int hostapd_read_sta_data(struct hostapd_data *hapd,
- struct hostap_sta_driver_data *data,
- const u8 *addr)
-{
- if (hapd->driver == NULL || hapd->driver->read_sta_data == NULL)
- return -1;
- return hapd->driver->read_sta_data(hapd->drv_priv, data, addr);
-}
-
-
-static int hostapd_sta_clear_stats(struct hostapd_data *hapd, const u8 *addr)
-{
- if (hapd->driver == NULL || hapd->driver->sta_clear_stats == NULL)
- return 0;
- return hapd->driver->sta_clear_stats(hapd->drv_priv, addr);
-}
-
-
-static int hostapd_set_sta_flags(struct hostapd_data *hapd,
- struct sta_info *sta)
+int hostapd_set_sta_flags(struct hostapd_data *hapd, struct sta_info *sta)
{
int set_flags, total_flags, flags_and, flags_or;
total_flags = hostapd_sta_flags_to_drv(sta->flags);
}
-static int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd,
- const char *ifname, int enabled)
+int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd, const char *ifname,
+ int enabled)
{
struct wpa_bss_params params;
os_memset(¶ms, 0, sizeof(params));
params.wpa_pairwise = hapd->conf->wpa_pairwise;
params.wpa_key_mgmt = hapd->conf->wpa_key_mgmt;
params.rsn_preauth = hapd->conf->rsn_preauth;
+#ifdef CONFIG_IEEE80211W
+ params.ieee80211w = hapd->conf->ieee80211w;
+#endif /* CONFIG_IEEE80211W */
}
return hostapd_set_ieee8021x(hapd, ¶ms);
}
-static int hostapd_set_radius_acl_auth(struct hostapd_data *hapd,
- const u8 *mac, int accepted,
- u32 session_timeout)
-{
- if (hapd->driver == NULL || hapd->driver->set_radius_acl_auth == NULL)
- return 0;
- return hapd->driver->set_radius_acl_auth(hapd->drv_priv, mac, accepted,
- session_timeout);
-}
-
-
-static int hostapd_set_radius_acl_expire(struct hostapd_data *hapd,
- const u8 *mac)
-{
- if (hapd->driver == NULL ||
- hapd->driver->set_radius_acl_expire == NULL)
- return 0;
- return hapd->driver->set_radius_acl_expire(hapd->drv_priv, mac);
-}
-
-
-static int hostapd_set_bss_params(struct hostapd_data *hapd,
- int use_protection)
-{
- int ret = 0;
- int preamble;
-#ifdef CONFIG_IEEE80211N
- u8 buf[60], *ht_capab, *ht_oper, *pos;
-
- pos = buf;
- ht_capab = pos;
- pos = hostapd_eid_ht_capabilities(hapd, pos);
- ht_oper = pos;
- pos = hostapd_eid_ht_operation(hapd, pos);
- if (pos > ht_oper && ht_oper > ht_capab &&
- hostapd_set_ht_params(hapd, ht_capab + 2, ht_capab[1],
- ht_oper + 2, ht_oper[1])) {
- wpa_printf(MSG_ERROR, "Could not set HT capabilities "
- "for kernel driver");
- ret = -1;
- }
-
-#endif /* CONFIG_IEEE80211N */
-
- if (hostapd_set_cts_protect(hapd, use_protection)) {
- wpa_printf(MSG_ERROR, "Failed to set CTS protect in kernel "
- "driver");
- ret = -1;
- }
-
- if (hapd->iface->current_mode &&
- hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G &&
- hostapd_set_short_slot_time(hapd,
- hapd->iface->num_sta_no_short_slot_time
- > 0 ? 0 : 1)) {
- wpa_printf(MSG_ERROR, "Failed to set Short Slot Time option "
- "in kernel driver");
- ret = -1;
- }
-
- if (hapd->iface->num_sta_no_short_preamble == 0 &&
- hapd->iconf->preamble == SHORT_PREAMBLE)
- preamble = SHORT_PREAMBLE;
- else
- preamble = LONG_PREAMBLE;
- if (hostapd_set_preamble(hapd, preamble)) {
- wpa_printf(MSG_ERROR, "Could not set preamble for kernel "
- "driver");
- ret = -1;
- }
-
- return ret;
-}
-
-
-static int hostapd_set_beacon(struct hostapd_data *hapd,
- const u8 *head, size_t head_len,
- const u8 *tail, size_t tail_len, int dtim_period,
- int beacon_int)
-{
- if (hapd->driver == NULL || hapd->driver->set_beacon == NULL)
- return 0;
- return hapd->driver->set_beacon(hapd->drv_priv,
- head, head_len, tail, tail_len,
- dtim_period, beacon_int);
-}
-
-
-static int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname)
+int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname)
{
char force_ifname[IFNAMSIZ];
u8 if_addr[ETH_ALEN];
- return hostapd_if_add(hapd, WPA_IF_AP_VLAN, ifname, NULL, NULL, NULL,
- force_ifname, if_addr);
+ return hostapd_if_add(hapd, WPA_IF_AP_VLAN, ifname, hapd->own_addr,
+ NULL, NULL, force_ifname, if_addr, NULL);
}
-static int hostapd_vlan_if_remove(struct hostapd_data *hapd,
- const char *ifname)
+
+int hostapd_vlan_if_remove(struct hostapd_data *hapd, const char *ifname)
{
return hostapd_if_remove(hapd, WPA_IF_AP_VLAN, ifname);
}
-static int hostapd_set_wds_sta(struct hostapd_data *hapd, const u8 *addr,
- int aid, int val)
+int hostapd_set_wds_sta(struct hostapd_data *hapd, const u8 *addr, int aid,
+ int val)
{
+ const char *bridge = NULL;
+
if (hapd->driver == NULL || hapd->driver->set_wds_sta == NULL)
return 0;
- return hapd->driver->set_wds_sta(hapd->drv_priv, addr, aid, val);
+ if (hapd->conf->wds_bridge[0])
+ bridge = hapd->conf->wds_bridge;
+ else if (hapd->conf->bridge[0])
+ bridge = hapd->conf->bridge;
+ return hapd->driver->set_wds_sta(hapd->drv_priv, addr, aid, val,
+ bridge);
}
-static int hostapd_set_sta_vlan(const char *ifname, struct hostapd_data *hapd,
- const u8 *addr, int vlan_id)
+int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr,
+ u16 auth_alg)
{
- if (hapd->driver == NULL || hapd->driver->set_sta_vlan == NULL)
+ if (hapd->driver == NULL || hapd->driver->add_sta_node == NULL)
return 0;
- return hapd->driver->set_sta_vlan(hapd->drv_priv, addr, ifname,
- vlan_id);
+ return hapd->driver->add_sta_node(hapd->drv_priv, addr, auth_alg);
}
-static int hostapd_get_inact_sec(struct hostapd_data *hapd, const u8 *addr)
+int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr,
+ u16 seq, u16 status, const u8 *ie, size_t len)
{
- if (hapd->driver == NULL || hapd->driver->get_inact_sec == NULL)
+ if (hapd->driver == NULL || hapd->driver->sta_auth == NULL)
return 0;
- return hapd->driver->get_inact_sec(hapd->drv_priv, addr);
+ return hapd->driver->sta_auth(hapd->drv_priv, hapd->own_addr, addr,
+ seq, status, ie, len);
}
-static int hostapd_sta_deauth(struct hostapd_data *hapd, const u8 *addr,
- int reason)
+int hostapd_sta_assoc(struct hostapd_data *hapd, const u8 *addr,
+ int reassoc, u16 status, const u8 *ie, size_t len)
{
- if (hapd->driver == NULL || hapd->driver->sta_deauth == NULL)
+ if (hapd->driver == NULL || hapd->driver->sta_assoc == NULL)
return 0;
- return hapd->driver->sta_deauth(hapd->drv_priv, hapd->own_addr, addr,
- reason);
+ return hapd->driver->sta_assoc(hapd->drv_priv, hapd->own_addr, addr,
+ reassoc, status, ie, len);
}
-static int hostapd_sta_disassoc(struct hostapd_data *hapd, const u8 *addr,
- int reason)
-{
- if (hapd->driver == NULL || hapd->driver->sta_disassoc == NULL)
- return 0;
- return hapd->driver->sta_disassoc(hapd->drv_priv, hapd->own_addr, addr,
- reason);
-}
-
-
-static int hostapd_sta_add(struct hostapd_data *hapd,
- const u8 *addr, u16 aid, u16 capability,
- const u8 *supp_rates, size_t supp_rates_len,
- u16 listen_interval,
- const struct ieee80211_ht_capabilities *ht_capab)
+int hostapd_sta_add(struct hostapd_data *hapd,
+ const u8 *addr, u16 aid, u16 capability,
+ const u8 *supp_rates, size_t supp_rates_len,
+ u16 listen_interval,
+ const struct ieee80211_ht_capabilities *ht_capab,
+ u32 flags)
{
struct hostapd_sta_add_params params;
params.supp_rates_len = supp_rates_len;
params.listen_interval = listen_interval;
params.ht_capabilities = ht_capab;
+ params.flags = hostapd_sta_flags_to_drv(flags);
return hapd->driver->sta_add(hapd->drv_priv, ¶ms);
}
-static int hostapd_sta_remove(struct hostapd_data *hapd, const u8 *addr)
-{
- if (hapd->driver == NULL || hapd->driver->sta_remove == NULL)
- return 0;
- return hapd->driver->sta_remove(hapd->drv_priv, addr);
-}
-
-
-static int hostapd_set_countermeasures(struct hostapd_data *hapd, int enabled)
+int hostapd_add_tspec(struct hostapd_data *hapd, const u8 *addr,
+ u8 *tspec_ie, size_t tspec_ielen)
{
- if (hapd->driver == NULL ||
- hapd->driver->hapd_set_countermeasures == NULL)
+ if (hapd->driver == NULL || hapd->driver->add_tspec == NULL)
return 0;
- return hapd->driver->hapd_set_countermeasures(hapd->drv_priv, enabled);
-}
-
-
-void hostapd_set_driver_ops(struct hostapd_driver_ops *ops)
-{
- ops->set_ap_wps_ie = hostapd_set_ap_wps_ie;
- ops->send_mgmt_frame = hostapd_send_mgmt_frame;
- ops->send_eapol = hostapd_send_eapol;
- ops->set_authorized = hostapd_set_authorized;
- ops->set_key = hostapd_set_key;
- ops->read_sta_data = hostapd_read_sta_data;
- ops->sta_clear_stats = hostapd_sta_clear_stats;
- ops->set_sta_flags = hostapd_set_sta_flags;
- ops->set_drv_ieee8021x = hostapd_set_drv_ieee8021x;
- ops->set_radius_acl_auth = hostapd_set_radius_acl_auth;
- ops->set_radius_acl_expire = hostapd_set_radius_acl_expire;
- ops->set_bss_params = hostapd_set_bss_params;
- ops->set_beacon = hostapd_set_beacon;
- ops->vlan_if_add = hostapd_vlan_if_add;
- ops->vlan_if_remove = hostapd_vlan_if_remove;
- ops->set_wds_sta = hostapd_set_wds_sta;
- ops->set_sta_vlan = hostapd_set_sta_vlan;
- ops->get_inact_sec = hostapd_get_inact_sec;
- ops->sta_deauth = hostapd_sta_deauth;
- ops->sta_disassoc = hostapd_sta_disassoc;
- ops->sta_add = hostapd_sta_add;
- ops->sta_remove = hostapd_sta_remove;
- ops->set_countermeasures = hostapd_set_countermeasures;
+ return hapd->driver->add_tspec(hapd->drv_priv, addr, tspec_ie,
+ tspec_ielen);
}
int hostapd_if_add(struct hostapd_data *hapd, enum wpa_driver_if_type type,
const char *ifname, const u8 *addr, void *bss_ctx,
- void **drv_priv, char *force_ifname, u8 *if_addr)
+ void **drv_priv, char *force_ifname, u8 *if_addr,
+ const char *bridge)
{
if (hapd->driver == NULL || hapd->driver->if_add == NULL)
return -1;
return hapd->driver->if_add(hapd->drv_priv, type, ifname, addr,
- bss_ctx, drv_priv, force_ifname, if_addr);
+ bss_ctx, drv_priv, force_ifname, if_addr,
+ bridge);
}
}
-int hostapd_set_cts_protect(struct hostapd_data *hapd, int value)
-{
- if (hapd->driver == NULL || hapd->driver->set_cts_protect == NULL)
- return 0;
- return hapd->driver->set_cts_protect(hapd->drv_priv, value);
-}
-
-
-int hostapd_set_preamble(struct hostapd_data *hapd, int value)
-{
- if (hapd->driver == NULL || hapd->driver->set_preamble == NULL)
- return 0;
- return hapd->driver->set_preamble(hapd->drv_priv, value);
-}
-
-
-int hostapd_set_short_slot_time(struct hostapd_data *hapd, int value)
-{
- if (hapd->driver == NULL || hapd->driver->set_short_slot_time == NULL)
- return 0;
- return hapd->driver->set_short_slot_time(hapd->drv_priv, value);
-}
-
-
int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs,
int cw_min, int cw_max, int burst_time)
{
}
-int hostapd_valid_bss_mask(struct hostapd_data *hapd, const u8 *addr,
- const u8 *mask)
-{
- if (hapd->driver == NULL || hapd->driver->valid_bss_mask == NULL)
- return 1;
- return hapd->driver->valid_bss_mask(hapd->drv_priv, addr, mask);
-}
-
-
struct hostapd_hw_modes *
hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes,
u16 *flags)
}
-int hostapd_set_ht_params(struct hostapd_data *hapd,
- const u8 *ht_capab, size_t ht_capab_len,
- const u8 *ht_oper, size_t ht_oper_len)
-{
- if (hapd->driver == NULL || hapd->driver->set_ht_params == NULL ||
- ht_capab == NULL || ht_oper == NULL)
- return 0;
- return hapd->driver->set_ht_params(hapd->drv_priv,
- ht_capab, ht_capab_len,
- ht_oper, ht_oper_len);
-}
-
-
int hostapd_drv_none(struct hostapd_data *hapd)
{
return hapd->driver && os_strcmp(hapd->driver->name, "none") == 0;
return hapd->driver->get_scan_results2(hapd->drv_priv);
return NULL;
}
+
+
+int hostapd_driver_set_noa(struct hostapd_data *hapd, u8 count, int start,
+ int duration)
+{
+ if (hapd->driver && hapd->driver->set_noa)
+ return hapd->driver->set_noa(hapd->drv_priv, count, start,
+ duration);
+ return -1;
+}
+
+
+int hostapd_drv_set_key(const char *ifname, struct hostapd_data *hapd,
+ enum wpa_alg alg, const u8 *addr,
+ int key_idx, int set_tx,
+ const u8 *seq, size_t seq_len,
+ const u8 *key, size_t key_len)
+{
+ if (hapd->driver == NULL || hapd->driver->set_key == NULL)
+ return 0;
+ return hapd->driver->set_key(ifname, hapd->drv_priv, alg, addr,
+ key_idx, set_tx, seq, seq_len, key,
+ key_len);
+}
+
+
+int hostapd_drv_send_mlme(struct hostapd_data *hapd,
+ const void *msg, size_t len)
+{
+ if (hapd->driver == NULL || hapd->driver->send_mlme == NULL)
+ return 0;
+ return hapd->driver->send_mlme(hapd->drv_priv, msg, len);
+}
+
+
+int hostapd_drv_sta_deauth(struct hostapd_data *hapd,
+ const u8 *addr, int reason)
+{
+ if (hapd->driver == NULL || hapd->driver->sta_deauth == NULL)
+ return 0;
+ return hapd->driver->sta_deauth(hapd->drv_priv, hapd->own_addr, addr,
+ reason);
+}
+
+
+int hostapd_drv_sta_disassoc(struct hostapd_data *hapd,
+ const u8 *addr, int reason)
+{
+ if (hapd->driver == NULL || hapd->driver->sta_disassoc == NULL)
+ return 0;
+ return hapd->driver->sta_disassoc(hapd->drv_priv, hapd->own_addr, addr,
+ reason);
+}
enum wpa_driver_if_type;
struct wpa_bss_params;
struct wpa_driver_scan_params;
+struct ieee80211_ht_capabilities;
-void hostapd_set_driver_ops(struct hostapd_driver_ops *ops);
+u32 hostapd_sta_flags_to_drv(u32 flags);
+int hostapd_build_ap_extra_ies(struct hostapd_data *hapd,
+ struct wpabuf **beacon,
+ struct wpabuf **proberesp,
+ struct wpabuf **assocresp);
+void hostapd_free_ap_extra_ies(struct hostapd_data *hapd, struct wpabuf *beacon,
+ struct wpabuf *proberesp,
+ struct wpabuf *assocresp);
+int hostapd_set_ap_wps_ie(struct hostapd_data *hapd);
+int hostapd_set_authorized(struct hostapd_data *hapd,
+ struct sta_info *sta, int authorized);
+int hostapd_set_sta_flags(struct hostapd_data *hapd, struct sta_info *sta);
+int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd, const char *ifname,
+ int enabled);
+int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname);
+int hostapd_vlan_if_remove(struct hostapd_data *hapd, const char *ifname);
+int hostapd_set_wds_sta(struct hostapd_data *hapd, const u8 *addr, int aid,
+ int val);
+int hostapd_sta_add(struct hostapd_data *hapd,
+ const u8 *addr, u16 aid, u16 capability,
+ const u8 *supp_rates, size_t supp_rates_len,
+ u16 listen_interval,
+ const struct ieee80211_ht_capabilities *ht_capab,
+ u32 flags);
int hostapd_set_privacy(struct hostapd_data *hapd, int enabled);
int hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem,
size_t elem_len);
int hostapd_set_ssid(struct hostapd_data *hapd, const u8 *buf, size_t len);
int hostapd_if_add(struct hostapd_data *hapd, enum wpa_driver_if_type type,
const char *ifname, const u8 *addr, void *bss_ctx,
- void **drv_priv, char *force_ifname, u8 *if_addr);
+ void **drv_priv, char *force_ifname, u8 *if_addr,
+ const char *bridge);
int hostapd_if_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type,
const char *ifname);
int hostapd_set_ieee8021x(struct hostapd_data *hapd,
int hostapd_set_rate_sets(struct hostapd_data *hapd, int *supp_rates,
int *basic_rates, int mode);
int hostapd_set_country(struct hostapd_data *hapd, const char *country);
-int hostapd_set_cts_protect(struct hostapd_data *hapd, int value);
-int hostapd_set_preamble(struct hostapd_data *hapd, int value);
-int hostapd_set_short_slot_time(struct hostapd_data *hapd, int value);
int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs,
int cw_min, int cw_max, int burst_time);
-int hostapd_valid_bss_mask(struct hostapd_data *hapd, const u8 *addr,
- const u8 *mask);
struct hostapd_hw_modes *
hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes,
u16 *flags);
int hostapd_driver_commit(struct hostapd_data *hapd);
-int hostapd_set_ht_params(struct hostapd_data *hapd,
- const u8 *ht_capab, size_t ht_capab_len,
- const u8 *ht_oper, size_t ht_oper_len);
int hostapd_drv_none(struct hostapd_data *hapd);
int hostapd_driver_scan(struct hostapd_data *hapd,
struct wpa_driver_scan_params *params);
struct wpa_scan_results * hostapd_driver_get_scan_results(
struct hostapd_data *hapd);
+int hostapd_driver_set_noa(struct hostapd_data *hapd, u8 count, int start,
+ int duration);
+int hostapd_drv_set_key(const char *ifname,
+ struct hostapd_data *hapd,
+ enum wpa_alg alg, const u8 *addr,
+ int key_idx, int set_tx,
+ const u8 *seq, size_t seq_len,
+ const u8 *key, size_t key_len);
+int hostapd_drv_send_mlme(struct hostapd_data *hapd,
+ const void *msg, size_t len);
+int hostapd_drv_sta_deauth(struct hostapd_data *hapd,
+ const u8 *addr, int reason);
+int hostapd_drv_sta_disassoc(struct hostapd_data *hapd,
+ const u8 *addr, int reason);
+int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr,
+ u16 auth_alg);
+int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr,
+ u16 seq, u16 status, const u8 *ie, size_t len);
+int hostapd_sta_assoc(struct hostapd_data *hapd, const u8 *addr,
+ int reassoc, u16 status, const u8 *ie, size_t len);
+int hostapd_add_tspec(struct hostapd_data *hapd, const u8 *addr,
+ u8 *tspec_ie, size_t tspec_ielen);
+
+
+#include "drivers/driver.h"
+
+static inline int hostapd_drv_set_countermeasures(struct hostapd_data *hapd,
+ int enabled)
+{
+ if (hapd->driver == NULL ||
+ hapd->driver->hapd_set_countermeasures == NULL)
+ return 0;
+ return hapd->driver->hapd_set_countermeasures(hapd->drv_priv, enabled);
+}
+
+static inline int hostapd_drv_set_sta_vlan(const char *ifname,
+ struct hostapd_data *hapd,
+ const u8 *addr, int vlan_id)
+{
+ if (hapd->driver == NULL || hapd->driver->set_sta_vlan == NULL)
+ return 0;
+ return hapd->driver->set_sta_vlan(hapd->drv_priv, addr, ifname,
+ vlan_id);
+}
+
+static inline int hostapd_drv_get_inact_sec(struct hostapd_data *hapd,
+ const u8 *addr)
+{
+ if (hapd->driver == NULL || hapd->driver->get_inact_sec == NULL)
+ return 0;
+ return hapd->driver->get_inact_sec(hapd->drv_priv, addr);
+}
+
+static inline int hostapd_drv_sta_remove(struct hostapd_data *hapd,
+ const u8 *addr)
+{
+ if (hapd->driver == NULL || hapd->driver->sta_remove == NULL)
+ return 0;
+ return hapd->driver->sta_remove(hapd->drv_priv, addr);
+}
+
+static inline int hostapd_drv_hapd_send_eapol(struct hostapd_data *hapd,
+ const u8 *addr, const u8 *data,
+ size_t data_len, int encrypt,
+ u32 flags)
+{
+ if (hapd->driver == NULL || hapd->driver->hapd_send_eapol == NULL)
+ return 0;
+ return hapd->driver->hapd_send_eapol(hapd->drv_priv, addr, data,
+ data_len, encrypt,
+ hapd->own_addr, flags);
+}
+
+static inline int hostapd_drv_read_sta_data(
+ struct hostapd_data *hapd, struct hostap_sta_driver_data *data,
+ const u8 *addr)
+{
+ if (hapd->driver == NULL || hapd->driver->read_sta_data == NULL)
+ return -1;
+ return hapd->driver->read_sta_data(hapd->drv_priv, data, addr);
+}
+
+static inline int hostapd_drv_sta_clear_stats(struct hostapd_data *hapd,
+ const u8 *addr)
+{
+ if (hapd->driver == NULL || hapd->driver->sta_clear_stats == NULL)
+ return 0;
+ return hapd->driver->sta_clear_stats(hapd->drv_priv, addr);
+}
+
+static inline int hostapd_drv_set_ap(struct hostapd_data *hapd,
+ struct wpa_driver_ap_params *params)
+{
+ if (hapd->driver == NULL || hapd->driver->set_ap == NULL)
+ return 0;
+ return hapd->driver->set_ap(hapd->drv_priv, params);
+}
+
+static inline int hostapd_drv_set_radius_acl_auth(struct hostapd_data *hapd,
+ const u8 *mac, int accepted,
+ u32 session_timeout)
+{
+ if (hapd->driver == NULL || hapd->driver->set_radius_acl_auth == NULL)
+ return 0;
+ return hapd->driver->set_radius_acl_auth(hapd->drv_priv, mac, accepted,
+ session_timeout);
+}
+
+static inline int hostapd_drv_set_radius_acl_expire(struct hostapd_data *hapd,
+ const u8 *mac)
+{
+ if (hapd->driver == NULL ||
+ hapd->driver->set_radius_acl_expire == NULL)
+ return 0;
+ return hapd->driver->set_radius_acl_expire(hapd->drv_priv, mac);
+}
+
+static inline int hostapd_drv_set_authmode(struct hostapd_data *hapd,
+ int auth_algs)
+{
+ if (hapd->driver == NULL || hapd->driver->set_authmode == NULL)
+ return 0;
+ return hapd->driver->set_authmode(hapd->drv_priv, auth_algs);
+}
+
+static inline void hostapd_drv_poll_client(struct hostapd_data *hapd,
+ const u8 *own_addr, const u8 *addr,
+ int qos)
+{
+ if (hapd->driver == NULL || hapd->driver->poll_client == NULL)
+ return;
+ hapd->driver->poll_client(hapd->drv_priv, own_addr, addr, qos);
+}
#endif /* AP_DRV_OPS */
struct hostapd_frame_info *fi)
{
struct ap_info *ap;
+ struct os_time now;
int new_ap = 0;
size_t len;
int set_beacon = 0;
ap->ht_support = 0;
ap->num_beacons++;
- time(&ap->last_beacon);
+ os_get_time(&now);
+ ap->last_beacon = now.sec;
if (fi) {
ap->ssi_signal = fi->ssi_signal;
ap->datarate = fi->datarate;
static void ap_list_timer(void *eloop_ctx, void *timeout_ctx)
{
struct hostapd_iface *iface = eloop_ctx;
- time_t now;
+ struct os_time now;
struct ap_info *ap;
int set_beacon = 0;
if (!iface->ap_list)
return;
- time(&now);
+ os_get_time(&now);
while (iface->ap_list) {
ap = iface->ap_list->prev;
if (ap->last_beacon + iface->conf->ap_table_expiration_time >=
- now)
+ now.sec)
break;
ap_free_ap(iface, ap);
int ht_support;
unsigned int num_beacons; /* number of beacon frames received */
- time_t last_beacon;
+ os_time_t last_beacon;
int already_seen; /* whether API call AP-NEW has already fetched
* information about this AP */
srv.get_eap_user = hostapd_radius_get_eap_user;
srv.eap_req_id_text = conf->eap_req_id_text;
srv.eap_req_id_text_len = conf->eap_req_id_text_len;
+ srv.pwd_group = conf->pwd_group;
hapd->radius_srv = radius_server_init(&srv);
if (hapd->radius_srv == NULL) {
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "drivers/driver.h"
+#include "wps/wps_defs.h"
+#include "p2p/p2p.h"
#include "hostapd.h"
#include "ieee802_11.h"
#include "wpa_auth.h"
#include "wmm.h"
#include "ap_config.h"
#include "sta_info.h"
+#include "p2p_hostapd.h"
+#include "ap_drv_ops.h"
#include "beacon.h"
+#ifdef NEED_AP_MLME
+
static u8 ieee802_11_erp_info(struct hostapd_data *hapd)
{
u8 erp = 0;
hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
return 0;
- switch (hapd->iconf->cts_protection_type) {
- case CTS_PROTECTION_FORCE_ENABLED:
- erp |= ERP_INFO_NON_ERP_PRESENT | ERP_INFO_USE_PROTECTION;
- break;
- case CTS_PROTECTION_FORCE_DISABLED:
- erp = 0;
- break;
- case CTS_PROTECTION_AUTOMATIC:
- if (hapd->iface->olbc)
- erp |= ERP_INFO_USE_PROTECTION;
- /* continue */
- case CTS_PROTECTION_AUTOMATIC_NO_OLBC:
- if (hapd->iface->num_sta_non_erp > 0) {
- erp |= ERP_INFO_NON_ERP_PRESENT |
- ERP_INFO_USE_PROTECTION;
- }
- break;
+ if (hapd->iface->olbc)
+ erp |= ERP_INFO_USE_PROTECTION;
+ if (hapd->iface->num_sta_non_erp > 0) {
+ erp |= ERP_INFO_NON_ERP_PRESENT |
+ ERP_INFO_USE_PROTECTION;
}
if (hapd->iface->num_sta_no_short_preamble > 0 ||
hapd->iconf->preamble == LONG_PREAMBLE)
}
-static u8 * hostapd_eid_wpa(struct hostapd_data *hapd, u8 *eid, size_t len,
- struct sta_info *sta)
+static u8 * hostapd_eid_wpa(struct hostapd_data *hapd, u8 *eid, size_t len)
{
const u8 *ie;
size_t ielen;
size_t i;
ie = mgmt->u.probe_req.variable;
+ if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req))
+ return;
ie_len = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req));
for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++)
if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx,
- mgmt->sa, ie, ie_len) > 0)
+ mgmt->sa, mgmt->da, mgmt->bssid,
+ ie, ie_len) > 0)
return;
if (!hapd->iconf->send_probe_response)
return;
}
+#ifdef CONFIG_P2P
+ if (hapd->p2p && elems.wps_ie) {
+ struct wpabuf *wps;
+ wps = ieee802_11_vendor_ie_concat(ie, ie_len, WPS_DEV_OUI_WFA);
+ if (wps && !p2p_group_match_dev_type(hapd->p2p_group, wps)) {
+ wpa_printf(MSG_MSGDUMP, "P2P: Ignore Probe Request "
+ "due to mismatch with Requested Device "
+ "Type");
+ wpabuf_free(wps);
+ return;
+ }
+ wpabuf_free(wps);
+ }
+
+ if (hapd->p2p && elems.p2p) {
+ struct wpabuf *p2p;
+ p2p = ieee802_11_vendor_ie_concat(ie, ie_len, P2P_IE_VENDOR_TYPE);
+ if (p2p && !p2p_group_match_dev_id(hapd->p2p_group, p2p)) {
+ wpa_printf(MSG_MSGDUMP, "P2P: Ignore Probe Request "
+ "due to mismatch with Device ID");
+ wpabuf_free(p2p);
+ return;
+ }
+ wpabuf_free(p2p);
+ }
+#endif /* CONFIG_P2P */
+
if (hapd->conf->ignore_broadcast_ssid && elems.ssid_len == 0) {
wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR " for "
"broadcast SSID ignored", MAC2STR(mgmt->sa));
sta = ap_get_sta(hapd, mgmt->sa);
+#ifdef CONFIG_P2P
+ if ((hapd->conf->p2p & P2P_GROUP_OWNER) &&
+ elems.ssid_len == P2P_WILDCARD_SSID_LEN &&
+ os_memcmp(elems.ssid, P2P_WILDCARD_SSID,
+ P2P_WILDCARD_SSID_LEN) == 0) {
+ /* Process P2P Wildcard SSID like Wildcard SSID */
+ elems.ssid_len = 0;
+ }
+#endif /* CONFIG_P2P */
+
if (elems.ssid_len == 0 ||
(elems.ssid_len == hapd->conf->ssid.ssid_len &&
os_memcmp(elems.ssid, hapd->conf->ssid.ssid, elems.ssid_len) ==
ieee802_11_print_ssid(ssid_txt, elems.ssid,
elems.ssid_len);
wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR
- " for foreign SSID '%s'",
- MAC2STR(mgmt->sa), ssid_txt);
+ " for foreign SSID '%s' (DA " MACSTR ")",
+ MAC2STR(mgmt->sa), ssid_txt,
+ MAC2STR(mgmt->da));
}
return;
}
+#ifdef CONFIG_INTERWORKING
+ if (elems.interworking && elems.interworking_len >= 1) {
+ u8 ant = elems.interworking[0] & 0x0f;
+ if (ant != INTERWORKING_ANT_WILDCARD &&
+ ant != hapd->conf->access_network_type) {
+ wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR
+ " for mismatching ANT %u ignored",
+ MAC2STR(mgmt->sa), ant);
+ return;
+ }
+ }
+
+ if (elems.interworking &&
+ (elems.interworking_len == 7 || elems.interworking_len == 9)) {
+ const u8 *hessid;
+ if (elems.interworking_len == 7)
+ hessid = elems.interworking + 1;
+ else
+ hessid = elems.interworking + 1 + 2;
+ if (!is_broadcast_ether_addr(hessid) &&
+ os_memcmp(hessid, hapd->conf->hessid, ETH_ALEN) != 0) {
+ wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR
+ " for mismatching HESSID " MACSTR
+ " ignored",
+ MAC2STR(mgmt->sa), MAC2STR(hessid));
+ return;
+ }
+ }
+#endif /* CONFIG_INTERWORKING */
+
/* TODO: verify that supp_rates contains at least one matching rate
* with AP configuration */
#define MAX_PROBERESP_LEN 768
if (hapd->wps_probe_resp_ie)
buflen += wpabuf_len(hapd->wps_probe_resp_ie);
#endif /* CONFIG_WPS */
+#ifdef CONFIG_P2P
+ if (hapd->p2p_probe_resp_ie)
+ buflen += wpabuf_len(hapd->p2p_probe_resp_ie);
+#endif /* CONFIG_P2P */
resp = os_zalloc(buflen);
if (resp == NULL)
return;
pos = hostapd_eid_ext_supp_rates(hapd, pos);
/* RSN, MDIE, WPA */
- pos = hostapd_eid_wpa(hapd, pos, epos - pos, sta);
+ pos = hostapd_eid_wpa(hapd, pos, epos - pos);
#ifdef CONFIG_IEEE80211N
pos = hostapd_eid_ht_capabilities(hapd, pos);
pos = hostapd_eid_ht_operation(hapd, pos);
#endif /* CONFIG_IEEE80211N */
+ pos = hostapd_eid_ext_capab(hapd, pos);
+
+ pos = hostapd_eid_time_adv(hapd, pos);
+ pos = hostapd_eid_time_zone(hapd, pos);
+
+ pos = hostapd_eid_interworking(hapd, pos);
+ pos = hostapd_eid_adv_proto(hapd, pos);
+ pos = hostapd_eid_roaming_consortium(hapd, pos);
+
/* Wi-Fi Alliance WMM */
pos = hostapd_eid_wmm(hapd, pos);
}
#endif /* CONFIG_WPS */
- if (hapd->drv.send_mgmt_frame(hapd, resp, pos - (u8 *) resp) < 0)
+#ifdef CONFIG_P2P
+ if ((hapd->conf->p2p & P2P_ENABLED) && elems.p2p &&
+ hapd->p2p_probe_resp_ie) {
+ os_memcpy(pos, wpabuf_head(hapd->p2p_probe_resp_ie),
+ wpabuf_len(hapd->p2p_probe_resp_ie));
+ pos += wpabuf_len(hapd->p2p_probe_resp_ie);
+ }
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_P2P_MANAGER
+ if ((hapd->conf->p2p & (P2P_MANAGE | P2P_ENABLED | P2P_GROUP_OWNER)) ==
+ P2P_MANAGE)
+ pos = hostapd_eid_p2p_manage(hapd, pos);
+#endif /* CONFIG_P2P_MANAGER */
+
+ if (hostapd_drv_send_mlme(hapd, resp, pos - (u8 *) resp) < 0)
perror("handle_probe_req: send");
os_free(resp);
- wpa_printf(MSG_MSGDUMP, "STA " MACSTR " sent probe request for %s "
+ wpa_printf(MSG_EXCESSIVE, "STA " MACSTR " sent probe request for %s "
"SSID", MAC2STR(mgmt->sa),
elems.ssid_len == 0 ? "broadcast" : "our");
}
+#endif /* NEED_AP_MLME */
+
void ieee802_11_set_beacon(struct hostapd_data *hapd)
{
- struct ieee80211_mgmt *head;
- u8 *pos, *tail, *tailpos;
+ struct ieee80211_mgmt *head = NULL;
+ u8 *tail = NULL;
+ size_t head_len = 0, tail_len = 0;
+ struct wpa_driver_ap_params params;
+ struct wpabuf *beacon, *proberesp, *assocresp;
+#ifdef NEED_AP_MLME
u16 capab_info;
- size_t head_len, tail_len;
+ u8 *pos, *tailpos;
+#endif /* NEED_AP_MLME */
+
+ hapd->beacon_set_done = 1;
+
+#ifdef NEED_AP_MLME
#define BEACON_HEAD_BUF_SIZE 256
#define BEACON_TAIL_BUF_SIZE 512
if (hapd->conf->wps_state && hapd->wps_beacon_ie)
tail_len += wpabuf_len(hapd->wps_beacon_ie);
#endif /* CONFIG_WPS */
+#ifdef CONFIG_P2P
+ if (hapd->p2p_beacon_ie)
+ tail_len += wpabuf_len(hapd->p2p_beacon_ie);
+#endif /* CONFIG_P2P */
tailpos = tail = os_malloc(tail_len);
if (head == NULL || tail == NULL) {
wpa_printf(MSG_ERROR, "Failed to set beacon data");
/* RSN, MDIE, WPA */
tailpos = hostapd_eid_wpa(hapd, tailpos, tail + BEACON_TAIL_BUF_SIZE -
- tailpos, NULL);
+ tailpos);
#ifdef CONFIG_IEEE80211N
tailpos = hostapd_eid_ht_capabilities(hapd, tailpos);
tailpos = hostapd_eid_ht_operation(hapd, tailpos);
#endif /* CONFIG_IEEE80211N */
+ tailpos = hostapd_eid_ext_capab(hapd, tailpos);
+
+ /*
+ * TODO: Time Advertisement element should only be included in some
+ * DTIM Beacon frames.
+ */
+ tailpos = hostapd_eid_time_adv(hapd, tailpos);
+
+ tailpos = hostapd_eid_interworking(hapd, tailpos);
+ tailpos = hostapd_eid_adv_proto(hapd, tailpos);
+ tailpos = hostapd_eid_roaming_consortium(hapd, tailpos);
+
/* Wi-Fi Alliance WMM */
tailpos = hostapd_eid_wmm(hapd, tailpos);
}
#endif /* CONFIG_WPS */
+#ifdef CONFIG_P2P
+ if ((hapd->conf->p2p & P2P_ENABLED) && hapd->p2p_beacon_ie) {
+ os_memcpy(tailpos, wpabuf_head(hapd->p2p_beacon_ie),
+ wpabuf_len(hapd->p2p_beacon_ie));
+ tailpos += wpabuf_len(hapd->p2p_beacon_ie);
+ }
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_P2P_MANAGER
+ if ((hapd->conf->p2p & (P2P_MANAGE | P2P_ENABLED | P2P_GROUP_OWNER)) ==
+ P2P_MANAGE)
+ tailpos = hostapd_eid_p2p_manage(hapd, tailpos);
+#endif /* CONFIG_P2P_MANAGER */
+
tail_len = tailpos > tail ? tailpos - tail : 0;
- if (hapd->drv.set_beacon(hapd, (u8 *) head, head_len,
- tail, tail_len, hapd->conf->dtim_period,
- hapd->iconf->beacon_int))
- wpa_printf(MSG_ERROR, "Failed to set beacon head/tail or DTIM "
- "period");
+#endif /* NEED_AP_MLME */
+
+ os_memset(¶ms, 0, sizeof(params));
+ params.head = (u8 *) head;
+ params.head_len = head_len;
+ params.tail = tail;
+ params.tail_len = tail_len;
+ params.dtim_period = hapd->conf->dtim_period;
+ params.beacon_int = hapd->iconf->beacon_int;
+ params.ssid = (u8 *) hapd->conf->ssid.ssid;
+ params.ssid_len = hapd->conf->ssid.ssid_len;
+ params.pairwise_ciphers = hapd->conf->rsn_pairwise ?
+ hapd->conf->rsn_pairwise : hapd->conf->wpa_pairwise;
+ params.group_cipher = hapd->conf->wpa_group;
+ params.key_mgmt_suites = hapd->conf->wpa_key_mgmt;
+ params.auth_algs = hapd->conf->auth_algs;
+ params.wpa_version = hapd->conf->wpa;
+ params.privacy = hapd->conf->ssid.wep.keys_set || hapd->conf->wpa ||
+ (hapd->conf->ieee802_1x &&
+ (hapd->conf->default_wep_key_len ||
+ hapd->conf->individual_wep_key_len));
+ switch (hapd->conf->ignore_broadcast_ssid) {
+ case 0:
+ params.hide_ssid = NO_SSID_HIDING;
+ break;
+ case 1:
+ params.hide_ssid = HIDDEN_SSID_ZERO_LEN;
+ break;
+ case 2:
+ params.hide_ssid = HIDDEN_SSID_ZERO_CONTENTS;
+ break;
+ }
+ hostapd_build_ap_extra_ies(hapd, &beacon, &proberesp, &assocresp);
+ params.beacon_ies = beacon;
+ params.proberesp_ies = proberesp;
+ params.assocresp_ies = assocresp;
+ params.isolate = hapd->conf->isolate;
+#ifdef NEED_AP_MLME
+ params.cts_protect = !!(ieee802_11_erp_info(hapd) &
+ ERP_INFO_USE_PROTECTION);
+ params.preamble = hapd->iface->num_sta_no_short_preamble == 0 &&
+ hapd->iconf->preamble == SHORT_PREAMBLE;
+ if (hapd->iface->current_mode &&
+ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
+ params.short_slot_time =
+ hapd->iface->num_sta_no_short_slot_time > 0 ? 0 : 1;
+ else
+ params.short_slot_time = -1;
+ if (!hapd->iconf->ieee80211n || hapd->conf->disable_11n)
+ params.ht_opmode = -1;
+ else
+ params.ht_opmode = hapd->iface->ht_op_mode;
+#endif /* NEED_AP_MLME */
+ params.interworking = hapd->conf->interworking;
+ if (hapd->conf->interworking &&
+ !is_zero_ether_addr(hapd->conf->hessid))
+ params.hessid = hapd->conf->hessid;
+ params.access_network_type = hapd->conf->access_network_type;
+ if (hostapd_drv_set_ap(hapd, ¶ms))
+ wpa_printf(MSG_ERROR, "Failed to set beacon parameters");
+ hostapd_free_ap_extra_ies(hapd, beacon, proberesp, assocresp);
os_free(tail);
os_free(head);
-
- hapd->drv.set_bss_params(hapd, !!(ieee802_11_erp_info(hapd) &
- ERP_INFO_USE_PROTECTION));
}
void handle_probe_req(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt, size_t len);
-#ifdef NEED_AP_MLME
void ieee802_11_set_beacon(struct hostapd_data *hapd);
void ieee802_11_set_beacons(struct hostapd_iface *iface);
-#else /* NEED_AP_MLME */
-static inline void ieee802_11_set_beacon(struct hostapd_data *hapd)
-{
-}
-
-static inline void ieee802_11_set_beacons(struct hostapd_iface *iface)
-{
-}
-#endif /* NEED_AP_MLME */
#endif /* BEACON_H */
#include "ieee802_11.h"
#include "sta_info.h"
#include "wps_hostapd.h"
+#include "p2p_hostapd.h"
#include "ctrl_iface_ap.h"
buflen - len);
if (res >= 0)
len += res;
+ res = hostapd_p2p_get_mib_sta(hapd, sta, buf + len, buflen - len);
+ if (res >= 0)
+ len += res;
return len;
}
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "common/wpa_ctrl.h"
+#include "crypto/random.h"
+#include "p2p/p2p.h"
+#include "wps/wps.h"
#include "hostapd.h"
#include "ieee802_11.h"
#include "sta_info.h"
#include "wpa_auth.h"
#include "wmm.h"
#include "wps_hostapd.h"
+#include "ap_drv_ops.h"
#include "ap_config.h"
int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
- const u8 *ie, size_t ielen)
+ const u8 *req_ies, size_t req_ies_len, int reassoc)
{
struct sta_info *sta;
int new_assoc, res;
struct ieee802_11_elems elems;
+ const u8 *ie;
+ size_t ielen;
+ u16 reason = WLAN_REASON_UNSPECIFIED;
if (addr == NULL) {
/*
"no address");
return -1;
}
+ random_add_randomness(addr, ETH_ALEN);
hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO, "associated");
- ieee802_11_parse_elems(ie, ielen, &elems, 0);
+ ieee802_11_parse_elems(req_ies, req_ies_len, &elems, 0);
if (elems.wps_ie) {
ie = elems.wps_ie - 2;
ielen = elems.wps_ie_len + 2;
sta = ap_get_sta(hapd, addr);
if (sta) {
accounting_sta_stop(hapd, sta);
+
+ /*
+ * Make sure that the previously registered inactivity timer
+ * will not remove the STA immediately.
+ */
+ sta->timeout_next = STA_NULLFUNC;
} else {
sta = ap_sta_add(hapd, addr);
if (sta == NULL)
return -1;
}
- sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS);
+ sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS | WLAN_STA_WPS2);
+
+#ifdef CONFIG_P2P
+ if (elems.p2p) {
+ wpabuf_free(sta->p2p_ie);
+ sta->p2p_ie = ieee802_11_vendor_ie_concat(req_ies, req_ies_len,
+ P2P_IE_VENDOR_TYPE);
+ }
+#endif /* CONFIG_P2P */
if (hapd->conf->wpa) {
if (ie == NULL || ielen == 0) {
+#ifdef CONFIG_WPS
if (hapd->conf->wps_state) {
wpa_printf(MSG_DEBUG, "STA did not include "
"WPA/RSN IE in (Re)Association "
sta->flags |= WLAN_STA_MAYBE_WPS;
goto skip_wpa_check;
}
+#endif /* CONFIG_WPS */
wpa_printf(MSG_DEBUG, "No WPA/RSN IE from STA");
return -1;
}
+#ifdef CONFIG_WPS
if (hapd->conf->wps_state && ie[0] == 0xdd && ie[1] >= 4 &&
os_memcmp(ie + 2, "\x00\x50\xf2\x04", 4) == 0) {
+ struct wpabuf *wps;
sta->flags |= WLAN_STA_WPS;
+ wps = ieee802_11_vendor_ie_concat(ie, ielen,
+ WPS_IE_VENDOR_TYPE);
+ if (wps) {
+ if (wps_is_20(wps)) {
+ wpa_printf(MSG_DEBUG, "WPS: STA "
+ "supports WPS 2.0");
+ sta->flags |= WLAN_STA_WPS2;
+ }
+ wpabuf_free(wps);
+ }
goto skip_wpa_check;
}
+#endif /* CONFIG_WPS */
if (sta->wpa_sm == NULL)
sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
ie, ielen, NULL, 0);
if (res != WPA_IE_OK) {
- int resp;
wpa_printf(MSG_DEBUG, "WPA/RSN information element "
"rejected? (res %u)", res);
wpa_hexdump(MSG_DEBUG, "IE", ie, ielen);
if (res == WPA_INVALID_GROUP)
- resp = WLAN_REASON_GROUP_CIPHER_NOT_VALID;
+ reason = WLAN_REASON_GROUP_CIPHER_NOT_VALID;
else if (res == WPA_INVALID_PAIRWISE)
- resp = WLAN_REASON_PAIRWISE_CIPHER_NOT_VALID;
+ reason = WLAN_REASON_PAIRWISE_CIPHER_NOT_VALID;
else if (res == WPA_INVALID_AKMP)
- resp = WLAN_REASON_AKMP_NOT_VALID;
+ reason = WLAN_REASON_AKMP_NOT_VALID;
#ifdef CONFIG_IEEE80211W
else if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION)
- resp = WLAN_REASON_INVALID_IE;
+ reason = WLAN_REASON_INVALID_IE;
else if (res == WPA_INVALID_MGMT_GROUP_CIPHER)
- resp = WLAN_REASON_GROUP_CIPHER_NOT_VALID;
+ reason = WLAN_REASON_GROUP_CIPHER_NOT_VALID;
#endif /* CONFIG_IEEE80211W */
else
- resp = WLAN_REASON_INVALID_IE;
- hapd->drv.sta_disassoc(hapd, sta->addr, resp);
- ap_free_sta(hapd, sta);
- return -1;
+ reason = WLAN_REASON_INVALID_IE;
+ goto fail;
}
} else if (hapd->conf->wps_state) {
- if (ie && ielen > 4 && ie[0] == 0xdd && ie[1] >= 4 &&
- os_memcmp(ie + 2, "\x00\x50\xf2\x04", 4) == 0) {
+#ifdef CONFIG_WPS
+ struct wpabuf *wps;
+ if (req_ies)
+ wps = ieee802_11_vendor_ie_concat(req_ies, req_ies_len,
+ WPS_IE_VENDOR_TYPE);
+ else
+ wps = NULL;
+#ifdef CONFIG_WPS_STRICT
+ if (wps && wps_validate_assoc_req(wps) < 0) {
+ reason = WLAN_REASON_INVALID_IE;
+ wpabuf_free(wps);
+ goto fail;
+ }
+#endif /* CONFIG_WPS_STRICT */
+ if (wps) {
sta->flags |= WLAN_STA_WPS;
+ if (wps_is_20(wps)) {
+ wpa_printf(MSG_DEBUG, "WPS: STA supports "
+ "WPS 2.0");
+ sta->flags |= WLAN_STA_WPS2;
+ }
} else
sta->flags |= WLAN_STA_MAYBE_WPS;
+ wpabuf_free(wps);
+#endif /* CONFIG_WPS */
}
+#ifdef CONFIG_WPS
skip_wpa_check:
+#endif /* CONFIG_WPS */
new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0;
sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC;
ieee802_1x_notify_port_enabled(sta->eapol_sm, 1);
+#ifdef CONFIG_P2P
+ if (req_ies) {
+ p2p_group_notif_assoc(hapd->p2p_group, sta->addr,
+ req_ies, req_ies_len);
+ }
+#endif /* CONFIG_P2P */
+
return 0;
+
+fail:
+ hostapd_drv_sta_disassoc(hapd, sta->addr, reason);
+ ap_free_sta(hapd, sta);
+ return -1;
}
{
struct sta_info *sta;
+ if (addr == NULL) {
+ /*
+ * This could potentially happen with unexpected event from the
+ * driver wrapper. This was seen at least in one case where the
+ * driver ended up reporting a station mode event while hostapd
+ * was running, so better make sure we stop processing such an
+ * event here.
+ */
+ wpa_printf(MSG_DEBUG, "hostapd_notif_disassoc: Skip event "
+ "with no address");
+ return;
+ }
+
hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO, "disassociated");
return;
}
+ ap_sta_set_authorized(hapd, sta, 0);
sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
- wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED MACSTR,
- MAC2STR(sta->addr));
wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC);
sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST;
ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
}
-#ifdef HOSTAPD
+void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr)
+{
+ struct sta_info *sta = ap_get_sta(hapd, addr);
-#ifdef NEED_AP_MLME
+ if (!sta || !hapd->conf->disassoc_low_ack)
+ return;
+
+ hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "disconnected due to excessive "
+ "missing ACKs");
+ hostapd_drv_sta_disassoc(hapd, addr, WLAN_REASON_DISASSOC_LOW_ACK);
+ if (sta)
+ ap_sta_disassociate(hapd, sta, WLAN_REASON_DISASSOC_LOW_ACK);
+}
-static const u8 * get_hdr_bssid(const struct ieee80211_hdr *hdr, size_t len)
+
+int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da,
+ const u8 *bssid, const u8 *ie, size_t ie_len)
{
- u16 fc, type, stype;
+ size_t i;
+ int ret = 0;
- /*
- * PS-Poll frames are 16 bytes. All other frames are
- * 24 bytes or longer.
- */
- if (len < 16)
- return NULL;
+ if (sa == NULL || ie == NULL)
+ return -1;
- fc = le_to_host16(hdr->frame_control);
- type = WLAN_FC_GET_TYPE(fc);
- stype = WLAN_FC_GET_STYPE(fc);
-
- switch (type) {
- case WLAN_FC_TYPE_DATA:
- if (len < 24)
- return NULL;
- switch (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) {
- case WLAN_FC_FROMDS | WLAN_FC_TODS:
- case WLAN_FC_TODS:
- return hdr->addr1;
- case WLAN_FC_FROMDS:
- return hdr->addr2;
- default:
- return NULL;
+ random_add_randomness(sa, ETH_ALEN);
+ for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++) {
+ if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx,
+ sa, da, bssid, ie, ie_len) > 0) {
+ ret = 1;
+ break;
}
- case WLAN_FC_TYPE_CTRL:
- if (stype != WLAN_FC_STYPE_PSPOLL)
- return NULL;
- return hdr->addr1;
- case WLAN_FC_TYPE_MGMT:
- return hdr->addr3;
- default:
- return NULL;
}
+ return ret;
}
+#ifdef HOSTAPD
+
+#ifdef NEED_AP_MLME
+
#define HAPD_BROADCAST ((struct hostapd_data *) -1)
static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface,
static void hostapd_rx_from_unknown_sta(struct hostapd_data *hapd,
- const u8 *frame, size_t len)
+ const u8 *bssid, const u8 *addr,
+ int wds)
{
- const struct ieee80211_hdr *hdr = (const struct ieee80211_hdr *) frame;
- u16 fc = le_to_host16(hdr->frame_control);
- hapd = get_hapd_bssid(hapd->iface, get_hdr_bssid(hdr, len));
+ hapd = get_hapd_bssid(hapd->iface, bssid);
if (hapd == NULL || hapd == HAPD_BROADCAST)
return;
- ieee802_11_rx_from_unknown(hapd, hdr->addr2,
- (fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) ==
- (WLAN_FC_TODS | WLAN_FC_FROMDS));
+ ieee802_11_rx_from_unknown(hapd, addr, wds);
}
rx_mgmt->frame_len, &fi);
} else
ieee802_11_mgmt(hapd, rx_mgmt->frame, rx_mgmt->frame_len, &fi);
+
+ random_add_randomness(&fi, sizeof(fi));
+}
+
+
+static void hostapd_rx_action(struct hostapd_data *hapd,
+ struct rx_action *rx_action)
+{
+ struct rx_mgmt rx_mgmt;
+ u8 *buf;
+ struct ieee80211_hdr *hdr;
+
+ wpa_printf(MSG_DEBUG, "EVENT_RX_ACTION DA=" MACSTR " SA=" MACSTR
+ " BSSID=" MACSTR " category=%u",
+ MAC2STR(rx_action->da), MAC2STR(rx_action->sa),
+ MAC2STR(rx_action->bssid), rx_action->category);
+ wpa_hexdump(MSG_MSGDUMP, "Received action frame contents",
+ rx_action->data, rx_action->len);
+
+ buf = os_zalloc(24 + 1 + rx_action->len);
+ if (buf == NULL)
+ return;
+ hdr = (struct ieee80211_hdr *) buf;
+ hdr->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_ACTION);
+ if (rx_action->category == WLAN_ACTION_SA_QUERY) {
+ /*
+ * Assume frame was protected; it would have been dropped if
+ * not.
+ */
+ hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP);
+ }
+ os_memcpy(hdr->addr1, rx_action->da, ETH_ALEN);
+ os_memcpy(hdr->addr2, rx_action->sa, ETH_ALEN);
+ os_memcpy(hdr->addr3, rx_action->bssid, ETH_ALEN);
+ buf[24] = rx_action->category;
+ os_memcpy(buf + 24 + 1, rx_action->data, rx_action->len);
+ os_memset(&rx_mgmt, 0, sizeof(rx_mgmt));
+ rx_mgmt.frame = buf;
+ rx_mgmt.frame_len = 24 + 1 + rx_action->len;
+ hostapd_mgmt_rx(hapd, &rx_mgmt);
+ os_free(buf);
}
#endif /* NEED_AP_MLME */
-static int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa,
- const u8 *ie, size_t ie_len)
-{
- size_t i;
- int ret = 0;
-
- for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++) {
- if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx,
- sa, ie, ie_len) > 0) {
- ret = 1;
- break;
- }
- }
- return ret;
-}
-
-
static int hostapd_event_new_sta(struct hostapd_data *hapd, const u8 *addr)
{
struct sta_info *sta = ap_get_sta(hapd, addr);
break;
#endif /* CONFIG_IEEE80211R */
case EVENT_WPS_BUTTON_PUSHED:
- hostapd_wps_button_pushed(hapd);
+ hostapd_wps_button_pushed(hapd, NULL);
break;
#ifdef NEED_AP_MLME
case EVENT_TX_STATUS:
break;
}
break;
+ case EVENT_DRIVER_CLIENT_POLL_OK:
+ hostapd_client_poll_ok(hapd, data->client_poll.addr);
+ break;
case EVENT_RX_FROM_UNKNOWN:
- hostapd_rx_from_unknown_sta(hapd, data->rx_from_unknown.frame,
- data->rx_from_unknown.len);
+ hostapd_rx_from_unknown_sta(hapd, data->rx_from_unknown.bssid,
+ data->rx_from_unknown.addr,
+ data->rx_from_unknown.wds);
break;
case EVENT_RX_MGMT:
hostapd_mgmt_rx(hapd, &data->rx_mgmt);
break;
#endif /* NEED_AP_MLME */
case EVENT_RX_PROBE_REQ:
+ if (data->rx_probe_req.sa == NULL ||
+ data->rx_probe_req.ie == NULL)
+ break;
hostapd_probe_req_rx(hapd, data->rx_probe_req.sa,
+ data->rx_probe_req.da,
+ data->rx_probe_req.bssid,
data->rx_probe_req.ie,
data->rx_probe_req.ie_len);
break;
case EVENT_ASSOC:
hostapd_notif_assoc(hapd, data->assoc_info.addr,
data->assoc_info.req_ies,
- data->assoc_info.req_ies_len);
+ data->assoc_info.req_ies_len,
+ data->assoc_info.reassoc);
break;
case EVENT_DISASSOC:
if (data)
if (data)
hostapd_notif_disassoc(hapd, data->deauth_info.addr);
break;
+ case EVENT_STATION_LOW_ACK:
+ if (!data)
+ break;
+ hostapd_event_sta_low_ack(hapd, data->low_ack.addr);
+ break;
+#ifdef NEED_AP_MLME
+ case EVENT_RX_ACTION:
+ if (data->rx_action.da == NULL || data->rx_action.sa == NULL ||
+ data->rx_action.bssid == NULL)
+ break;
+ hostapd_rx_action(hapd, &data->rx_action);
+ break;
+#endif /* NEED_AP_MLME */
default:
wpa_printf(MSG_DEBUG, "Unknown event %d", event);
break;
#include "wpa_auth_glue.h"
#include "ap_drv_ops.h"
#include "ap_config.h"
+#include "p2p_hostapd.h"
-static int hostapd_flush_old_stations(struct hostapd_data *hapd);
+static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason);
static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd);
+static int hostapd_broadcast_wep_clear(struct hostapd_data *hapd);
extern int wpa_debug_level;
-int hostapd_reload_config(struct hostapd_iface *iface)
+static void hostapd_reload_bss(struct hostapd_data *hapd)
{
- struct hostapd_data *hapd = iface->bss[0];
- struct hostapd_config *newconf, *oldconf;
- size_t j;
-
- if (iface->config_read_cb == NULL)
- return -1;
- newconf = iface->config_read_cb(iface->config_fname);
- if (newconf == NULL)
- return -1;
-
- /*
- * Deauthenticate all stations since the new configuration may not
- * allow them to use the BSS anymore.
- */
- for (j = 0; j < iface->num_bss; j++)
- hostapd_flush_old_stations(iface->bss[j]);
-
#ifndef CONFIG_NO_RADIUS
- /* TODO: update dynamic data based on changed configuration
- * items (e.g., open/close sockets, etc.) */
- radius_client_flush(hapd->radius, 0);
+ radius_client_reconfig(hapd->radius, hapd->conf->radius);
#endif /* CONFIG_NO_RADIUS */
- oldconf = hapd->iconf;
- hapd->iconf = newconf;
- hapd->conf = &newconf->bss[0];
- iface->conf = newconf;
-
if (hostapd_setup_wpa_psk(hapd->conf)) {
wpa_printf(MSG_ERROR, "Failed to re-configure WPA PSK "
"after reloading configuration");
}
if (hapd->conf->ieee802_1x || hapd->conf->wpa)
- hapd->drv.set_drv_ieee8021x(hapd, hapd->conf->iface, 1);
+ hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 1);
else
- hapd->drv.set_drv_ieee8021x(hapd, hapd->conf->iface, 0);
+ hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 0);
- if (hapd->conf->wpa && hapd->wpa_auth == NULL)
+ if (hapd->conf->wpa && hapd->wpa_auth == NULL) {
hostapd_setup_wpa(hapd);
- else if (hapd->conf->wpa) {
+ if (hapd->wpa_auth)
+ wpa_init_keys(hapd->wpa_auth);
+ } else if (hapd->conf->wpa) {
const u8 *wpa_ie;
size_t wpa_ie_len;
hostapd_reconfig_wpa(hapd);
wpa_printf(MSG_ERROR, "Could not set SSID for kernel driver");
/* try to continue */
}
+ wpa_printf(MSG_DEBUG, "Reconfigured interface %s", hapd->conf->iface);
+}
+
+
+int hostapd_reload_config(struct hostapd_iface *iface)
+{
+ struct hostapd_data *hapd = iface->bss[0];
+ struct hostapd_config *newconf, *oldconf;
+ size_t j;
+
+ if (iface->config_read_cb == NULL)
+ return -1;
+ newconf = iface->config_read_cb(iface->config_fname);
+ if (newconf == NULL)
+ return -1;
+
+ /*
+ * Deauthenticate all stations since the new configuration may not
+ * allow them to use the BSS anymore.
+ */
+ for (j = 0; j < iface->num_bss; j++) {
+ hostapd_flush_old_stations(iface->bss[j],
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ hostapd_broadcast_wep_clear(iface->bss[j]);
+
+#ifndef CONFIG_NO_RADIUS
+ /* TODO: update dynamic data based on changed configuration
+ * items (e.g., open/close sockets, etc.) */
+ radius_client_flush(iface->bss[j]->radius, 0);
+#endif /* CONFIG_NO_RADIUS */
+ }
+
+ oldconf = hapd->iconf;
+ iface->conf = newconf;
+
+ for (j = 0; j < iface->num_bss; j++) {
+ hapd = iface->bss[j];
+ hapd->iconf = newconf;
+ hapd->conf = &newconf->bss[j];
+ hostapd_reload_bss(hapd);
+ }
hostapd_config_free(oldconf);
- wpa_printf(MSG_DEBUG, "Reconfigured interface %s", hapd->conf->iface);
return 0;
}
int i;
for (i = 0; i < NUM_WEP_KEYS; i++) {
- if (hapd->drv.set_key(ifname, hapd, WPA_ALG_NONE, NULL, i,
- i == 0 ? 1 : 0, NULL, 0, NULL, 0)) {
+ if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_NONE, NULL, i,
+ 0, NULL, 0, NULL, 0)) {
wpa_printf(MSG_DEBUG, "Failed to clear default "
"encryption keys (ifname=%s keyidx=%d)",
ifname, i);
#ifdef CONFIG_IEEE80211W
if (hapd->conf->ieee80211w) {
for (i = NUM_WEP_KEYS; i < NUM_WEP_KEYS + 2; i++) {
- if (hapd->drv.set_key(ifname, hapd, WPA_ALG_NONE, NULL,
- i, i == 0 ? 1 : 0, NULL, 0,
- NULL, 0)) {
+ if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_NONE,
+ NULL, i, 0, NULL,
+ 0, NULL, 0)) {
wpa_printf(MSG_DEBUG, "Failed to clear "
"default mgmt encryption keys "
"(ifname=%s keyidx=%d)", ifname, i);
idx = ssid->wep.idx;
if (ssid->wep.default_len &&
- hapd->drv.set_key(hapd->conf->iface,
- hapd, WPA_ALG_WEP, NULL, idx,
- idx == ssid->wep.idx,
- NULL, 0, ssid->wep.key[idx],
- ssid->wep.len[idx])) {
+ hostapd_drv_set_key(hapd->conf->iface,
+ hapd, WPA_ALG_WEP, broadcast_ether_addr, idx,
+ 1, NULL, 0, ssid->wep.key[idx],
+ ssid->wep.len[idx])) {
wpa_printf(MSG_WARNING, "Could not set WEP encryption.");
errors++;
}
continue;
idx = key->idx;
- if (hapd->drv.set_key(ifname, hapd, WPA_ALG_WEP, NULL,
- idx, idx == key->idx, NULL, 0,
- key->key[idx], key->len[idx])) {
+ if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_WEP,
+ broadcast_ether_addr, idx, 1,
+ NULL, 0, key->key[idx],
+ key->len[idx])) {
wpa_printf(MSG_WARNING, "Could not set "
"dynamic VLAN WEP encryption.");
errors++;
os_free(hapd->probereq_cb);
hapd->probereq_cb = NULL;
+
+#ifdef CONFIG_P2P
+ wpabuf_free(hapd->p2p_beacon_ie);
+ hapd->p2p_beacon_ie = NULL;
+ wpabuf_free(hapd->p2p_probe_resp_ie);
+ hapd->p2p_probe_resp_ie = NULL;
+#endif /* CONFIG_P2P */
+
+ wpabuf_free(hapd->time_adv);
}
return 0;
}
+ /*
+ * When IEEE 802.1X is not enabled, the driver may need to know how to
+ * set authentication algorithms for static WEP.
+ */
+ hostapd_drv_set_authmode(hapd, hapd->conf->auth_algs);
+
for (i = 0; i < 4; i++) {
if (hapd->conf->ssid.wep.key[i] &&
- hapd->drv.set_key(iface, hapd, WPA_ALG_WEP, NULL, i,
- i == hapd->conf->ssid.wep.idx, NULL, 0,
- hapd->conf->ssid.wep.key[i],
- hapd->conf->ssid.wep.len[i])) {
+ hostapd_drv_set_key(iface, hapd, WPA_ALG_WEP, NULL, i,
+ i == hapd->conf->ssid.wep.idx, NULL, 0,
+ hapd->conf->ssid.wep.key[i],
+ hapd->conf->ssid.wep.len[i])) {
wpa_printf(MSG_WARNING, "Could not set WEP "
"encryption.");
return -1;
}
-static int hostapd_flush_old_stations(struct hostapd_data *hapd)
+static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason)
{
int ret = 0;
+ u8 addr[ETH_ALEN];
if (hostapd_drv_none(hapd) || hapd->drv_priv == NULL)
return 0;
ret = -1;
}
wpa_printf(MSG_DEBUG, "Deauthenticate all stations");
-
- /* New Prism2.5/3 STA firmware versions seem to have issues with this
- * broadcast deauth frame. This gets the firmware in odd state where
- * nothing works correctly, so let's skip sending this for the hostap
- * driver. */
- if (hapd->driver && os_strcmp(hapd->driver->name, "hostap") != 0) {
- u8 addr[ETH_ALEN];
- os_memset(addr, 0xff, ETH_ALEN);
- hapd->drv.sta_deauth(hapd, addr,
- WLAN_REASON_PREV_AUTH_NOT_VALID);
- }
+ os_memset(addr, 0xff, ETH_ALEN);
+ hostapd_drv_sta_deauth(hapd, addr, reason);
+ hostapd_free_stas(hapd);
return ret;
}
u8 mask[ETH_ALEN] = { 0 };
struct hostapd_data *hapd = iface->bss[0];
unsigned int i = iface->conf->num_bss, bits = 0, j;
- int res;
int auto_addr = 0;
if (hostapd_drv_none(hapd))
wpa_printf(MSG_DEBUG, "BSS count %lu, BSSID mask " MACSTR " (%d bits)",
(unsigned long) iface->conf->num_bss, MAC2STR(mask), bits);
- res = hostapd_valid_bss_mask(hapd, hapd->own_addr, mask);
- if (res == 0)
- return 0;
-
- if (res < 0) {
- wpa_printf(MSG_ERROR, "Driver did not accept BSSID mask "
- MACSTR " for start address " MACSTR ".",
- MAC2STR(mask), MAC2STR(hapd->own_addr));
- return -1;
- }
-
if (!auto_addr)
return 0;
hapd->interface_added = 1;
if (hostapd_if_add(hapd->iface->bss[0], WPA_IF_AP_BSS,
hapd->conf->iface, hapd->own_addr, hapd,
- &hapd->drv_priv, force_ifname, if_addr)) {
+ &hapd->drv_priv, force_ifname, if_addr,
+ hapd->conf->bridge[0] ? hapd->conf->bridge :
+ NULL)) {
wpa_printf(MSG_ERROR, "Failed to add BSS (BSSID="
MACSTR ")", MAC2STR(hapd->own_addr));
return -1;
}
}
- hostapd_flush_old_stations(hapd);
+ if (conf->wmm_enabled < 0)
+ conf->wmm_enabled = hapd->iconf->ieee80211n;
+
+ hostapd_flush_old_stations(hapd, WLAN_REASON_PREV_AUTH_NOT_VALID);
hostapd_set_privacy(hapd, 0);
hostapd_broadcast_wep_clear(hapd);
ieee802_11_set_beacon(hapd);
+ if (hapd->wpa_auth && wpa_init_keys(hapd->wpa_auth) < 0)
+ return -1;
+
+ if (hapd->driver && hapd->driver->set_operstate)
+ hapd->driver->set_operstate(hapd->drv_priv, 1);
+
return 0;
}
for (i = 0; i < NUM_TX_QUEUES; i++) {
p = &iface->conf->tx_queue[i];
- if (!p->configured)
- continue;
-
if (hostapd_set_tx_queue_params(hapd, i, p->aifs, p->cwmin,
p->cwmax, p->burst)) {
wpa_printf(MSG_DEBUG, "Failed to set TX queue "
}
}
+ if (iface->current_mode) {
+ if (hostapd_prepare_rates(hapd, iface->current_mode)) {
+ wpa_printf(MSG_ERROR, "Failed to prepare rates "
+ "table.");
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_WARNING,
+ "Failed to prepare rates table.");
+ return -1;
+ }
+ }
+
if (hapd->iconf->rts_threshold > -1 &&
hostapd_set_rts(hapd, hapd->iconf->rts_threshold)) {
wpa_printf(MSG_ERROR, "Could not set RTS threshold for "
return -1;
}
+ /*
+ * WPS UPnP module can be initialized only when the "upnp_iface" is up.
+ * If "interface" and "upnp_iface" are the same (e.g., non-bridge
+ * mode), the interface is up only after driver_commit, so initialize
+ * WPS after driver_commit.
+ */
+ for (j = 0; j < iface->num_bss; j++) {
+ if (hostapd_init_wps_complete(iface->bss[j]))
+ return -1;
+ }
+
+ if (hapd->setup_complete_cb)
+ hapd->setup_complete_cb(hapd->setup_complete_cb_ctx);
+
wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
iface->bss[0]->conf->iface);
if (hapd == NULL)
return NULL;
- hostapd_set_driver_ops(&hapd->drv);
hapd->new_assoc_sta_cb = hostapd_new_assoc_sta;
hapd->iconf = conf;
hapd->conf = bss;
for (j = 0; j < iface->num_bss; j++) {
struct hostapd_data *hapd = iface->bss[j];
hostapd_free_stas(hapd);
- hostapd_flush_old_stations(hapd);
+ hostapd_flush_old_stations(hapd, WLAN_REASON_DEAUTH_LEAVING);
hostapd_cleanup(hapd);
}
}
int reassoc)
{
if (hapd->tkip_countermeasures) {
- hapd->drv.sta_deauth(hapd, sta->addr,
- WLAN_REASON_MICHAEL_MIC_FAILURE);
+ hostapd_drv_sta_deauth(hapd, sta->addr,
+ WLAN_REASON_MICHAEL_MIC_FAILURE);
return;
}
if (hapd->conf->ieee802_11f)
iapp_new_station(hapd->iapp, sta);
+#ifdef CONFIG_P2P
+ if (sta->p2p_ie == NULL && !sta->no_p2p_set) {
+ sta->no_p2p_set = 1;
+ hapd->num_sta_no_p2p++;
+ if (hapd->num_sta_no_p2p == 1)
+ hostapd_p2p_non_p2p_sta_connected(hapd);
+ }
+#endif /* CONFIG_P2P */
+
/* Start accounting here, if IEEE 802.1X and WPA are not used.
* IEEE 802.1X/WPA code will start accounting after the station has
* been authorized. */
struct hostap_sta_driver_data;
struct ieee80211_ht_capabilities;
struct full_dynamic_vlan;
+enum wps_event;
+union wps_event_data;
struct hostapd_probereq_cb {
- int (*cb)(void *ctx, const u8 *sa, const u8 *ie, size_t ie_len);
+ int (*cb)(void *ctx, const u8 *sa, const u8 *da, const u8 *bssid,
+ const u8 *ie, size_t ie_len);
void *ctx;
};
};
-struct hostapd_driver_ops {
- int (*set_ap_wps_ie)(struct hostapd_data *hapd);
- int (*send_mgmt_frame)(struct hostapd_data *hapd, const void *msg,
- size_t len);
- int (*send_eapol)(struct hostapd_data *hapd, const u8 *addr,
- const u8 *data, size_t data_len, int encrypt);
- int (*set_authorized)(struct hostapd_data *hapd, struct sta_info *sta,
- int authorized);
- int (*set_key)(const char *ifname, struct hostapd_data *hapd,
- enum wpa_alg alg, const u8 *addr, int key_idx,
- int set_tx, const u8 *seq, size_t seq_len,
- const u8 *key, size_t key_len);
- int (*read_sta_data)(struct hostapd_data *hapd,
- struct hostap_sta_driver_data *data,
- const u8 *addr);
- int (*sta_clear_stats)(struct hostapd_data *hapd, const u8 *addr);
- int (*set_sta_flags)(struct hostapd_data *hapd, struct sta_info *sta);
- int (*set_drv_ieee8021x)(struct hostapd_data *hapd, const char *ifname,
- int enabled);
- int (*set_radius_acl_auth)(struct hostapd_data *hapd,
- const u8 *mac, int accepted,
- u32 session_timeout);
- int (*set_radius_acl_expire)(struct hostapd_data *hapd,
- const u8 *mac);
- int (*set_bss_params)(struct hostapd_data *hapd, int use_protection);
- int (*set_beacon)(struct hostapd_data *hapd,
- const u8 *head, size_t head_len,
- const u8 *tail, size_t tail_len, int dtim_period,
- int beacon_int);
- int (*vlan_if_add)(struct hostapd_data *hapd, const char *ifname);
- int (*vlan_if_remove)(struct hostapd_data *hapd, const char *ifname);
- int (*set_wds_sta)(struct hostapd_data *hapd, const u8 *addr, int aid,
- int val);
- int (*set_sta_vlan)(const char *ifname, struct hostapd_data *hapd,
- const u8 *addr, int vlan_id);
- int (*get_inact_sec)(struct hostapd_data *hapd, const u8 *addr);
- int (*sta_deauth)(struct hostapd_data *hapd, const u8 *addr,
- int reason);
- int (*sta_disassoc)(struct hostapd_data *hapd, const u8 *addr,
- int reason);
- int (*sta_add)(struct hostapd_data *hapd,
- const u8 *addr, u16 aid, u16 capability,
- const u8 *supp_rates, size_t supp_rates_len,
- u16 listen_interval,
- const struct ieee80211_ht_capabilities *ht_capab);
- int (*sta_remove)(struct hostapd_data *hapd, const u8 *addr);
- int (*set_countermeasures)(struct hostapd_data *hapd, int enabled);
-};
-
/**
* struct hostapd_data - hostapd per-BSS data structure
*/
const struct wpa_driver_ops *driver;
void *drv_priv;
- struct hostapd_driver_ops drv;
void (*new_assoc_sta_cb)(struct hostapd_data *hapd,
struct sta_info *sta, int reassoc);
void *msg_ctx; /* ctx for wpa_msg() calls */
+ void *msg_ctx_parent; /* parent interface ctx for wpa_msg() calls */
struct radius_client_data *radius;
u32 acct_session_id_hi, acct_session_id_lo;
int parameter_set_count;
+ /* Time Advertisement */
+ u8 time_update_counter;
+ struct wpabuf *time_adv;
+
#ifdef CONFIG_FULL_DYNAMIC_VLAN
struct full_dynamic_vlan *full_dynamic_vlan;
#endif /* CONFIG_FULL_DYNAMIC_VLAN */
struct l2_packet_data *l2;
struct wps_context *wps;
+ int beacon_set_done;
struct wpabuf *wps_beacon_ie;
struct wpabuf *wps_probe_resp_ie;
#ifdef CONFIG_WPS
unsigned int ap_pin_failures;
+ unsigned int ap_pin_failures_consecutive;
struct upnp_wps_device_sm *wps_upnp;
unsigned int ap_pin_lockout_time;
#endif /* CONFIG_WPS */
int freq);
void *public_action_cb_ctx;
+ int (*vendor_action_cb)(void *ctx, const u8 *buf, size_t len,
+ int freq);
+ void *vendor_action_cb_ctx;
+
void (*wps_reg_success_cb)(void *ctx, const u8 *mac_addr,
const u8 *uuid_e);
void *wps_reg_success_cb_ctx;
+
+ void (*wps_event_cb)(void *ctx, enum wps_event event,
+ union wps_event_data *data);
+ void *wps_event_cb_ctx;
+
+ void (*sta_authorized_cb)(void *ctx, const u8 *mac_addr,
+ int authorized, const u8 *p2p_dev_addr);
+ void *sta_authorized_cb_ctx;
+
+ void (*setup_complete_cb)(void *ctx);
+ void *setup_complete_cb_ctx;
+
+#ifdef CONFIG_P2P
+ struct p2p_data *p2p;
+ struct p2p_group *p2p_group;
+ struct wpabuf *p2p_beacon_ie;
+ struct wpabuf *p2p_probe_resp_ie;
+
+ /* Number of non-P2P association stations */
+ int num_sta_no_p2p;
+
+ /* Periodic NoA (used only when no non-P2P clients in the group) */
+ int noa_enabled;
+ int noa_start;
+ int noa_duration;
+#endif /* CONFIG_P2P */
};
struct ap_info *ap_hash[STA_HASH_SIZE];
struct ap_info *ap_iter_list;
+ unsigned int drv_flags;
struct hostapd_hw_modes *hw_features;
int num_hw_features;
struct hostapd_hw_modes *current_mode;
/* utils.c */
int hostapd_register_probereq_cb(struct hostapd_data *hapd,
int (*cb)(void *ctx, const u8 *sa,
+ const u8 *da, const u8 *bssid,
const u8 *ie, size_t ie_len),
void *ctx);
void hostapd_prune_associations(struct hostapd_data *hapd, const u8 *addr);
/* drv_callbacks.c (TODO: move to somewhere else?) */
int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
- const u8 *ie, size_t ielen);
+ const u8 *ie, size_t ielen, int reassoc);
void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr);
+void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr);
+int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da,
+ const u8 *bssid, const u8 *ie, size_t ie_len);
#endif /* HOSTAPD_H */
}
-static int hostapd_prepare_rates(struct hostapd_data *hapd,
- struct hostapd_hw_modes *mode)
+int hostapd_prepare_rates(struct hostapd_data *hapd,
+ struct hostapd_hw_modes *mode)
{
int i, num_basic_rates = 0;
int basic_rates_a[] = { 60, 120, 240, -1 };
hapd->iface->num_rates++;
}
- if (hapd->iface->num_rates == 0 || num_basic_rates == 0) {
+ if ((hapd->iface->num_rates == 0 || num_basic_rates == 0) &&
+ (!hapd->iconf->ieee80211n || !hapd->iconf->require_ht)) {
wpa_printf(MSG_ERROR, "No rates remaining in supported/basic "
"rate sets (%d,%d).",
hapd->iface->num_rates, num_basic_rates);
oper = (struct ieee80211_ht_operation *) elems.ht_operation;
*pri_chan = oper->control_chan;
if (oper->ht_param & HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH) {
- if (oper->ht_param &
- HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
+ int sec = oper->ht_param &
+ HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK;
+ if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
*sec_chan = *pri_chan + 4;
- else if (oper->ht_param &
- HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
+ else if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
*sec_chan = *pri_chan - 4;
}
}
}
-static void wpa_scan_results_free(struct wpa_scan_results *res)
-{
- size_t i;
-
- if (res == NULL)
- return;
-
- for (i = 0; i < res->num; i++)
- os_free(res->res[i]);
- os_free(res->res);
- os_free(res);
-}
-
-
static void ieee80211n_check_scan(struct hostapd_iface *iface)
{
struct wpa_scan_results *scan_res;
int oper40;
+ int res;
/* Check list of neighboring BSSes (from scan) to see whether 40 MHz is
* allowed per IEEE 802.11n/D7.0, 11.14.3.2 */
iface->conf->ht_capab &= ~HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
}
- hostapd_setup_interface_complete(iface, 0);
+ res = ieee80211n_allowed_ht40_channel_pair(iface);
+ hostapd_setup_interface_complete(iface, !res);
}
u16 hw = iface->current_mode->ht_capab;
u16 conf = iface->conf->ht_capab;
- if (!iface->conf->ieee80211n)
- return 1;
-
if ((conf & HT_CAP_INFO_LDPC_CODING_CAP) &&
!(hw & HT_CAP_INFO_LDPC_CODING_CAP)) {
wpa_printf(MSG_ERROR, "Driver does not support configured "
{
#ifdef CONFIG_IEEE80211N
int ret;
+ if (!iface->conf->ieee80211n)
+ return 0;
+ if (!ieee80211n_supported_ht_capab(iface))
+ return -1;
ret = ieee80211n_check_40mhz(iface);
if (ret)
return ret;
if (!ieee80211n_allowed_ht40_channel_pair(iface))
return -1;
- if (!ieee80211n_supported_ht_capab(iface))
- return -1;
#endif /* CONFIG_IEEE80211N */
return 0;
/**
* hostapd_select_hw_mode - Select the hardware mode
* @iface: Pointer to interface data.
- * Returns: 0 on success, -1 on failure
+ * Returns: 0 on success, < 0 on failure
*
* Sets up the hardware mode, channel, rates, and passive scanning
* based on the configuration.
hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_WARNING,
"Hardware does not support configured mode "
- "(%d)", (int) iface->conf->hw_mode);
- return -1;
+ "(%d) (hw_mode in hostapd.conf)",
+ (int) iface->conf->hw_mode);
+ return -2;
}
ok = 0;
for (j = 0; j < iface->current_mode->num_channels; j++) {
struct hostapd_channel_data *chan =
&iface->current_mode->channels[j];
- if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
- (chan->chan == iface->conf->channel)) {
- ok = 1;
- break;
+ if (chan->chan == iface->conf->channel) {
+ if (chan->flag & HOSTAPD_CHAN_DISABLED) {
+ wpa_printf(MSG_ERROR,
+ "channel [%i] (%i) is disabled for "
+ "use in AP mode, flags: 0x%x",
+ j, chan->chan, chan->flag);
+ } else {
+ ok = 1;
+ break;
+ }
+ }
+ }
+ if (ok && iface->conf->secondary_channel) {
+ int sec_ok = 0;
+ int sec_chan = iface->conf->channel +
+ iface->conf->secondary_channel * 4;
+ for (j = 0; j < iface->current_mode->num_channels; j++) {
+ struct hostapd_channel_data *chan =
+ &iface->current_mode->channels[j];
+ if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
+ (chan->chan == sec_chan)) {
+ sec_ok = 1;
+ break;
+ }
+ }
+ if (!sec_ok) {
+ hostapd_logger(iface->bss[0], NULL,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_WARNING,
+ "Configured HT40 secondary channel "
+ "(%d) not found from the channel list "
+ "of current mode (%d) %s",
+ sec_chan, iface->current_mode->mode,
+ hostapd_hw_mode_txt(
+ iface->current_mode->mode));
+ ok = 0;
}
}
if (iface->conf->channel == 0) {
* the channel automatically */
wpa_printf(MSG_ERROR, "Channel not configured "
"(hw_mode/channel in hostapd.conf)");
- return -1;
+ return -3;
}
if (ok == 0 && iface->conf->channel != 0) {
hostapd_logger(iface->bss[0], NULL,
hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_WARNING,
"Hardware does not support configured channel");
- return -1;
- }
-
- if (hostapd_prepare_rates(iface->bss[0], iface->current_mode)) {
- wpa_printf(MSG_ERROR, "Failed to prepare rates table.");
- hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211,
- HOSTAPD_LEVEL_WARNING,
- "Failed to prepare rates table.");
- return -1;
+ return -4;
}
return 0;
int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan);
int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq);
int hostapd_check_ht_capab(struct hostapd_iface *iface);
+int hostapd_prepare_rates(struct hostapd_data *hapd,
+ struct hostapd_hw_modes *mode);
#else /* NEED_AP_MLME */
static inline void
hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
static inline int hostapd_select_hw_mode(struct hostapd_iface *iface)
{
- return -1;
+ return -100;
}
static inline const char * hostapd_hw_mode_txt(int mode)
return 0;
}
+static inline int hostapd_prepare_rates(struct hostapd_data *hapd,
+ struct hostapd_hw_modes *mode)
+{
+ return 0;
+}
+
#endif /* NEED_AP_MLME */
#endif /* HW_FEATURES_H */
/*
* hostapd / IEEE 802.11 Management
- * Copyright (c) 2002-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2011, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
#include "common/wpa_ctrl.h"
#include "radius/radius.h"
#include "radius/radius_client.h"
+#include "p2p/p2p.h"
#include "wps/wps.h"
#include "hostapd.h"
#include "beacon.h"
#include "accounting.h"
#include "ap_config.h"
#include "ap_mlme.h"
+#include "p2p_hostapd.h"
+#include "ap_drv_ops.h"
#include "ieee802_11.h"
*pos++ = WLAN_EID_SUPP_RATES;
num = hapd->iface->num_rates;
+ if (hapd->iconf->ieee80211n && hapd->iconf->require_ht)
+ num++;
if (num > 8) {
/* rest of the rates are encoded in Extended supported
* rates element */
pos++;
}
+ if (hapd->iconf->ieee80211n && hapd->iconf->require_ht &&
+ hapd->iface->num_rates < 8)
+ *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HT_PHY;
+
return pos;
}
return eid;
num = hapd->iface->num_rates;
+ if (hapd->iconf->ieee80211n && hapd->iconf->require_ht)
+ num++;
if (num <= 8)
return eid;
num -= 8;
pos++;
}
+ if (hapd->iconf->ieee80211n && hapd->iconf->require_ht &&
+ hapd->iface->num_rates >= 8)
+ *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HT_PHY;
+
return pos;
}
}
-#ifdef CONFIG_IEEE80211W
-static u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd,
- struct sta_info *sta, u8 *eid)
-{
- u8 *pos = eid;
- u32 timeout, tu;
- struct os_time now, passed;
-
- *pos++ = WLAN_EID_TIMEOUT_INTERVAL;
- *pos++ = 5;
- *pos++ = WLAN_TIMEOUT_ASSOC_COMEBACK;
- os_get_time(&now);
- os_time_sub(&now, &sta->sa_query_start, &passed);
- tu = (passed.sec * 1000000 + passed.usec) / 1024;
- if (hapd->conf->assoc_sa_query_max_timeout > tu)
- timeout = hapd->conf->assoc_sa_query_max_timeout - tu;
- else
- timeout = 0;
- if (timeout < hapd->conf->assoc_sa_query_max_timeout)
- timeout++; /* add some extra time for local timers */
- WPA_PUT_LE32(pos, timeout);
- pos += 4;
-
- return pos;
-}
-#endif /* CONFIG_IEEE80211W */
-
-
void ieee802_11_print_ssid(char *buf, const u8 *ssid, u8 len)
{
int i;
if (!sta->challenge) {
/* Generate a pseudo-random challenge */
u8 key[8];
- time_t now;
+ struct os_time now;
int r;
sta->challenge = os_zalloc(WLAN_AUTH_CHALLENGE_LEN);
if (sta->challenge == NULL)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
- now = time(NULL);
- r = random();
- os_memcpy(key, &now, 4);
+ os_get_time(&now);
+ r = os_random();
+ os_memcpy(key, &now.sec, 4);
os_memcpy(key + 4, &r, 4);
rc4_skip(key, sizeof(key), 0,
sta->challenge, WLAN_AUTH_CHALLENGE_LEN);
" auth_alg=%d auth_transaction=%d resp=%d (IE len=%lu)",
MAC2STR(dst), auth_alg, auth_transaction,
resp, (unsigned long) ies_len);
- if (hapd->drv.send_mgmt_frame(hapd, reply, rlen) < 0)
+ if (hostapd_drv_send_mlme(hapd, reply, rlen) < 0)
perror("send_auth_reply: send");
os_free(buf);
{
sta->flags &= ~WLAN_STA_WMM;
if (wmm_ie && hapd->conf->wmm_enabled) {
- if (hostapd_eid_wmm_valid(hapd, wmm_ie, wmm_ie_len))
+ if (!hostapd_eid_wmm_valid(hapd, wmm_ie, wmm_ie_len))
hostapd_logger(hapd, sta->addr,
HOSTAPD_MODULE_WPA,
HOSTAPD_LEVEL_DEBUG,
if (resp != WLAN_STATUS_SUCCESS)
return resp;
#ifdef CONFIG_IEEE80211N
- resp = copy_sta_ht_capab(sta, elems.ht_capabilities,
+ resp = copy_sta_ht_capab(hapd, sta, elems.ht_capabilities,
elems.ht_capabilities_len);
if (resp != WLAN_STATUS_SUCCESS)
return resp;
+ if (hapd->iconf->ieee80211n && hapd->iconf->require_ht &&
+ !(sta->flags & WLAN_STA_HT)) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "Station does not support "
+ "mandatory HT PHY - reject association");
+ return WLAN_STATUS_ASSOC_DENIED_NO_HT;
+ }
#endif /* CONFIG_IEEE80211N */
if ((hapd->conf->wpa & WPA_PROTO_RSN) && elems.rsn_ie) {
}
#ifdef CONFIG_WPS
- sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS);
+ sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS | WLAN_STA_WPS2);
if (hapd->conf->wps_state && elems.wps_ie) {
wpa_printf(MSG_DEBUG, "STA included WPS IE in (Re)Association "
"Request - assume WPS is used");
wpabuf_free(sta->wps_ie);
sta->wps_ie = ieee802_11_vendor_ie_concat(ies, ies_len,
WPS_IE_VENDOR_TYPE);
+ if (sta->wps_ie && wps_is_20(sta->wps_ie)) {
+ wpa_printf(MSG_DEBUG, "WPS: STA supports WPS 2.0");
+ sta->flags |= WLAN_STA_WPS2;
+ }
wpa_ie = NULL;
wpa_ie_len = 0;
+ if (sta->wps_ie && wps_validate_assoc_req(sta->wps_ie) < 0) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid WPS IE in "
+ "(Re)Association Request - reject");
+ return WLAN_STATUS_INVALID_IE;
+ }
} else if (hapd->conf->wps_state && wpa_ie == NULL) {
wpa_printf(MSG_DEBUG, "STA did not include WPA/RSN IE in "
"(Re)Association Request - possible WPS use");
} else
wpa_auth_sta_no_wpa(sta->wpa_sm);
+#ifdef CONFIG_P2P
+ if (elems.p2p) {
+ wpabuf_free(sta->p2p_ie);
+ sta->p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len,
+ P2P_IE_VENDOR_TYPE);
+
+ } else {
+ wpabuf_free(sta->p2p_ie);
+ sta->p2p_ie = NULL;
+ }
+
+ p2p_group_notif_assoc(hapd->p2p_group, sta->addr, ies, ies_len);
+#endif /* CONFIG_P2P */
+
return WLAN_STATUS_SUCCESS;
}
send_len = IEEE80211_HDRLEN + sizeof(reply.u.deauth);
reply.u.deauth.reason_code = host_to_le16(reason_code);
- if (hapd->drv.send_mgmt_frame(hapd, &reply, send_len) < 0)
+ if (hostapd_drv_send_mlme(hapd, &reply, send_len) < 0)
wpa_printf(MSG_INFO, "Failed to send deauth: %s",
strerror(errno));
}
p = hostapd_eid_ht_operation(hapd, p);
#endif /* CONFIG_IEEE80211N */
+ p = hostapd_eid_ext_capab(hapd, p);
+
if (sta->flags & WLAN_STA_WMM)
p = hostapd_eid_wmm(hapd, p);
#ifdef CONFIG_WPS
- if (sta->flags & WLAN_STA_WPS) {
+ if ((sta->flags & WLAN_STA_WPS) ||
+ ((sta->flags & WLAN_STA_MAYBE_WPS) && hapd->conf->wpa)) {
struct wpabuf *wps = wps_build_assoc_resp_ie();
if (wps) {
os_memcpy(p, wpabuf_head(wps), wpabuf_len(wps));
}
#endif /* CONFIG_WPS */
+#ifdef CONFIG_P2P
+ if (sta->p2p_ie) {
+ struct wpabuf *p2p_resp_ie;
+ enum p2p_status_code status;
+ switch (status_code) {
+ case WLAN_STATUS_SUCCESS:
+ status = P2P_SC_SUCCESS;
+ break;
+ case WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA:
+ status = P2P_SC_FAIL_LIMIT_REACHED;
+ break;
+ default:
+ status = P2P_SC_FAIL_INVALID_PARAMS;
+ break;
+ }
+ p2p_resp_ie = p2p_group_assoc_resp_ie(hapd->p2p_group, status);
+ if (p2p_resp_ie) {
+ os_memcpy(p, wpabuf_head(p2p_resp_ie),
+ wpabuf_len(p2p_resp_ie));
+ p += wpabuf_len(p2p_resp_ie);
+ wpabuf_free(p2p_resp_ie);
+ }
+ }
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_P2P_MANAGER
+ if (hapd->conf->p2p & P2P_MANAGE)
+ p = hostapd_eid_p2p_manage(hapd, p);
+#endif /* CONFIG_P2P_MANAGER */
+
send_len += p - reply->u.assoc_resp.variable;
- if (hapd->drv.send_mgmt_frame(hapd, reply, send_len) < 0)
+ if (hostapd_drv_send_mlme(hapd, reply, send_len) < 0)
wpa_printf(MSG_INFO, "Failed to send assoc resp: %s",
strerror(errno));
}
"association OK (aid %d)", sta->aid);
/* Station will be marked associated, after it acknowledges AssocResp
*/
+ sta->flags |= WLAN_STA_ASSOC_REQ_OK;
#ifdef CONFIG_IEEE80211W
if ((sta->flags & WLAN_STA_MFP) && sta->sa_query_timed_out) {
return;
}
- sta->flags &= ~WLAN_STA_ASSOC;
- wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED MACSTR,
- MAC2STR(sta->addr));
+ ap_sta_set_authorized(hapd, sta, 0);
+ sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK);
wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC);
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO, "disassociated");
* authenticated. */
accounting_sta_stop(hapd, sta);
ieee802_1x_free_station(sta);
- hapd->drv.sta_remove(hapd, sta->addr);
+ hostapd_drv_sta_remove(hapd, sta->addr);
if (sta->timeout_next == STA_NULLFUNC ||
sta->timeout_next == STA_DISASSOC) {
struct sta_info *sta;
if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.deauth)) {
- printf("handle_deauth - too short payload (len=%lu)\n",
- (unsigned long) len);
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG, "handle_deauth - too short "
+ "payload (len=%lu)", (unsigned long) len);
return;
}
- wpa_printf(MSG_DEBUG, "deauthentication: STA=" MACSTR
- " reason_code=%d",
- MAC2STR(mgmt->sa),
- le_to_host16(mgmt->u.deauth.reason_code));
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG, "deauthentication: STA=" MACSTR
+ " reason_code=%d",
+ MAC2STR(mgmt->sa), le_to_host16(mgmt->u.deauth.reason_code));
sta = ap_get_sta(hapd, mgmt->sa);
if (sta == NULL) {
- printf("Station " MACSTR " trying to deauthenticate, but it "
- "is not authenticated.\n", MAC2STR(mgmt->sa));
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Station " MACSTR " trying "
+ "to deauthenticate, but it is not authenticated",
+ MAC2STR(mgmt->sa));
return;
}
- sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
- wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED MACSTR,
- MAC2STR(sta->addr));
+ ap_sta_set_authorized(hapd, sta, 0);
+ sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC |
+ WLAN_STA_ASSOC_REQ_OK);
wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH);
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG, "deauthenticated");
#ifdef CONFIG_IEEE80211W
-/* MLME-SAQuery.request */
-void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
- const u8 *addr, const u8 *trans_id)
-{
- struct ieee80211_mgmt mgmt;
- u8 *end;
-
- wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Request to "
- MACSTR, MAC2STR(addr));
- wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
- trans_id, WLAN_SA_QUERY_TR_ID_LEN);
-
- os_memset(&mgmt, 0, sizeof(mgmt));
- mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
- WLAN_FC_STYPE_ACTION);
- os_memcpy(mgmt.da, addr, ETH_ALEN);
- os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN);
- os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN);
- mgmt.u.action.category = WLAN_ACTION_SA_QUERY;
- mgmt.u.action.u.sa_query_req.action = WLAN_SA_QUERY_REQUEST;
- os_memcpy(mgmt.u.action.u.sa_query_req.trans_id, trans_id,
- WLAN_SA_QUERY_TR_ID_LEN);
- end = mgmt.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN;
- if (hapd->drv.send_mgmt_frame(hapd, &mgmt, end - (u8 *) &mgmt) < 0)
- perror("ieee802_11_send_sa_query_req: send");
-}
-
-
-static void hostapd_sa_query_request(struct hostapd_data *hapd,
- const struct ieee80211_mgmt *mgmt)
-{
- struct sta_info *sta;
- struct ieee80211_mgmt resp;
- u8 *end;
-
- wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Request from "
- MACSTR, MAC2STR(mgmt->sa));
- wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
- mgmt->u.action.u.sa_query_resp.trans_id,
- WLAN_SA_QUERY_TR_ID_LEN);
-
- sta = ap_get_sta(hapd, mgmt->sa);
- if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) {
- wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignore SA Query Request "
- "from unassociated STA " MACSTR, MAC2STR(mgmt->sa));
- return;
- }
-
- wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Response to "
- MACSTR, MAC2STR(mgmt->sa));
-
- os_memset(&resp, 0, sizeof(resp));
- resp.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
- WLAN_FC_STYPE_ACTION);
- os_memcpy(resp.da, mgmt->sa, ETH_ALEN);
- os_memcpy(resp.sa, hapd->own_addr, ETH_ALEN);
- os_memcpy(resp.bssid, hapd->own_addr, ETH_ALEN);
- resp.u.action.category = WLAN_ACTION_SA_QUERY;
- resp.u.action.u.sa_query_req.action = WLAN_SA_QUERY_RESPONSE;
- os_memcpy(resp.u.action.u.sa_query_req.trans_id,
- mgmt->u.action.u.sa_query_req.trans_id,
- WLAN_SA_QUERY_TR_ID_LEN);
- end = resp.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN;
- if (hapd->drv.send_mgmt_frame(hapd, &resp, end - (u8 *) &resp) < 0)
- perror("hostapd_sa_query_request: send");
-}
-
-
static void hostapd_sa_query_action(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt,
size_t len)
{
- struct sta_info *sta;
const u8 *end;
- int i;
end = mgmt->u.action.u.sa_query_resp.trans_id +
WLAN_SA_QUERY_TR_ID_LEN;
return;
}
- if (mgmt->u.action.u.sa_query_resp.action == WLAN_SA_QUERY_REQUEST) {
- hostapd_sa_query_request(hapd, mgmt);
- return;
- }
-
- if (mgmt->u.action.u.sa_query_resp.action != WLAN_SA_QUERY_RESPONSE) {
- wpa_printf(MSG_DEBUG, "IEEE 802.11: Unexpected SA Query "
- "Action %d", mgmt->u.action.u.sa_query_resp.action);
- return;
- }
-
- wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Response from "
- MACSTR, MAC2STR(mgmt->sa));
- wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
- mgmt->u.action.u.sa_query_resp.trans_id,
- WLAN_SA_QUERY_TR_ID_LEN);
-
- /* MLME-SAQuery.confirm */
-
- sta = ap_get_sta(hapd, mgmt->sa);
- if (sta == NULL || sta->sa_query_trans_id == NULL) {
- wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching STA with "
- "pending SA Query request found");
- return;
- }
-
- for (i = 0; i < sta->sa_query_count; i++) {
- if (os_memcmp(sta->sa_query_trans_id +
- i * WLAN_SA_QUERY_TR_ID_LEN,
- mgmt->u.action.u.sa_query_resp.trans_id,
- WLAN_SA_QUERY_TR_ID_LEN) == 0)
- break;
- }
-
- if (i >= sta->sa_query_count) {
- wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching SA Query "
- "transaction identifier found");
- return;
- }
-
- hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
- HOSTAPD_LEVEL_DEBUG,
- "Reply to pending SA Query received");
- ap_sta_stop_sa_query(hapd, sta);
+ ieee802_11_sa_query_action(hapd, mgmt->sa,
+ mgmt->u.action.u.sa_query_resp.action,
+ mgmt->u.action.u.sa_query_resp.trans_id);
}
static void handle_action(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt, size_t len)
{
+#if defined(CONFIG_IEEE80211W) || defined(CONFIG_IEEE80211R)
struct sta_info *sta;
+ sta = ap_get_sta(hapd, mgmt->sa);
+#endif /* CONFIG_IEEE80211W || CONFIG_IEEE80211R */
if (len < IEEE80211_HDRLEN + 1) {
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
return;
}
- sta = ap_get_sta(hapd, mgmt->sa);
#ifdef CONFIG_IEEE80211W
if (sta && (sta->flags & WLAN_STA_MFP) &&
!(mgmt->frame_control & host_to_le16(WLAN_FC_ISWEP) &&
return;
}
break;
+ case WLAN_ACTION_VENDOR_SPECIFIC:
+ if (hapd->vendor_action_cb) {
+ if (hapd->vendor_action_cb(hapd->vendor_action_cb_ctx,
+ (u8 *) mgmt, len,
+ hapd->iface->freq) == 0)
+ return;
+ }
+ break;
}
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN);
resp->u.action.category |= 0x80;
- hapd->drv.send_mgmt_frame(hapd, resp, len);
+ hostapd_drv_send_mlme(hapd, resp, len);
os_free(resp);
}
}
int broadcast;
u16 fc, stype;
+ if (len < 24)
+ return;
+
mgmt = (struct ieee80211_mgmt *) buf;
fc = le_to_host16(mgmt->frame_control);
stype = WLAN_FC_GET_STYPE(fc);
mgmt->bssid[4] == 0xff && mgmt->bssid[5] == 0xff;
if (!broadcast &&
+#ifdef CONFIG_P2P
+ /* Invitation responses can be sent with the peer MAC as BSSID */
+ !((hapd->conf->p2p & P2P_GROUP_OWNER) &&
+ stype == WLAN_FC_STYPE_ACTION) &&
+#endif /* CONFIG_P2P */
os_memcmp(mgmt->bssid, hapd->own_addr, ETH_ALEN) != 0) {
printf("MGMT: BSSID=" MACSTR " not our address\n",
MAC2STR(mgmt->bssid));
handle_disassoc(hapd, mgmt, len);
break;
case WLAN_FC_STYPE_DEAUTH:
- wpa_printf(MSG_DEBUG, "mgmt::deauth");
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG, "mgmt::deauth");
handle_deauth(hapd, mgmt, len);
break;
case WLAN_FC_STYPE_ACTION:
* Open, static WEP, or FT protocol; no separate authorization
* step.
*/
- sta->flags |= WLAN_STA_AUTHORIZED;
- wpa_msg(hapd->msg_ctx, MSG_INFO,
- AP_STA_CONNECTED MACSTR, MAC2STR(sta->addr));
+ ap_sta_set_authorized(hapd, sta, 1);
}
if (reassoc)
* cleared and configuration gets updated in case of reassociation back
* to the same AP.
*/
- hapd->drv.sta_remove(hapd, sta->addr);
+ hostapd_drv_sta_remove(hapd, sta->addr);
#ifdef CONFIG_IEEE80211N
if (sta->flags & WLAN_STA_HT)
hostapd_get_ht_capab(hapd, sta->ht_capabilities, &ht_cap);
#endif /* CONFIG_IEEE80211N */
- if (hapd->drv.sta_add(hapd, sta->addr, sta->aid, sta->capability,
- sta->supported_rates, sta->supported_rates_len,
- sta->listen_interval,
- sta->flags & WLAN_STA_HT ? &ht_cap : NULL)) {
+ if (hostapd_sta_add(hapd, sta->addr, sta->aid, sta->capability,
+ sta->supported_rates, sta->supported_rates_len,
+ sta->listen_interval,
+ sta->flags & WLAN_STA_HT ? &ht_cap : NULL,
+ sta->flags)) {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_NOTICE,
"Could not add STA to kernel driver");
+
+ ap_sta_disconnect(hapd, sta, sta->addr,
+ WLAN_REASON_DISASSOC_AP_BUSY);
+
+ goto fail;
}
+ if (sta->flags & WLAN_STA_WDS)
+ hostapd_set_wds_sta(hapd, sta->addr, sta->aid, 1);
+
if (sta->eapol_sm == NULL) {
/*
* This STA does not use RADIUS server for EAP authentication,
goto fail;
}
- hapd->drv.set_sta_flags(hapd, sta);
+ hostapd_set_sta_flags(hapd, sta);
if (sta->auth_alg == WLAN_AUTH_FT)
wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT);
}
+static void handle_deauth_cb(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len, int ok)
+{
+ struct sta_info *sta;
+ if (mgmt->da[0] & 0x01)
+ return;
+ sta = ap_get_sta(hapd, mgmt->da);
+ if (!sta) {
+ wpa_printf(MSG_DEBUG, "handle_deauth_cb: STA " MACSTR
+ " not found", MAC2STR(mgmt->da));
+ return;
+ }
+ if (ok)
+ wpa_printf(MSG_DEBUG, "STA " MACSTR " acknowledged deauth",
+ MAC2STR(sta->addr));
+ else
+ wpa_printf(MSG_DEBUG, "STA " MACSTR " did not acknowledge "
+ "deauth", MAC2STR(sta->addr));
+
+ ap_sta_deauth_cb(hapd, sta);
+}
+
+
+static void handle_disassoc_cb(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len, int ok)
+{
+ struct sta_info *sta;
+ if (mgmt->da[0] & 0x01)
+ return;
+ sta = ap_get_sta(hapd, mgmt->da);
+ if (!sta) {
+ wpa_printf(MSG_DEBUG, "handle_disassoc_cb: STA " MACSTR
+ " not found", MAC2STR(mgmt->da));
+ return;
+ }
+ if (ok)
+ wpa_printf(MSG_DEBUG, "STA " MACSTR " acknowledged disassoc",
+ MAC2STR(sta->addr));
+ else
+ wpa_printf(MSG_DEBUG, "STA " MACSTR " did not acknowledge "
+ "disassoc", MAC2STR(sta->addr));
+
+ ap_sta_disassoc_cb(hapd, sta);
+}
+
+
/**
* ieee802_11_mgmt_cb - Process management frame TX status callback
* @hapd: hostapd BSS data structure (the BSS from which the management frame
handle_assoc_cb(hapd, mgmt, len, 1, ok);
break;
case WLAN_FC_STYPE_PROBE_RESP:
- wpa_printf(MSG_DEBUG, "mgmt::proberesp cb");
+ wpa_printf(MSG_EXCESSIVE, "mgmt::proberesp cb");
break;
case WLAN_FC_STYPE_DEAUTH:
- /* ignore */
+ wpa_printf(MSG_DEBUG, "mgmt::deauth cb");
+ handle_deauth_cb(hapd, mgmt, len, ok);
+ break;
+ case WLAN_FC_STYPE_DISASSOC:
+ wpa_printf(MSG_DEBUG, "mgmt::disassoc cb");
+ handle_disassoc_cb(hapd, mgmt, len, ok);
break;
case WLAN_FC_STYPE_ACTION:
wpa_printf(MSG_DEBUG, "mgmt::action cb");
break;
}
}
- if (sta == NULL)
+ if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC))
return;
if (sta->flags & WLAN_STA_PENDING_POLL) {
wpa_printf(MSG_DEBUG, "STA " MACSTR " %s pending "
}
+void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr)
+{
+ struct sta_info *sta;
+ struct hostapd_iface *iface = hapd->iface;
+
+ sta = ap_get_sta(hapd, addr);
+ if (sta == NULL && iface->num_bss > 1) {
+ size_t j;
+ for (j = 0; j < iface->num_bss; j++) {
+ hapd = iface->bss[j];
+ sta = ap_get_sta(hapd, addr);
+ if (sta)
+ break;
+ }
+ }
+ if (sta == NULL)
+ return;
+ if (!(sta->flags & WLAN_STA_PENDING_POLL))
+ return;
+
+ wpa_printf(MSG_DEBUG, "STA " MACSTR " ACKed pending "
+ "activity poll", MAC2STR(sta->addr));
+ sta->flags &= ~WLAN_STA_PENDING_POLL;
+}
+
+
void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
int wds)
{
"STA " MACSTR " (aid %u)",
MAC2STR(sta->addr), sta->aid);
sta->flags |= WLAN_STA_WDS;
- hapd->drv.set_wds_sta(hapd, sta->addr, sta->aid, 1);
+ hostapd_set_wds_sta(hapd, sta->addr, sta->aid, 1);
}
return;
}
wpa_printf(MSG_DEBUG, "Data/PS-poll frame from not associated STA "
MACSTR, MAC2STR(src));
+ if (src[0] & 0x01) {
+ /* Broadcast bit set in SA?! Ignore the frame silently. */
+ return;
+ }
+
+ if (sta && (sta->flags & WLAN_STA_ASSOC_REQ_OK)) {
+ wpa_printf(MSG_DEBUG, "Association Response to the STA has "
+ "already been sent, but no TX status yet known - "
+ "ignore Class 3 frame issue with " MACSTR,
+ MAC2STR(src));
+ return;
+ }
+
if (sta && (sta->flags & WLAN_STA_AUTH))
- hapd->drv.sta_disassoc(
+ hostapd_drv_sta_disassoc(
hapd, src,
WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA);
else
- hapd->drv.sta_deauth(
+ hostapd_drv_sta_deauth(
hapd, src,
WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA);
}
#endif /* NEED_AP_MLME */
u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta,
int probe);
+u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid);
void hostapd_get_ht_capab(struct hostapd_data *hapd,
struct ieee80211_ht_capabilities *ht_cap,
struct ieee80211_ht_capabilities *neg_ht_cap);
-u16 copy_sta_ht_capab(struct sta_info *sta, const u8 *ht_capab,
- size_t ht_capab_len);
+u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *ht_capab, size_t ht_capab_len);
void update_ht_state(struct hostapd_data *hapd, struct sta_info *sta);
void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
const u8 *buf, size_t len, int ack);
void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
int wds);
+u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd,
+ struct sta_info *sta, u8 *eid);
+void ieee802_11_sa_query_action(struct hostapd_data *hapd,
+ const u8 *sa, const u8 action_type,
+ const u8 *trans_id);
+u8 * hostapd_eid_interworking(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_adv_proto(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_roaming_consortium(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_time_adv(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_time_zone(struct hostapd_data *hapd, u8 *eid);
+int hostapd_update_time_adv(struct hostapd_data *hapd);
+void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr);
#endif /* IEEE802_11_H */
#include "radius/radius_client.h"
#include "hostapd.h"
#include "ap_config.h"
+#include "ap_drv_ops.h"
#include "ieee802_11.h"
#include "ieee802_11_auth.h"
struct hostapd_cached_radius_acl {
- time_t timestamp;
+ os_time_t timestamp;
macaddr addr;
int accepted; /* HOSTAPD_ACL_* */
struct hostapd_cached_radius_acl *next;
struct hostapd_acl_query_data {
- time_t timestamp;
+ os_time_t timestamp;
u8 radius_id;
macaddr addr;
u8 *auth_msg; /* IEEE 802.11 authentication frame from station */
u32 *acct_interim_interval, int *vlan_id)
{
struct hostapd_cached_radius_acl *entry;
- time_t now;
+ struct os_time now;
- time(&now);
+ os_get_time(&now);
entry = hapd->acl_cache;
while (entry) {
if (os_memcmp(entry->addr, addr, ETH_ALEN) == 0) {
- if (now - entry->timestamp > RADIUS_ACL_TIMEOUT)
+ if (now.sec - entry->timestamp > RADIUS_ACL_TIMEOUT)
return -1; /* entry has expired */
if (entry->accepted == HOSTAPD_ACL_ACCEPT_TIMEOUT)
if (session_timeout)
goto fail;
}
- radius_client_send(hapd->radius, msg, RADIUS_AUTH, addr);
+ if (radius_client_send(hapd->radius, msg, RADIUS_AUTH, addr) < 0)
+ goto fail;
return 0;
fail:
return HOSTAPD_ACL_REJECT;
#else /* CONFIG_NO_RADIUS */
struct hostapd_acl_query_data *query;
+ struct os_time t;
/* Check whether ACL cache has an entry for this station */
int res = hostapd_acl_cache_get(hapd, addr, session_timeout,
wpa_printf(MSG_ERROR, "malloc for query data failed");
return HOSTAPD_ACL_REJECT;
}
- time(&query->timestamp);
+ os_get_time(&t);
+ query->timestamp = t.sec;
os_memcpy(query->addr, addr, ETH_ALEN);
if (hostapd_radius_acl_query(hapd, addr, query)) {
wpa_printf(MSG_DEBUG, "Failed to send Access-Request "
#ifndef CONFIG_NO_RADIUS
-static void hostapd_acl_expire_cache(struct hostapd_data *hapd, time_t now)
+static void hostapd_acl_expire_cache(struct hostapd_data *hapd, os_time_t now)
{
struct hostapd_cached_radius_acl *prev, *entry, *tmp;
prev->next = entry->next;
else
hapd->acl_cache = entry->next;
-#ifdef CONFIG_DRIVER_RADIUS_ACL
- hapd->drv.set_radius_acl_expire(hapd, entry->addr);
-#endif /* CONFIG_DRIVER_RADIUS_ACL */
+ hostapd_drv_set_radius_acl_expire(hapd, entry->addr);
tmp = entry;
entry = entry->next;
os_free(tmp);
}
-static void hostapd_acl_expire_queries(struct hostapd_data *hapd, time_t now)
+static void hostapd_acl_expire_queries(struct hostapd_data *hapd,
+ os_time_t now)
{
struct hostapd_acl_query_data *prev, *entry, *tmp;
static void hostapd_acl_expire(void *eloop_ctx, void *timeout_ctx)
{
struct hostapd_data *hapd = eloop_ctx;
- time_t now;
+ struct os_time now;
- time(&now);
- hostapd_acl_expire_cache(hapd, now);
- hostapd_acl_expire_queries(hapd, now);
+ os_get_time(&now);
+ hostapd_acl_expire_cache(hapd, now.sec);
+ hostapd_acl_expire_queries(hapd, now.sec);
eloop_register_timeout(10, 0, hostapd_acl_expire, hapd, NULL);
}
struct hostapd_acl_query_data *query, *prev;
struct hostapd_cached_radius_acl *cache;
struct radius_hdr *hdr = radius_msg_get_hdr(msg);
+ struct os_time t;
query = hapd->acl_queries;
prev = NULL;
wpa_printf(MSG_DEBUG, "Failed to add ACL cache entry");
goto done;
}
- time(&cache->timestamp);
+ os_get_time(&t);
+ cache->timestamp = t.sec;
os_memcpy(cache->addr, query->addr, sizeof(cache->addr));
if (hdr->code == RADIUS_CODE_ACCESS_ACCEPT) {
if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_SESSION_TIMEOUT,
hapd->acl_cache = cache;
#ifdef CONFIG_DRIVER_RADIUS_ACL
- hapd->drv.set_radius_acl_auth(hapd, query->addr, cache->accepted,
- cache->session_timeout);
+ hostapd_drv_set_radius_acl_auth(hapd, query->addr, cache->accepted,
+ cache->session_timeout);
#else /* CONFIG_DRIVER_RADIUS_ACL */
#ifdef NEED_AP_MLME
/* Re-send original authentication frame for 802.11 processing */
struct ieee80211_ht_capabilities *cap;
u8 *pos = eid;
- if (!hapd->iconf->ieee80211n || !hapd->iface->current_mode)
+ if (!hapd->iconf->ieee80211n || !hapd->iface->current_mode ||
+ hapd->conf->disable_11n)
return eid;
*pos++ = WLAN_EID_HT_CAP;
struct ieee80211_ht_operation *oper;
u8 *pos = eid;
- if (!hapd->iconf->ieee80211n)
+ if (!hapd->iconf->ieee80211n || hapd->conf->disable_11n)
return eid;
*pos++ = WLAN_EID_HT_OPERATION;
Set to 2 if only HT STAs are associated in BSS,
however and at least one 20 MHz HT STA is associated
Set to 3 (HT mixed mode) when one or more non-HT STAs are associated
- (currently non-GF HT station is considered as non-HT STA also)
*/
int hostapd_ht_operation_update(struct hostapd_iface *iface)
{
op_mode_changes++;
}
- /* Note: currently we switch to the MIXED op mode if HT non-greenfield
- * station is associated. Probably it's a theoretical case, since
- * it looks like all known HT STAs support greenfield.
- */
new_op_mode = 0;
- if (iface->num_sta_no_ht ||
- (iface->ht_op_mode & HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT))
+ if (iface->num_sta_no_ht)
new_op_mode = OP_MODE_MIXED;
else if ((iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)
&& iface->num_sta_ht_20mhz)
}
-u16 copy_sta_ht_capab(struct sta_info *sta, const u8 *ht_capab,
- size_t ht_capab_len)
+u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *ht_capab, size_t ht_capab_len)
{
+ /* Disable HT caps for STAs associated to no-HT BSSes. */
if (!ht_capab ||
- ht_capab_len < sizeof(struct ieee80211_ht_capabilities)) {
+ ht_capab_len < sizeof(struct ieee80211_ht_capabilities) ||
+ hapd->conf->disable_11n) {
sta->flags &= ~WLAN_STA_HT;
os_free(sta->ht_capabilities);
sta->ht_capabilities = NULL;
return;
os_memcpy(neg_ht_cap, ht_cap, sizeof(*neg_ht_cap));
cap = le_to_host16(neg_ht_cap->ht_capabilities_info);
- cap &= hapd->iconf->ht_capab;
- cap |= (hapd->iconf->ht_capab & HT_CAP_INFO_SMPS_DISABLED);
+
+ /*
+ * Mask out HT features we don't support, but don't overwrite
+ * non-symmetric features like STBC and SMPS. Just because
+ * we're not in dynamic SMPS mode the STA might still be.
+ */
+ cap &= (hapd->iconf->ht_capab | HT_CAP_INFO_RX_STBC_MASK |
+ HT_CAP_INFO_TX_STBC | HT_CAP_INFO_SMPS_MASK);
/*
* STBC needs to be handled specially
--- /dev/null
+/*
+ * hostapd / IEEE 802.11 Management
+ * Copyright (c) 2002-2010, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "hostapd.h"
+#include "sta_info.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "ieee802_11.h"
+
+
+#ifdef CONFIG_IEEE80211W
+
+u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd,
+ struct sta_info *sta, u8 *eid)
+{
+ u8 *pos = eid;
+ u32 timeout, tu;
+ struct os_time now, passed;
+
+ *pos++ = WLAN_EID_TIMEOUT_INTERVAL;
+ *pos++ = 5;
+ *pos++ = WLAN_TIMEOUT_ASSOC_COMEBACK;
+ os_get_time(&now);
+ os_time_sub(&now, &sta->sa_query_start, &passed);
+ tu = (passed.sec * 1000000 + passed.usec) / 1024;
+ if (hapd->conf->assoc_sa_query_max_timeout > tu)
+ timeout = hapd->conf->assoc_sa_query_max_timeout - tu;
+ else
+ timeout = 0;
+ if (timeout < hapd->conf->assoc_sa_query_max_timeout)
+ timeout++; /* add some extra time for local timers */
+ WPA_PUT_LE32(pos, timeout);
+ pos += 4;
+
+ return pos;
+}
+
+
+/* MLME-SAQuery.request */
+void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
+ const u8 *addr, const u8 *trans_id)
+{
+ struct ieee80211_mgmt mgmt;
+ u8 *end;
+
+ wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Request to "
+ MACSTR, MAC2STR(addr));
+ wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
+ trans_id, WLAN_SA_QUERY_TR_ID_LEN);
+
+ os_memset(&mgmt, 0, sizeof(mgmt));
+ mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_ACTION);
+ os_memcpy(mgmt.da, addr, ETH_ALEN);
+ os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN);
+ os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN);
+ mgmt.u.action.category = WLAN_ACTION_SA_QUERY;
+ mgmt.u.action.u.sa_query_req.action = WLAN_SA_QUERY_REQUEST;
+ os_memcpy(mgmt.u.action.u.sa_query_req.trans_id, trans_id,
+ WLAN_SA_QUERY_TR_ID_LEN);
+ end = mgmt.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN;
+ if (hostapd_drv_send_mlme(hapd, &mgmt, end - (u8 *) &mgmt) < 0)
+ perror("ieee802_11_send_sa_query_req: send");
+}
+
+
+void ieee802_11_send_sa_query_resp(struct hostapd_data *hapd,
+ const u8 *sa, const u8 *trans_id)
+{
+ struct sta_info *sta;
+ struct ieee80211_mgmt resp;
+ u8 *end;
+
+ wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Request from "
+ MACSTR, MAC2STR(sa));
+ wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
+ trans_id, WLAN_SA_QUERY_TR_ID_LEN);
+
+ sta = ap_get_sta(hapd, sa);
+ if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) {
+ wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignore SA Query Request "
+ "from unassociated STA " MACSTR, MAC2STR(sa));
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Response to "
+ MACSTR, MAC2STR(sa));
+
+ os_memset(&resp, 0, sizeof(resp));
+ resp.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_ACTION);
+ os_memcpy(resp.da, sa, ETH_ALEN);
+ os_memcpy(resp.sa, hapd->own_addr, ETH_ALEN);
+ os_memcpy(resp.bssid, hapd->own_addr, ETH_ALEN);
+ resp.u.action.category = WLAN_ACTION_SA_QUERY;
+ resp.u.action.u.sa_query_req.action = WLAN_SA_QUERY_RESPONSE;
+ os_memcpy(resp.u.action.u.sa_query_req.trans_id, trans_id,
+ WLAN_SA_QUERY_TR_ID_LEN);
+ end = resp.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN;
+ if (hostapd_drv_send_mlme(hapd, &resp, end - (u8 *) &resp) < 0)
+ perror("ieee80211_mgmt_sa_query_request: send");
+}
+
+
+void ieee802_11_sa_query_action(struct hostapd_data *hapd, const u8 *sa,
+ const u8 action_type, const u8 *trans_id)
+{
+ struct sta_info *sta;
+ int i;
+
+ if (action_type == WLAN_SA_QUERY_REQUEST) {
+ ieee802_11_send_sa_query_resp(hapd, sa, trans_id);
+ return;
+ }
+
+ if (action_type != WLAN_SA_QUERY_RESPONSE) {
+ wpa_printf(MSG_DEBUG, "IEEE 802.11: Unexpected SA Query "
+ "Action %d", action_type);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Response from "
+ MACSTR, MAC2STR(sa));
+ wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
+ trans_id, WLAN_SA_QUERY_TR_ID_LEN);
+
+ /* MLME-SAQuery.confirm */
+
+ sta = ap_get_sta(hapd, sa);
+ if (sta == NULL || sta->sa_query_trans_id == NULL) {
+ wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching STA with "
+ "pending SA Query request found");
+ return;
+ }
+
+ for (i = 0; i < sta->sa_query_count; i++) {
+ if (os_memcmp(sta->sa_query_trans_id +
+ i * WLAN_SA_QUERY_TR_ID_LEN,
+ trans_id, WLAN_SA_QUERY_TR_ID_LEN) == 0)
+ break;
+ }
+
+ if (i >= sta->sa_query_count) {
+ wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching SA Query "
+ "transaction identifier found");
+ return;
+ }
+
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "Reply to pending SA Query received");
+ ap_sta_stop_sa_query(hapd, sta);
+}
+
+#endif /* CONFIG_IEEE80211W */
+
+
+u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 *pos = eid;
+ u8 len = 0;
+
+ if (hapd->conf->tdls & (TDLS_PROHIBIT | TDLS_PROHIBIT_CHAN_SWITCH))
+ len = 5;
+ if (len < 4 && hapd->conf->interworking)
+ len = 4;
+ if (len == 0)
+ return eid;
+
+ *pos++ = WLAN_EID_EXT_CAPAB;
+ *pos++ = len;
+ *pos++ = 0x00;
+ *pos++ = 0x00;
+ *pos++ = 0x00;
+
+ *pos = 0x00;
+ if (hapd->conf->time_advertisement == 2)
+ *pos |= 0x08; /* Bit 27 - UTC TSF Offset */
+ if (hapd->conf->interworking)
+ *pos |= 0x80; /* Bit 31 - Interworking */
+ pos++;
+
+ if (len < 5)
+ return pos;
+ *pos = 0x00;
+ if (hapd->conf->tdls & TDLS_PROHIBIT)
+ *pos |= 0x40; /* Bit 38 - TDLS Prohibited */
+ if (hapd->conf->tdls & TDLS_PROHIBIT_CHAN_SWITCH)
+ *pos |= 0x80; /* Bit 39 - TDLS Channel Switching Prohibited */
+ pos++;
+
+ return pos;
+}
+
+
+u8 * hostapd_eid_interworking(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 *pos = eid;
+#ifdef CONFIG_INTERWORKING
+ u8 *len;
+
+ if (!hapd->conf->interworking)
+ return eid;
+
+ *pos++ = WLAN_EID_INTERWORKING;
+ len = pos++;
+
+ *pos = hapd->conf->access_network_type;
+ if (hapd->conf->internet)
+ *pos |= INTERWORKING_ANO_INTERNET;
+ if (hapd->conf->asra)
+ *pos |= INTERWORKING_ANO_ASRA;
+ if (hapd->conf->esr)
+ *pos |= INTERWORKING_ANO_ESR;
+ if (hapd->conf->uesa)
+ *pos |= INTERWORKING_ANO_UESA;
+ pos++;
+
+ if (hapd->conf->venue_info_set) {
+ *pos++ = hapd->conf->venue_group;
+ *pos++ = hapd->conf->venue_type;
+ }
+
+ if (!is_zero_ether_addr(hapd->conf->hessid)) {
+ os_memcpy(pos, hapd->conf->hessid, ETH_ALEN);
+ pos += ETH_ALEN;
+ }
+
+ *len = pos - len - 1;
+#endif /* CONFIG_INTERWORKING */
+
+ return pos;
+}
+
+
+u8 * hostapd_eid_adv_proto(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 *pos = eid;
+#ifdef CONFIG_INTERWORKING
+
+ /* TODO: Separate configuration for ANQP? */
+ if (!hapd->conf->interworking)
+ return eid;
+
+ *pos++ = WLAN_EID_ADV_PROTO;
+ *pos++ = 2;
+ *pos++ = 0x7F; /* Query Response Length Limit | PAME-BI */
+ *pos++ = ACCESS_NETWORK_QUERY_PROTOCOL;
+#endif /* CONFIG_INTERWORKING */
+
+ return pos;
+}
+
+
+u8 * hostapd_eid_roaming_consortium(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 *pos = eid;
+#ifdef CONFIG_INTERWORKING
+ u8 *len;
+ unsigned int i, count;
+
+ if (!hapd->conf->interworking ||
+ hapd->conf->roaming_consortium == NULL ||
+ hapd->conf->roaming_consortium_count == 0)
+ return eid;
+
+ *pos++ = WLAN_EID_ROAMING_CONSORTIUM;
+ len = pos++;
+
+ /* Number of ANQP OIs (in addition to the max 3 listed here) */
+ if (hapd->conf->roaming_consortium_count > 3 + 255)
+ *pos++ = 255;
+ else if (hapd->conf->roaming_consortium_count > 3)
+ *pos++ = hapd->conf->roaming_consortium_count - 3;
+ else
+ *pos++ = 0;
+
+ /* OU #1 and #2 Lengths */
+ *pos = hapd->conf->roaming_consortium[0].len;
+ if (hapd->conf->roaming_consortium_count > 1)
+ *pos |= hapd->conf->roaming_consortium[1].len << 4;
+ pos++;
+
+ if (hapd->conf->roaming_consortium_count > 3)
+ count = 3;
+ else
+ count = hapd->conf->roaming_consortium_count;
+
+ for (i = 0; i < count; i++) {
+ os_memcpy(pos, hapd->conf->roaming_consortium[i].oi,
+ hapd->conf->roaming_consortium[i].len);
+ pos += hapd->conf->roaming_consortium[i].len;
+ }
+
+ *len = pos - len - 1;
+#endif /* CONFIG_INTERWORKING */
+
+ return pos;
+}
+
+
+u8 * hostapd_eid_time_adv(struct hostapd_data *hapd, u8 *eid)
+{
+ if (hapd->conf->time_advertisement != 2)
+ return eid;
+
+ if (hapd->time_adv == NULL &&
+ hostapd_update_time_adv(hapd) < 0)
+ return eid;
+
+ if (hapd->time_adv == NULL)
+ return eid;
+
+ os_memcpy(eid, wpabuf_head(hapd->time_adv),
+ wpabuf_len(hapd->time_adv));
+ eid += wpabuf_len(hapd->time_adv);
+
+ return eid;
+}
+
+
+u8 * hostapd_eid_time_zone(struct hostapd_data *hapd, u8 *eid)
+{
+ size_t len;
+
+ if (hapd->conf->time_advertisement != 2)
+ return eid;
+
+ len = os_strlen(hapd->conf->time_zone);
+
+ *eid++ = WLAN_EID_TIME_ZONE;
+ *eid++ = len;
+ os_memcpy(eid, hapd->conf->time_zone, len);
+ eid += len;
+
+ return eid;
+}
+
+
+int hostapd_update_time_adv(struct hostapd_data *hapd)
+{
+ const int elen = 2 + 1 + 10 + 5 + 1;
+ struct os_time t;
+ struct os_tm tm;
+ u8 *pos;
+
+ if (hapd->conf->time_advertisement != 2)
+ return 0;
+
+ if (os_get_time(&t) < 0 || os_gmtime(t.sec, &tm) < 0)
+ return -1;
+
+ if (!hapd->time_adv) {
+ hapd->time_adv = wpabuf_alloc(elen);
+ if (hapd->time_adv == NULL)
+ return -1;
+ pos = wpabuf_put(hapd->time_adv, elen);
+ } else
+ pos = wpabuf_mhead_u8(hapd->time_adv);
+
+ *pos++ = WLAN_EID_TIME_ADVERTISEMENT;
+ *pos++ = 1 + 10 + 5 + 1;
+
+ *pos++ = 2; /* UTC time at which the TSF timer is 0 */
+
+ /* Time Value at TSF 0 */
+ /* FIX: need to calculate this based on the current TSF value */
+ WPA_PUT_LE16(pos, tm.year); /* Year */
+ pos += 2;
+ *pos++ = tm.month; /* Month */
+ *pos++ = tm.day; /* Day of month */
+ *pos++ = tm.hour; /* Hours */
+ *pos++ = tm.min; /* Minutes */
+ *pos++ = tm.sec; /* Seconds */
+ WPA_PUT_LE16(pos, 0); /* Milliseconds (not used) */
+ pos += 2;
+ *pos++ = 0; /* Reserved */
+
+ /* Time Error */
+ /* TODO: fill in an estimate on the error */
+ *pos++ = 0;
+ *pos++ = 0;
+ *pos++ = 0;
+ *pos++ = 0;
+ *pos++ = 0;
+
+ *pos++ = hapd->time_update_counter++;
+
+ return 0;
+}
/*
* hostapd / IEEE 802.1X-2004 Authenticator
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
#include "utils/eloop.h"
#include "crypto/md5.h"
#include "crypto/crypto.h"
+#include "crypto/random.h"
#include "common/ieee802_11_defs.h"
#include "common/wpa_ctrl.h"
#include "radius/radius.h"
#include "eap_common/eap_wsc_common.h"
#include "eapol_auth/eapol_auth_sm.h"
#include "eapol_auth/eapol_auth_sm_i.h"
+#include "p2p/p2p.h"
#include "hostapd.h"
#include "accounting.h"
#include "sta_info.h"
#include "preauth_auth.h"
#include "pmksa_cache_auth.h"
#include "ap_config.h"
+#include "ap_drv_ops.h"
#include "ieee802_1x.h"
if (sta->flags & WLAN_STA_PREAUTH) {
rsn_preauth_send(hapd, sta, buf, len);
} else {
- hapd->drv.send_eapol(hapd, sta->addr, buf, len, encrypt);
+ hostapd_drv_hapd_send_eapol(hapd, sta->addr, buf, len,
+ encrypt, sta->flags);
}
os_free(buf);
return;
if (authorized) {
- if (!(sta->flags & WLAN_STA_AUTHORIZED))
- wpa_msg(hapd->msg_ctx, MSG_INFO,
- AP_STA_CONNECTED MACSTR, MAC2STR(sta->addr));
- sta->flags |= WLAN_STA_AUTHORIZED;
- res = hapd->drv.set_authorized(hapd, sta, 1);
+ ap_sta_set_authorized(hapd, sta, 1);
+ res = hostapd_set_authorized(hapd, sta, 1);
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
HOSTAPD_LEVEL_DEBUG, "authorizing port");
} else {
- if ((sta->flags & (WLAN_STA_AUTHORIZED | WLAN_STA_ASSOC)) ==
- (WLAN_STA_AUTHORIZED | WLAN_STA_ASSOC))
- wpa_msg(hapd->msg_ctx, MSG_INFO,
- AP_STA_DISCONNECTED MACSTR,
- MAC2STR(sta->addr));
- sta->flags &= ~WLAN_STA_AUTHORIZED;
- res = hapd->drv.set_authorized(hapd, sta, 0);
+ ap_sta_set_authorized(hapd, sta, 0);
+ res = hostapd_set_authorized(hapd, sta, 0);
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
HOSTAPD_LEVEL_DEBUG, "unauthorizing port");
}
key->key_length = htons(key_len);
wpa_get_ntp_timestamp(key->replay_counter);
- if (os_get_random(key->key_iv, sizeof(key->key_iv))) {
+ if (random_get_bytes(key->key_iv, sizeof(key->key_iv))) {
wpa_printf(MSG_ERROR, "Could not get random numbers");
os_free(buf);
return;
if (!key->key[key->idx])
key->key[key->idx] = os_malloc(key->default_len);
if (key->key[key->idx] == NULL ||
- os_get_random(key->key[key->idx], key->default_len)) {
+ random_get_bytes(key->key[key->idx], key->default_len)) {
printf("Could not generate random WEP key (dynamic VLAN).\n");
os_free(key->key[key->idx]);
key->key[key->idx] = NULL;
wpa_hexdump_key(MSG_DEBUG, "Default WEP key (dynamic VLAN)",
key->key[key->idx], key->len[key->idx]);
- if (hapd->drv.set_key(ifname, hapd, WPA_ALG_WEP, NULL, key->idx, 1,
- NULL, 0, key->key[key->idx], key->len[key->idx]))
+ if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_WEP,
+ broadcast_ether_addr, key->idx, 1,
+ NULL, 0, key->key[key->idx],
+ key->len[key->idx]))
printf("Could not set dynamic VLAN WEP encryption key.\n");
- hapd->drv.set_drv_ieee8021x(hapd, ifname, 1);
+ hostapd_set_drv_ieee8021x(hapd, ifname, 1);
return key;
}
u8 *ikey;
ikey = os_malloc(hapd->conf->individual_wep_key_len);
if (ikey == NULL ||
- os_get_random(ikey, hapd->conf->individual_wep_key_len)) {
+ random_get_bytes(ikey, hapd->conf->individual_wep_key_len))
+ {
wpa_printf(MSG_ERROR, "Could not generate random "
"individual WEP key.");
os_free(ikey);
/* TODO: set encryption in TX callback, i.e., only after STA
* has ACKed EAPOL-Key frame */
- if (hapd->drv.set_key(hapd->conf->iface, hapd, WPA_ALG_WEP,
- sta->addr, 0, 1, NULL, 0, ikey,
- hapd->conf->individual_wep_key_len)) {
+ if (hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_WEP,
+ sta->addr, 0, 1, NULL, 0, ikey,
+ hapd->conf->individual_wep_key_len)) {
wpa_printf(MSG_ERROR, "Could not set individual WEP "
"encryption.");
}
}
}
- radius_client_send(hapd->radius, msg, RADIUS_AUTH, sta->addr);
+ if (radius_client_send(hapd->radius, msg, RADIUS_AUTH, sta->addr) < 0)
+ goto fail;
+
return;
fail:
flags |= EAPOL_SM_FROM_PMKSA_CACHE;
}
return eapol_auth_alloc(hapd->eapol_auth, sta->addr, flags,
- sta->wps_ie, sta);
+ sta->wps_ie, sta->p2p_ie, sta);
}
struct ieee802_1x_eapol_key *key;
u16 datalen;
struct rsn_pmksa_cache_entry *pmksa;
+ int key_mgmt;
if (!hapd->conf->ieee802_1x && !hapd->conf->wpa &&
!hapd->conf->wps_state)
wpa_printf(MSG_DEBUG, "IEEE 802.1X: %lu bytes from " MACSTR,
(unsigned long) len, MAC2STR(sa));
sta = ap_get_sta(hapd, sa);
- if (!sta || !(sta->flags & WLAN_STA_ASSOC)) {
+ if (!sta || (!(sta->flags & (WLAN_STA_ASSOC | WLAN_STA_PREAUTH)) &&
+ !(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_WIRED))) {
wpa_printf(MSG_DEBUG, "IEEE 802.1X data frame from not "
- "associated STA");
+ "associated/Pre-authenticating STA");
return;
}
return;
}
- if ((!hapd->conf->ieee802_1x &&
- !(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) ||
- wpa_key_mgmt_wpa_psk(wpa_auth_sta_key_mgmt(sta->wpa_sm)))
+ if (!hapd->conf->ieee802_1x &&
+ !(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) {
+ wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore EAPOL message - "
+ "802.1X not enabled and WPS not used");
return;
+ }
+
+ key_mgmt = wpa_auth_sta_key_mgmt(sta->wpa_sm);
+ if (key_mgmt != -1 && wpa_key_mgmt_wpa_psk(key_mgmt)) {
+ wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore EAPOL message - "
+ "STA is using PSK");
+ return;
+ }
if (!sta->eapol_sm) {
sta->eapol_sm = ieee802_1x_alloc_eapol_sm(hapd, sta);
return;
#ifdef CONFIG_WPS
- if (!hapd->conf->ieee802_1x &&
- ((sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS)) ==
- WLAN_STA_MAYBE_WPS)) {
- /*
- * Delay EAPOL frame transmission until a possible WPS
- * STA initiates the handshake with EAPOL-Start.
- */
- sta->eapol_sm->flags |= EAPOL_SM_WAIT_START;
+ if (!hapd->conf->ieee802_1x) {
+ u32 wflags = sta->flags & (WLAN_STA_WPS |
+ WLAN_STA_WPS2 |
+ WLAN_STA_MAYBE_WPS);
+ if (wflags == WLAN_STA_MAYBE_WPS ||
+ wflags == (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS)) {
+ /*
+ * Delay EAPOL frame transmission until a
+ * possible WPS STA initiates the handshake
+ * with EAPOL-Start. Only allow the wait to be
+ * skipped if the STA is known to support WPS
+ * 2.0.
+ */
+ wpa_printf(MSG_DEBUG, "WPS: Do not start "
+ "EAPOL until EAPOL-Start is "
+ "received");
+ sta->eapol_sm->flags |= EAPOL_SM_WAIT_START;
+ }
}
#endif /* CONFIG_WPS */
}
sta->eapol_sm->eapolStart = TRUE;
sta->eapol_sm->dot1xAuthEapolStartFramesRx++;
+ eap_server_clear_identity(sta->eapol_sm->eap);
wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH_EAPOL);
break;
accounting_sta_stop(hapd, sta);
sta->eapol_sm->eapolLogoff = TRUE;
sta->eapol_sm->dot1xAuthEapolLogoffFramesRx++;
+ eap_server_clear_identity(sta->eapol_sm->eap);
break;
case IEEE802_1X_TYPE_EAPOL_KEY:
wpa_printf(MSG_DEBUG, " EAPOL-Key");
- if (!(sta->flags & WLAN_STA_AUTHORIZED)) {
+ if (!ap_sta_is_authorized(sta)) {
wpa_printf(MSG_DEBUG, " Dropped key data from "
"unauthorized Supplicant");
break;
struct rsn_pmksa_cache_entry *pmksa;
int reassoc = 1;
int force_1x = 0;
+ int key_mgmt;
#ifdef CONFIG_WPS
if (hapd->conf->wps_state && hapd->conf->wpa &&
}
#endif /* CONFIG_WPS */
- if ((!force_1x && !hapd->conf->ieee802_1x) ||
- wpa_key_mgmt_wpa_psk(wpa_auth_sta_key_mgmt(sta->wpa_sm)))
+ if (!force_1x && !hapd->conf->ieee802_1x) {
+ wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore STA - "
+ "802.1X not enabled or forced for WPS");
+ /*
+ * Clear any possible EAPOL authenticator state to support
+ * reassociation change from WPS to PSK.
+ */
+ ieee802_1x_free_station(sta);
return;
+ }
+
+ key_mgmt = wpa_auth_sta_key_mgmt(sta->wpa_sm);
+ if (key_mgmt != -1 && wpa_key_mgmt_wpa_psk(key_mgmt)) {
+ wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore STA - using PSK");
+ /*
+ * Clear any possible EAPOL authenticator state to support
+ * reassociation change from WPA-EAP to PSK.
+ */
+ ieee802_1x_free_station(sta);
+ return;
+ }
if (sta->eapol_sm == NULL) {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
#ifdef CONFIG_WPS
sta->eapol_sm->flags &= ~EAPOL_SM_WAIT_START;
- if (!hapd->conf->ieee802_1x && !(sta->flags & WLAN_STA_WPS)) {
+ if (!hapd->conf->ieee802_1x && !(sta->flags & WLAN_STA_WPS2)) {
/*
- * Delay EAPOL frame transmission until a possible WPS
- * initiates the handshake with EAPOL-Start.
+ * Delay EAPOL frame transmission until a possible WPS STA
+ * initiates the handshake with EAPOL-Start. Only allow the
+ * wait to be skipped if the STA is known to support WPS 2.0.
*/
+ wpa_printf(MSG_DEBUG, "WPS: Do not start EAPOL until "
+ "EAPOL-Start is received");
sta->eapol_sm->flags |= EAPOL_SM_WAIT_START;
}
#endif /* CONFIG_WPS */
sta->eapol_sm->eap_if->portEnabled = TRUE;
+#ifdef CONFIG_IEEE80211R
+ if (sta->auth_alg == WLAN_AUTH_FT) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_DEBUG,
+ "PMK from FT - skip IEEE 802.1X/EAP");
+ /* Setup EAPOL state machines to already authenticated state
+ * because of existing FT information from R0KH. */
+ sta->eapol_sm->keyRun = TRUE;
+ sta->eapol_sm->eap_if->eapKeyAvailable = TRUE;
+ sta->eapol_sm->auth_pae_state = AUTH_PAE_AUTHENTICATING;
+ sta->eapol_sm->be_auth_state = BE_AUTH_SUCCESS;
+ sta->eapol_sm->authSuccess = TRUE;
+ sta->eapol_sm->authFail = FALSE;
+ if (sta->eapol_sm->eap)
+ eap_sm_notify_cached(sta->eapol_sm->eap);
+ /* TODO: get vlan_id from R0KH using RRB message */
+ return;
+ }
+#endif /* CONFIG_IEEE80211R */
+
pmksa = wpa_auth_sta_get_pmksa(sta->wpa_sm);
if (pmksa) {
int old_vlanid;
sta->eapol_sm->auth_pae_state = AUTH_PAE_AUTHENTICATING;
sta->eapol_sm->be_auth_state = BE_AUTH_SUCCESS;
sta->eapol_sm->authSuccess = TRUE;
+ sta->eapol_sm->authFail = FALSE;
if (sta->eapol_sm->eap)
eap_sm_notify_cached(sta->eapol_sm->eap);
old_vlanid = sta->vlan_id;
os_free(eapol->default_wep_key);
eapol->default_wep_key = os_malloc(hapd->conf->default_wep_key_len);
if (eapol->default_wep_key == NULL ||
- os_get_random(eapol->default_wep_key,
- hapd->conf->default_wep_key_len)) {
+ random_get_bytes(eapol->default_wep_key,
+ hapd->conf->default_wep_key_len)) {
printf("Could not generate random WEP key.\n");
os_free(eapol->default_wep_key);
eapol->default_wep_key = NULL;
/* TODO: Could setup key for RX here, but change default TX keyid only
* after new broadcast key has been sent to all stations. */
- if (hapd->drv.set_key(hapd->conf->iface, hapd, WPA_ALG_WEP, NULL,
- eapol->default_wep_key_idx, 1, NULL, 0,
- eapol->default_wep_key,
- hapd->conf->default_wep_key_len)) {
+ if (hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_WEP,
+ broadcast_ether_addr,
+ eapol->default_wep_key_idx, 1, NULL, 0,
+ eapol->default_wep_key,
+ hapd->conf->default_wep_key_len)) {
hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE8021X,
HOSTAPD_LEVEL_WARNING, "failed to configure a "
"new broadcast key");
os_memcpy(user->password, eap_user->password,
eap_user->password_len);
user->password_len = eap_user->password_len;
+ user->password_hash = eap_user->password_hash;
}
user->force_version = eap_user->force_version;
user->ttls_auth = eap_user->ttls_auth;
conf.eap_sim_aka_result_ind = hapd->conf->eap_sim_aka_result_ind;
conf.tnc = hapd->conf->tnc;
conf.wps = hapd->wps;
+ conf.fragment_size = hapd->conf->fragment_size;
+ conf.pwd_group = hapd->conf->pwd_group;
+ conf.pbc_in_m1 = hapd->conf->pbc_in_m1;
os_memset(&cb, 0, sizeof(cb));
cb.eapol_send = ieee802_1x_eapol_send;
return -1;
if ((hapd->conf->ieee802_1x || hapd->conf->wpa) &&
- hapd->drv.set_drv_ieee8021x(hapd, hapd->conf->iface, 1))
+ hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 1))
return -1;
#ifndef CONFIG_NO_RADIUS
if (hapd->conf->default_wep_key_len) {
for (i = 0; i < 4; i++)
- hapd->drv.set_key(hapd->conf->iface, hapd,
- WPA_ALG_NONE, NULL, i, 0, NULL, 0,
- NULL, 0);
+ hostapd_drv_set_key(hapd->conf->iface, hapd,
+ WPA_ALG_NONE, NULL, i, 0, NULL, 0,
+ NULL, 0);
ieee802_1x_rekey(hapd, NULL);
if (hapd->driver != NULL &&
(hapd->conf->ieee802_1x || hapd->conf->wpa))
- hapd->drv.set_drv_ieee8021x(hapd, hapd->conf->iface, 0);
+ hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 0);
eapol_auth_deinit(hapd->eapol_auth);
hapd->eapol_auth = NULL;
MAC2STR(sta->addr), xhdr->version, xhdr->type,
be_to_host16(xhdr->length), ack);
+ if (xhdr->type == IEEE802_1X_TYPE_EAPOL_KEY &&
+ pos + sizeof(struct wpa_eapol_key) <= buf + len) {
+ const struct wpa_eapol_key *wpa;
+ wpa = (const struct wpa_eapol_key *) pos;
+ if (wpa->type == EAPOL_KEY_TYPE_RSN ||
+ wpa->type == EAPOL_KEY_TYPE_WPA)
+ wpa_auth_eapol_key_tx_status(hapd->wpa_auth,
+ sta->wpa_sm, ack);
+ }
+
/* EAPOL EAP-Packet packets are eventually re-sent by either Supplicant
* or Authenticator state machines, but EAPOL-Key packets are not
- * retransmitted in case of failure. Try to re-sent failed EAPOL-Key
+ * retransmitted in case of failure. Try to re-send failed EAPOL-Key
* packets couple of times because otherwise STA keys become
* unsynchronized with AP. */
if (xhdr->type == IEEE802_1X_TYPE_EAPOL_KEY && !ack &&
const u8 * ieee802_1x_get_key(struct eapol_state_machine *sm, size_t *len)
{
+ *len = 0;
if (sm == NULL)
return NULL;
{
int len = 0, ret;
struct eapol_state_machine *sm = sta->eapol_sm;
+ struct os_time t;
if (sm == NULL)
return 0;
len += ret;
/* dot1xAuthSessionStatsTable */
+ os_get_time(&t);
ret = os_snprintf(buf + len, buflen - len,
/* TODO: dot1xAuthSessionOctetsRx */
/* TODO: dot1xAuthSessionOctetsTx */
(wpa_key_mgmt_wpa_ieee8021x(
wpa_auth_sta_key_mgmt(sta->wpa_sm))) ?
1 : 2,
- (unsigned int) (time(NULL) -
- sta->acct_session_start),
+ (unsigned int) (t.sec - sta->acct_session_start),
sm->identity);
if (ret < 0 || (size_t) ret >= buflen - len)
return len;
"Added PMKSA cache entry (IEEE 802.1X)");
}
-#ifdef CONFIG_WPS
- if (!success && (sta->flags & WLAN_STA_WPS)) {
+ if (!success) {
/*
* Many devices require deauthentication after WPS provisioning
* and some may not be be able to do that themselves, so
- * disconnect the client here.
+ * disconnect the client here. In addition, this may also
+ * benefit IEEE 802.1X/EAPOL authentication cases, too since
+ * the EAPOL PAE state machine would remain in HELD state for
+ * considerable amount of time and some EAP methods, like
+ * EAP-FAST with anonymous provisioning, may require another
+ * EAPOL authentication to be started to complete connection.
*/
- wpa_printf(MSG_DEBUG, "WPS: Force disconnection after "
+ wpa_printf(MSG_DEBUG, "IEEE 802.1X: Force disconnection after "
"EAP-Failure");
/* Add a small sleep to increase likelihood of previously
* requested EAP-Failure TX getting out before this should the
*/
os_sleep(0, 10000);
ap_sta_disconnect(hapd, sta, sta->addr,
- WLAN_REASON_PREV_AUTH_NOT_VALID);
+ WLAN_REASON_IEEE_802_1X_AUTH_FAILED);
}
-#endif /* CONFIG_WPS */
}
--- /dev/null
+/*
+ * hostapd / P2P integration
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "p2p/p2p.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "sta_info.h"
+#include "p2p_hostapd.h"
+
+
+#ifdef CONFIG_P2P
+
+int hostapd_p2p_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
+ char *buf, size_t buflen)
+{
+ if (sta->p2p_ie == NULL)
+ return 0;
+
+ return p2p_ie_text(sta->p2p_ie, buf, buf + buflen);
+}
+
+
+int hostapd_p2p_set_noa(struct hostapd_data *hapd, u8 count, int start,
+ int duration)
+{
+ wpa_printf(MSG_DEBUG, "P2P: Set NoA parameters: count=%u start=%d "
+ "duration=%d", count, start, duration);
+
+ if (count == 0) {
+ hapd->noa_enabled = 0;
+ hapd->noa_start = 0;
+ hapd->noa_duration = 0;
+ }
+
+ if (count != 255) {
+ wpa_printf(MSG_DEBUG, "P2P: Non-periodic NoA - set "
+ "NoA parameters");
+ return hostapd_driver_set_noa(hapd, count, start, duration);
+ }
+
+ hapd->noa_enabled = 1;
+ hapd->noa_start = start;
+ hapd->noa_duration = duration;
+
+ if (hapd->num_sta_no_p2p == 0) {
+ wpa_printf(MSG_DEBUG, "P2P: No legacy STAs connected - update "
+ "periodic NoA parameters");
+ return hostapd_driver_set_noa(hapd, count, start, duration);
+ }
+
+ wpa_printf(MSG_DEBUG, "P2P: Legacy STA(s) connected - do not enable "
+ "periodic NoA");
+
+ return 0;
+}
+
+
+void hostapd_p2p_non_p2p_sta_connected(struct hostapd_data *hapd)
+{
+ wpa_printf(MSG_DEBUG, "P2P: First non-P2P device connected");
+
+ if (hapd->noa_enabled) {
+ wpa_printf(MSG_DEBUG, "P2P: Disable periodic NoA");
+ hostapd_driver_set_noa(hapd, 0, 0, 0);
+ }
+}
+
+
+void hostapd_p2p_non_p2p_sta_disconnected(struct hostapd_data *hapd)
+{
+ wpa_printf(MSG_DEBUG, "P2P: Last non-P2P device disconnected");
+
+ if (hapd->noa_enabled) {
+ wpa_printf(MSG_DEBUG, "P2P: Enable periodic NoA");
+ hostapd_driver_set_noa(hapd, 255, hapd->noa_start,
+ hapd->noa_duration);
+ }
+}
+
+#endif /* CONFIG_P2P */
+
+
+#ifdef CONFIG_P2P_MANAGER
+u8 * hostapd_eid_p2p_manage(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 bitmap;
+ *eid++ = WLAN_EID_VENDOR_SPECIFIC;
+ *eid++ = 4 + 3 + 1;
+ WPA_PUT_BE24(eid, OUI_WFA);
+ eid += 3;
+ *eid++ = P2P_OUI_TYPE;
+
+ *eid++ = P2P_ATTR_MANAGEABILITY;
+ WPA_PUT_LE16(eid, 1);
+ eid += 2;
+ bitmap = P2P_MAN_DEVICE_MANAGEMENT;
+ if (hapd->conf->p2p & P2P_ALLOW_CROSS_CONNECTION)
+ bitmap |= P2P_MAN_CROSS_CONNECTION_PERMITTED;
+ bitmap |= P2P_MAN_COEXISTENCE_OPTIONAL;
+ *eid++ = bitmap;
+
+ return eid;
+}
+#endif /* CONFIG_P2P_MANAGER */
--- /dev/null
+/*
+ * hostapd / P2P integration
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef P2P_HOSTAPD_H
+#define P2P_HOSTAPD_H
+
+#ifdef CONFIG_P2P
+
+int hostapd_p2p_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
+ char *buf, size_t buflen);
+int hostapd_p2p_set_noa(struct hostapd_data *hapd, u8 count, int start,
+ int duration);
+void hostapd_p2p_non_p2p_sta_connected(struct hostapd_data *hapd);
+void hostapd_p2p_non_p2p_sta_disconnected(struct hostapd_data *hapd);
+
+
+#else /* CONFIG_P2P */
+
+static inline int hostapd_p2p_get_mib_sta(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ char *buf, size_t buflen)
+{
+ return 0;
+}
+
+#endif /* CONFIG_P2P */
+
+u8 * hostapd_eid_p2p_manage(struct hostapd_data *hapd, u8 *eid);
+
+#endif /* P2P_HOSTAPD_H */
#include "utils/eloop.h"
#include "crypto/sha1.h"
#include "crypto/sha256.h"
+#include "crypto/random.h"
#include "wpa_auth.h"
#include "wpa_auth_i.h"
#include "wpa_auth_ie.h"
return;
}
- if (os_get_random(smk, PMK_LEN)) {
+ if (random_get_bytes(smk, PMK_LEN)) {
wpa_printf(MSG_DEBUG, "RSN: Failed to generate SMK");
return;
}
/*
* hostapd / Station table
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2011, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
#include "utils/common.h"
#include "utils/eloop.h"
#include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
#include "radius/radius.h"
#include "radius/radius_client.h"
#include "drivers/driver.h"
+#include "p2p/p2p.h"
#include "hostapd.h"
#include "accounting.h"
#include "ieee802_1x.h"
#include "beacon.h"
#include "ap_mlme.h"
#include "vlan_init.h"
+#include "p2p_hostapd.h"
+#include "ap_drv_ops.h"
#include "sta_info.h"
static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd,
struct sta_info *sta);
static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx);
+static void ap_sta_deauth_cb_timeout(void *eloop_ctx, void *timeout_ctx);
+static void ap_sta_disassoc_cb_timeout(void *eloop_ctx, void *timeout_ctx);
#ifdef CONFIG_IEEE80211W
static void ap_sa_query_timer(void *eloop_ctx, void *timeout_ctx);
#endif /* CONFIG_IEEE80211W */
+static int ap_sta_remove(struct hostapd_data *hapd, struct sta_info *sta);
int ap_for_each_sta(struct hostapd_data *hapd,
int (*cb)(struct hostapd_data *hapd, struct sta_info *sta,
accounting_sta_stop(hapd, sta);
+ /* just in case */
+ ap_sta_set_authorized(hapd, sta, 0);
+
if (sta->flags & WLAN_STA_WDS)
- hapd->drv.set_wds_sta(hapd, sta->addr, sta->aid, 0);
+ hostapd_set_wds_sta(hapd, sta->addr, sta->aid, 0);
if (!(sta->flags & WLAN_STA_PREAUTH))
- hapd->drv.sta_remove(hapd, sta->addr);
+ hostapd_drv_sta_remove(hapd, sta->addr);
ap_sta_hash_del(hapd, sta);
ap_sta_list_del(hapd, sta);
hapd->iface->num_sta_ht_20mhz--;
}
+#ifdef CONFIG_P2P
+ if (sta->no_p2p_set) {
+ sta->no_p2p_set = 0;
+ hapd->num_sta_no_p2p--;
+ if (hapd->num_sta_no_p2p == 0)
+ hostapd_p2p_non_p2p_sta_disconnected(hapd);
+ }
+#endif /* CONFIG_P2P */
+
#if defined(NEED_AP_MLME) && defined(CONFIG_IEEE80211N)
if (hostapd_ht_operation_update(hapd->iface) > 0)
set_beacon++;
eloop_cancel_timeout(ap_handle_timer, hapd, sta);
eloop_cancel_timeout(ap_handle_session_timer, hapd, sta);
+ eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta);
+ eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta);
ieee802_1x_free_station(sta);
wpa_auth_sta_deinit(sta->wpa_sm);
eloop_cancel_timeout(ap_sa_query_timer, hapd, sta);
#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_P2P
+ p2p_group_notif_disassoc(hapd->p2p_group, sta->addr);
+#endif /* CONFIG_P2P */
+
wpabuf_free(sta->wps_ie);
+ wpabuf_free(sta->p2p_ie);
os_free(sta->ht_capabilities);
(sta->timeout_next == STA_NULLFUNC ||
sta->timeout_next == STA_DISASSOC)) {
int inactive_sec;
- wpa_printf(MSG_DEBUG, "Checking STA " MACSTR " inactivity:",
- MAC2STR(sta->addr));
- inactive_sec = hapd->drv.get_inact_sec(hapd, sta->addr);
+ inactive_sec = hostapd_drv_get_inact_sec(hapd, sta->addr);
if (inactive_sec == -1) {
- wpa_printf(MSG_DEBUG, "Could not get station info "
- "from kernel driver for " MACSTR ".",
- MAC2STR(sta->addr));
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+ "Check inactivity: Could not "
+ "get station info from kernel driver for "
+ MACSTR, MAC2STR(sta->addr));
+ /*
+ * The driver may not support this functionality.
+ * Anyway, try again after the next inactivity timeout,
+ * but do not disconnect the station now.
+ */
+ next_time = hapd->conf->ap_max_inactivity;
} else if (inactive_sec < hapd->conf->ap_max_inactivity &&
sta->flags & WLAN_STA_ASSOC) {
/* station activity detected; reset timeout state */
- wpa_printf(MSG_DEBUG, " Station has been active");
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+ "Station " MACSTR " has been active %is ago",
+ MAC2STR(sta->addr), inactive_sec);
sta->timeout_next = STA_NULLFUNC;
next_time = hapd->conf->ap_max_inactivity -
inactive_sec;
+ } else {
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+ "Station " MACSTR " has been "
+ "inactive too long: %d sec, max allowed: %d",
+ MAC2STR(sta->addr), inactive_sec,
+ hapd->conf->ap_max_inactivity);
}
}
if ((sta->flags & WLAN_STA_ASSOC) &&
sta->timeout_next == STA_DISASSOC &&
!(sta->flags & WLAN_STA_PENDING_POLL)) {
- wpa_printf(MSG_DEBUG, " Station has ACKed data poll");
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Station " MACSTR
+ " has ACKed data poll", MAC2STR(sta->addr));
/* data nullfunc frame poll did not produce TX errors; assume
* station ACKed it */
sta->timeout_next = STA_NULLFUNC;
if (sta->timeout_next == STA_NULLFUNC &&
(sta->flags & WLAN_STA_ASSOC)) {
-#ifndef CONFIG_NATIVE_WINDOWS
- /* send data frame to poll STA and check whether this frame
- * is ACKed */
- struct ieee80211_hdr hdr;
-
- wpa_printf(MSG_DEBUG, " Polling STA with data frame");
+ wpa_printf(MSG_DEBUG, " Polling STA");
sta->flags |= WLAN_STA_PENDING_POLL;
-
- os_memset(&hdr, 0, sizeof(hdr));
- if (hapd->driver &&
- os_strcmp(hapd->driver->name, "hostap") == 0) {
- /*
- * WLAN_FC_STYPE_NULLFUNC would be more appropriate,
- * but it is apparently not retried so TX Exc events
- * are not received for it.
- */
- hdr.frame_control =
- IEEE80211_FC(WLAN_FC_TYPE_DATA,
- WLAN_FC_STYPE_DATA);
- } else {
- hdr.frame_control =
- IEEE80211_FC(WLAN_FC_TYPE_DATA,
- WLAN_FC_STYPE_NULLFUNC);
- }
-
- hdr.frame_control |= host_to_le16(WLAN_FC_FROMDS);
- os_memcpy(hdr.IEEE80211_DA_FROMDS, sta->addr, ETH_ALEN);
- os_memcpy(hdr.IEEE80211_BSSID_FROMDS, hapd->own_addr,
- ETH_ALEN);
- os_memcpy(hdr.IEEE80211_SA_FROMDS, hapd->own_addr, ETH_ALEN);
-
- if (hapd->drv.send_mgmt_frame(hapd, &hdr, sizeof(hdr)) < 0)
- perror("ap_handle_timer: send");
-#endif /* CONFIG_NATIVE_WINDOWS */
+ hostapd_drv_poll_client(hapd, hapd->own_addr, sta->addr,
+ sta->flags & WLAN_STA_WMM);
} else if (sta->timeout_next != STA_REMOVE) {
int deauth = sta->timeout_next == STA_DEAUTH;
MAC2STR(sta->addr));
if (deauth) {
- hapd->drv.sta_deauth(hapd, sta->addr,
- WLAN_REASON_PREV_AUTH_NOT_VALID);
+ hostapd_drv_sta_deauth(
+ hapd, sta->addr,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
} else {
- hapd->drv.sta_disassoc(
+ hostapd_drv_sta_disassoc(
hapd, sta->addr,
WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY);
}
hapd, sta);
break;
case STA_DISASSOC:
+ ap_sta_set_authorized(hapd, sta, 0);
sta->flags &= ~WLAN_STA_ASSOC;
ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
if (!sta->acct_terminate_cause)
RADIUS_ACCT_TERMINATE_CAUSE_SESSION_TIMEOUT;
os_memcpy(addr, sta->addr, ETH_ALEN);
ap_free_sta(hapd, sta);
- hapd->drv.sta_deauth(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
+ hostapd_drv_sta_deauth(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
}
wpa_printf(MSG_DEBUG, "Removing STA " MACSTR " from kernel driver",
MAC2STR(sta->addr));
- if (hapd->drv.sta_remove(hapd, sta->addr) &&
+ if (hostapd_drv_sta_remove(hapd, sta->addr) &&
sta->flags & WLAN_STA_ASSOC) {
wpa_printf(MSG_DEBUG, "Could not remove station " MACSTR
" from kernel driver.", MAC2STR(sta->addr));
}
+static void ap_sta_disassoc_cb_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct sta_info *sta = timeout_ctx;
+
+ ap_sta_remove(hapd, sta);
+ mlme_disassociate_indication(hapd, sta, sta->disassoc_reason);
+}
+
+
void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta,
u16 reason)
{
wpa_printf(MSG_DEBUG, "%s: disassociate STA " MACSTR,
hapd->conf->iface, MAC2STR(sta->addr));
sta->flags &= ~WLAN_STA_ASSOC;
- ap_sta_remove(hapd, sta);
+ ap_sta_set_authorized(hapd, sta, 0);
sta->timeout_next = STA_DEAUTH;
eloop_cancel_timeout(ap_handle_timer, hapd, sta);
eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DISASSOC, 0,
accounting_sta_stop(hapd, sta);
ieee802_1x_free_station(sta);
- mlme_disassociate_indication(hapd, sta, reason);
+ sta->disassoc_reason = reason;
+ sta->flags |= WLAN_STA_PENDING_DISASSOC_CB;
+ eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta);
+ eloop_register_timeout(hapd->iface->drv_flags &
+ WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? 2 : 0, 0,
+ ap_sta_disassoc_cb_timeout, hapd, sta);
+}
+
+
+static void ap_sta_deauth_cb_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct sta_info *sta = timeout_ctx;
+
+ ap_sta_remove(hapd, sta);
+ mlme_deauthenticate_indication(hapd, sta, sta->deauth_reason);
}
wpa_printf(MSG_DEBUG, "%s: deauthenticate STA " MACSTR,
hapd->conf->iface, MAC2STR(sta->addr));
sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
- ap_sta_remove(hapd, sta);
+ ap_sta_set_authorized(hapd, sta, 0);
sta->timeout_next = STA_REMOVE;
eloop_cancel_timeout(ap_handle_timer, hapd, sta);
eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DEAUTH, 0,
accounting_sta_stop(hapd, sta);
ieee802_1x_free_station(sta);
- mlme_deauthenticate_indication(hapd, sta, reason);
+ sta->deauth_reason = reason;
+ sta->flags |= WLAN_STA_PENDING_DEAUTH_CB;
+ eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta);
+ eloop_register_timeout(hapd->iface->drv_flags &
+ WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? 2 : 0, 0,
+ ap_sta_deauth_cb_timeout, hapd, sta);
}
if (wpa_auth_sta_set_vlan(sta->wpa_sm, sta->vlan_id) < 0)
wpa_printf(MSG_INFO, "Failed to update VLAN-ID for WPA");
- ret = hapd->drv.set_sta_vlan(iface, hapd, sta->addr, sta->vlan_id);
+ ret = hostapd_drv_set_sta_vlan(iface, hapd, sta->addr, sta->vlan_id);
if (ret < 0) {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG, "could not bind the STA "
#endif /* CONFIG_IEEE80211W */
+void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta,
+ int authorized)
+{
+ const u8 *dev_addr = NULL;
+ if (!!authorized == !!(sta->flags & WLAN_STA_AUTHORIZED))
+ return;
+
+#ifdef CONFIG_P2P
+ dev_addr = p2p_group_get_dev_addr(hapd->p2p_group, sta->addr);
+#endif /* CONFIG_P2P */
+
+ if (authorized) {
+ if (dev_addr)
+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED
+ MACSTR " p2p_dev_addr=" MACSTR,
+ MAC2STR(sta->addr), MAC2STR(dev_addr));
+ else
+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED
+ MACSTR, MAC2STR(sta->addr));
+ if (hapd->msg_ctx_parent &&
+ hapd->msg_ctx_parent != hapd->msg_ctx && dev_addr)
+ wpa_msg(hapd->msg_ctx_parent, MSG_INFO,
+ AP_STA_CONNECTED MACSTR " p2p_dev_addr="
+ MACSTR,
+ MAC2STR(sta->addr), MAC2STR(dev_addr));
+ else if (hapd->msg_ctx_parent &&
+ hapd->msg_ctx_parent != hapd->msg_ctx)
+ wpa_msg(hapd->msg_ctx_parent, MSG_INFO,
+ AP_STA_CONNECTED MACSTR, MAC2STR(sta->addr));
+
+ sta->flags |= WLAN_STA_AUTHORIZED;
+ } else {
+ if (dev_addr)
+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED
+ MACSTR " p2p_dev_addr=" MACSTR,
+ MAC2STR(sta->addr), MAC2STR(dev_addr));
+ else
+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED
+ MACSTR, MAC2STR(sta->addr));
+ if (hapd->msg_ctx_parent &&
+ hapd->msg_ctx_parent != hapd->msg_ctx && dev_addr)
+ wpa_msg(hapd->msg_ctx_parent, MSG_INFO,
+ AP_STA_DISCONNECTED MACSTR " p2p_dev_addr="
+ MACSTR, MAC2STR(sta->addr), MAC2STR(dev_addr));
+ else if (hapd->msg_ctx_parent &&
+ hapd->msg_ctx_parent != hapd->msg_ctx)
+ wpa_msg(hapd->msg_ctx_parent, MSG_INFO,
+ AP_STA_DISCONNECTED MACSTR,
+ MAC2STR(sta->addr));
+ sta->flags &= ~WLAN_STA_AUTHORIZED;
+ }
+
+ if (hapd->sta_authorized_cb)
+ hapd->sta_authorized_cb(hapd->sta_authorized_cb_ctx,
+ sta->addr, authorized, dev_addr);
+}
+
+
void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *addr, u16 reason)
{
sta = ap_get_sta(hapd, addr);
if (addr)
- hapd->drv.sta_deauth(hapd, addr, reason);
+ hostapd_drv_sta_deauth(hapd, addr, reason);
if (sta == NULL)
return;
- sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | WLAN_STA_AUTHORIZED);
+ ap_sta_set_authorized(hapd, sta, 0);
+ wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH);
+ ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
+ sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
eloop_cancel_timeout(ap_handle_timer, hapd, sta);
- eloop_register_timeout(0, 0, ap_handle_timer, hapd, sta);
+ eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DEAUTH, 0,
+ ap_handle_timer, hapd, sta);
sta->timeout_next = STA_REMOVE;
+
+ sta->deauth_reason = reason;
+ sta->flags |= WLAN_STA_PENDING_DEAUTH_CB;
+ eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta);
+ eloop_register_timeout(hapd->iface->drv_flags &
+ WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? 2 : 0, 0,
+ ap_sta_deauth_cb_timeout, hapd, sta);
+}
+
+
+void ap_sta_deauth_cb(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ if (!(sta->flags & WLAN_STA_PENDING_DEAUTH_CB)) {
+ wpa_printf(MSG_DEBUG, "Ignore deauth cb for test frame");
+ return;
+ }
+ sta->flags &= ~WLAN_STA_PENDING_DEAUTH_CB;
+ eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta);
+ ap_sta_deauth_cb_timeout(hapd, sta);
+}
+
+
+void ap_sta_disassoc_cb(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ if (!(sta->flags & WLAN_STA_PENDING_DISASSOC_CB)) {
+ wpa_printf(MSG_DEBUG, "Ignore disassoc cb for test frame");
+ return;
+ }
+ sta->flags &= ~WLAN_STA_PENDING_DISASSOC_CB;
+ eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta);
+ ap_sta_disassoc_cb_timeout(hapd, sta);
}
/*
* hostapd / Station table
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2011, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
#define WLAN_STA_WPS BIT(12)
#define WLAN_STA_MAYBE_WPS BIT(13)
#define WLAN_STA_WDS BIT(14)
+#define WLAN_STA_ASSOC_REQ_OK BIT(15)
+#define WLAN_STA_WPS2 BIT(16)
+#define WLAN_STA_PENDING_DISASSOC_CB BIT(29)
+#define WLAN_STA_PENDING_DEAUTH_CB BIT(30)
#define WLAN_STA_NONERP BIT(31)
/* Maximum number of supported rates (from both Supported Rates and Extended
unsigned int no_ht_gf_set:1;
unsigned int no_ht_set:1;
unsigned int ht_20mhz_set:1;
+ unsigned int no_p2p_set:1;
u16 auth_alg;
u8 previous_ap[6];
STA_NULLFUNC = 0, STA_DISASSOC, STA_DEAUTH, STA_REMOVE
} timeout_next;
+ u16 deauth_reason;
+ u16 disassoc_reason;
+
/* IEEE 802.1X related data */
struct eapol_state_machine *eapol_sm;
#endif /* CONFIG_IEEE80211W */
struct wpabuf *wps_ie; /* WPS IE from (Re)Association Request */
+ struct wpabuf *p2p_ie; /* P2P IE from (Re)Association Request */
};
* passed since last received frame from the station, a nullfunc data frame is
* sent to the station. If this frame is not acknowledged and no other frames
* have been received, the station will be disassociated after
- * AP_DISASSOC_DELAY seconds. Similarily, the station will be deauthenticated
+ * AP_DISASSOC_DELAY seconds. Similarly, the station will be deauthenticated
* after AP_DEAUTH_DELAY seconds has passed after disassociation. */
#define AP_MAX_INACTIVITY (5 * 60)
#define AP_DISASSOC_DELAY (1)
void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *addr, u16 reason);
+void ap_sta_set_authorized(struct hostapd_data *hapd,
+ struct sta_info *sta, int authorized);
+static inline int ap_sta_is_authorized(struct sta_info *sta)
+{
+ return sta->flags & WLAN_STA_AUTHORIZED;
+}
+
+void ap_sta_deauth_cb(struct hostapd_data *hapd, struct sta_info *sta);
+void ap_sta_disassoc_cb(struct hostapd_data *hapd, struct sta_info *sta);
+
#endif /* STA_INFO_H */
/*
* hostapd / TKIP countermeasures
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2011, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
#include "sta_info.h"
#include "ap_mlme.h"
#include "wpa_auth.h"
+#include "ap_drv_ops.h"
#include "tkip_countermeasures.h"
{
struct hostapd_data *hapd = eloop_ctx;
hapd->tkip_countermeasures = 0;
- hapd->drv.set_countermeasures(hapd, 0);
+ hostapd_drv_set_countermeasures(hapd, 0);
hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO, "TKIP countermeasures ended");
}
wpa_auth_countermeasures_start(hapd->wpa_auth);
hapd->tkip_countermeasures = 1;
- hapd->drv.set_countermeasures(hapd, 1);
+ hostapd_drv_set_countermeasures(hapd, 1);
wpa_gtk_rekey(hapd->wpa_auth);
eloop_cancel_timeout(ieee80211_tkip_countermeasures_stop, hapd, NULL);
eloop_register_timeout(60, 0, ieee80211_tkip_countermeasures_stop,
hapd, NULL);
for (sta = hapd->sta_list; sta != NULL; sta = sta->next) {
- hapd->drv.sta_deauth(hapd, sta->addr,
- WLAN_REASON_MICHAEL_MIC_FAILURE);
- sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC |
- WLAN_STA_AUTHORIZED);
- hapd->drv.sta_remove(hapd, sta->addr);
+ hostapd_drv_sta_deauth(hapd, sta->addr,
+ WLAN_REASON_MICHAEL_MIC_FAILURE);
+ ap_sta_set_authorized(hapd, sta, 0);
+ sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
+ hostapd_drv_sta_remove(hapd, sta->addr);
}
}
+void ieee80211_tkip_countermeasures_deinit(struct hostapd_data *hapd)
+{
+ eloop_cancel_timeout(ieee80211_tkip_countermeasures_stop, hapd, NULL);
+}
+
+
void michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local)
{
- time_t now;
+ struct os_time now;
if (addr && local) {
struct sta_info *sta = ap_get_sta(hapd, addr);
}
}
- time(&now);
- if (now > hapd->michael_mic_failure + 60) {
+ os_get_time(&now);
+ if (now.sec > hapd->michael_mic_failure + 60) {
hapd->michael_mic_failures = 1;
} else {
hapd->michael_mic_failures++;
if (hapd->michael_mic_failures > 1)
ieee80211_tkip_countermeasures_start(hapd);
}
- hapd->michael_mic_failure = now;
+ hapd->michael_mic_failure = now.sec;
}
/*
* hostapd / TKIP countermeasures
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2011, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
#define TKIP_COUNTERMEASURES_H
void michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local);
+void ieee80211_tkip_countermeasures_deinit(struct hostapd_data *hapd);
#endif /* TKIP_COUNTERMEASURES_H */
int hostapd_register_probereq_cb(struct hostapd_data *hapd,
int (*cb)(void *ctx, const u8 *sa,
+ const u8 *da, const u8 *bssid,
const u8 *ie, size_t ie_len),
void *ctx)
{
#include "utils/common.h"
#include "hostapd.h"
#include "ap_config.h"
+#include "ap_drv_ops.h"
#include "vlan_init.h"
* functions for setting up dynamic broadcast keys. */
for (i = 0; i < 4; i++) {
if (mssid->wep.key[i] &&
- hapd->drv.set_key(dyn_vlan, hapd, WPA_ALG_WEP, NULL, i,
- i == mssid->wep.idx, NULL, 0,
- mssid->wep.key[i], mssid->wep.len[i])) {
+ hostapd_drv_set_key(dyn_vlan, hapd, WPA_ALG_WEP, NULL, i,
+ i == mssid->wep.idx, NULL, 0,
+ mssid->wep.key[i], mssid->wep.len[i]))
+ {
wpa_printf(MSG_ERROR, "VLAN: Could not set WEP "
"encryption for dynamic VLAN");
return -1;
{
while (vlan) {
if (vlan->vlan_id != VLAN_ID_WILDCARD) {
- if (hapd->drv.vlan_if_add(hapd, vlan->ifname)) {
+ if (hostapd_vlan_if_add(hapd, vlan->ifname)) {
if (errno != EEXIST) {
wpa_printf(MSG_ERROR, "VLAN: Could "
"not add VLAN %s: %s",
next = vlan->next;
if (vlan->vlan_id != VLAN_ID_WILDCARD &&
- hapd->drv.vlan_if_remove(hapd, vlan->ifname)) {
+ hostapd_vlan_if_remove(hapd, vlan->ifname)) {
wpa_printf(MSG_ERROR, "VLAN: Could not remove VLAN "
"iface: %s: %s",
vlan->ifname, strerror(errno));
pos);
os_free(ifname);
- if (hapd->drv.vlan_if_add(hapd, n->ifname)) {
+ if (hostapd_vlan_if_add(hapd, n->ifname)) {
os_free(n);
return NULL;
}
return 1;
if (vlan->dynamic_vlan == 0)
- hapd->drv.vlan_if_remove(hapd, vlan->ifname);
+ hostapd_vlan_if_remove(hapd, vlan->ifname);
return 0;
}
#include "ieee802_11.h"
#include "sta_info.h"
#include "ap_config.h"
+#include "ap_drv_ops.h"
#include "wmm.h"
if (hapd->conf->wmm_uapsd)
wmm->qos_info |= 0x80;
+ wmm->reserved = 0;
+
/* fill in a parameter set record for each AC */
for (e = 0; e < 4; e++) {
struct wmm_ac_parameter *ac = &wmm->ac[e];
}
-/* This function is called when a station sends an association request with
- * WMM info element. The function returns zero on success or non-zero on any
- * error in WMM element. eid does not include Element ID and Length octets. */
+/*
+ * This function is called when a station sends an association request with
+ * WMM info element. The function returns 1 on success or 0 on any error in WMM
+ * element. eid does not include Element ID and Length octets.
+ */
int hostapd_eid_wmm_valid(struct hostapd_data *hapd, const u8 *eid, size_t len)
{
struct wmm_information_element *wmm;
if (len < sizeof(struct wmm_information_element)) {
wpa_printf(MSG_DEBUG, "Too short WMM IE (len=%lu)",
(unsigned long) len);
- return -1;
+ return 0;
}
wmm = (struct wmm_information_element *) eid;
if (wmm->oui_subtype != WMM_OUI_SUBTYPE_INFORMATION_ELEMENT ||
wmm->version != WMM_VERSION) {
wpa_printf(MSG_DEBUG, "Unsupported WMM IE Subtype/Version");
- return -1;
+ return 0;
}
- return 0;
+ return 1;
}
os_memcpy(t, tspec, sizeof(struct wmm_tspec_element));
len = ((u8 *) (t + 1)) - buf;
- if (hapd->drv.send_mgmt_frame(hapd, m, len) < 0)
+ if (hostapd_drv_send_mlme(hapd, m, len) < 0)
perror("wmm_send_action: send");
}
#include "crypto/crypto.h"
#include "crypto/sha1.h"
#include "crypto/sha256.h"
+#include "crypto/random.h"
#include "eapol_auth/eapol_auth_sm.h"
#include "ap_config.h"
#include "ieee802_11.h"
static void wpa_request_new_ptk(struct wpa_state_machine *sm);
static int wpa_gtk_update(struct wpa_authenticator *wpa_auth,
struct wpa_group *group);
+static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth,
+ struct wpa_group *group);
static const u32 dot11RSNAConfigGroupUpdateCount = 4;
static const u32 dot11RSNAConfigPairwiseUpdateCount = 4;
{
struct wpa_authenticator *wpa_auth = eloop_ctx;
- if (os_get_random(wpa_auth->group->GMK, WPA_GMK_LEN)) {
+ if (random_get_bytes(wpa_auth->group->GMK, WPA_GMK_LEN)) {
wpa_printf(MSG_ERROR, "Failed to get random data for WPA "
"initialization.");
} else {
wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "GMK rekeyd");
+ wpa_hexdump_key(MSG_DEBUG, "GMK",
+ wpa_auth->group->GMK, WPA_GMK_LEN);
}
if (wpa_auth->conf.wpa_gmk_rekey) {
}
-static struct wpa_group * wpa_group_init(struct wpa_authenticator *wpa_auth,
- int vlan_id)
+static int wpa_group_init_gmk_and_counter(struct wpa_authenticator *wpa_auth,
+ struct wpa_group *group)
{
- struct wpa_group *group;
u8 buf[ETH_ALEN + 8 + sizeof(group)];
u8 rkey[32];
+ if (random_get_bytes(group->GMK, WPA_GMK_LEN) < 0)
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG, "GMK", group->GMK, WPA_GMK_LEN);
+
+ /*
+ * Counter = PRF-256(Random number, "Init Counter",
+ * Local MAC Address || Time)
+ */
+ os_memcpy(buf, wpa_auth->addr, ETH_ALEN);
+ wpa_get_ntp_timestamp(buf + ETH_ALEN);
+ os_memcpy(buf + ETH_ALEN + 8, &group, sizeof(group));
+ if (random_get_bytes(rkey, sizeof(rkey)) < 0)
+ return -1;
+
+ if (sha1_prf(rkey, sizeof(rkey), "Init Counter", buf, sizeof(buf),
+ group->Counter, WPA_NONCE_LEN) < 0)
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG, "Key Counter",
+ group->Counter, WPA_NONCE_LEN);
+
+ return 0;
+}
+
+
+static struct wpa_group * wpa_group_init(struct wpa_authenticator *wpa_auth,
+ int vlan_id, int delay_init)
+{
+ struct wpa_group *group;
+
group = os_zalloc(sizeof(struct wpa_group));
if (group == NULL)
return NULL;
wpa_group_set_key_len(group, wpa_auth->conf.wpa_group);
- /* Counter = PRF-256(Random number, "Init Counter",
- * Local MAC Address || Time)
+ if (random_pool_ready() != 1) {
+ wpa_printf(MSG_INFO, "WPA: Not enough entropy in random pool "
+ "for secure operations - update keys later when "
+ "the first station connects");
+ }
+
+ /*
+ * Set initial GMK/Counter value here. The actual values that will be
+ * used in negotiations will be set once the first station tries to
+ * connect. This allows more time for collecting additional randomness
+ * on embedded devices.
*/
- os_memcpy(buf, wpa_auth->addr, ETH_ALEN);
- wpa_get_ntp_timestamp(buf + ETH_ALEN);
- os_memcpy(buf + ETH_ALEN + 8, &group, sizeof(group));
- if (os_get_random(rkey, sizeof(rkey)) ||
- os_get_random(group->GMK, WPA_GMK_LEN)) {
+ if (wpa_group_init_gmk_and_counter(wpa_auth, group) < 0) {
wpa_printf(MSG_ERROR, "Failed to get random data for WPA "
"initialization.");
os_free(group);
return NULL;
}
- sha1_prf(rkey, sizeof(rkey), "Init Counter", buf, sizeof(buf),
- group->Counter, WPA_NONCE_LEN);
-
group->GInit = TRUE;
- wpa_group_sm_step(wpa_auth, group);
- group->GInit = FALSE;
- wpa_group_sm_step(wpa_auth, group);
+ if (delay_init) {
+ wpa_printf(MSG_DEBUG, "WPA: Delay group state machine start "
+ "until Beacon frames have been configured");
+ /* Initialization is completed in wpa_init_keys(). */
+ } else {
+ wpa_group_sm_step(wpa_auth, group);
+ group->GInit = FALSE;
+ wpa_group_sm_step(wpa_auth, group);
+ }
return group;
}
return NULL;
}
- wpa_auth->group = wpa_group_init(wpa_auth, 0);
+ wpa_auth->group = wpa_group_init(wpa_auth, 0, 1);
if (wpa_auth->group == NULL) {
os_free(wpa_auth->wpa_ie);
os_free(wpa_auth);
}
+int wpa_init_keys(struct wpa_authenticator *wpa_auth)
+{
+ struct wpa_group *group = wpa_auth->group;
+
+ wpa_printf(MSG_DEBUG, "WPA: Start group state machine to set initial "
+ "keys");
+ wpa_group_sm_step(wpa_auth, group);
+ group->GInit = FALSE;
+ wpa_group_sm_step(wpa_auth, group);
+ return 0;
+}
+
+
/**
* wpa_deinit - Deinitialize WPA authenticator
* @wpa_auth: Pointer to WPA authenticator data from wpa_init()
static void wpa_free_sta_sm(struct wpa_state_machine *sm)
{
+ if (sm->GUpdateStationKeys) {
+ sm->group->GKeyDoneStations--;
+ sm->GUpdateStationKeys = FALSE;
+ }
#ifdef CONFIG_IEEE80211R
os_free(sm->assoc_resp_ftie);
#endif /* CONFIG_IEEE80211R */
}
eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm);
+ sm->pending_1_of_4_timeout = 0;
eloop_cancel_timeout(wpa_sm_call_step, sm, NULL);
eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm);
if (sm->in_step_loop) {
#endif /* CONFIG_IEEE80211R */
+static void wpa_receive_error_report(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm, int group)
+{
+ /* Supplicant reported a Michael MIC error */
+ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+ "received EAPOL-Key Error Request "
+ "(STA detected Michael MIC failure (group=%d))",
+ group);
+
+ if (group && wpa_auth->conf.wpa_group != WPA_CIPHER_TKIP) {
+ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+ "ignore Michael MIC failure report since "
+ "group cipher is not TKIP");
+ } else if (!group && sm->pairwise != WPA_CIPHER_TKIP) {
+ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+ "ignore Michael MIC failure report since "
+ "pairwise cipher is not TKIP");
+ } else {
+ wpa_auth_mic_failure_report(wpa_auth, sm->addr);
+ sm->dot11RSNAStatsTKIPRemoteMICFailures++;
+ wpa_auth->dot11RSNAStatsTKIPRemoteMICFailures++;
+ }
+
+ /*
+ * Error report is not a request for a new key handshake, but since
+ * Authenticator may do it, let's change the keys now anyway.
+ */
+ wpa_request_new_ptk(sm);
+}
+
+
void wpa_receive(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm,
u8 *data, size_t data_len)
key = (struct wpa_eapol_key *) (hdr + 1);
key_info = WPA_GET_BE16(key->key_info);
key_data_length = WPA_GET_BE16(key->key_data_length);
+ wpa_printf(MSG_DEBUG, "WPA: Received EAPOL-Key from " MACSTR
+ " key_info=0x%x type=%u key_data_length=%u",
+ MAC2STR(sm->addr), key_info, key->type, key_data_length);
if (key_data_length > data_len - sizeof(*hdr) - sizeof(*key)) {
wpa_printf(MSG_INFO, "WPA: Invalid EAPOL-Key frame - "
"key_data overflow (%d > %lu)",
}
if (sm->wpa == WPA_VERSION_WPA2) {
- if (key->type != EAPOL_KEY_TYPE_RSN) {
+ if (key->type == EAPOL_KEY_TYPE_WPA) {
+ /*
+ * Some deployed station implementations seem to send
+ * msg 4/4 with incorrect type value in WPA2 mode.
+ */
+ wpa_printf(MSG_DEBUG, "Workaround: Allow EAPOL-Key "
+ "with unexpected WPA type in RSN mode");
+ } else if (key->type != EAPOL_KEY_TYPE_RSN) {
wpa_printf(MSG_DEBUG, "Ignore EAPOL-Key with "
"unexpected type %d in RSN mode",
key->type);
}
}
+ wpa_hexdump(MSG_DEBUG, "WPA: Received Key Nonce", key->key_nonce,
+ WPA_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPA: Received Replay Counter",
+ key->replay_counter, WPA_REPLAY_COUNTER_LEN);
+
/* FIX: verify that the EAPOL-Key frame was encrypted if pairwise keys
* are set */
if (!(key_info & WPA_KEY_INFO_REQUEST) &&
!wpa_replay_counter_valid(sm, key->replay_counter)) {
int i;
- wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
"received EAPOL-Key %s with unexpected "
"replay counter", msgtxt);
for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) {
sm->wpa_ptk_state);
return;
}
+ random_add_randomness(key->key_nonce, WPA_NONCE_LEN);
+ if (sm->group->reject_4way_hs_for_entropy) {
+ /*
+ * The system did not have enough entropy to generate
+ * strong random numbers. Reject the first 4-way
+ * handshake(s) and collect some entropy based on the
+ * information from it. Once enough entropy is
+ * available, the next atempt will trigger GMK/Key
+ * Counter update and the station will be allowed to
+ * continue.
+ */
+ wpa_printf(MSG_DEBUG, "WPA: Reject 4-way handshake to "
+ "collect more entropy for random number "
+ "generation");
+ sm->group->reject_4way_hs_for_entropy = FALSE;
+ random_mark_pool_ready();
+ sm->group->first_sta_seen = FALSE;
+ wpa_sta_disconnect(wpa_auth, sm->addr);
+ return;
+ }
if (wpa_parse_kde_ies((u8 *) (key + 1), key_data_length,
&kde) < 0) {
wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
}
sm->MICVerified = TRUE;
eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm);
+ sm->pending_1_of_4_timeout = 0;
}
if (key_info & WPA_KEY_INFO_REQUEST) {
#endif /* CONFIG_PEERKEY */
return;
} else if (key_info & WPA_KEY_INFO_ERROR) {
- /* Supplicant reported a Michael MIC error */
- wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
- "received EAPOL-Key Error Request "
- "(STA detected Michael MIC failure)");
- wpa_auth_mic_failure_report(wpa_auth, sm->addr);
- sm->dot11RSNAStatsTKIPRemoteMICFailures++;
- wpa_auth->dot11RSNAStatsTKIPRemoteMICFailures++;
- /* Error report is not a request for a new key
- * handshake, but since Authenticator may do it, let's
- * change the keys now anyway. */
- wpa_request_new_ptk(sm);
+ wpa_receive_error_report(
+ wpa_auth, sm,
+ !(key_info & WPA_KEY_INFO_KEY_TYPE));
} else if (key_info & WPA_KEY_INFO_KEY_TYPE) {
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
"received EAPOL-Key Request for new "
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
"received EAPOL-Key Request for GTK "
"rekeying");
- /* FIX: why was this triggering PTK rekeying for the
- * STA that requested Group Key rekeying?? */
- /* wpa_request_new_ptk(sta->wpa_sm); */
eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL);
wpa_rekey_gtk(wpa_auth, NULL);
}
os_memcpy(sm->last_rx_eapol_key, data, data_len);
sm->last_rx_eapol_key_len = data_len;
+ sm->rx_eapol_key_secure = !!(key_info & WPA_KEY_INFO_SECURE);
sm->EAPOLKeyReceived = TRUE;
sm->EAPOLKeyPairwise = !!(key_info & WPA_KEY_INFO_KEY_TYPE);
sm->EAPOLKeyRequest = !!(key_info & WPA_KEY_INFO_REQUEST);
}
-static void wpa_gmk_to_gtk(const u8 *gmk, const u8 *addr, const u8 *gnonce,
- u8 *gtk, size_t gtk_len)
+static int wpa_gmk_to_gtk(const u8 *gmk, const char *label, const u8 *addr,
+ const u8 *gnonce, u8 *gtk, size_t gtk_len)
{
- u8 data[ETH_ALEN + WPA_NONCE_LEN];
+ u8 data[ETH_ALEN + WPA_NONCE_LEN + 8 + 16];
+ u8 *pos;
+ int ret = 0;
- /* GTK = PRF-X(GMK, "Group key expansion", AA || GNonce) */
+ /* GTK = PRF-X(GMK, "Group key expansion",
+ * AA || GNonce || Time || random data)
+ * The example described in the IEEE 802.11 standard uses only AA and
+ * GNonce as inputs here. Add some more entropy since this derivation
+ * is done only at the Authenticator and as such, does not need to be
+ * exactly same.
+ */
os_memcpy(data, addr, ETH_ALEN);
os_memcpy(data + ETH_ALEN, gnonce, WPA_NONCE_LEN);
+ pos = data + ETH_ALEN + WPA_NONCE_LEN;
+ wpa_get_ntp_timestamp(pos);
+ pos += 8;
+ if (random_get_bytes(pos, 16) < 0)
+ ret = -1;
#ifdef CONFIG_IEEE80211W
- sha256_prf(gmk, WPA_GMK_LEN, "Group key expansion",
- data, sizeof(data), gtk, gtk_len);
+ sha256_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), gtk, gtk_len);
#else /* CONFIG_IEEE80211W */
- sha1_prf(gmk, WPA_GMK_LEN, "Group key expansion",
- data, sizeof(data), gtk, gtk_len);
+ if (sha1_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), gtk, gtk_len)
+ < 0)
+ ret = -1;
#endif /* CONFIG_IEEE80211W */
- wpa_hexdump_key(MSG_DEBUG, "GMK", gmk, WPA_GMK_LEN);
- wpa_hexdump_key(MSG_DEBUG, "GTK", gtk, gtk_len);
+ return ret;
}
struct wpa_authenticator *wpa_auth = eloop_ctx;
struct wpa_state_machine *sm = timeout_ctx;
+ sm->pending_1_of_4_timeout = 0;
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "EAPOL-Key timeout");
sm->TimeoutEvt = TRUE;
wpa_sm_step(sm);
keyidx, encr, 0);
ctr = pairwise ? sm->TimeoutCtr : sm->GTimeoutCtr;
- if (ctr == 1)
+ if (ctr == 1 && wpa_auth->conf.tx_status)
timeout_ms = eapol_key_timeout_first;
else
timeout_ms = eapol_key_timeout_subseq;
+ if (pairwise && ctr == 1 && !(key_info & WPA_KEY_INFO_MIC))
+ sm->pending_1_of_4_timeout = 1;
+ wpa_printf(MSG_DEBUG, "WPA: Use EAPOL-Key timeout of %u ms (retry "
+ "counter %d)", timeout_ms, ctr);
eloop_register_timeout(timeout_ms / 1000, (timeout_ms % 1000) * 1000,
wpa_send_eapol_timeout, wpa_auth, sm);
}
{
sm->PTK_valid = FALSE;
os_memset(&sm->PTK, 0, sizeof(sm->PTK));
- wpa_auth_set_key(sm->wpa_auth, 0, WPA_ALG_NONE, sm->addr, 0, (u8 *) "",
- 0);
+ wpa_auth_set_key(sm->wpa_auth, 0, WPA_ALG_NONE, sm->addr, 0, NULL, 0);
sm->pairwise_set = FALSE;
eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm);
}
}
+static void wpa_group_first_station(struct wpa_authenticator *wpa_auth,
+ struct wpa_group *group)
+{
+ /*
+ * System has run bit further than at the time hostapd was started
+ * potentially very early during boot up. This provides better chances
+ * of collecting more randomness on embedded systems. Re-initialize the
+ * GMK and Counter here to improve their strength if there was not
+ * enough entropy available immediately after system startup.
+ */
+ wpa_printf(MSG_DEBUG, "WPA: Re-initialize GMK/Counter on first "
+ "station");
+ if (random_pool_ready() != 1) {
+ wpa_printf(MSG_INFO, "WPA: Not enough entropy in random pool "
+ "to proceed - reject first 4-way handshake");
+ group->reject_4way_hs_for_entropy = TRUE;
+ }
+ wpa_group_init_gmk_and_counter(wpa_auth, group);
+ wpa_gtk_update(wpa_auth, group);
+ wpa_group_config_group_keys(wpa_auth, group);
+}
+
+
SM_STATE(WPA_PTK, AUTHENTICATION2)
{
SM_ENTRY_MA(WPA_PTK, AUTHENTICATION2, wpa_ptk);
+
+ if (!sm->group->first_sta_seen) {
+ wpa_group_first_station(sm->wpa_auth, sm->group);
+ sm->group->first_sta_seen = TRUE;
+ }
+
os_memcpy(sm->ANonce, sm->group->Counter, WPA_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPA: Assign ANonce", sm->ANonce,
+ WPA_NONCE_LEN);
inc_byte_array(sm->group->Counter, WPA_NONCE_LEN);
sm->ReAuthenticationRequest = FALSE;
/* IEEE 802.11i does not clear TimeoutCtr here, but this is more
}
#endif /* CONFIG_IEEE80211R */
+ sm->pending_1_of_4_timeout = 0;
eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm);
if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
gtk_len = 0;
keyidx = 0;
_rsc = NULL;
+ if (sm->rx_eapol_key_secure) {
+ /*
+ * It looks like Windows 7 supplicant tries to use
+ * Secure bit in msg 2/4 after having reported Michael
+ * MIC failure and it then rejects the 4-way handshake
+ * if msg 3/4 does not set Secure bit. Work around this
+ * by setting the Secure bit here even in the case of
+ * WPA if the supplicant used it first.
+ */
+ wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+ "STA used Secure bit in WPA msg 2/4 - "
+ "set Secure for 3/4 as workaround");
+ secure = 1;
+ }
}
kde_len = wpa_ie_len + ieee80211w_kde_len(sm);
if (sm->Init)
SM_ENTER(WPA_PTK, INITIALIZE);
else if (sm->Disconnect
- /* || FIX: dot11RSNAConfigSALifetime timeout */)
+ /* || FIX: dot11RSNAConfigSALifetime timeout */) {
+ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
+ "WPA_PTK: sm->Disconnect");
SM_ENTER(WPA_PTK, DISCONNECT);
+ }
else if (sm->DeauthenticationRequest)
SM_ENTER(WPA_PTK, DISCONNECTED);
else if (sm->AuthenticationRequest)
SM_ENTER(WPA_PTK, PTKSTART);
else {
wpa_auth->dot11RSNA4WayHandshakeFailures++;
+ wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO,
+ "INITPMK - keyAvailable = false");
SM_ENTER(WPA_PTK, DISCONNECT);
}
break;
else if (sm->TimeoutCtr >
(int) dot11RSNAConfigPairwiseUpdateCount) {
wpa_auth->dot11RSNA4WayHandshakeFailures++;
+ wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+ "PTKSTART: Retry limit %d reached",
+ dot11RSNAConfigPairwiseUpdateCount);
SM_ENTER(WPA_PTK, DISCONNECT);
} else if (sm->TimeoutEvt)
SM_ENTER(WPA_PTK, PTKSTART);
else if (sm->TimeoutCtr >
(int) dot11RSNAConfigPairwiseUpdateCount) {
wpa_auth->dot11RSNA4WayHandshakeFailures++;
+ wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+ "PTKINITNEGOTIATING: Retry limit %d "
+ "reached",
+ dot11RSNAConfigPairwiseUpdateCount);
SM_ENTER(WPA_PTK, DISCONNECT);
} else if (sm->TimeoutEvt)
SM_ENTER(WPA_PTK, PTKINITNEGOTIATING);
{
int ret = 0;
- /* FIX: is this the correct way of getting GNonce? */
os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN);
inc_byte_array(group->Counter, WPA_NONCE_LEN);
- wpa_gmk_to_gtk(group->GMK, wpa_auth->addr, group->GNonce,
- group->GTK[group->GN - 1], group->GTK_len);
+ if (wpa_gmk_to_gtk(group->GMK, "Group key expansion",
+ wpa_auth->addr, group->GNonce,
+ group->GTK[group->GN - 1], group->GTK_len) < 0)
+ ret = -1;
+ wpa_hexdump_key(MSG_DEBUG, "GTK",
+ group->GTK[group->GN - 1], group->GTK_len);
#ifdef CONFIG_IEEE80211W
if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION) {
- if (os_get_random(group->IGTK[group->GN_igtk - 4],
- WPA_IGTK_LEN) < 0) {
- wpa_printf(MSG_INFO, "RSN: Failed to get new random "
- "IGTK");
+ os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN);
+ inc_byte_array(group->Counter, WPA_NONCE_LEN);
+ if (wpa_gmk_to_gtk(group->GMK, "IGTK key expansion",
+ wpa_auth->addr, group->GNonce,
+ group->IGTK[group->GN_igtk - 4],
+ WPA_IGTK_LEN) < 0)
ret = -1;
- }
wpa_hexdump_key(MSG_DEBUG, "IGTK",
group->IGTK[group->GN_igtk - 4], WPA_IGTK_LEN);
}
if (sm->wpa_ptk_state != WPA_PTK_PTKINITDONE) {
wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
"Not in PTKINITDONE; skip Group Key update");
+ sm->GUpdateStationKeys = FALSE;
return 0;
}
if (sm->GUpdateStationKeys) {
/*
- * This should not really happen, but just in case, make sure
- * we do not count the same STA twice in GKeyDoneStations.
+ * This should not really happen, so add a debug log entry.
+ * Since we clear the GKeyDoneStations before the loop, the
+ * station needs to be counted here anyway.
*/
wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
- "GUpdateStationKeys already set - do not "
- "increment GKeyDoneStations");
- } else {
- sm->group->GKeyDoneStations++;
- sm->GUpdateStationKeys = TRUE;
+ "GUpdateStationKeys was already set when "
+ "marking station for GTK rekeying");
}
+
+ sm->group->GKeyDoneStations++;
+ sm->GUpdateStationKeys = TRUE;
+
wpa_sm_step(sm);
return 0;
}
* including all STAs that could be in not-yet-completed state. */
wpa_gtk_update(wpa_auth, group);
+ if (group->GKeyDoneStations) {
+ wpa_printf(MSG_DEBUG, "wpa_group_setkeys: Unexpected "
+ "GKeyDoneStations=%d when starting new GTK rekey",
+ group->GKeyDoneStations);
+ group->GKeyDoneStations = 0;
+ }
wpa_auth_for_each_sta(wpa_auth, wpa_group_update_sta, NULL);
wpa_printf(MSG_DEBUG, "wpa_group_setkeys: GKeyDoneStations=%d",
group->GKeyDoneStations);
}
-static void wpa_group_setkeysdone(struct wpa_authenticator *wpa_auth,
- struct wpa_group *group)
+static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth,
+ struct wpa_group *group)
+{
+ int ret = 0;
+
+ if (wpa_auth_set_key(wpa_auth, group->vlan_id,
+ wpa_alg_enum(wpa_auth->conf.wpa_group),
+ broadcast_ether_addr, group->GN,
+ group->GTK[group->GN - 1], group->GTK_len) < 0)
+ ret = -1;
+
+#ifdef CONFIG_IEEE80211W
+ if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION &&
+ wpa_auth_set_key(wpa_auth, group->vlan_id, WPA_ALG_IGTK,
+ broadcast_ether_addr, group->GN_igtk,
+ group->IGTK[group->GN_igtk - 4],
+ WPA_IGTK_LEN) < 0)
+ ret = -1;
+#endif /* CONFIG_IEEE80211W */
+
+ return ret;
+}
+
+
+static int wpa_group_setkeysdone(struct wpa_authenticator *wpa_auth,
+ struct wpa_group *group)
{
wpa_printf(MSG_DEBUG, "WPA: group state machine entering state "
"SETKEYSDONE (VLAN-ID %d)", group->vlan_id);
group->changed = TRUE;
group->wpa_group_state = WPA_GROUP_SETKEYSDONE;
- wpa_auth_set_key(wpa_auth, group->vlan_id,
- wpa_alg_enum(wpa_auth->conf.wpa_group),
- NULL, group->GN, group->GTK[group->GN - 1],
- group->GTK_len);
-#ifdef CONFIG_IEEE80211W
- if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION) {
- wpa_auth_set_key(wpa_auth, group->vlan_id, WPA_ALG_IGTK,
- NULL, group->GN_igtk,
- group->IGTK[group->GN_igtk - 4],
- WPA_IGTK_LEN);
- }
-#endif /* CONFIG_IEEE80211W */
+ if (wpa_group_config_group_keys(wpa_auth, group) < 0)
+ return -1;
+
+ return 0;
}
group->GN_igtk = tmp;
#endif /* CONFIG_IEEE80211W */
wpa_gtk_update(wpa_auth, group);
+ wpa_group_config_group_keys(wpa_auth, group);
}
}
{
int len = 0, ret;
char pmkid_txt[PMKID_LEN * 2 + 1];
+#ifdef CONFIG_RSN_PREAUTH
+ const int preauth = 1;
+#else /* CONFIG_RSN_PREAUTH */
+ const int preauth = 0;
+#endif /* CONFIG_RSN_PREAUTH */
if (wpa_auth == NULL)
return len;
ret = os_snprintf(buf + len, buflen - len,
"dot11RSNAOptionImplemented=TRUE\n"
-#ifdef CONFIG_RSN_PREAUTH
- "dot11RSNAPreauthenticationImplemented=TRUE\n"
-#else /* CONFIG_RSN_PREAUTH */
- "dot11RSNAPreauthenticationImplemented=FALSE\n"
-#endif /* CONFIG_RSN_PREAUTH */
+ "dot11RSNAPreauthenticationImplemented=%s\n"
"dot11RSNAEnabled=%s\n"
"dot11RSNAPreauthenticationEnabled=%s\n",
+ wpa_bool_txt(preauth),
wpa_bool_txt(wpa_auth->conf.wpa & WPA_PROTO_RSN),
wpa_bool_txt(wpa_auth->conf.rsn_preauth));
if (ret < 0 || (size_t) ret >= buflen - len)
"dot11RSNAStatsSelectedPairwiseCipher=" RSN_SUITE "\n"
/* TODO: dot11RSNAStatsTKIPICVErrors */
"dot11RSNAStatsTKIPLocalMICFailures=%u\n"
- "dot11RSNAStatsTKIPRemoveMICFailures=%u\n"
+ "dot11RSNAStatsTKIPRemoteMICFailures=%u\n"
/* TODO: dot11RSNAStatsCCMPReplays */
/* TODO: dot11RSNAStatsCCMPDecryptErrors */
/* TODO: dot11RSNAStatsTKIPReplays */,
int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk,
int session_timeout, struct eapol_state_machine *eapol)
{
- if (sm == NULL || sm->wpa != WPA_VERSION_WPA2)
+ if (sm == NULL || sm->wpa != WPA_VERSION_WPA2 ||
+ sm->wpa_auth->conf.disable_pmksa_caching)
return -1;
if (pmksa_cache_auth_add(sm->wpa_auth->pmksa, pmk, PMK_LEN,
wpa_printf(MSG_DEBUG, "WPA: Add group state machine for VLAN-ID %d",
vlan_id);
- group = wpa_group_init(wpa_auth, vlan_id);
+ group = wpa_group_init(wpa_auth, vlan_id, 0);
if (group == NULL)
return NULL;
sm->group = group;
return 0;
}
+
+
+void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm, int ack)
+{
+ if (wpa_auth == NULL || sm == NULL)
+ return;
+ wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key TX status for STA " MACSTR
+ " ack=%d", MAC2STR(sm->addr), ack);
+ if (sm->pending_1_of_4_timeout && ack) {
+ /*
+ * Some deployed supplicant implementations update their SNonce
+ * for each EAPOL-Key 2/4 message even within the same 4-way
+ * handshake and then fail to use the first SNonce when
+ * deriving the PTK. This results in unsuccessful 4-way
+ * handshake whenever the relatively short initial timeout is
+ * reached and EAPOL-Key 1/4 is retransmitted. Try to work
+ * around this by increasing the timeout now that we know that
+ * the station has received the frame.
+ */
+ int timeout_ms = eapol_key_timeout_subseq;
+ wpa_printf(MSG_DEBUG, "WPA: Increase initial EAPOL-Key 1/4 "
+ "timeout by %u ms because of acknowledged frame",
+ timeout_ms);
+ eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm);
+ eloop_register_timeout(timeout_ms / 1000,
+ (timeout_ms % 1000) * 1000,
+ wpa_send_eapol_timeout, wpa_auth, sm);
+ }
+}
int peerkey;
int wmm_enabled;
int wmm_uapsd;
+ int disable_pmksa_caching;
int okc;
+ int tx_status;
#ifdef CONFIG_IEEE80211W
enum mfp_options ieee80211w;
#endif /* CONFIG_IEEE80211W */
struct ft_remote_r0kh *r0kh_list;
struct ft_remote_r1kh *r1kh_list;
int pmk_r1_push;
+ int ft_over_ds;
#endif /* CONFIG_IEEE80211R */
};
struct wpa_authenticator * wpa_init(const u8 *addr,
struct wpa_auth_config *conf,
struct wpa_auth_callbacks *cb);
+int wpa_init_keys(struct wpa_authenticator *wpa_auth);
void wpa_deinit(struct wpa_authenticator *wpa_auth);
int wpa_reconfig(struct wpa_authenticator *wpa_auth,
struct wpa_auth_config *conf);
int session_timeout,
struct eapol_state_machine *eapol);
int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id);
+void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm, int ack);
#ifdef CONFIG_IEEE80211R
u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "crypto/aes_wrap.h"
+#include "crypto/random.h"
#include "ap_config.h"
#include "ieee802_11.h"
#include "wmm.h"
#ifdef CONFIG_IEEE80211R
-struct wpa_ft_ies {
- const u8 *mdie;
- size_t mdie_len;
- const u8 *ftie;
- size_t ftie_len;
- const u8 *r1kh_id;
- const u8 *gtk;
- size_t gtk_len;
- const u8 *r0kh_id;
- size_t r0kh_id_len;
- const u8 *rsn;
- size_t rsn_len;
- const u8 *rsn_pmkid;
- const u8 *ric;
- size_t ric_len;
-};
-
-
-static int wpa_ft_parse_ies(const u8 *ies, size_t ies_len,
- struct wpa_ft_ies *parse);
-
-
static int wpa_ft_rrb_send(struct wpa_authenticator *wpa_auth, const u8 *dst,
const u8 *data, size_t data_len)
{
*pos++ = MOBILITY_DOMAIN_ID_LEN + 1;
os_memcpy(pos, conf->mobility_domain, MOBILITY_DOMAIN_ID_LEN);
pos += MOBILITY_DOMAIN_ID_LEN;
- capab = RSN_FT_CAPAB_FT_OVER_DS;
+ capab = 0;
+ if (conf->ft_over_ds)
+ capab |= RSN_FT_CAPAB_FT_OVER_DS;
*pos++ = capab;
return pos - buf;
/* aes_wrap() does not support inplace encryption, so use a temporary
* buffer for the data. */
- if (os_get_random(f.nonce, sizeof(f.nonce))) {
+ if (random_get_bytes(f.nonce, sizeof(f.nonce))) {
wpa_printf(MSG_DEBUG, "FT: Failed to get random data for "
"nonce");
return -1;
}
-static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len,
- struct wpa_ft_ies *parse)
-{
- const u8 *end, *pos;
-
- parse->ftie = ie;
- parse->ftie_len = ie_len;
-
- pos = ie + sizeof(struct rsn_ftie);
- end = ie + ie_len;
-
- while (pos + 2 <= end && pos + 2 + pos[1] <= end) {
- switch (pos[0]) {
- case FTIE_SUBELEM_R1KH_ID:
- if (pos[1] != FT_R1KH_ID_LEN) {
- wpa_printf(MSG_DEBUG, "FT: Invalid R1KH-ID "
- "length in FTIE: %d", pos[1]);
- return -1;
- }
- parse->r1kh_id = pos + 2;
- break;
- case FTIE_SUBELEM_GTK:
- parse->gtk = pos + 2;
- parse->gtk_len = pos[1];
- break;
- case FTIE_SUBELEM_R0KH_ID:
- if (pos[1] < 1 || pos[1] > FT_R0KH_ID_MAX_LEN) {
- wpa_printf(MSG_DEBUG, "FT: Invalid R0KH-ID "
- "length in FTIE: %d", pos[1]);
- return -1;
- }
- parse->r0kh_id = pos + 2;
- parse->r0kh_id_len = pos[1];
- break;
- }
-
- pos += 2 + pos[1];
- }
-
- return 0;
-}
-
-
-static int wpa_ft_parse_ies(const u8 *ies, size_t ies_len,
- struct wpa_ft_ies *parse)
-{
- const u8 *end, *pos;
- struct wpa_ie_data data;
- int ret;
- const struct rsn_ftie *ftie;
- int prot_ie_count = 0;
-
- os_memset(parse, 0, sizeof(*parse));
- if (ies == NULL)
- return 0;
-
- pos = ies;
- end = ies + ies_len;
- while (pos + 2 <= end && pos + 2 + pos[1] <= end) {
- switch (pos[0]) {
- case WLAN_EID_RSN:
- parse->rsn = pos + 2;
- parse->rsn_len = pos[1];
- ret = wpa_parse_wpa_ie_rsn(parse->rsn - 2,
- parse->rsn_len + 2,
- &data);
- if (ret < 0) {
- wpa_printf(MSG_DEBUG, "FT: Failed to parse "
- "RSN IE: %d", ret);
- return -1;
- }
- if (data.num_pmkid == 1 && data.pmkid)
- parse->rsn_pmkid = data.pmkid;
- break;
- case WLAN_EID_MOBILITY_DOMAIN:
- parse->mdie = pos + 2;
- parse->mdie_len = pos[1];
- break;
- case WLAN_EID_FAST_BSS_TRANSITION:
- if (pos[1] < sizeof(*ftie))
- return -1;
- ftie = (const struct rsn_ftie *) (pos + 2);
- prot_ie_count = ftie->mic_control[1];
- if (wpa_ft_parse_ftie(pos + 2, pos[1], parse) < 0)
- return -1;
- break;
- case WLAN_EID_RIC_DATA:
- if (parse->ric == NULL)
- parse->ric = pos;
- }
-
- pos += 2 + pos[1];
- }
-
- if (prot_ie_count == 0)
- return 0; /* no MIC */
-
- /*
- * Check that the protected IE count matches with IEs included in the
- * frame.
- */
- if (parse->rsn)
- prot_ie_count--;
- if (parse->mdie)
- prot_ie_count--;
- if (parse->ftie)
- prot_ie_count--;
- if (prot_ie_count < 0) {
- wpa_printf(MSG_DEBUG, "FT: Some required IEs not included in "
- "the protected IE count");
- return -1;
- }
-
- if (prot_ie_count == 0 && parse->ric) {
- wpa_printf(MSG_DEBUG, "FT: RIC IE(s) in the frame, but not "
- "included in protected IE count");
- return -1;
- }
-
- /* Determine the end of the RIC IE(s) */
- pos = parse->ric;
- while (pos && pos + 2 <= end && pos + 2 + pos[1] <= end &&
- prot_ie_count) {
- prot_ie_count--;
- pos += 2 + pos[1];
- }
- parse->ric_len = pos - parse->ric;
- if (prot_ie_count) {
- wpa_printf(MSG_DEBUG, "FT: %d protected IEs missing from "
- "frame", (int) prot_ie_count);
- return -1;
- }
-
- return 0;
-}
-
-
static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth,
int vlan_id,
enum wpa_alg alg, const u8 *addr, int idx,
sm->pmk_r1_name_valid = 1;
os_memcpy(sm->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN);
- if (os_get_random(sm->ANonce, WPA_NONCE_LEN)) {
+ if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) {
wpa_printf(MSG_DEBUG, "FT: Failed to get random data for "
"ANonce");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
count = 3;
if (parse.ric)
- count++;
+ count += ieee802_11_ie_count(parse.ric, parse.ric_len);
if (ftie->mic_control[1] != count) {
wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC "
"Control: received %u expected %u",
/*
* hostapd / WPA authenticator glue code
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2011, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
#include "wpa_auth.h"
-#ifdef CONFIG_IEEE80211R
-static void hostapd_rrb_receive(void *ctx, const u8 *src_addr, const u8 *buf,
- size_t len);
-#endif /* CONFIG_IEEE80211R */
-
-
static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
struct wpa_auth_config *wconf)
{
+ os_memset(wconf, 0, sizeof(*wconf));
wconf->wpa = conf->wpa;
wconf->wpa_key_mgmt = conf->wpa_key_mgmt;
wconf->wpa_pairwise = conf->wpa_pairwise;
wconf->peerkey = conf->peerkey;
wconf->wmm_enabled = conf->wmm_enabled;
wconf->wmm_uapsd = conf->wmm_uapsd;
+ wconf->disable_pmksa_caching = conf->disable_pmksa_caching;
wconf->okc = conf->okc;
#ifdef CONFIG_IEEE80211W
wconf->ieee80211w = conf->ieee80211w;
wconf->r0kh_list = conf->r0kh_list;
wconf->r1kh_list = conf->r1kh_list;
wconf->pmk_r1_push = conf->pmk_r1_push;
+ wconf->ft_over_ds = conf->ft_over_ds;
#endif /* CONFIG_IEEE80211R */
}
return -1;
}
- return hapd->drv.set_key(ifname, hapd, alg, addr, idx, 1, NULL, 0,
- key, key_len);
+ return hostapd_drv_set_key(ifname, hapd, alg, addr, idx, 1, NULL, 0,
+ key, key_len);
}
int encrypt)
{
struct hostapd_data *hapd = ctx;
- return hapd->drv.send_eapol(hapd, addr, data, data_len, encrypt);
+ struct sta_info *sta;
+ u32 flags = 0;
+
+ sta = ap_get_sta(hapd, addr);
+ if (sta)
+ flags = hostapd_sta_flags_to_drv(sta->flags);
+
+ return hostapd_drv_hapd_send_eapol(hapd, addr, data, data_len,
+ encrypt, flags);
}
MAC2STR(idata->src_hapd->own_addr),
idata->src_hapd->conf->iface,
MAC2STR(hapd->own_addr), hapd->conf->iface);
- hostapd_rrb_receive(hapd, idata->src_hapd->own_addr,
- idata->data, idata->data_len);
+ wpa_ft_rrb_rx(hapd->wpa_auth,
+ idata->src_hapd->own_addr,
+ idata->data, idata->data_len);
return 1;
}
}
const u8 *data, size_t data_len)
{
struct hostapd_data *hapd = ctx;
+ struct l2_ethhdr *buf;
+ int ret;
#ifdef CONFIG_IEEE80211R
if (proto == ETH_P_RRB && hapd->iface->for_each_interface) {
data, data_len);
if (hapd->l2 == NULL)
return -1;
- return l2_packet_send(hapd->l2, dst, proto, data, data_len);
+
+ buf = os_malloc(sizeof(*buf) + data_len);
+ if (buf == NULL)
+ return -1;
+ os_memcpy(buf->h_dest, dst, ETH_ALEN);
+ os_memcpy(buf->h_source, hapd->own_addr, ETH_ALEN);
+ buf->h_proto = host_to_be16(proto);
+ os_memcpy(buf + 1, data, data_len);
+ ret = l2_packet_send(hapd->l2, dst, proto, (u8 *) buf,
+ sizeof(*buf) + data_len);
+ os_free(buf);
+ return ret;
}
os_memcpy(m->bssid, hapd->own_addr, ETH_ALEN);
os_memcpy(&m->u, data, data_len);
- res = hapd->drv.send_mgmt_frame(hapd, (u8 *) m, mlen);
+ res = hostapd_drv_send_mlme(hapd, (u8 *) m, mlen);
os_free(m);
return res;
}
size_t len)
{
struct hostapd_data *hapd = ctx;
- wpa_ft_rrb_rx(hapd->wpa_auth, src_addr, buf, len);
+ struct l2_ethhdr *ethhdr;
+ if (len < sizeof(*ethhdr))
+ return;
+ ethhdr = (struct l2_ethhdr *) buf;
+ wpa_printf(MSG_DEBUG, "FT: RRB received packet " MACSTR " -> "
+ MACSTR, MAC2STR(ethhdr->h_source), MAC2STR(ethhdr->h_dest));
+ wpa_ft_rrb_rx(hapd->wpa_auth, ethhdr->h_source, buf + sizeof(*ethhdr),
+ len - sizeof(*ethhdr));
}
#endif /* CONFIG_IEEE80211R */
size_t wpa_ie_len;
hostapd_wpa_auth_conf(hapd->conf, &_conf);
+ if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_EAPOL_TX_STATUS)
+ _conf.tx_status = 1;
os_memset(&cb, 0, sizeof(cb));
cb.ctx = hapd;
cb.logger = hostapd_wpa_auth_logger;
hapd->l2 = l2_packet_init(hapd->conf->bridge[0] ?
hapd->conf->bridge :
hapd->conf->iface, NULL, ETH_P_RRB,
- hostapd_rrb_receive, hapd, 0);
+ hostapd_rrb_receive, hapd, 1);
if (hapd->l2 == NULL &&
(hapd->driver == NULL ||
hapd->driver->send_ether == NULL)) {
void hostapd_deinit_wpa(struct hostapd_data *hapd)
{
+ ieee80211_tkip_countermeasures_deinit(hapd);
rsn_preauth_iface_deinit(hapd);
if (hapd->wpa_auth) {
wpa_deinit(hapd->wpa_auth);
unsigned int pending_deinit:1;
unsigned int started:1;
unsigned int mgmt_frame_prot:1;
+ unsigned int rx_eapol_key_secure:1;
#ifdef CONFIG_IEEE80211R
unsigned int ft_completed:1;
unsigned int pmk_r1_name_valid:1;
* message 2/4 */
u8 *assoc_resp_ftie;
#endif /* CONFIG_IEEE80211R */
+
+ int pending_1_of_4_timeout;
};
u8 GTK[2][WPA_GTK_MAX_LEN];
u8 GNonce[WPA_NONCE_LEN];
Boolean changed;
+ Boolean first_sta_seen;
+ Boolean reject_4way_hs_for_entropy;
#ifdef CONFIG_IEEE80211W
u8 IGTK[2][WPA_IGTK_LEN];
int GN_igtk, GM_igtk;
#include "wpa_auth_i.h"
+#ifdef CONFIG_RSN_TESTING
+int rsn_testing = 0;
+#endif /* CONFIG_RSN_TESTING */
+
+
static int wpa_write_wpa_ie(struct wpa_auth_config *conf, u8 *buf, size_t len)
{
struct wpa_ie_hdr *hdr;
count = pos;
pos += 2;
+#ifdef CONFIG_RSN_TESTING
+ if (rsn_testing) {
+ RSN_SELECTOR_PUT(pos, RSN_SELECTOR(0x12, 0x34, 0x56, 1));
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+#endif /* CONFIG_RSN_TESTING */
+
if (conf->rsn_pairwise & WPA_CIPHER_CCMP) {
RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
pos += RSN_SELECTOR_LEN;
num_suites++;
}
+#ifdef CONFIG_RSN_TESTING
+ if (rsn_testing) {
+ RSN_SELECTOR_PUT(pos, RSN_SELECTOR(0x12, 0x34, 0x56, 2));
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+#endif /* CONFIG_RSN_TESTING */
+
if (num_suites == 0) {
wpa_printf(MSG_DEBUG, "Invalid pairwise cipher (%d).",
conf->rsn_pairwise);
count = pos;
pos += 2;
+#ifdef CONFIG_RSN_TESTING
+ if (rsn_testing) {
+ RSN_SELECTOR_PUT(pos, RSN_SELECTOR(0x12, 0x34, 0x56, 1));
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+#endif /* CONFIG_RSN_TESTING */
+
if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X);
pos += RSN_SELECTOR_LEN;
}
#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_RSN_TESTING
+ if (rsn_testing) {
+ RSN_SELECTOR_PUT(pos, RSN_SELECTOR(0x12, 0x34, 0x56, 2));
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+#endif /* CONFIG_RSN_TESTING */
+
if (num_suites == 0) {
wpa_printf(MSG_DEBUG, "Invalid key management type (%d).",
conf->wpa_key_mgmt);
capab |= WPA_CAPABILITY_MFPR;
}
#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_RSN_TESTING
+ if (rsn_testing)
+ capab |= BIT(8) | BIT(14) | BIT(15);
+#endif /* CONFIG_RSN_TESTING */
WPA_PUT_LE16(pos, capab);
pos += 2;
}
#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_RSN_TESTING
+ if (rsn_testing) {
+ /*
+ * Fill in any defined fields and add extra data to the end of
+ * the element.
+ */
+ int pmkid_count_set = pmkid != NULL;
+ if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION)
+ pmkid_count_set = 1;
+ /* PMKID Count */
+ WPA_PUT_LE16(pos, 0);
+ pos += 2;
+ if (conf->ieee80211w == NO_MGMT_FRAME_PROTECTION) {
+ /* Management Group Cipher Suite */
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC);
+ pos += RSN_SELECTOR_LEN;
+ }
+
+ os_memset(pos, 0x12, 17);
+ pos += 17;
+ }
+#endif /* CONFIG_RSN_TESTING */
+
hdr->len = (pos - buf) - 2;
return pos - buf;
}
-static int wpa_selector_to_bitfield(const u8 *s)
-{
- if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_NONE)
- return WPA_CIPHER_NONE;
- if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP40)
- return WPA_CIPHER_WEP40;
- if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_TKIP)
- return WPA_CIPHER_TKIP;
- if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_CCMP)
- return WPA_CIPHER_CCMP;
- if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP104)
- return WPA_CIPHER_WEP104;
- return 0;
-}
-
-
-static int wpa_key_mgmt_to_bitfield(const u8 *s)
-{
- if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_UNSPEC_802_1X)
- return WPA_KEY_MGMT_IEEE8021X;
- if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X)
- return WPA_KEY_MGMT_PSK;
- if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_NONE)
- return WPA_KEY_MGMT_WPA_NONE;
- return 0;
-}
-
-
-static int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len,
- struct wpa_ie_data *data)
-{
- const struct wpa_ie_hdr *hdr;
- const u8 *pos;
- int left;
- int i, count;
-
- os_memset(data, 0, sizeof(*data));
- data->pairwise_cipher = WPA_CIPHER_TKIP;
- data->group_cipher = WPA_CIPHER_TKIP;
- data->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
- data->mgmt_group_cipher = 0;
-
- if (wpa_ie_len < sizeof(struct wpa_ie_hdr))
- return -1;
-
- hdr = (const struct wpa_ie_hdr *) wpa_ie;
-
- if (hdr->elem_id != WLAN_EID_VENDOR_SPECIFIC ||
- hdr->len != wpa_ie_len - 2 ||
- RSN_SELECTOR_GET(hdr->oui) != WPA_OUI_TYPE ||
- WPA_GET_LE16(hdr->version) != WPA_VERSION) {
- return -2;
- }
-
- pos = (const u8 *) (hdr + 1);
- left = wpa_ie_len - sizeof(*hdr);
-
- if (left >= WPA_SELECTOR_LEN) {
- data->group_cipher = wpa_selector_to_bitfield(pos);
- pos += WPA_SELECTOR_LEN;
- left -= WPA_SELECTOR_LEN;
- } else if (left > 0)
- return -3;
-
- if (left >= 2) {
- data->pairwise_cipher = 0;
- count = WPA_GET_LE16(pos);
- pos += 2;
- left -= 2;
- if (count == 0 || left < count * WPA_SELECTOR_LEN)
- return -4;
- for (i = 0; i < count; i++) {
- data->pairwise_cipher |= wpa_selector_to_bitfield(pos);
- pos += WPA_SELECTOR_LEN;
- left -= WPA_SELECTOR_LEN;
- }
- } else if (left == 1)
- return -5;
-
- if (left >= 2) {
- data->key_mgmt = 0;
- count = WPA_GET_LE16(pos);
- pos += 2;
- left -= 2;
- if (count == 0 || left < count * WPA_SELECTOR_LEN)
- return -6;
- for (i = 0; i < count; i++) {
- data->key_mgmt |= wpa_key_mgmt_to_bitfield(pos);
- pos += WPA_SELECTOR_LEN;
- left -= WPA_SELECTOR_LEN;
- }
- } else if (left == 1)
- return -7;
-
- if (left >= 2) {
- data->capabilities = WPA_GET_LE16(pos);
- pos += 2;
- left -= 2;
- }
-
- if (left > 0) {
- return -8;
- }
-
- return 0;
-}
-
-
struct wpa_auth_okc_iter_data {
struct rsn_pmksa_cache_entry *pmksa;
const u8 *aa;
#include "wps/wps_dev_attr.h"
#include "hostapd.h"
#include "ap_config.h"
+#include "ap_drv_ops.h"
#include "beacon.h"
#include "sta_info.h"
#include "wps_hostapd.h"
static void hostapd_wps_upnp_deinit(struct hostapd_data *hapd);
#endif /* CONFIG_WPS_UPNP */
-static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr,
+static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr, const u8 *da,
+ const u8 *bssid,
const u8 *ie, size_t ie_len);
static void hostapd_wps_ap_pin_timeout(void *eloop_data, void *user_ctx);
+struct wps_for_each_data {
+ int (*func)(struct hostapd_data *h, void *ctx);
+ void *ctx;
+};
+
+
+static int wps_for_each(struct hostapd_iface *iface, void *ctx)
+{
+ struct wps_for_each_data *data = ctx;
+ size_t j;
+
+ if (iface == NULL)
+ return 0;
+ for (j = 0; j < iface->num_bss; j++) {
+ struct hostapd_data *hapd = iface->bss[j];
+ int ret = data->func(hapd, data->ctx);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+
+static int hostapd_wps_for_each(struct hostapd_data *hapd,
+ int (*func)(struct hostapd_data *h, void *ctx),
+ void *ctx)
+{
+ struct hostapd_iface *iface = hapd->iface;
+ struct wps_for_each_data data;
+ data.func = func;
+ data.ctx = ctx;
+ if (iface->for_each_interface == NULL)
+ return wps_for_each(iface, &data);
+ return iface->for_each_interface(iface->interfaces, wps_for_each,
+ &data);
+}
+
+
static int hostapd_wps_new_psk_cb(void *ctx, const u8 *mac_addr, const u8 *psk,
size_t psk_len)
{
hapd->wps_beacon_ie = beacon_ie;
wpabuf_free(hapd->wps_probe_resp_ie);
hapd->wps_probe_resp_ie = probe_resp_ie;
- ieee802_11_set_beacon(hapd);
- return hapd->drv.set_ap_wps_ie(hapd);
+ if (hapd->beacon_set_done)
+ ieee802_11_set_beacon(hapd);
+ return hostapd_set_ap_wps_ie(hapd);
}
}
+struct wps_stop_reg_data {
+ struct hostapd_data *current_hapd;
+ const u8 *uuid_e;
+};
+
+static int wps_stop_registrar(struct hostapd_data *hapd, void *ctx)
+{
+ struct wps_stop_reg_data *data = ctx;
+ if (hapd != data->current_hapd && hapd->wps != NULL)
+ wps_registrar_complete(hapd->wps->registrar, data->uuid_e);
+ return 0;
+}
+
+
static void hostapd_wps_reg_success_cb(void *ctx, const u8 *mac_addr,
const u8 *uuid_e)
{
struct hostapd_data *hapd = ctx;
char uuid[40];
+ struct wps_stop_reg_data data;
if (uuid_bin2str(uuid_e, uuid, sizeof(uuid)))
return;
wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_REG_SUCCESS MACSTR " %s",
if (hapd->wps_reg_success_cb)
hapd->wps_reg_success_cb(hapd->wps_reg_success_cb_ctx,
mac_addr, uuid_e);
+ data.current_hapd = hapd;
+ data.uuid_e = uuid_e;
+ hostapd_wps_for_each(hapd, wps_stop_registrar, &data);
}
}
-static int hostapd_wps_cred_cb(void *ctx, const struct wps_credential *cred)
+static void hapd_new_ap_event(struct hostapd_data *hapd, const u8 *attr,
+ size_t attr_len)
{
- struct hostapd_data *hapd = ctx;
+ size_t blen = attr_len * 2 + 1;
+ char *buf = os_malloc(blen);
+ if (buf) {
+ wpa_snprintf_hex(buf, blen, attr, attr_len);
+ wpa_msg(hapd->msg_ctx, MSG_INFO,
+ WPS_EVENT_NEW_AP_SETTINGS "%s", buf);
+ os_free(buf);
+ }
+}
+
+
+static int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx)
+{
+ const struct wps_credential *cred = ctx;
FILE *oconf, *nconf;
size_t len, i;
char *tmp_fname;
int multi_bss;
int wpa;
+ if (hapd->wps == NULL)
+ return 0;
+
wpa_hexdump_key(MSG_DEBUG, "WPS: Received Credential attribute",
cred->cred_attr, cred->cred_attr_len);
if ((hapd->conf->wps_cred_processing == 1 ||
hapd->conf->wps_cred_processing == 2) && cred->cred_attr) {
- size_t blen = cred->cred_attr_len * 2 + 1;
- char *_buf = os_malloc(blen);
- if (_buf) {
- wpa_snprintf_hex(_buf, blen,
- cred->cred_attr, cred->cred_attr_len);
- wpa_msg(hapd->msg_ctx, MSG_INFO, "%s%s",
- WPS_EVENT_NEW_AP_SETTINGS, _buf);
- os_free(_buf);
- }
+ hapd_new_ap_event(hapd, cred->cred_attr, cred->cred_attr_len);
+ } else if (hapd->conf->wps_cred_processing == 1 ||
+ hapd->conf->wps_cred_processing == 2) {
+ struct wpabuf *attr;
+ attr = wpabuf_alloc(200);
+ if (attr && wps_build_credential_wrap(attr, cred) == 0)
+ hapd_new_ap_event(hapd, wpabuf_head_u8(attr),
+ wpabuf_len(attr));
+ wpabuf_free(attr);
} else
wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_NEW_AP_SETTINGS);
if (!multi_bss &&
(str_starts(buf, "ssid=") ||
str_starts(buf, "auth_algs=") ||
+ str_starts(buf, "wep_default_key=") ||
+ str_starts(buf, "wep_key") ||
str_starts(buf, "wps_state=") ||
str_starts(buf, "wpa=") ||
str_starts(buf, "wpa_psk=") ||
eloop_register_timeout(0, 100000, wps_reload_config, hapd->iface,
NULL);
- /* TODO: dualband AP may need to update multiple configuration files */
-
wpa_printf(MSG_DEBUG, "WPS: AP configuration updated");
return 0;
}
+static int hostapd_wps_cred_cb(void *ctx, const struct wps_credential *cred)
+{
+ struct hostapd_data *hapd = ctx;
+ return hostapd_wps_for_each(hapd, hapd_wps_cred_cb, (void *) cred);
+}
+
+
static void hostapd_wps_reenable_ap_pin(void *eloop_data, void *user_ctx)
{
struct hostapd_data *hapd = eloop_data;
if (hapd->conf->ap_setup_locked)
return;
+ if (hapd->ap_pin_failures_consecutive >= 10)
+ return;
wpa_printf(MSG_DEBUG, "WPS: Re-enable AP PIN");
wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_UNLOCKED);
}
-static void hostapd_pwd_auth_fail(struct hostapd_data *hapd,
- struct wps_event_pwd_auth_fail *data)
+static int wps_pwd_auth_fail(struct hostapd_data *hapd, void *ctx)
{
- if (!data->enrollee || hapd->conf->ap_pin == NULL)
- return;
+ struct wps_event_pwd_auth_fail *data = ctx;
+
+ if (!data->enrollee || hapd->conf->ap_pin == NULL || hapd->wps == NULL)
+ return 0;
/*
* Registrar failed to prove its knowledge of the AP PIN. Lock AP setup
* force attacks.
*/
hapd->ap_pin_failures++;
- wpa_printf(MSG_DEBUG, "WPS: AP PIN authentication failure number %u",
- hapd->ap_pin_failures);
+ hapd->ap_pin_failures_consecutive++;
+ wpa_printf(MSG_DEBUG, "WPS: AP PIN authentication failure number %u "
+ "(%u consecutive)",
+ hapd->ap_pin_failures, hapd->ap_pin_failures_consecutive);
if (hapd->ap_pin_failures < 3)
- return;
+ return 0;
wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_LOCKED);
hapd->wps->ap_setup_locked = 1;
wps_registrar_update_ie(hapd->wps->registrar);
- if (!hapd->conf->ap_setup_locked) {
+ if (!hapd->conf->ap_setup_locked &&
+ hapd->ap_pin_failures_consecutive >= 10) {
+ /*
+ * In indefinite lockdown - disable automatic AP PIN
+ * reenablement.
+ */
+ eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL);
+ wpa_printf(MSG_DEBUG, "WPS: AP PIN disabled indefinitely");
+ } else if (!hapd->conf->ap_setup_locked) {
if (hapd->ap_pin_lockout_time == 0)
hapd->ap_pin_lockout_time = 60;
else if (hapd->ap_pin_lockout_time < 365 * 24 * 60 * 60 &&
NULL);
}
- /* TODO: dualband AP may need to update other interfaces */
+ return 0;
+}
+
+
+static void hostapd_pwd_auth_fail(struct hostapd_data *hapd,
+ struct wps_event_pwd_auth_fail *data)
+{
+ hostapd_wps_for_each(hapd, wps_pwd_auth_fail, data);
+}
+
+
+static int wps_ap_pin_success(struct hostapd_data *hapd, void *ctx)
+{
+ if (hapd->conf->ap_pin == NULL || hapd->wps == NULL)
+ return 0;
+
+ if (hapd->ap_pin_failures_consecutive == 0)
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "WPS: Clear consecutive AP PIN failure counter "
+ "- total validation failures %u (%u consecutive)",
+ hapd->ap_pin_failures, hapd->ap_pin_failures_consecutive);
+ hapd->ap_pin_failures_consecutive = 0;
+
+ return 0;
+}
+
+
+static void hostapd_wps_ap_pin_success(struct hostapd_data *hapd)
+{
+ hostapd_wps_for_each(hapd, wps_ap_pin_success, NULL);
+}
+
+
+static const char * wps_event_fail_reason[NUM_WPS_EI_VALUES] = {
+ "No Error", /* WPS_EI_NO_ERROR */
+ "TKIP Only Prohibited", /* WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED */
+ "WEP Prohibited" /* WPS_EI_SECURITY_WEP_PROHIBITED */
+};
+
+static void hostapd_wps_event_fail(struct hostapd_data *hapd,
+ struct wps_event_fail *fail)
+{
+ if (fail->error_indication > 0 &&
+ fail->error_indication < NUM_WPS_EI_VALUES) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO,
+ WPS_EVENT_FAIL "msg=%d config_error=%d reason=%d (%s)",
+ fail->msg, fail->config_error, fail->error_indication,
+ wps_event_fail_reason[fail->error_indication]);
+ } else {
+ wpa_msg(hapd->msg_ctx, MSG_INFO,
+ WPS_EVENT_FAIL "msg=%d config_error=%d",
+ fail->msg, fail->config_error);
+ }
}
{
struct hostapd_data *hapd = ctx;
- if (event == WPS_EV_PWD_AUTH_FAIL)
+ switch (event) {
+ case WPS_EV_M2D:
+ wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_M2D);
+ break;
+ case WPS_EV_FAIL:
+ hostapd_wps_event_fail(hapd, &data->fail);
+ break;
+ case WPS_EV_SUCCESS:
+ wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_SUCCESS);
+ break;
+ case WPS_EV_PWD_AUTH_FAIL:
hostapd_pwd_auth_fail(hapd, &data->pwd_auth_fail);
+ break;
+ case WPS_EV_PBC_OVERLAP:
+ wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_OVERLAP);
+ break;
+ case WPS_EV_PBC_TIMEOUT:
+ wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_TIMEOUT);
+ break;
+ case WPS_EV_ER_AP_ADD:
+ break;
+ case WPS_EV_ER_AP_REMOVE:
+ break;
+ case WPS_EV_ER_ENROLLEE_ADD:
+ break;
+ case WPS_EV_ER_ENROLLEE_REMOVE:
+ break;
+ case WPS_EV_ER_AP_SETTINGS:
+ break;
+ case WPS_EV_ER_SET_SELECTED_REGISTRAR:
+ break;
+ case WPS_EV_AP_PIN_SUCCESS:
+ hostapd_wps_ap_pin_success(hapd);
+ break;
+ }
+ if (hapd->wps_event_cb)
+ hapd->wps_event_cb(hapd->wps_event_cb_ctx, event, data);
}
wpabuf_free(hapd->wps_probe_resp_ie);
hapd->wps_probe_resp_ie = NULL;
- hapd->drv.set_ap_wps_ie(hapd);
+ hostapd_set_ap_wps_ie(hapd);
+}
+
+
+static int get_uuid_cb(struct hostapd_iface *iface, void *ctx)
+{
+ const u8 **uuid = ctx;
+ size_t j;
+
+ if (iface == NULL)
+ return 0;
+ for (j = 0; j < iface->num_bss; j++) {
+ struct hostapd_data *hapd = iface->bss[j];
+ if (hapd->wps && !is_nil_uuid(hapd->wps->uuid)) {
+ *uuid = hapd->wps->uuid;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+
+static const u8 * get_own_uuid(struct hostapd_iface *iface)
+{
+ const u8 *uuid;
+ if (iface->for_each_interface == NULL)
+ return NULL;
+ uuid = NULL;
+ iface->for_each_interface(iface->interfaces, get_uuid_cb, &uuid);
+ return uuid;
+}
+
+
+static int count_interface_cb(struct hostapd_iface *iface, void *ctx)
+{
+ int *count= ctx;
+ (*count)++;
+ return 0;
+}
+
+
+static int interface_count(struct hostapd_iface *iface)
+{
+ int count = 0;
+ if (iface->for_each_interface == NULL)
+ return 0;
+ iface->for_each_interface(iface->interfaces, count_interface_cb,
+ &count);
+ return count;
+}
+
+
+static int hostapd_wps_set_vendor_ext(struct hostapd_data *hapd,
+ struct wps_context *wps)
+{
+ int i;
+
+ for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) {
+ wpabuf_free(wps->dev.vendor_ext[i]);
+ wps->dev.vendor_ext[i] = NULL;
+
+ if (hapd->conf->wps_vendor_ext[i] == NULL)
+ continue;
+
+ wps->dev.vendor_ext[i] =
+ wpabuf_dup(hapd->conf->wps_vendor_ext[i]);
+ if (wps->dev.vendor_ext[i] == NULL) {
+ while (--i >= 0)
+ wpabuf_free(wps->dev.vendor_ext[i]);
+ return -1;
+ }
+ }
+
+ return 0;
}
wps->wps_state = hapd->conf->wps_state;
wps->ap_setup_locked = hapd->conf->ap_setup_locked;
if (is_nil_uuid(hapd->conf->uuid)) {
- uuid_gen_mac_addr(hapd->own_addr, wps->uuid);
- wpa_hexdump(MSG_DEBUG, "WPS: UUID based on MAC address",
- wps->uuid, UUID_LEN);
- } else
+ const u8 *uuid;
+ uuid = get_own_uuid(hapd->iface);
+ if (uuid) {
+ os_memcpy(wps->uuid, uuid, UUID_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: Clone UUID from another "
+ "interface", wps->uuid, UUID_LEN);
+ } else {
+ uuid_gen_mac_addr(hapd->own_addr, wps->uuid);
+ wpa_hexdump(MSG_DEBUG, "WPS: UUID based on MAC "
+ "address", wps->uuid, UUID_LEN);
+ }
+ } else {
os_memcpy(wps->uuid, hapd->conf->uuid, UUID_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: Use configured UUID",
+ wps->uuid, UUID_LEN);
+ }
wps->ssid_len = hapd->conf->ssid.ssid_len;
os_memcpy(wps->ssid, hapd->conf->ssid.ssid, wps->ssid_len);
wps->ap = 1;
os_strdup(hapd->conf->serial_number) : NULL;
wps->config_methods =
wps_config_methods_str2bin(hapd->conf->config_methods);
- if (hapd->conf->device_type &&
- wps_dev_type_str2bin(hapd->conf->device_type,
- wps->dev.pri_dev_type) < 0) {
- wpa_printf(MSG_ERROR, "WPS: Invalid device_type");
+#ifdef CONFIG_WPS2
+ if ((wps->config_methods &
+ (WPS_CONFIG_DISPLAY | WPS_CONFIG_VIRT_DISPLAY |
+ WPS_CONFIG_PHY_DISPLAY)) == WPS_CONFIG_DISPLAY) {
+ wpa_printf(MSG_INFO, "WPS: Converting display to "
+ "virtual_display for WPS 2.0 compliance");
+ wps->config_methods |= WPS_CONFIG_VIRT_DISPLAY;
+ }
+ if ((wps->config_methods &
+ (WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_VIRT_PUSHBUTTON |
+ WPS_CONFIG_PHY_PUSHBUTTON)) == WPS_CONFIG_PUSHBUTTON) {
+ wpa_printf(MSG_INFO, "WPS: Converting push_button to "
+ "virtual_push_button for WPS 2.0 compliance");
+ wps->config_methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
+ }
+#endif /* CONFIG_WPS2 */
+ os_memcpy(wps->dev.pri_dev_type, hapd->conf->device_type,
+ WPS_DEV_TYPE_LEN);
+
+ if (hostapd_wps_set_vendor_ext(hapd, wps) < 0) {
os_free(wps);
return -1;
}
+
wps->dev.os_version = WPA_GET_BE32(hapd->conf->os_version);
wps->dev.rf_bands = hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211A ?
WPS_RF_50GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */
conf->skip_cred_build;
if (conf->ssid.security_policy == SECURITY_STATIC_WEP)
cfg.static_wep_only = 1;
+ cfg.dualband = interface_count(hapd->iface) > 1;
+ if (cfg.dualband)
+ wpa_printf(MSG_DEBUG, "WPS: Dualband AP");
wps->registrar = wps_registrar_init(wps, &cfg);
if (wps->registrar == NULL) {
- printf("Failed to initialize WPS Registrar\n");
+ wpa_printf(MSG_ERROR, "Failed to initialize WPS Registrar");
os_free(wps->network_key);
os_free(wps);
return -1;
wps->model_description = hapd->conf->model_description;
wps->model_url = hapd->conf->model_url;
wps->upc = hapd->conf->upc;
+#endif /* CONFIG_WPS_UPNP */
+ hostapd_register_probereq_cb(hapd, hostapd_wps_probe_req_rx, hapd);
+
+ hapd->wps = wps;
+
+ return 0;
+}
+
+
+int hostapd_init_wps_complete(struct hostapd_data *hapd)
+{
+ struct wps_context *wps = hapd->wps;
+
+ if (wps == NULL)
+ return 0;
+
+#ifdef CONFIG_WPS_UPNP
if (hostapd_wps_upnp_init(hapd, wps) < 0) {
wpa_printf(MSG_ERROR, "Failed to initialize WPS UPnP");
wps_registrar_deinit(wps->registrar);
os_free(wps->network_key);
os_free(wps);
+ hapd->wps = NULL;
return -1;
}
#endif /* CONFIG_WPS_UPNP */
- hostapd_register_probereq_cb(hapd, hostapd_wps_probe_req_rx, hapd);
-
- hapd->wps = wps;
-
return 0;
}
{
if (hapd->wps == NULL)
return;
+
+#ifdef CONFIG_WPS_UPNP
+ hapd->wps->friendly_name = hapd->conf->friendly_name;
+ hapd->wps->manufacturer_url = hapd->conf->manufacturer_url;
+ hapd->wps->model_description = hapd->conf->model_description;
+ hapd->wps->model_url = hapd->conf->model_url;
+ hapd->wps->upc = hapd->conf->upc;
+#endif /* CONFIG_WPS_UPNP */
+
+ hostapd_wps_set_vendor_ext(hapd, hapd->wps);
+
if (hapd->conf->wps_state)
wps_registrar_update_ie(hapd->wps->registrar);
else
}
-int hostapd_wps_add_pin(struct hostapd_data *hapd, const char *uuid,
- const char *pin, int timeout)
+struct wps_add_pin_data {
+ const u8 *addr;
+ const u8 *uuid;
+ const u8 *pin;
+ size_t pin_len;
+ int timeout;
+ int added;
+};
+
+
+static int wps_add_pin(struct hostapd_data *hapd, void *ctx)
{
- u8 u[UUID_LEN];
- int any = 0;
+ struct wps_add_pin_data *data = ctx;
+ int ret;
if (hapd->wps == NULL)
- return -1;
+ return 0;
+ ret = wps_registrar_add_pin(hapd->wps->registrar, data->addr,
+ data->uuid, data->pin, data->pin_len,
+ data->timeout);
+ if (ret == 0)
+ data->added++;
+ return ret;
+}
+
+
+int hostapd_wps_add_pin(struct hostapd_data *hapd, const u8 *addr,
+ const char *uuid, const char *pin, int timeout)
+{
+ u8 u[UUID_LEN];
+ struct wps_add_pin_data data;
+
+ data.addr = addr;
+ data.uuid = u;
+ data.pin = (const u8 *) pin;
+ data.pin_len = os_strlen(pin);
+ data.timeout = timeout;
+ data.added = 0;
+
if (os_strcmp(uuid, "any") == 0)
- any = 1;
- else if (uuid_str2bin(uuid, u))
+ data.uuid = NULL;
+ else {
+ if (uuid_str2bin(uuid, u))
+ return -1;
+ data.uuid = u;
+ }
+ if (hostapd_wps_for_each(hapd, wps_add_pin, &data) < 0)
return -1;
- return wps_registrar_add_pin(hapd->wps->registrar, any ? NULL : u,
- (const u8 *) pin, os_strlen(pin),
- timeout);
+ return data.added ? 0 : -1;
}
-int hostapd_wps_button_pushed(struct hostapd_data *hapd)
+static int wps_button_pushed(struct hostapd_data *hapd, void *ctx)
{
+ const u8 *p2p_dev_addr = ctx;
if (hapd->wps == NULL)
- return -1;
- return wps_registrar_button_pushed(hapd->wps->registrar);
+ return 0;
+ return wps_registrar_button_pushed(hapd->wps->registrar, p2p_dev_addr);
+}
+
+
+int hostapd_wps_button_pushed(struct hostapd_data *hapd,
+ const u8 *p2p_dev_addr)
+{
+ return hostapd_wps_for_each(hapd, wps_button_pushed,
+ (void *) p2p_dev_addr);
}
if ((wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_E ||
wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_R) &&
- hostapd_wps_add_pin(hapd, "any",
+ hostapd_wps_add_pin(hapd, NULL, "any",
wpabuf_head(wps->oob_conf.dev_password), 0) <
0)
goto error;
#endif /* CONFIG_WPS_OOB */
-static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr,
+static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr, const u8 *da,
+ const u8 *bssid,
const u8 *ie, size_t ie_len)
{
struct hostapd_data *hapd = ctx;
wps_ie = ieee802_11_vendor_ie_concat(ie, ie_len, WPS_DEV_OUI_WFA);
if (wps_ie == NULL)
return 0;
+ if (wps_validate_probe_req(wps_ie, addr) < 0) {
+ wpabuf_free(wps_ie);
+ return 0;
+ }
if (wpabuf_len(wps_ie) > 0) {
- wps_registrar_probe_req_rx(hapd->wps->registrar, addr, wps_ie);
+ int p2p_wildcard = 0;
+#ifdef CONFIG_P2P
+ if (elems.ssid && elems.ssid_len == P2P_WILDCARD_SSID_LEN &&
+ os_memcmp(elems.ssid, P2P_WILDCARD_SSID,
+ P2P_WILDCARD_SSID_LEN) == 0)
+ p2p_wildcard = 1;
+#endif /* CONFIG_P2P */
+ wps_registrar_probe_req_rx(hapd->wps->registrar, addr, wps_ie,
+ p2p_wildcard);
#ifdef CONFIG_WPS_UPNP
/* FIX: what exactly should be included in the WLANEvent?
* WPS attributes? Full ProbeReq frame? */
- upnp_wps_device_send_wlan_event(hapd->wps_upnp, addr,
- UPNP_WPS_WLANEVENT_TYPE_PROBE,
- wps_ie);
+ if (!p2p_wildcard)
+ upnp_wps_device_send_wlan_event(
+ hapd->wps_upnp, addr,
+ UPNP_WPS_WLANEVENT_TYPE_PROBE, wps_ie);
#endif /* CONFIG_WPS_UPNP */
}
*/
sta = ap_get_sta(hapd, mac_addr);
+#ifndef CONFIG_WPS_STRICT
if (!sta) {
/*
* Workaround - Intel wsccmd uses bogus NewWLANEventMAC:
break;
}
}
+#endif /* CONFIG_WPS_STRICT */
- if (!sta) {
+ if (!sta || !(sta->flags & WLAN_STA_WPS)) {
wpa_printf(MSG_DEBUG, "WPS UPnP: No matching STA found");
return 0;
}
if (hapd->conf->ap_pin)
ctx->ap_pin = os_strdup(hapd->conf->ap_pin);
- hapd->wps_upnp = upnp_wps_device_init(ctx, wps, hapd);
- if (hapd->wps_upnp == NULL) {
- os_free(ctx);
+ hapd->wps_upnp = upnp_wps_device_init(ctx, wps, hapd,
+ hapd->conf->upnp_iface);
+ if (hapd->wps_upnp == NULL)
return -1;
- }
wps->wps_upnp = hapd->wps_upnp;
- if (upnp_wps_device_start(hapd->wps_upnp, hapd->conf->upnp_iface)) {
- upnp_wps_device_deinit(hapd->wps_upnp);
- hapd->wps_upnp = NULL;
- return -1;
- }
-
return 0;
}
static void hostapd_wps_upnp_deinit(struct hostapd_data *hapd)
{
- upnp_wps_device_deinit(hapd->wps_upnp);
+ upnp_wps_device_deinit(hapd->wps_upnp, hapd);
}
#endif /* CONFIG_WPS_UPNP */
struct hostapd_data *hapd = eloop_data;
wpa_printf(MSG_DEBUG, "WPS: AP PIN timed out");
hostapd_wps_ap_pin_disable(hapd);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_PIN_DISABLED);
}
{
wpa_printf(MSG_DEBUG, "WPS: Enabling AP PIN (timeout=%d)", timeout);
hapd->ap_pin_failures = 0;
+ hapd->ap_pin_failures_consecutive = 0;
hapd->conf->ap_setup_locked = 0;
if (hapd->wps->ap_setup_locked) {
wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_UNLOCKED);
}
-void hostapd_wps_ap_pin_disable(struct hostapd_data *hapd)
+static int wps_ap_pin_disable(struct hostapd_data *hapd, void *ctx)
{
- wpa_printf(MSG_DEBUG, "WPS: Disabling AP PIN");
os_free(hapd->conf->ap_pin);
hapd->conf->ap_pin = NULL;
#ifdef CONFIG_WPS_UPNP
upnp_wps_set_ap_pin(hapd->wps_upnp, NULL);
#endif /* CONFIG_WPS_UPNP */
eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL);
+ return 0;
}
-const char * hostapd_wps_ap_pin_random(struct hostapd_data *hapd, int timeout)
+void hostapd_wps_ap_pin_disable(struct hostapd_data *hapd)
{
- unsigned int pin;
+ wpa_printf(MSG_DEBUG, "WPS: Disabling AP PIN");
+ hostapd_wps_for_each(hapd, wps_ap_pin_disable, NULL);
+}
+
+
+struct wps_ap_pin_data {
char pin_txt[9];
+ int timeout;
+};
- pin = wps_generate_pin();
- os_snprintf(pin_txt, sizeof(pin_txt), "%u", pin);
+
+static int wps_ap_pin_set(struct hostapd_data *hapd, void *ctx)
+{
+ struct wps_ap_pin_data *data = ctx;
os_free(hapd->conf->ap_pin);
- hapd->conf->ap_pin = os_strdup(pin_txt);
+ hapd->conf->ap_pin = os_strdup(data->pin_txt);
#ifdef CONFIG_WPS_UPNP
- upnp_wps_set_ap_pin(hapd->wps_upnp, pin_txt);
+ upnp_wps_set_ap_pin(hapd->wps_upnp, data->pin_txt);
#endif /* CONFIG_WPS_UPNP */
- hostapd_wps_ap_pin_enable(hapd, timeout);
+ hostapd_wps_ap_pin_enable(hapd, data->timeout);
+ return 0;
+}
+
+
+const char * hostapd_wps_ap_pin_random(struct hostapd_data *hapd, int timeout)
+{
+ unsigned int pin;
+ struct wps_ap_pin_data data;
+
+ pin = wps_generate_pin();
+ os_snprintf(data.pin_txt, sizeof(data.pin_txt), "%08u", pin);
+ data.timeout = timeout;
+ hostapd_wps_for_each(hapd, wps_ap_pin_set, &data);
return hapd->conf->ap_pin;
}
int hostapd_wps_ap_pin_set(struct hostapd_data *hapd, const char *pin,
int timeout)
{
- os_free(hapd->conf->ap_pin);
- hapd->conf->ap_pin = os_strdup(pin);
- if (hapd->conf->ap_pin == NULL)
+ struct wps_ap_pin_data data;
+ int ret;
+
+ ret = os_snprintf(data.pin_txt, sizeof(data.pin_txt), "%s", pin);
+ if (ret < 0 || ret >= (int) sizeof(data.pin_txt))
return -1;
-#ifdef CONFIG_WPS_UPNP
- upnp_wps_set_ap_pin(hapd->wps_upnp, hapd->conf->ap_pin);
-#endif /* CONFIG_WPS_UPNP */
- hostapd_wps_ap_pin_enable(hapd, timeout);
+ data.timeout = timeout;
+ return hostapd_wps_for_each(hapd, wps_ap_pin_set, &data);
+}
+
+
+static int wps_update_ie(struct hostapd_data *hapd, void *ctx)
+{
+ if (hapd->wps)
+ wps_registrar_update_ie(hapd->wps->registrar);
return 0;
}
+
+
+void hostapd_wps_update_ie(struct hostapd_data *hapd)
+{
+ hostapd_wps_for_each(hapd, wps_update_ie, NULL);
+}
+
+
+int hostapd_wps_config_ap(struct hostapd_data *hapd, const char *ssid,
+ const char *auth, const char *encr, const char *key)
+{
+ struct wps_credential cred;
+ size_t len;
+
+ os_memset(&cred, 0, sizeof(cred));
+
+ len = os_strlen(ssid);
+ if ((len & 1) || len > 2 * sizeof(cred.ssid) ||
+ hexstr2bin(ssid, cred.ssid, len / 2))
+ return -1;
+ cred.ssid_len = len / 2;
+
+ if (os_strncmp(auth, "OPEN", 4) == 0)
+ cred.auth_type = WPS_AUTH_OPEN;
+ else if (os_strncmp(auth, "WPAPSK", 6) == 0)
+ cred.auth_type = WPS_AUTH_WPAPSK;
+ else if (os_strncmp(auth, "WPA2PSK", 7) == 0)
+ cred.auth_type = WPS_AUTH_WPA2PSK;
+ else
+ return -1;
+
+ if (encr) {
+ if (os_strncmp(encr, "NONE", 4) == 0)
+ cred.encr_type = WPS_ENCR_NONE;
+ else if (os_strncmp(encr, "WEP", 3) == 0)
+ cred.encr_type = WPS_ENCR_WEP;
+ else if (os_strncmp(encr, "TKIP", 4) == 0)
+ cred.encr_type = WPS_ENCR_TKIP;
+ else if (os_strncmp(encr, "CCMP", 4) == 0)
+ cred.encr_type = WPS_ENCR_AES;
+ else
+ return -1;
+ } else
+ cred.encr_type = WPS_ENCR_NONE;
+
+ if (key) {
+ len = os_strlen(key);
+ if ((len & 1) || len > 2 * sizeof(cred.key) ||
+ hexstr2bin(key, cred.key, len / 2))
+ return -1;
+ cred.key_len = len / 2;
+ }
+
+ return wps_registrar_config_ap(hapd->wps->registrar, &cred);
+}
int hostapd_init_wps(struct hostapd_data *hapd,
struct hostapd_bss_config *conf);
+int hostapd_init_wps_complete(struct hostapd_data *hapd);
void hostapd_deinit_wps(struct hostapd_data *hapd);
void hostapd_update_wps(struct hostapd_data *hapd);
-int hostapd_wps_add_pin(struct hostapd_data *hapd, const char *uuid,
- const char *pin, int timeout);
-int hostapd_wps_button_pushed(struct hostapd_data *hapd);
+int hostapd_wps_add_pin(struct hostapd_data *hapd, const u8 *addr,
+ const char *uuid, const char *pin, int timeout);
+int hostapd_wps_button_pushed(struct hostapd_data *hapd,
+ const u8 *p2p_dev_addr);
int hostapd_wps_start_oob(struct hostapd_data *hapd, char *device_type,
char *path, char *method, char *name);
int hostapd_wps_get_mib_sta(struct hostapd_data *hapd, const u8 *addr,
const char * hostapd_wps_ap_pin_get(struct hostapd_data *hapd);
int hostapd_wps_ap_pin_set(struct hostapd_data *hapd, const char *pin,
int timeout);
+void hostapd_wps_update_ie(struct hostapd_data *hapd);
+int hostapd_wps_config_ap(struct hostapd_data *hapd, const char *ssid,
+ const char *auth, const char *encr, const char *key);
#else /* CONFIG_WPS */
{
}
+static inline int hostapd_init_wps_complete(struct hostapd_data *hapd)
+{
+ return 0;
+}
+
static inline void hostapd_update_wps(struct hostapd_data *hapd)
{
}
return 0;
}
-static inline int hostapd_wps_button_pushed(struct hostapd_data *hapd)
+static inline int hostapd_wps_button_pushed(struct hostapd_data *hapd,
+ const u8 *p2p_dev_addr)
{
return 0;
}
static inline int wpa_key_mgmt_wpa_ieee8021x(int akm)
{
- return akm == WPA_KEY_MGMT_IEEE8021X ||
- akm == WPA_KEY_MGMT_FT_IEEE8021X ||
- akm == WPA_KEY_MGMT_IEEE8021X_SHA256;
+ return !!(akm & (WPA_KEY_MGMT_IEEE8021X |
+ WPA_KEY_MGMT_FT_IEEE8021X |
+ WPA_KEY_MGMT_IEEE8021X_SHA256));
}
static inline int wpa_key_mgmt_wpa_psk(int akm)
{
- return akm == WPA_KEY_MGMT_PSK ||
- akm == WPA_KEY_MGMT_FT_PSK ||
- akm == WPA_KEY_MGMT_PSK_SHA256;
+ return !!(akm & (WPA_KEY_MGMT_PSK |
+ WPA_KEY_MGMT_FT_PSK |
+ WPA_KEY_MGMT_PSK_SHA256));
}
static inline int wpa_key_mgmt_ft(int akm)
{
- return akm == WPA_KEY_MGMT_FT_PSK ||
- akm == WPA_KEY_MGMT_FT_IEEE8021X;
+ return !!(akm & (WPA_KEY_MGMT_FT_PSK |
+ WPA_KEY_MGMT_FT_IEEE8021X));
}
static inline int wpa_key_mgmt_sha256(int akm)
{
- return akm == WPA_KEY_MGMT_PSK_SHA256 ||
- akm == WPA_KEY_MGMT_IEEE8021X_SHA256;
+ return !!(akm & (WPA_KEY_MGMT_PSK_SHA256 |
+ WPA_KEY_MGMT_IEEE8021X_SHA256));
+}
+
+static inline int wpa_key_mgmt_wpa(int akm)
+{
+ return wpa_key_mgmt_wpa_ieee8021x(akm) ||
+ wpa_key_mgmt_wpa_psk(akm);
}
WPA_DISCONNECTED,
/**
+ * WPA_INTERFACE_DISABLED - Interface disabled
+ *
+ * This stat eis entered if the network interface is disabled, e.g.,
+ * due to rfkill. wpa_supplicant refuses any new operations that would
+ * use the radio until the interface has been enabled.
+ */
+ WPA_INTERFACE_DISABLED,
+
+ /**
* WPA_INACTIVE - Inactive state (wpa_supplicant disabled)
*
* This state is entered if there are no enabled networks in the
NUM_HOSTAPD_MODES
};
+/**
+ * enum wpa_ctrl_req_type - Control interface request types
+ */
+enum wpa_ctrl_req_type {
+ WPA_CTRL_REQ_UNKNOWN,
+ WPA_CTRL_REQ_EAP_IDENTITY,
+ WPA_CTRL_REQ_EAP_PASSWORD,
+ WPA_CTRL_REQ_EAP_NEW_PASSWORD,
+ WPA_CTRL_REQ_EAP_PIN,
+ WPA_CTRL_REQ_EAP_OTP,
+ WPA_CTRL_REQ_EAP_PASSPHRASE,
+ NUM_WPA_CTRL_REQS
+};
+
#endif /* DEFS_H */
--- /dev/null
+/*
+ * Generic advertisement service (GAS) (IEEE 802.11u)
+ * Copyright (c) 2009, Atheros Communications
+ * Copyright (c) 2011, Qualcomm Atheros
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "ieee802_11_defs.h"
+#include "gas.h"
+
+
+static struct wpabuf *
+gas_build_req(u8 action, u8 dialog_token, size_t size)
+{
+ struct wpabuf *buf;
+
+ buf = wpabuf_alloc(100 + size);
+ if (buf == NULL)
+ return NULL;
+
+ wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC);
+ wpabuf_put_u8(buf, action);
+ wpabuf_put_u8(buf, dialog_token);
+
+ return buf;
+}
+
+
+static struct wpabuf * gas_build_initial_req(u8 dialog_token, size_t size)
+{
+ return gas_build_req(WLAN_PA_GAS_INITIAL_REQ, dialog_token,
+ size);
+}
+
+
+struct wpabuf * gas_build_comeback_req(u8 dialog_token)
+{
+ return gas_build_req(WLAN_PA_GAS_COMEBACK_REQ, dialog_token, 0);
+}
+
+
+static struct wpabuf *
+gas_build_resp(u8 action, u8 dialog_token, u16 status_code, u8 frag_id,
+ u8 more, u16 comeback_delay, size_t size)
+{
+ struct wpabuf *buf;
+
+ buf = wpabuf_alloc(100 + size);
+ if (buf == NULL)
+ return NULL;
+
+ wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC);
+ wpabuf_put_u8(buf, action);
+ wpabuf_put_u8(buf, dialog_token);
+ wpabuf_put_le16(buf, status_code);
+ if (action == WLAN_PA_GAS_COMEBACK_RESP)
+ wpabuf_put_u8(buf, frag_id | (more ? 0x80 : 0));
+ wpabuf_put_le16(buf, comeback_delay);
+
+ return buf;
+}
+
+
+struct wpabuf *
+gas_build_initial_resp(u8 dialog_token, u16 status_code, u16 comeback_delay,
+ size_t size)
+{
+ return gas_build_resp(WLAN_PA_GAS_INITIAL_RESP, dialog_token,
+ status_code, 0, 0, comeback_delay, size);
+}
+
+
+static struct wpabuf *
+gas_build_comeback_resp(u8 dialog_token, u16 status_code, u8 frag_id, u8 more,
+ u16 comeback_delay, size_t size)
+{
+ return gas_build_resp(WLAN_PA_GAS_COMEBACK_RESP, dialog_token,
+ status_code, frag_id, more, comeback_delay,
+ size);
+}
+
+
+/**
+ * gas_add_adv_proto_anqp - Add an Advertisement Protocol element
+ * @buf: Buffer to which the element is added
+ * @query_resp_len_limit: Query Response Length Limit in units of 256 octets
+ * @pame_bi: Pre-Association Message Exchange BSSID Independent (0/1)
+ *
+ *
+ * @query_resp_len_limit is 0 for request and 1-0x7f for response. 0x7f means
+ * that the maximum limit is determined by the maximum allowable number of
+ * fragments in the GAS Query Response Fragment ID.
+ */
+static void gas_add_adv_proto_anqp(struct wpabuf *buf, u8 query_resp_len_limit,
+ u8 pame_bi)
+{
+ /* Advertisement Protocol IE */
+ wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO);
+ wpabuf_put_u8(buf, 2); /* Length */
+ wpabuf_put_u8(buf, (query_resp_len_limit & 0x7f) |
+ (pame_bi ? 0x80 : 0));
+ /* Advertisement Protocol */
+ wpabuf_put_u8(buf, ACCESS_NETWORK_QUERY_PROTOCOL);
+}
+
+
+struct wpabuf * gas_anqp_build_initial_req(u8 dialog_token, size_t size)
+{
+ struct wpabuf *buf;
+
+ buf = gas_build_initial_req(dialog_token, 4 + size);
+ if (buf == NULL)
+ return NULL;
+
+ gas_add_adv_proto_anqp(buf, 0, 0);
+
+ wpabuf_put(buf, 2); /* Query Request Length to be filled */
+
+ return buf;
+}
+
+
+struct wpabuf * gas_anqp_build_initial_resp(u8 dialog_token, u16 status_code,
+ u16 comeback_delay, size_t size)
+{
+ struct wpabuf *buf;
+
+ buf = gas_build_initial_resp(dialog_token, status_code, comeback_delay,
+ 4 + size);
+ if (buf == NULL)
+ return NULL;
+
+ gas_add_adv_proto_anqp(buf, 0x7f, 0);
+
+ wpabuf_put(buf, 2); /* Query Response Length to be filled */
+
+ return buf;
+}
+
+
+struct wpabuf * gas_anqp_build_initial_resp_buf(u8 dialog_token,
+ u16 status_code,
+ u16 comeback_delay,
+ struct wpabuf *payload)
+{
+ struct wpabuf *buf;
+
+ buf = gas_anqp_build_initial_resp(dialog_token, status_code,
+ comeback_delay,
+ payload ? wpabuf_len(payload) : 0);
+ if (buf == NULL)
+ return NULL;
+
+ if (payload)
+ wpabuf_put_buf(buf, payload);
+
+ gas_anqp_set_len(buf);
+
+ return buf;
+}
+
+
+struct wpabuf * gas_anqp_build_comeback_resp(u8 dialog_token, u16 status_code,
+ u8 frag_id, u8 more,
+ u16 comeback_delay, size_t size)
+{
+ struct wpabuf *buf;
+
+ buf = gas_build_comeback_resp(dialog_token, status_code,
+ frag_id, more, comeback_delay, 4 + size);
+ if (buf == NULL)
+ return NULL;
+
+ gas_add_adv_proto_anqp(buf, 0x7f, 0);
+
+ wpabuf_put(buf, 2); /* Query Response Length to be filled */
+
+ return buf;
+}
+
+
+struct wpabuf * gas_anqp_build_comeback_resp_buf(u8 dialog_token,
+ u16 status_code,
+ u8 frag_id, u8 more,
+ u16 comeback_delay,
+ struct wpabuf *payload)
+{
+ struct wpabuf *buf;
+
+ buf = gas_anqp_build_comeback_resp(dialog_token, status_code, frag_id,
+ more, comeback_delay,
+ payload ? wpabuf_len(payload) : 0);
+ if (buf == NULL)
+ return NULL;
+
+ if (payload)
+ wpabuf_put_buf(buf, payload);
+
+ gas_anqp_set_len(buf);
+
+ return buf;
+}
+
+
+/**
+ * gas_anqp_set_len - Set Query Request/Response Length
+ * @buf: GAS message
+ *
+ * This function is used to update the Query Request/Response Length field once
+ * the payload has been filled.
+ */
+void gas_anqp_set_len(struct wpabuf *buf)
+{
+ u8 action;
+ size_t offset;
+ u8 *len;
+
+ if (buf == NULL || wpabuf_len(buf) < 2)
+ return;
+
+ action = *(wpabuf_head_u8(buf) + 1);
+ switch (action) {
+ case WLAN_PA_GAS_INITIAL_REQ:
+ offset = 3 + 4;
+ break;
+ case WLAN_PA_GAS_INITIAL_RESP:
+ offset = 7 + 4;
+ break;
+ case WLAN_PA_GAS_COMEBACK_RESP:
+ offset = 8 + 4;
+ break;
+ default:
+ return;
+ }
+
+ if (wpabuf_len(buf) < offset + 2)
+ return;
+
+ len = wpabuf_mhead_u8(buf) + offset;
+ WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2);
+}
+
+
+/**
+ * gas_anqp_add_element - Add ANQP element header
+ * @buf: GAS message
+ * @info_id: ANQP Info ID
+ * Returns: Pointer to the Length field for gas_anqp_set_element_len()
+ */
+u8 * gas_anqp_add_element(struct wpabuf *buf, u16 info_id)
+{
+ wpabuf_put_le16(buf, info_id);
+ return wpabuf_put(buf, 2); /* Length to be filled */
+}
+
+
+/**
+ * gas_anqp_set_element_len - Update ANQP element Length field
+ * @buf: GAS message
+ * @len_pos: Length field position from gas_anqp_add_element()
+ *
+ * This function is called after the ANQP element payload has been added to the
+ * buffer.
+ */
+void gas_anqp_set_element_len(struct wpabuf *buf, u8 *len_pos)
+{
+ WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(buf, 0) - len_pos - 2);
+}
--- /dev/null
+/*
+ * Generic advertisement service (GAS) (IEEE 802.11u)
+ * Copyright (c) 2009, Atheros Communications
+ * Copyright (c) 2011, Qualcomm Atheros
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef GAS_H
+#define GAS_H
+
+struct wpabuf * gas_build_comeback_req(u8 dialog_token);
+struct wpabuf * gas_build_initial_resp(u8 dialog_token, u16 status_code,
+ u16 comeback_delay, size_t size);
+struct wpabuf * gas_anqp_build_initial_req(u8 dialog_token, size_t size);
+struct wpabuf * gas_anqp_build_initial_resp(u8 dialog_token, u16 status_code,
+ u16 comeback_delay, size_t size);
+struct wpabuf * gas_anqp_build_initial_resp_buf(u8 dialog_token,
+ u16 status_code,
+ u16 comeback_delay,
+ struct wpabuf *payload);
+struct wpabuf * gas_anqp_build_comeback_resp(u8 dialog_token, u16 status_code,
+ u8 frag_id, u8 more,
+ u16 comeback_delay, size_t size);
+struct wpabuf * gas_anqp_build_comeback_resp_buf(u8 dialog_token,
+ u16 status_code,
+ u8 frag_id, u8 more,
+ u16 comeback_delay,
+ struct wpabuf *payload);
+void gas_anqp_set_len(struct wpabuf *buf);
+
+u8 * gas_anqp_add_element(struct wpabuf *buf, u16 info_id);
+void gas_anqp_set_element_len(struct wpabuf *buf, u8 *len_pos);
+
+#endif /* GAS_H */
elems->wmm_tspec_len = elen;
break;
default:
- wpa_printf(MSG_MSGDUMP, "unknown WMM "
+ wpa_printf(MSG_EXCESSIVE, "unknown WMM "
"information element ignored "
"(subtype=%d len=%lu)",
pos[4], (unsigned long) elen);
elems->wps_ie_len = elen;
break;
default:
- wpa_printf(MSG_MSGDUMP, "Unknown Microsoft "
+ wpa_printf(MSG_EXCESSIVE, "Unknown Microsoft "
+ "information element ignored "
+ "(type=%d len=%lu)",
+ pos[3], (unsigned long) elen);
+ return -1;
+ }
+ break;
+
+ case OUI_WFA:
+ switch (pos[3]) {
+ case P2P_OUI_TYPE:
+ /* Wi-Fi Alliance - P2P IE */
+ elems->p2p = pos;
+ elems->p2p_len = elen;
+ break;
+ default:
+ wpa_printf(MSG_MSGDUMP, "Unknown WFA "
"information element ignored "
"(type=%d len=%lu)\n",
pos[3], (unsigned long) elen);
elems->vendor_ht_cap_len = elen;
break;
default:
- wpa_printf(MSG_MSGDUMP, "Unknown Broadcom "
+ wpa_printf(MSG_EXCESSIVE, "Unknown Broadcom "
"information element ignored "
- "(type=%d len=%lu)\n",
+ "(type=%d len=%lu)",
pos[3], (unsigned long) elen);
return -1;
}
break;
default:
- wpa_printf(MSG_MSGDUMP, "unknown vendor specific information "
- "element ignored (vendor OUI %02x:%02x:%02x "
- "len=%lu)",
+ wpa_printf(MSG_EXCESSIVE, "unknown vendor specific "
+ "information element ignored (vendor OUI "
+ "%02x:%02x:%02x len=%lu)",
pos[0], pos[1], pos[2], (unsigned long) elen);
return -1;
}
elems->ht_operation = pos;
elems->ht_operation_len = elen;
break;
+ case WLAN_EID_LINK_ID:
+ if (elen < 18)
+ break;
+ elems->link_id = pos;
+ break;
+ case WLAN_EID_INTERWORKING:
+ elems->interworking = pos;
+ elems->interworking_len = elen;
+ break;
default:
unknown++;
if (!show_errors)
return buf;
}
+
+
+const u8 * get_hdr_bssid(const struct ieee80211_hdr *hdr, size_t len)
+{
+ u16 fc, type, stype;
+
+ /*
+ * PS-Poll frames are 16 bytes. All other frames are
+ * 24 bytes or longer.
+ */
+ if (len < 16)
+ return NULL;
+
+ fc = le_to_host16(hdr->frame_control);
+ type = WLAN_FC_GET_TYPE(fc);
+ stype = WLAN_FC_GET_STYPE(fc);
+
+ switch (type) {
+ case WLAN_FC_TYPE_DATA:
+ if (len < 24)
+ return NULL;
+ switch (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) {
+ case WLAN_FC_FROMDS | WLAN_FC_TODS:
+ case WLAN_FC_TODS:
+ return hdr->addr1;
+ case WLAN_FC_FROMDS:
+ return hdr->addr2;
+ default:
+ return NULL;
+ }
+ case WLAN_FC_TYPE_CTRL:
+ if (stype != WLAN_FC_STYPE_PSPOLL)
+ return NULL;
+ return hdr->addr1;
+ case WLAN_FC_TYPE_MGMT:
+ return hdr->addr3;
+ default:
+ return NULL;
+ }
+}
const u8 *ht_capabilities;
const u8 *ht_operation;
const u8 *vendor_ht_cap;
+ const u8 *p2p;
+ const u8 *link_id;
+ const u8 *interworking;
u8 ssid_len;
u8 supp_rates_len;
u8 ht_capabilities_len;
u8 ht_operation_len;
u8 vendor_ht_cap_len;
+ u8 p2p_len;
+ u8 interworking_len;
};
typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes;
int ieee802_11_ie_count(const u8 *ies, size_t ies_len);
struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len,
u32 oui_type);
+struct ieee80211_hdr;
+const u8 * get_hdr_bssid(const struct ieee80211_hdr *hdr, size_t len);
#endif /* IEEE802_11_COMMON_H */
#define WLAN_FC_STYPE_CFPOLL 6
#define WLAN_FC_STYPE_CFACKPOLL 7
#define WLAN_FC_STYPE_QOS_DATA 8
+#define WLAN_FC_STYPE_QOS_DATA_CFACK 9
+#define WLAN_FC_STYPE_QOS_DATA_CFPOLL 10
+#define WLAN_FC_STYPE_QOS_DATA_CFACKPOLL 11
+#define WLAN_FC_STYPE_QOS_NULL 12
+#define WLAN_FC_STYPE_QOS_CFPOLL 14
+#define WLAN_FC_STYPE_QOS_CFACKPOLL 15
/* Authentication algorithms */
#define WLAN_AUTH_OPEN 0
/* Status codes (IEEE 802.11-2007, 7.3.1.9, Table 7-23) */
#define WLAN_STATUS_SUCCESS 0
#define WLAN_STATUS_UNSPECIFIED_FAILURE 1
+#define WLAN_STATUS_TDLS_WAKEUP_ALTERNATE 2
+#define WLAN_STATUS_TDLS_WAKEUP_REJECT 3
+#define WLAN_STATUS_SECURITY_DISABLED 5
+#define WLAN_STATUS_UNACCEPTABLE_LIFETIME 6
+#define WLAN_STATUS_NOT_IN_SAME_BSS 7
#define WLAN_STATUS_CAPS_UNSUPPORTED 10
#define WLAN_STATUS_REASSOC_NO_ASSOC 11
#define WLAN_STATUS_ASSOC_DENIED_UNSPEC 12
#define WLAN_STATUS_SUPPORTED_CHANNEL_NOT_VALID 24
/* IEEE 802.11g */
#define WLAN_STATUS_ASSOC_DENIED_NO_SHORT_SLOT_TIME 25
-#define WLAN_STATUS_ASSOC_DENIED_NO_ER_PBCC 26
-#define WLAN_STATUS_ASSOC_DENIED_NO_DSSS_OFDM 27
+#define WLAN_STATUS_ASSOC_DENIED_NO_DSSS_OFDM 26
+#define WLAN_STATUS_ASSOC_DENIED_NO_HT 27
#define WLAN_STATUS_R0KH_UNREACHABLE 28
+#define WLAN_STATUS_ASSOC_DENIED_NO_PCO 29
/* IEEE 802.11w */
#define WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY 30
#define WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION 31
#define WLAN_STATUS_INVALID_PMKID 53
#define WLAN_STATUS_INVALID_MDIE 54
#define WLAN_STATUS_INVALID_FTIE 55
+#define WLAN_STATUS_GAS_ADV_PROTO_NOT_SUPPORTED 59
+#define WLAN_STATUS_NO_OUTSTANDING_GAS_REQ 60
+#define WLAN_STATUS_GAS_RESP_NOT_RECEIVED 61
+#define WLAN_STATUS_STA_TIMED_OUT_WAITING_FOR_GAS_RESP 62
+#define WLAN_STATUS_GAS_RESP_LARGER_THAN_LIMIT 63
+#define WLAN_STATUS_REQ_REFUSED_HOME 64
+#define WLAN_STATUS_ADV_SRV_UNREACHABLE 65
+#define WLAN_STATUS_REQ_REFUSED_SSPN 67
+#define WLAN_STATUS_REQ_REFUSED_UNAUTH_ACCESS 68
+#define WLAN_STATUS_INVALID_RSNIE 72
+#define WLAN_STATUS_TRANSMISSION_FAILURE 79
/* Reason codes (IEEE 802.11-2007, 7.3.1.7, Table 7-22) */
#define WLAN_REASON_UNSPECIFIED 1
#define WLAN_REASON_INVALID_RSN_IE_CAPAB 22
#define WLAN_REASON_IEEE_802_1X_AUTH_FAILED 23
#define WLAN_REASON_CIPHER_SUITE_REJECTED 24
+#define WLAN_REASON_TDLS_TEARDOWN_UNREACHABLE 25
+#define WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED 26
+/* IEEE 802.11e */
+#define WLAN_REASON_DISASSOC_LOW_ACK 34
/* Information Element IDs */
#define WLAN_EID_RIC_DATA 57
#define WLAN_EID_HT_OPERATION 61
#define WLAN_EID_SECONDARY_CHANNEL_OFFSET 62
+#define WLAN_EID_TIME_ADVERTISEMENT 69
#define WLAN_EID_20_40_BSS_COEXISTENCE 72
#define WLAN_EID_20_40_BSS_INTOLERANT 73
#define WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS 74
#define WLAN_EID_MMIE 76
+#define WLAN_EID_TIME_ZONE 98
+#define WLAN_EID_LINK_ID 101
+#define WLAN_EID_INTERWORKING 107
+#define WLAN_EID_ADV_PROTO 108
+#define WLAN_EID_ROAMING_CONSORTIUM 111
+#define WLAN_EID_EXT_CAPAB 127
#define WLAN_EID_VENDOR_SPECIFIC 221
#define WLAN_ACTION_FT 6
#define WLAN_ACTION_HT 7
#define WLAN_ACTION_SA_QUERY 8
+#define WLAN_ACTION_WNM 10
+#define WLAN_ACTION_UNPROTECTED_WNM 11
+#define WLAN_ACTION_TDLS 12
#define WLAN_ACTION_WMM 17 /* WMM Specification 1.1 */
+#define WLAN_ACTION_VENDOR_SPECIFIC 127
+
+/* Public action codes */
+#define WLAN_PA_VENDOR_SPECIFIC 9
+#define WLAN_PA_GAS_INITIAL_REQ 10
+#define WLAN_PA_GAS_INITIAL_RESP 11
+#define WLAN_PA_GAS_COMEBACK_REQ 12
+#define WLAN_PA_GAS_COMEBACK_RESP 13
+#define WLAN_TDLS_DISCOVERY_RESPONSE 14
/* SA Query Action frame (IEEE 802.11w/D8.0, 7.4.9) */
#define WLAN_SA_QUERY_REQUEST 0
#define WLAN_SA_QUERY_TR_ID_LEN 2
+/* TDLS action codes */
+#define WLAN_TDLS_SETUP_REQUEST 0
+#define WLAN_TDLS_SETUP_RESPONSE 1
+#define WLAN_TDLS_SETUP_CONFIRM 2
+#define WLAN_TDLS_TEARDOWN 3
+#define WLAN_TDLS_PEER_TRAFFIC_INDICATION 4
+#define WLAN_TDLS_CHANNEL_SWITCH_REQUEST 5
+#define WLAN_TDLS_CHANNEL_SWITCH_RESPONSE 6
+#define WLAN_TDLS_PEER_PSM_REQUEST 7
+#define WLAN_TDLS_PEER_PSM_RESPONSE 8
+#define WLAN_TDLS_PEER_TRAFFIC_RESPONSE 9
+#define WLAN_TDLS_DISCOVERY_REQUEST 10
+
/* Timeout Interval Type */
#define WLAN_TIMEOUT_REASSOC_DEADLINE 1
#define WLAN_TIMEOUT_KEY_LIFETIME 2
#define WLAN_TIMEOUT_ASSOC_COMEBACK 3
+/* Interworking element (IEEE 802.11u) - Access Network Options */
+#define INTERWORKING_ANO_ACCESS_NETWORK_MASK 0x0f
+#define INTERWORKING_ANO_INTERNET 0x10
+#define INTERWORKING_ANO_ASRA 0x20
+#define INTERWORKING_ANO_ESR 0x40
+#define INTERWORKING_ANO_UESA 0x80
+
+#define INTERWORKING_ANT_PRIVATE 0
+#define INTERWORKING_ANT_PRIVATE_WITH_GUEST 1
+#define INTERWORKING_ANT_CHARGEABLE_PUBLIC 2
+#define INTERWORKING_ANT_FREE_PUBLIC 3
+#define INTERWORKING_ANT_PERSONAL_DEVICE 4
+#define INTERWORKING_ANT_EMERGENCY_SERVICES 5
+#define INTERWORKING_ANT_TEST 6
+#define INTERWORKING_ANT_WILDCARD 15
+
+/* Advertisement Protocol ID definitions (IEEE Std 802.11u-2011) */
+enum adv_proto_id {
+ ACCESS_NETWORK_QUERY_PROTOCOL = 0,
+ MIH_INFO_SERVICE = 1,
+ MIH_CMD_AND_EVENT_DISCOVERY = 2,
+ EMERGENCY_ALERT_SYSTEM = 3,
+ ADV_PROTO_VENDOR_SPECIFIC = 221
+};
+
+/* Access Network Query Protocol info ID definitions (IEEE Std 802.11u-2011) */
+enum anqp_info_id {
+ ANQP_QUERY_LIST = 256,
+ ANQP_CAPABILITY_LIST = 257,
+ ANQP_VENUE_NAME = 258,
+ ANQP_EMERGENCY_CALL_NUMBER = 259,
+ ANQP_NETWORK_AUTH_TYPE = 260,
+ ANQP_ROAMING_CONSORTIUM = 261,
+ ANQP_IP_ADDR_TYPE_AVAILABILITY = 262,
+ ANQP_NAI_REALM = 263,
+ ANQP_3GPP_CELLULAR_NETWORK = 264,
+ ANQP_AP_GEOSPATIAL_LOCATION = 265,
+ ANQP_AP_CIVIC_LOCATION = 266,
+ ANQP_AP_LOCATION_PUBLIC_URI = 267,
+ ANQP_DOMAIN_NAME = 268,
+ ANQP_EMERGENCY_ALERT_URI = 269,
+ ANQP_EMERGENCY_NAI = 271,
+ ANQP_VENDOR_SPECIFIC = 56797
+};
+
+/* NAI Realm list - EAP Method subfield - Authentication Parameter ID */
+enum nai_realm_eap_auth_param {
+ NAI_REALM_EAP_AUTH_EXPANDED_EAP_METHOD = 1,
+ NAI_REALM_EAP_AUTH_NON_EAP_INNER_AUTH = 2,
+ NAI_REALM_EAP_AUTH_INNER_AUTH_EAP_METHOD = 3,
+ NAI_REALM_EAP_AUTH_EXPANDED_INNER_EAP_METHOD = 4,
+ NAI_REALM_EAP_AUTH_CRED_TYPE = 5,
+ NAI_REALM_EAP_AUTH_TUNNELED_CRED_TYPE = 6,
+ NAI_REALM_EAP_AUTH_VENDOR_SPECIFIC = 221
+};
+
+enum nai_realm_eap_auth_inner_non_eap {
+ NAI_REALM_INNER_NON_EAP_PAP = 1,
+ NAI_REALM_INNER_NON_EAP_CHAP = 2,
+ NAI_REALM_INNER_NON_EAP_MSCHAP = 3,
+ NAI_REALM_INNER_NON_EAP_MSCHAPV2 = 4
+};
+
+enum nai_realm_eap_cred_type {
+ NAI_REALM_CRED_TYPE_SIM = 1,
+ NAI_REALM_CRED_TYPE_USIM = 2,
+ NAI_REALM_CRED_TYPE_NFC_SECURE_ELEMENT = 3,
+ NAI_REALM_CRED_TYPE_HARDWARE_TOKEN = 4,
+ NAI_REALM_CRED_TYPE_SOFTOKEN = 5,
+ NAI_REALM_CRED_TYPE_CERTIFICATE = 6,
+ NAI_REALM_CRED_TYPE_USERNAME_PASSWORD = 7,
+ NAI_REALM_CRED_TYPE_NONE = 8,
+ NAI_REALM_CRED_TYPE_ANONYMOUS = 9,
+ NAI_REALM_CRED_TYPE_VENDOR_SPECIFIC = 10
+};
#ifdef _MSC_VER
#pragma pack(push, 1)
} STRUCT_PACKED auth;
struct {
le16 reason_code;
+ u8 variable[0];
} STRUCT_PACKED deauth;
struct {
le16 capab_info;
} STRUCT_PACKED reassoc_req;
struct {
le16 reason_code;
+ u8 variable[0];
} STRUCT_PACKED disassoc;
struct {
u8 timestamp[8];
u8 action; /* */
u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN];
} STRUCT_PACKED sa_query_resp;
+ struct {
+ u8 action;
+ u8 variable[0];
+ } STRUCT_PACKED public_action;
+ struct {
+ u8 action; /* 9 */
+ u8 oui[3];
+ /* Vendor-specific content */
+ u8 variable[0];
+ } STRUCT_PACKED vs_public_action;
+ struct {
+ u8 action; /* 7 */
+ u8 dialog_token;
+ u8 req_mode;
+ le16 disassoc_timer;
+ u8 validity_interval;
+ /* BSS Termination Duration (optional),
+ * Session Information URL (optional),
+ * BSS Transition Candidate List
+ * Entries */
+ u8 variable[0];
+ } STRUCT_PACKED bss_tm_req;
} u;
} STRUCT_PACKED action;
} u;
#define HT_INFO_STBC_PARAM_PCO_ACTIVE ((u16) BIT(10))
#define HT_INFO_STBC_PARAM_PCO_PHASE ((u16) BIT(11))
+#define BSS_MEMBERSHIP_SELECTOR_HT_PHY 127
#define OUI_MICROSOFT 0x0050f2 /* Microsoft (also used in Wi-Fi specs)
* 00:50:F2 */
#define WPA_IE_VENDOR_TYPE 0x0050f201
#define WPS_IE_VENDOR_TYPE 0x0050f204
+#define OUI_WFA 0x506f9a
+#define P2P_IE_VENDOR_TYPE 0x506f9a09
#define WMM_OUI_TYPE 2
#define WMM_OUI_SUBTYPE_INFORMATION_ELEMENT 0
u8 oui_type; /* 2 */
u8 oui_subtype; /* 1 */
u8 version; /* 1 for WMM version 1.0 */
- u8 qos_info; /* AP/STA specif QoS info */
+ u8 qos_info; /* AP/STA specific QoS info */
u8 reserved; /* 0 */
struct wmm_ac_parameter ac[4]; /* AC_BE, AC_BK, AC_VI, AC_VO */
};
+/* Wi-Fi Direct (P2P) */
+
+#define P2P_OUI_TYPE 9
+
+enum p2p_attr_id {
+ P2P_ATTR_STATUS = 0,
+ P2P_ATTR_MINOR_REASON_CODE = 1,
+ P2P_ATTR_CAPABILITY = 2,
+ P2P_ATTR_DEVICE_ID = 3,
+ P2P_ATTR_GROUP_OWNER_INTENT = 4,
+ P2P_ATTR_CONFIGURATION_TIMEOUT = 5,
+ P2P_ATTR_LISTEN_CHANNEL = 6,
+ P2P_ATTR_GROUP_BSSID = 7,
+ P2P_ATTR_EXT_LISTEN_TIMING = 8,
+ P2P_ATTR_INTENDED_INTERFACE_ADDR = 9,
+ P2P_ATTR_MANAGEABILITY = 10,
+ P2P_ATTR_CHANNEL_LIST = 11,
+ P2P_ATTR_NOTICE_OF_ABSENCE = 12,
+ P2P_ATTR_DEVICE_INFO = 13,
+ P2P_ATTR_GROUP_INFO = 14,
+ P2P_ATTR_GROUP_ID = 15,
+ P2P_ATTR_INTERFACE = 16,
+ P2P_ATTR_OPERATING_CHANNEL = 17,
+ P2P_ATTR_INVITATION_FLAGS = 18,
+ P2P_ATTR_VENDOR_SPECIFIC = 221
+};
+
+#define P2P_MAX_GO_INTENT 15
+
+/* P2P Capability - Device Capability bitmap */
+#define P2P_DEV_CAPAB_SERVICE_DISCOVERY BIT(0)
+#define P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY BIT(1)
+#define P2P_DEV_CAPAB_CONCURRENT_OPER BIT(2)
+#define P2P_DEV_CAPAB_INFRA_MANAGED BIT(3)
+#define P2P_DEV_CAPAB_DEVICE_LIMIT BIT(4)
+#define P2P_DEV_CAPAB_INVITATION_PROCEDURE BIT(5)
+
+/* P2P Capability - Group Capability bitmap */
+#define P2P_GROUP_CAPAB_GROUP_OWNER BIT(0)
+#define P2P_GROUP_CAPAB_PERSISTENT_GROUP BIT(1)
+#define P2P_GROUP_CAPAB_GROUP_LIMIT BIT(2)
+#define P2P_GROUP_CAPAB_INTRA_BSS_DIST BIT(3)
+#define P2P_GROUP_CAPAB_CROSS_CONN BIT(4)
+#define P2P_GROUP_CAPAB_PERSISTENT_RECONN BIT(5)
+#define P2P_GROUP_CAPAB_GROUP_FORMATION BIT(6)
+
+/* Invitation Flags */
+#define P2P_INVITATION_FLAGS_TYPE BIT(0)
+
+/* P2P Manageability */
+#define P2P_MAN_DEVICE_MANAGEMENT BIT(0)
+#define P2P_MAN_CROSS_CONNECTION_PERMITTED BIT(1)
+#define P2P_MAN_COEXISTENCE_OPTIONAL BIT(2)
+
+enum p2p_status_code {
+ P2P_SC_SUCCESS = 0,
+ P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE = 1,
+ P2P_SC_FAIL_INCOMPATIBLE_PARAMS = 2,
+ P2P_SC_FAIL_LIMIT_REACHED = 3,
+ P2P_SC_FAIL_INVALID_PARAMS = 4,
+ P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE = 5,
+ P2P_SC_FAIL_PREV_PROTOCOL_ERROR = 6,
+ P2P_SC_FAIL_NO_COMMON_CHANNELS = 7,
+ P2P_SC_FAIL_UNKNOWN_GROUP = 8,
+ P2P_SC_FAIL_BOTH_GO_INTENT_15 = 9,
+ P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD = 10,
+ P2P_SC_FAIL_REJECTED_BY_USER = 11,
+};
+
+#define P2P_WILDCARD_SSID "DIRECT-"
+#define P2P_WILDCARD_SSID_LEN 7
+
+/* P2P action frames */
+enum p2p_act_frame_type {
+ P2P_NOA = 0,
+ P2P_PRESENCE_REQ = 1,
+ P2P_PRESENCE_RESP = 2,
+ P2P_GO_DISC_REQ = 3
+};
+
+/* P2P public action frames */
+enum p2p_action_frame_type {
+ P2P_GO_NEG_REQ = 0,
+ P2P_GO_NEG_RESP = 1,
+ P2P_GO_NEG_CONF = 2,
+ P2P_INVITATION_REQ = 3,
+ P2P_INVITATION_RESP = 4,
+ P2P_DEV_DISC_REQ = 5,
+ P2P_DEV_DISC_RESP = 6,
+ P2P_PROV_DISC_REQ = 7,
+ P2P_PROV_DISC_RESP = 8
+};
+
+enum p2p_service_protocol_type {
+ P2P_SERV_ALL_SERVICES = 0,
+ P2P_SERV_BONJOUR = 1,
+ P2P_SERV_UPNP = 2,
+ P2P_SERV_WS_DISCOVERY = 3,
+ P2P_SERV_VENDOR_SPECIFIC = 255
+};
+
+enum p2p_sd_status {
+ P2P_SD_SUCCESS = 0,
+ P2P_SD_PROTO_NOT_AVAILABLE = 1,
+ P2P_SD_REQUESTED_INFO_NOT_AVAILABLE = 2,
+ P2P_SD_BAD_REQUEST = 3
+};
+
+
#define OUI_BROADCOM 0x00904c /* Broadcom (Epigram) */
#define VENDOR_HT_CAPAB_OUI_TYPE 0x33 /* 00-90-4c:0x33 */
#define WLAN_AKM_SUITE_8021X 0x000FAC01
#define WLAN_AKM_SUITE_PSK 0x000FAC02
+
+/* IEEE 802.11v - WNM Action field values */
+enum wnm_action {
+ WNM_EVENT_REQ = 0,
+ WNM_EVENT_REPORT = 1,
+ WNM_DIAGNOSTIC_REQ = 2,
+ WNM_DIAGNOSTIC_REPORT = 3,
+ WNM_LOCATION_CFG_REQ = 4,
+ WNM_LOCATION_CFG_RESP = 5,
+ WNM_BSS_TRANS_MGMT_QUERY = 6,
+ WNM_BSS_TRANS_MGMT_REQ = 7,
+ WNM_BSS_TRANS_MGMT_RESP = 8,
+ WNM_FMS_REQ = 9,
+ WNM_FMS_RESP = 10,
+ WNM_COLLOCATED_INTERFERENCE_REQ = 11,
+ WNM_COLLOCATED_INTERFERENCE_REPORT = 12,
+ WNM_TFS_REQ = 13,
+ WNM_TFS_RESP = 14,
+ WNM_TFS_NOTIFY = 15,
+ WNM_SLEEP_MODE_REQ = 16,
+ WNM_SLEEP_MODE_RESP = 17,
+ WNM_TIM_BROADCAST_REQ = 18,
+ WNM_TIM_BROADCAST_RESP = 19,
+ WNM_QOS_TRAFFIC_CAPAB_UPDATE = 20,
+ WNM_CHANNEL_USAGE_REQ = 21,
+ WNM_CHANNEL_USAGE_RESP = 22,
+ WNM_DMS_REQ = 23,
+ WNM_DMS_RESP = 24,
+ WNM_TIMING_MEASUREMENT_REQ = 25,
+ WNM_NOTIFICATION_REQ = 26,
+ WNM_NOTIFICATION_RESP = 27
+};
+
+/* IEEE 802.11v - BSS Transition Management Request - Request Mode */
+#define WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED BIT(0)
+#define WNM_BSS_TM_REQ_ABRIDGED BIT(1)
+#define WNM_BSS_TM_REQ_DISASSOC_IMMINENT BIT(2)
+#define WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED BIT(3)
+#define WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT BIT(4)
+
#endif /* IEEE802_11_DEFS_H */
#ifndef VERSION_H
#define VERSION_H
-#define VERSION_STR "0.7.3"
+#ifndef VERSION_STR_POSTFIX
+#define VERSION_STR_POSTFIX ""
+#endif /* VERSION_STR_POSTFIX */
+
+#define VERSION_STR "1.0" VERSION_STR_POSTFIX
#endif /* VERSION_H */
wpa_printf(MSG_DEBUG, "WPA: PTK derivation - A1=" MACSTR " A2=" MACSTR,
MAC2STR(addr1), MAC2STR(addr2));
+ wpa_hexdump(MSG_DEBUG, "WPA: Nonce1", nonce1, WPA_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPA: Nonce2", nonce2, WPA_NONCE_LEN);
wpa_hexdump_key(MSG_DEBUG, "WPA: PMK", pmk, pmk_len);
wpa_hexdump_key(MSG_DEBUG, "WPA: PTK", ptk, ptk_len);
}
return 0;
}
+
+
+static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len,
+ struct wpa_ft_ies *parse)
+{
+ const u8 *end, *pos;
+
+ parse->ftie = ie;
+ parse->ftie_len = ie_len;
+
+ pos = ie + sizeof(struct rsn_ftie);
+ end = ie + ie_len;
+
+ while (pos + 2 <= end && pos + 2 + pos[1] <= end) {
+ switch (pos[0]) {
+ case FTIE_SUBELEM_R1KH_ID:
+ if (pos[1] != FT_R1KH_ID_LEN) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid R1KH-ID "
+ "length in FTIE: %d", pos[1]);
+ return -1;
+ }
+ parse->r1kh_id = pos + 2;
+ break;
+ case FTIE_SUBELEM_GTK:
+ parse->gtk = pos + 2;
+ parse->gtk_len = pos[1];
+ break;
+ case FTIE_SUBELEM_R0KH_ID:
+ if (pos[1] < 1 || pos[1] > FT_R0KH_ID_MAX_LEN) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid R0KH-ID "
+ "length in FTIE: %d", pos[1]);
+ return -1;
+ }
+ parse->r0kh_id = pos + 2;
+ parse->r0kh_id_len = pos[1];
+ break;
+#ifdef CONFIG_IEEE80211W
+ case FTIE_SUBELEM_IGTK:
+ parse->igtk = pos + 2;
+ parse->igtk_len = pos[1];
+ break;
+#endif /* CONFIG_IEEE80211W */
+ }
+
+ pos += 2 + pos[1];
+ }
+
+ return 0;
+}
+
+
+int wpa_ft_parse_ies(const u8 *ies, size_t ies_len,
+ struct wpa_ft_ies *parse)
+{
+ const u8 *end, *pos;
+ struct wpa_ie_data data;
+ int ret;
+ const struct rsn_ftie *ftie;
+ int prot_ie_count = 0;
+
+ os_memset(parse, 0, sizeof(*parse));
+ if (ies == NULL)
+ return 0;
+
+ pos = ies;
+ end = ies + ies_len;
+ while (pos + 2 <= end && pos + 2 + pos[1] <= end) {
+ switch (pos[0]) {
+ case WLAN_EID_RSN:
+ parse->rsn = pos + 2;
+ parse->rsn_len = pos[1];
+ ret = wpa_parse_wpa_ie_rsn(parse->rsn - 2,
+ parse->rsn_len + 2,
+ &data);
+ if (ret < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to parse "
+ "RSN IE: %d", ret);
+ return -1;
+ }
+ if (data.num_pmkid == 1 && data.pmkid)
+ parse->rsn_pmkid = data.pmkid;
+ break;
+ case WLAN_EID_MOBILITY_DOMAIN:
+ parse->mdie = pos + 2;
+ parse->mdie_len = pos[1];
+ break;
+ case WLAN_EID_FAST_BSS_TRANSITION:
+ if (pos[1] < sizeof(*ftie))
+ return -1;
+ ftie = (const struct rsn_ftie *) (pos + 2);
+ prot_ie_count = ftie->mic_control[1];
+ if (wpa_ft_parse_ftie(pos + 2, pos[1], parse) < 0)
+ return -1;
+ break;
+ case WLAN_EID_TIMEOUT_INTERVAL:
+ parse->tie = pos + 2;
+ parse->tie_len = pos[1];
+ break;
+ case WLAN_EID_RIC_DATA:
+ if (parse->ric == NULL)
+ parse->ric = pos;
+ break;
+ }
+
+ pos += 2 + pos[1];
+ }
+
+ if (prot_ie_count == 0)
+ return 0; /* no MIC */
+
+ /*
+ * Check that the protected IE count matches with IEs included in the
+ * frame.
+ */
+ if (parse->rsn)
+ prot_ie_count--;
+ if (parse->mdie)
+ prot_ie_count--;
+ if (parse->ftie)
+ prot_ie_count--;
+ if (prot_ie_count < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Some required IEs not included in "
+ "the protected IE count");
+ return -1;
+ }
+
+ if (prot_ie_count == 0 && parse->ric) {
+ wpa_printf(MSG_DEBUG, "FT: RIC IE(s) in the frame, but not "
+ "included in protected IE count");
+ return -1;
+ }
+
+ /* Determine the end of the RIC IE(s) */
+ pos = parse->ric;
+ while (pos && pos + 2 <= end && pos + 2 + pos[1] <= end &&
+ prot_ie_count) {
+ prot_ie_count--;
+ pos += 2 + pos[1];
+ }
+ parse->ric_len = pos - parse->ric;
+ if (prot_ie_count) {
+ wpa_printf(MSG_DEBUG, "FT: %d protected IEs missing from "
+ "frame", (int) prot_ie_count);
+ return -1;
+ }
+
+ return 0;
+}
#endif /* CONFIG_IEEE80211R */
}
+static int wpa_selector_to_bitfield(const u8 *s)
+{
+ if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_NONE)
+ return WPA_CIPHER_NONE;
+ if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP40)
+ return WPA_CIPHER_WEP40;
+ if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_TKIP)
+ return WPA_CIPHER_TKIP;
+ if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_CCMP)
+ return WPA_CIPHER_CCMP;
+ if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP104)
+ return WPA_CIPHER_WEP104;
+ return 0;
+}
+
+
+static int wpa_key_mgmt_to_bitfield(const u8 *s)
+{
+ if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_UNSPEC_802_1X)
+ return WPA_KEY_MGMT_IEEE8021X;
+ if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X)
+ return WPA_KEY_MGMT_PSK;
+ if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_NONE)
+ return WPA_KEY_MGMT_WPA_NONE;
+ return 0;
+}
+
+
+int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len,
+ struct wpa_ie_data *data)
+{
+ const struct wpa_ie_hdr *hdr;
+ const u8 *pos;
+ int left;
+ int i, count;
+
+ os_memset(data, 0, sizeof(*data));
+ data->proto = WPA_PROTO_WPA;
+ data->pairwise_cipher = WPA_CIPHER_TKIP;
+ data->group_cipher = WPA_CIPHER_TKIP;
+ data->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
+ data->capabilities = 0;
+ data->pmkid = NULL;
+ data->num_pmkid = 0;
+ data->mgmt_group_cipher = 0;
+
+ if (wpa_ie_len == 0) {
+ /* No WPA IE - fail silently */
+ return -1;
+ }
+
+ if (wpa_ie_len < sizeof(struct wpa_ie_hdr)) {
+ wpa_printf(MSG_DEBUG, "%s: ie len too short %lu",
+ __func__, (unsigned long) wpa_ie_len);
+ return -1;
+ }
+
+ hdr = (const struct wpa_ie_hdr *) wpa_ie;
+
+ if (hdr->elem_id != WLAN_EID_VENDOR_SPECIFIC ||
+ hdr->len != wpa_ie_len - 2 ||
+ RSN_SELECTOR_GET(hdr->oui) != WPA_OUI_TYPE ||
+ WPA_GET_LE16(hdr->version) != WPA_VERSION) {
+ wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version",
+ __func__);
+ return -2;
+ }
+
+ pos = (const u8 *) (hdr + 1);
+ left = wpa_ie_len - sizeof(*hdr);
+
+ if (left >= WPA_SELECTOR_LEN) {
+ data->group_cipher = wpa_selector_to_bitfield(pos);
+ pos += WPA_SELECTOR_LEN;
+ left -= WPA_SELECTOR_LEN;
+ } else if (left > 0) {
+ wpa_printf(MSG_DEBUG, "%s: ie length mismatch, %u too much",
+ __func__, left);
+ return -3;
+ }
+
+ if (left >= 2) {
+ data->pairwise_cipher = 0;
+ count = WPA_GET_LE16(pos);
+ pos += 2;
+ left -= 2;
+ if (count == 0 || left < count * WPA_SELECTOR_LEN) {
+ wpa_printf(MSG_DEBUG, "%s: ie count botch (pairwise), "
+ "count %u left %u", __func__, count, left);
+ return -4;
+ }
+ for (i = 0; i < count; i++) {
+ data->pairwise_cipher |= wpa_selector_to_bitfield(pos);
+ pos += WPA_SELECTOR_LEN;
+ left -= WPA_SELECTOR_LEN;
+ }
+ } else if (left == 1) {
+ wpa_printf(MSG_DEBUG, "%s: ie too short (for key mgmt)",
+ __func__);
+ return -5;
+ }
+
+ if (left >= 2) {
+ data->key_mgmt = 0;
+ count = WPA_GET_LE16(pos);
+ pos += 2;
+ left -= 2;
+ if (count == 0 || left < count * WPA_SELECTOR_LEN) {
+ wpa_printf(MSG_DEBUG, "%s: ie count botch (key mgmt), "
+ "count %u left %u", __func__, count, left);
+ return -6;
+ }
+ for (i = 0; i < count; i++) {
+ data->key_mgmt |= wpa_key_mgmt_to_bitfield(pos);
+ pos += WPA_SELECTOR_LEN;
+ left -= WPA_SELECTOR_LEN;
+ }
+ } else if (left == 1) {
+ wpa_printf(MSG_DEBUG, "%s: ie too short (for capabilities)",
+ __func__);
+ return -7;
+ }
+
+ if (left >= 2) {
+ data->capabilities = WPA_GET_LE16(pos);
+ pos += 2;
+ left -= 2;
+ }
+
+ if (left > 0) {
+ wpa_printf(MSG_DEBUG, "%s: ie has %u trailing bytes - ignored",
+ __func__, left);
+ }
+
+ return 0;
+}
+
+
#ifdef CONFIG_IEEE80211R
/**
#endif /* CONFIG_IEEE80211R */
#define RSN_AUTH_KEY_MGMT_802_1X_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 5)
#define RSN_AUTH_KEY_MGMT_PSK_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 6)
+#define RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE RSN_SELECTOR(0x00, 0x0f, 0xac, 7)
#define RSN_CIPHER_SUITE_NONE RSN_SELECTOR(0x00, 0x0f, 0xac, 0)
#define RSN_CIPHER_SUITE_WEP40 RSN_SELECTOR(0x00, 0x0f, 0xac, 1)
#ifdef CONFIG_IEEE80211W
#define RSN_CIPHER_SUITE_AES_128_CMAC RSN_SELECTOR(0x00, 0x0f, 0xac, 6)
#endif /* CONFIG_IEEE80211W */
+#define RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED RSN_SELECTOR(0x00, 0x0f, 0xac, 7)
/* EAPOL-Key Key Data Encapsulation
* GroupKey and PeerKey require encryption, otherwise, encryption is optional.
/* B4-B5: GTKSA Replay Counter */
#define WPA_CAPABILITY_MFPR BIT(6)
#define WPA_CAPABILITY_MFPC BIT(7)
+/* B8: Reserved */
#define WPA_CAPABILITY_PEERKEY_ENABLED BIT(9)
+#define WPA_CAPABILITY_SPP_A_MSDU_CAPABLE BIT(10)
+#define WPA_CAPABILITY_SPP_A_MSDU_REQUIRED BIT(11)
+#define WPA_CAPABILITY_PBAC BIT(12)
+#define WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST BIT(13)
+/* B14-B15: Reserved */
/* IEEE 802.11r */
int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len,
struct wpa_ie_data *data);
+int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len,
+ struct wpa_ie_data *data);
void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa,
u8 *pmkid, int use_sha256);
const u8 *ie2, size_t ie2len);
int wpa_insert_pmkid(u8 *ies, size_t ies_len, const u8 *pmkid);
+struct wpa_ft_ies {
+ const u8 *mdie;
+ size_t mdie_len;
+ const u8 *ftie;
+ size_t ftie_len;
+ const u8 *r1kh_id;
+ const u8 *gtk;
+ size_t gtk_len;
+ const u8 *r0kh_id;
+ size_t r0kh_id_len;
+ const u8 *rsn;
+ size_t rsn_len;
+ const u8 *rsn_pmkid;
+ const u8 *tie;
+ size_t tie_len;
+ const u8 *igtk;
+ size_t igtk_len;
+ const u8 *ric;
+ size_t ric_len;
+};
+
+int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, struct wpa_ft_ies *parse);
+
#endif /* WPA_COMMON_H */
#include <sys/un.h>
#endif /* CONFIG_CTRL_IFACE_UNIX */
+#ifdef ANDROID
+#include <dirent.h>
+#include <cutils/sockets.h>
+#include "private/android_filesystem_config.h"
+#endif /* ANDROID */
+
#include "wpa_ctrl.h"
#include "common.h"
#ifdef CONFIG_CTRL_IFACE_UNIX
+#ifndef CONFIG_CTRL_IFACE_CLIENT_DIR
+#define CONFIG_CTRL_IFACE_CLIENT_DIR "/tmp"
+#endif /* CONFIG_CTRL_IFACE_CLIENT_DIR */
+#ifndef CONFIG_CTRL_IFACE_CLIENT_PREFIX
+#define CONFIG_CTRL_IFACE_CLIENT_PREFIX "wpa_ctrl_"
+#endif /* CONFIG_CTRL_IFACE_CLIENT_PREFIX */
+
+
struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path)
{
struct wpa_ctrl *ctrl;
counter++;
try_again:
ret = os_snprintf(ctrl->local.sun_path, sizeof(ctrl->local.sun_path),
- "/tmp/wpa_ctrl_%d-%d", getpid(), counter);
+ CONFIG_CTRL_IFACE_CLIENT_DIR "/"
+ CONFIG_CTRL_IFACE_CLIENT_PREFIX "%d-%d",
+ (int) getpid(), counter);
if (ret < 0 || (size_t) ret >= sizeof(ctrl->local.sun_path)) {
close(ctrl->s);
os_free(ctrl);
return NULL;
}
+#ifdef ANDROID
+ chmod(ctrl->local.sun_path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
+ chown(ctrl->local.sun_path, AID_SYSTEM, AID_WIFI);
+ /*
+ * If the ctrl_path isn't an absolute pathname, assume that
+ * it's the name of a socket in the Android reserved namespace.
+ * Otherwise, it's a normal UNIX domain socket appearing in the
+ * filesystem.
+ */
+ if (ctrl_path != NULL && *ctrl_path != '/') {
+ char buf[21];
+ os_snprintf(buf, sizeof(buf), "wpa_%s", ctrl_path);
+ if (socket_local_client_connect(
+ ctrl->s, buf,
+ ANDROID_SOCKET_NAMESPACE_RESERVED,
+ SOCK_DGRAM) < 0) {
+ close(ctrl->s);
+ unlink(ctrl->local.sun_path);
+ os_free(ctrl);
+ return NULL;
+ }
+ return ctrl;
+ }
+#endif /* ANDROID */
+
ctrl->dest.sun_family = AF_UNIX;
res = os_strlcpy(ctrl->dest.sun_path, ctrl_path,
sizeof(ctrl->dest.sun_path));
void wpa_ctrl_close(struct wpa_ctrl *ctrl)
{
+ if (ctrl == NULL)
+ return;
unlink(ctrl->local.sun_path);
- close(ctrl->s);
+ if (ctrl->s >= 0)
+ close(ctrl->s);
os_free(ctrl);
}
+
+#ifdef ANDROID
+/**
+ * wpa_ctrl_cleanup() - Delete any local UNIX domain socket files that
+ * may be left over from clients that were previously connected to
+ * wpa_supplicant. This keeps these files from being orphaned in the
+ * event of crashes that prevented them from being removed as part
+ * of the normal orderly shutdown.
+ */
+void wpa_ctrl_cleanup(void)
+{
+ DIR *dir;
+ struct dirent entry;
+ struct dirent *result;
+ size_t dirnamelen;
+ int prefixlen = os_strlen(CONFIG_CTRL_IFACE_CLIENT_PREFIX);
+ size_t maxcopy;
+ char pathname[PATH_MAX];
+ char *namep;
+
+ if ((dir = opendir(CONFIG_CTRL_IFACE_CLIENT_DIR)) == NULL)
+ return;
+
+ dirnamelen = (size_t) os_snprintf(pathname, sizeof(pathname), "%s/",
+ CONFIG_CTRL_IFACE_CLIENT_DIR);
+ if (dirnamelen >= sizeof(pathname)) {
+ closedir(dir);
+ return;
+ }
+ namep = pathname + dirnamelen;
+ maxcopy = PATH_MAX - dirnamelen;
+ while (readdir_r(dir, &entry, &result) == 0 && result != NULL) {
+ if (os_strncmp(entry.d_name, CONFIG_CTRL_IFACE_CLIENT_PREFIX,
+ prefixlen) == 0) {
+ if (os_strlcpy(namep, entry.d_name, maxcopy) < maxcopy)
+ unlink(pathname);
+ }
+ }
+ closedir(dir);
+}
+#endif /* ANDROID */
+
+#else /* CONFIG_CTRL_IFACE_UNIX */
+
+#ifdef ANDROID
+void wpa_ctrl_cleanup(void)
+{
+}
+#endif /* ANDROID */
+
#endif /* CONFIG_CTRL_IFACE_UNIX */
os_free(cmd_buf);
for (;;) {
- tv.tv_sec = 2;
+ tv.tv_sec = 10;
tv.tv_usec = 0;
FD_ZERO(&rfds);
FD_SET(ctrl->s, &rfds);
res = select(ctrl->s + 1, &rfds, NULL, NULL, &tv);
+ if (res < 0)
+ return res;
if (FD_ISSET(ctrl->s, &rfds)) {
res = recv(ctrl->s, reply, *reply_len, 0);
if (res < 0)
#define WPA_EVENT_CONNECTED "CTRL-EVENT-CONNECTED "
/** Disconnected, data connection is not available */
#define WPA_EVENT_DISCONNECTED "CTRL-EVENT-DISCONNECTED "
+/** Association rejected during connection attempt */
+#define WPA_EVENT_ASSOC_REJECT "CTRL-EVENT-ASSOC-REJECT "
/** wpa_supplicant is exiting */
#define WPA_EVENT_TERMINATING "CTRL-EVENT-TERMINATING "
/** Password change was completed successfully */
#define WPA_EVENT_EAP_FAILURE "CTRL-EVENT-EAP-FAILURE "
/** New scan results available */
#define WPA_EVENT_SCAN_RESULTS "CTRL-EVENT-SCAN-RESULTS "
+/** wpa_supplicant state change */
+#define WPA_EVENT_STATE_CHANGE "CTRL-EVENT-STATE-CHANGE "
/** A new BSS entry was added (followed by BSS entry id and BSSID) */
#define WPA_EVENT_BSS_ADDED "CTRL-EVENT-BSS-ADDED "
/** A BSS entry was removed (followed by BSS entry id and BSSID) */
#define WPS_EVENT_OVERLAP "WPS-OVERLAP-DETECTED "
/** Available WPS AP with active PBC found in scan results */
#define WPS_EVENT_AP_AVAILABLE_PBC "WPS-AP-AVAILABLE-PBC "
+/** Available WPS AP with our address as authorized in scan results */
+#define WPS_EVENT_AP_AVAILABLE_AUTH "WPS-AP-AVAILABLE-AUTH "
/** Available WPS AP with recently selected PIN registrar found in scan results
*/
#define WPS_EVENT_AP_AVAILABLE_PIN "WPS-AP-AVAILABLE-PIN "
#define WPS_EVENT_ENROLLEE_SEEN "WPS-ENROLLEE-SEEN "
+#define WPS_EVENT_OPEN_NETWORK "WPS-OPEN-NETWORK "
+
/* WPS ER events */
#define WPS_EVENT_ER_AP_ADD "WPS-ER-AP-ADD "
#define WPS_EVENT_ER_AP_REMOVE "WPS-ER-AP-REMOVE "
#define WPS_EVENT_ER_ENROLLEE_ADD "WPS-ER-ENROLLEE-ADD "
#define WPS_EVENT_ER_ENROLLEE_REMOVE "WPS-ER-ENROLLEE-REMOVE "
+#define WPS_EVENT_ER_AP_SETTINGS "WPS-ER-AP-SETTINGS "
+#define WPS_EVENT_ER_SET_SEL_REG "WPS-ER-AP-SET-SEL-REG "
+
+/** P2P device found */
+#define P2P_EVENT_DEVICE_FOUND "P2P-DEVICE-FOUND "
+
+/** P2P device lost */
+#define P2P_EVENT_DEVICE_LOST "P2P-DEVICE-LOST "
+
+/** A P2P device requested GO negotiation, but we were not ready to start the
+ * negotiation */
+#define P2P_EVENT_GO_NEG_REQUEST "P2P-GO-NEG-REQUEST "
+#define P2P_EVENT_GO_NEG_SUCCESS "P2P-GO-NEG-SUCCESS "
+#define P2P_EVENT_GO_NEG_FAILURE "P2P-GO-NEG-FAILURE "
+#define P2P_EVENT_GROUP_FORMATION_SUCCESS "P2P-GROUP-FORMATION-SUCCESS "
+#define P2P_EVENT_GROUP_FORMATION_FAILURE "P2P-GROUP-FORMATION-FAILURE "
+#define P2P_EVENT_GROUP_STARTED "P2P-GROUP-STARTED "
+#define P2P_EVENT_GROUP_REMOVED "P2P-GROUP-REMOVED "
+#define P2P_EVENT_CROSS_CONNECT_ENABLE "P2P-CROSS-CONNECT-ENABLE "
+#define P2P_EVENT_CROSS_CONNECT_DISABLE "P2P-CROSS-CONNECT-DISABLE "
+/* parameters: <peer address> <PIN> */
+#define P2P_EVENT_PROV_DISC_SHOW_PIN "P2P-PROV-DISC-SHOW-PIN "
+/* parameters: <peer address> */
+#define P2P_EVENT_PROV_DISC_ENTER_PIN "P2P-PROV-DISC-ENTER-PIN "
+/* parameters: <peer address> */
+#define P2P_EVENT_PROV_DISC_PBC_REQ "P2P-PROV-DISC-PBC-REQ "
+/* parameters: <peer address> */
+#define P2P_EVENT_PROV_DISC_PBC_RESP "P2P-PROV-DISC-PBC-RESP "
+/* parameters: <freq> <src addr> <dialog token> <update indicator> <TLVs> */
+#define P2P_EVENT_SERV_DISC_REQ "P2P-SERV-DISC-REQ "
+/* parameters: <src addr> <update indicator> <TLVs> */
+#define P2P_EVENT_SERV_DISC_RESP "P2P-SERV-DISC-RESP "
+#define P2P_EVENT_INVITATION_RECEIVED "P2P-INVITATION-RECEIVED "
+#define P2P_EVENT_INVITATION_RESULT "P2P-INVITATION-RESULT "
+#define P2P_EVENT_FIND_STOPPED "P2P-FIND-STOPPED "
+
+#define INTERWORKING_AP "INTERWORKING-AP "
+#define INTERWORKING_NO_MATCH "INTERWORKING-NO-MATCH "
/* hostapd control interface - fixed message prefixes */
#define WPS_EVENT_PIN_NEEDED "WPS-PIN-NEEDED "
*/
int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl);
+#ifdef ANDROID
+/**
+ * wpa_ctrl_cleanup() - Delete any local UNIX domain socket files that
+ * may be left over from clients that were previously connected to
+ * wpa_supplicant. This keeps these files from being orphaned in the
+ * event of crashes that prevented them from being removed as part
+ * of the normal orderly shutdown.
+ */
+void wpa_ctrl_cleanup(void);
+#endif /* ANDROID */
+
#ifdef CONFIG_CTRL_IFACE_UDP
#define WPA_CTRL_IFACE_PORT 9877
#define WPA_GLOBAL_CTRL_IFACE_PORT 9878
LIB_OBJS += crypto_internal-rsa.o
LIB_OBJS += tls_internal.o
LIB_OBJS += fips_prf_internal.o
+LIB_OBJS += random.o
libcrypto.a: $(LIB_OBJS)
ctx->key_len = key_len;
os_memcpy(k_pad, key, key_len);
- os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len);
+ if (key_len < sizeof(k_pad))
+ os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len);
for (i = 0; i < sizeof(k_pad); i++)
k_pad[i] ^= 0x36;
MD5Init(&ctx->u.md5);
ctx->key_len = key_len;
os_memcpy(k_pad, key, key_len);
- os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len);
+ if (key_len < sizeof(k_pad))
+ os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len);
for (i = 0; i < sizeof(k_pad); i++)
k_pad[i] ^= 0x36;
SHA1Init(&ctx->u.sha1);
#include "common.h"
#include "crypto.h"
+#include "random.h"
#include "dh_groups.h"
if (*priv == NULL)
return NULL;
- if (os_get_random(wpabuf_put(*priv, dh->prime_len), dh->prime_len)) {
+ if (random_get_bytes(wpabuf_put(*priv, dh->prime_len), dh->prime_len))
+ {
wpabuf_free(*priv);
*priv = NULL;
return NULL;
u8 *xpos = x;
u32 carry;
- if (seed_len > sizeof(xkey))
+ if (seed_len < sizeof(xkey))
+ os_memset(xkey + seed_len, 0, sizeof(xkey) - seed_len);
+ else
seed_len = sizeof(xkey);
/* FIPS 186-2 + change notice 1 */
os_memcpy(xkey, seed, seed_len);
- os_memset(xkey + seed_len, 0, 64 - seed_len);
t[0] = 0x67452301;
t[1] = 0xEFCDAB89;
t[2] = 0x98BADCFE;
MD5Transform(ctx->buf, (u32 *) ctx->in);
byteReverse((unsigned char *) ctx->buf, 4);
os_memcpy(digest, ctx->buf, 16);
- os_memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */
+ os_memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */
}
/* The four core functions - F1 is optimized somewhat */
#include "ms_funcs.h"
#include "crypto.h"
+/**
+ * utf8_to_ucs2 - Convert UTF-8 string to UCS-2 encoding
+ * @utf8_string: UTF-8 string (IN)
+ * @utf8_string_len: Length of utf8_string (IN)
+ * @ucs2_buffer: UCS-2 buffer (OUT)
+ * @ucs2_buffer_size: Length of UCS-2 buffer (IN)
+ * @ucs2_string_size: Number of 2-byte words in the resulting UCS-2 string
+ * Returns: 0 on success, -1 on failure
+ */
+static int utf8_to_ucs2(const u8 *utf8_string, size_t utf8_string_len,
+ u8 *ucs2_buffer, size_t ucs2_buffer_size,
+ size_t *ucs2_string_size)
+{
+ size_t i, j;
+
+ for (i = 0, j = 0; i < utf8_string_len; i++) {
+ u8 c = utf8_string[i];
+ if (j >= ucs2_buffer_size) {
+ /* input too long */
+ return -1;
+ }
+ if (c <= 0x7F) {
+ WPA_PUT_LE16(ucs2_buffer + j, c);
+ j += 2;
+ } else if (i == utf8_string_len - 1 ||
+ j >= ucs2_buffer_size - 1) {
+ /* incomplete surrogate */
+ return -1;
+ } else {
+ u8 c2 = utf8_string[++i];
+ if ((c & 0xE0) == 0xC0) {
+ /* two-byte encoding */
+ WPA_PUT_LE16(ucs2_buffer + j,
+ ((c & 0x1F) << 6) | (c2 & 0x3F));
+ j += 2;
+ } else if (i == utf8_string_len ||
+ j >= ucs2_buffer_size - 1) {
+ /* incomplete surrogate */
+ return -1;
+ } else {
+ /* three-byte encoding */
+ u8 c3 = utf8_string[++i];
+ WPA_PUT_LE16(ucs2_buffer + j,
+ ((c & 0xF) << 12) |
+ ((c2 & 0x3F) << 6) | (c3 & 0x3F));
+ }
+ }
+ }
+
+ if (ucs2_string_size)
+ *ucs2_string_size = j / 2;
+ return 0;
+}
+
/**
* challenge_hash - ChallengeHash() - RFC 2759, Sect. 8.2
/**
* nt_password_hash - NtPasswordHash() - RFC 2759, Sect. 8.3
- * @password: 0-to-256-unicode-char Password (IN; ASCII)
+ * @password: 0-to-256-unicode-char Password (IN; UTF-8)
* @password_len: Length of password
* @password_hash: 16-octet PasswordHash (OUT)
* Returns: 0 on success, -1 on failure
u8 *password_hash)
{
u8 buf[512], *pos;
- size_t i, len;
+ size_t len, max_len;
- if (password_len > 256)
- password_len = 256;
-
- /* Convert password into unicode */
- for (i = 0; i < password_len; i++) {
- buf[2 * i] = password[i];
- buf[2 * i + 1] = 0;
- }
+ max_len = sizeof(buf);
+ if (utf8_to_ucs2(password, password_len, buf, max_len, &len) < 0)
+ return -1;
- len = password_len * 2;
+ len *= 2;
pos = buf;
return md4_vector(1, (const u8 **) &pos, &len, password_hash);
}
* @peer_challenge: 16-octet PeerChallenge (IN)
* @username: 0-to-256-char UserName (IN)
* @username_len: Length of username
- * @password: 0-to-256-unicode-char Password (IN; ASCII)
+ * @password: 0-to-256-unicode-char Password (IN; UTF-8)
* @password_len: Length of password
* @response: 24-octet Response (OUT)
* Returns: 0 on success, -1 on failure
/**
* generate_authenticator_response - GenerateAuthenticatorResponse() - RFC 2759, Sect. 8.7
- * @password: 0-to-256-unicode-char Password (IN; ASCII)
+ * @password: 0-to-256-unicode-char Password (IN; UTF-8)
* @password_len: Length of password
* @nt_response: 24-octet NT-Response (IN)
* @peer_challenge: 16-octet PeerChallenge (IN)
/**
* nt_challenge_response - NtChallengeResponse() - RFC 2433, Sect. A.5
* @challenge: 8-octet Challenge (IN)
- * @password: 0-to-256-unicode-char Password (IN; ASCII)
+ * @password: 0-to-256-unicode-char Password (IN; UTF-8)
* @password_len: Length of password
* @response: 24-octet Response (OUT)
* Returns: 0 on success, -1 on failure
/**
* encrypt_pw_block_with_password_hash - EncryptPwBlockWithPasswordHash() - RFC 2759, Sect. 8.10
- * @password: 0-to-256-unicode-char Password (IN; ASCII)
+ * @password: 0-to-256-unicode-char Password (IN; UTF-8)
* @password_len: Length of password
* @password_hash: 16-octet PasswordHash (IN)
* @pw_block: 516-byte PwBlock (OUT)
const u8 *password, size_t password_len,
const u8 *password_hash, u8 *pw_block)
{
- size_t i, offset;
+ size_t ucs2_len, offset;
u8 *pos;
- if (password_len > 256)
+ os_memset(pw_block, 0, PWBLOCK_LEN);
+
+ if (utf8_to_ucs2(password, password_len, pw_block, 512, &ucs2_len) < 0)
return -1;
- os_memset(pw_block, 0, PWBLOCK_LEN);
- offset = (256 - password_len) * 2;
- if (os_get_random(pw_block, offset) < 0)
+ if (ucs2_len > 256)
return -1;
- for (i = 0; i < password_len; i++)
- pw_block[offset + i * 2] = password[i];
+
+ offset = (256 - ucs2_len) * 2;
+ if (offset != 0) {
+ os_memmove(pw_block + offset, pw_block, ucs2_len * 2);
+ if (os_get_random(pw_block, offset) < 0)
+ return -1;
+ }
/*
* PasswordLength is 4 octets, but since the maximum password length is
* 256, only first two (in little endian byte order) can be non-zero.
/**
* new_password_encrypted_with_old_nt_password_hash - NewPasswordEncryptedWithOldNtPasswordHash() - RFC 2759, Sect. 8.9
- * @new_password: 0-to-256-unicode-char NewPassword (IN; ASCII)
+ * @new_password: 0-to-256-unicode-char NewPassword (IN; UTF-8)
* @new_password_len: Length of new_password
- * @old_password: 0-to-256-unicode-char OldPassword (IN; ASCII)
+ * @old_password: 0-to-256-unicode-char OldPassword (IN; UTF-8)
* @old_password_len: Length of old_password
* @encrypted_pw_block: 516-octet EncryptedPwBlock (OUT)
* Returns: 0 on success, -1 on failure
/**
* old_nt_password_hash_encrypted_with_new_nt_password_hash - OldNtPasswordHashEncryptedWithNewNtPasswordHash() - RFC 2759, Sect. 8.12
- * @new_password: 0-to-256-unicode-char NewPassword (IN; ASCII)
+ * @new_password: 0-to-256-unicode-char NewPassword (IN; UTF-8)
* @new_password_len: Length of new_password
- * @old_password: 0-to-256-unicode-char OldPassword (IN; ASCII)
+ * @old_password: 0-to-256-unicode-char OldPassword (IN; UTF-8)
* @old_password_len: Length of old_password
* @encrypted_password_hash: 16-octet EncryptedPasswordHash (OUT)
* Returns: 0 on success, -1 on failure
--- /dev/null
+/*
+ * Random number generator
+ * Copyright (c) 2010-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ *
+ * This random number generator is used to provide additional entropy to the
+ * one provided by the operating system (os_get_random()) for session key
+ * generation. The os_get_random() output is expected to be secure and the
+ * implementation here is expected to provide only limited protection against
+ * cases where os_get_random() cannot provide strong randomness. This
+ * implementation shall not be assumed to be secure as the sole source of
+ * randomness. The random_get_bytes() function mixes in randomness from
+ * os_get_random() and as such, calls to os_get_random() can be replaced with
+ * calls to random_get_bytes() without reducing security.
+ *
+ * The design here follows partially the design used in the Linux
+ * drivers/char/random.c, but the implementation here is simpler and not as
+ * strong. This is a compromise to reduce duplicated CPU effort and to avoid
+ * extra code/memory size. As pointed out above, os_get_random() needs to be
+ * guaranteed to be secure for any of the security assumptions to hold.
+ */
+
+#include "utils/includes.h"
+#ifdef __linux__
+#include <fcntl.h>
+#endif /* __linux__ */
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "sha1.h"
+#include "random.h"
+
+#define POOL_WORDS 32
+#define POOL_WORDS_MASK (POOL_WORDS - 1)
+#define POOL_TAP1 26
+#define POOL_TAP2 20
+#define POOL_TAP3 14
+#define POOL_TAP4 7
+#define POOL_TAP5 1
+#define EXTRACT_LEN 16
+#define MIN_READY_MARK 2
+
+static u32 pool[POOL_WORDS];
+static unsigned int input_rotate = 0;
+static unsigned int pool_pos = 0;
+static u8 dummy_key[20];
+#ifdef __linux__
+static size_t dummy_key_avail = 0;
+static int random_fd = -1;
+#endif /* __linux__ */
+static unsigned int own_pool_ready = 0;
+#define RANDOM_ENTROPY_SIZE 20
+static char *random_entropy_file = NULL;
+static int random_entropy_file_read = 0;
+
+#define MIN_COLLECT_ENTROPY 1000
+static unsigned int entropy = 0;
+static unsigned int total_collected = 0;
+
+
+static void random_write_entropy(void);
+
+
+static u32 __ROL32(u32 x, u32 y)
+{
+ return (x << (y & 31)) | (x >> (32 - (y & 31)));
+}
+
+
+static void random_mix_pool(const void *buf, size_t len)
+{
+ static const u32 twist[8] = {
+ 0x00000000, 0x3b6e20c8, 0x76dc4190, 0x4db26158,
+ 0xedb88320, 0xd6d6a3e8, 0x9b64c2b0, 0xa00ae278
+ };
+ const u8 *pos = buf;
+ u32 w;
+
+ wpa_hexdump_key(MSG_EXCESSIVE, "random_mix_pool", buf, len);
+
+ while (len--) {
+ w = __ROL32(*pos++, input_rotate & 31);
+ input_rotate += pool_pos ? 7 : 14;
+ pool_pos = (pool_pos - 1) & POOL_WORDS_MASK;
+ w ^= pool[pool_pos];
+ w ^= pool[(pool_pos + POOL_TAP1) & POOL_WORDS_MASK];
+ w ^= pool[(pool_pos + POOL_TAP2) & POOL_WORDS_MASK];
+ w ^= pool[(pool_pos + POOL_TAP3) & POOL_WORDS_MASK];
+ w ^= pool[(pool_pos + POOL_TAP4) & POOL_WORDS_MASK];
+ w ^= pool[(pool_pos + POOL_TAP5) & POOL_WORDS_MASK];
+ pool[pool_pos] = (w >> 3) ^ twist[w & 7];
+ }
+}
+
+
+static void random_extract(u8 *out)
+{
+ unsigned int i;
+ u8 hash[SHA1_MAC_LEN];
+ u32 *hash_ptr;
+ u32 buf[POOL_WORDS / 2];
+
+ /* First, add hash back to pool to make backtracking more difficult. */
+ hmac_sha1(dummy_key, sizeof(dummy_key), (const u8 *) pool,
+ sizeof(pool), hash);
+ random_mix_pool(hash, sizeof(hash));
+ /* Hash half the pool to extra data */
+ for (i = 0; i < POOL_WORDS / 2; i++)
+ buf[i] = pool[(pool_pos - i) & POOL_WORDS_MASK];
+ hmac_sha1(dummy_key, sizeof(dummy_key), (const u8 *) buf,
+ sizeof(buf), hash);
+ /*
+ * Fold the hash to further reduce any potential output pattern.
+ * Though, compromise this to reduce CPU use for the most common output
+ * length (32) and return 16 bytes from instead of only half.
+ */
+ hash_ptr = (u32 *) hash;
+ hash_ptr[0] ^= hash_ptr[4];
+ os_memcpy(out, hash, EXTRACT_LEN);
+}
+
+
+void random_add_randomness(const void *buf, size_t len)
+{
+ struct os_time t;
+ static unsigned int count = 0;
+
+ count++;
+ wpa_printf(MSG_MSGDUMP, "Add randomness: count=%u entropy=%u",
+ count, entropy);
+ if (entropy > MIN_COLLECT_ENTROPY && (count & 0x3ff) != 0) {
+ /*
+ * No need to add more entropy at this point, so save CPU and
+ * skip the update.
+ */
+ return;
+ }
+
+ os_get_time(&t);
+ wpa_hexdump_key(MSG_EXCESSIVE, "random pool",
+ (const u8 *) pool, sizeof(pool));
+ random_mix_pool(&t, sizeof(t));
+ random_mix_pool(buf, len);
+ wpa_hexdump_key(MSG_EXCESSIVE, "random pool",
+ (const u8 *) pool, sizeof(pool));
+ entropy++;
+ total_collected++;
+}
+
+
+int random_get_bytes(void *buf, size_t len)
+{
+ int ret;
+ u8 *bytes = buf;
+ size_t left;
+
+ wpa_printf(MSG_MSGDUMP, "Get randomness: len=%u entropy=%u",
+ (unsigned int) len, entropy);
+
+ /* Start with assumed strong randomness from OS */
+ ret = os_get_random(buf, len);
+ wpa_hexdump_key(MSG_EXCESSIVE, "random from os_get_random",
+ buf, len);
+
+ /* Mix in additional entropy extracted from the internal pool */
+ left = len;
+ while (left) {
+ size_t siz, i;
+ u8 tmp[EXTRACT_LEN];
+ random_extract(tmp);
+ wpa_hexdump_key(MSG_EXCESSIVE, "random from internal pool",
+ tmp, sizeof(tmp));
+ siz = left > EXTRACT_LEN ? EXTRACT_LEN : left;
+ for (i = 0; i < siz; i++)
+ *bytes++ ^= tmp[i];
+ left -= siz;
+ }
+ wpa_hexdump_key(MSG_EXCESSIVE, "mixed random", buf, len);
+
+ if (entropy < len)
+ entropy = 0;
+ else
+ entropy -= len;
+
+ return ret;
+}
+
+
+int random_pool_ready(void)
+{
+#ifdef __linux__
+ int fd;
+ ssize_t res;
+
+ /*
+ * Make sure that there is reasonable entropy available before allowing
+ * some key derivation operations to proceed.
+ */
+
+ if (dummy_key_avail == sizeof(dummy_key))
+ return 1; /* Already initialized - good to continue */
+
+ /*
+ * Try to fetch some more data from the kernel high quality
+ * /dev/random. There may not be enough data available at this point,
+ * so use non-blocking read to avoid blocking the application
+ * completely.
+ */
+ fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
+ if (fd < 0) {
+#ifndef CONFIG_NO_STDOUT_DEBUG
+ int error = errno;
+ perror("open(/dev/random)");
+ wpa_printf(MSG_ERROR, "random: Cannot open /dev/random: %s",
+ strerror(error));
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+ return -1;
+ }
+
+ res = read(fd, dummy_key + dummy_key_avail,
+ sizeof(dummy_key) - dummy_key_avail);
+ if (res < 0) {
+ wpa_printf(MSG_ERROR, "random: Cannot read from /dev/random: "
+ "%s", strerror(errno));
+ res = 0;
+ }
+ wpa_printf(MSG_DEBUG, "random: Got %u/%u bytes from "
+ "/dev/random", (unsigned) res,
+ (unsigned) (sizeof(dummy_key) - dummy_key_avail));
+ dummy_key_avail += res;
+ close(fd);
+
+ if (dummy_key_avail == sizeof(dummy_key)) {
+ if (own_pool_ready < MIN_READY_MARK)
+ own_pool_ready = MIN_READY_MARK;
+ random_write_entropy();
+ return 1;
+ }
+
+ wpa_printf(MSG_INFO, "random: Only %u/%u bytes of strong "
+ "random data available from /dev/random",
+ (unsigned) dummy_key_avail, (unsigned) sizeof(dummy_key));
+
+ if (own_pool_ready >= MIN_READY_MARK ||
+ total_collected + 10 * own_pool_ready > MIN_COLLECT_ENTROPY) {
+ wpa_printf(MSG_INFO, "random: Allow operation to proceed "
+ "based on internal entropy");
+ return 1;
+ }
+
+ wpa_printf(MSG_INFO, "random: Not enough entropy pool available for "
+ "secure operations");
+ return 0;
+#else /* __linux__ */
+ /* TODO: could do similar checks on non-Linux platforms */
+ return 1;
+#endif /* __linux__ */
+}
+
+
+void random_mark_pool_ready(void)
+{
+ own_pool_ready++;
+ wpa_printf(MSG_DEBUG, "random: Mark internal entropy pool to be "
+ "ready (count=%u/%u)", own_pool_ready, MIN_READY_MARK);
+ random_write_entropy();
+}
+
+
+#ifdef __linux__
+
+static void random_close_fd(void)
+{
+ if (random_fd >= 0) {
+ eloop_unregister_read_sock(random_fd);
+ close(random_fd);
+ random_fd = -1;
+ }
+}
+
+
+static void random_read_fd(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ ssize_t res;
+
+ if (dummy_key_avail == sizeof(dummy_key)) {
+ random_close_fd();
+ return;
+ }
+
+ res = read(sock, dummy_key + dummy_key_avail,
+ sizeof(dummy_key) - dummy_key_avail);
+ if (res < 0) {
+ wpa_printf(MSG_ERROR, "random: Cannot read from /dev/random: "
+ "%s", strerror(errno));
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "random: Got %u/%u bytes from /dev/random",
+ (unsigned) res,
+ (unsigned) (sizeof(dummy_key) - dummy_key_avail));
+ dummy_key_avail += res;
+
+ if (dummy_key_avail == sizeof(dummy_key)) {
+ random_close_fd();
+ if (own_pool_ready < MIN_READY_MARK)
+ own_pool_ready = MIN_READY_MARK;
+ random_write_entropy();
+ }
+}
+
+#endif /* __linux__ */
+
+
+static void random_read_entropy(void)
+{
+ char *buf;
+ size_t len;
+
+ if (!random_entropy_file)
+ return;
+
+ buf = os_readfile(random_entropy_file, &len);
+ if (buf == NULL)
+ return; /* entropy file not yet available */
+
+ if (len != 1 + RANDOM_ENTROPY_SIZE) {
+ wpa_printf(MSG_DEBUG, "random: Invalid entropy file %s",
+ random_entropy_file);
+ os_free(buf);
+ return;
+ }
+
+ own_pool_ready = (u8) buf[0];
+ random_add_randomness(buf + 1, RANDOM_ENTROPY_SIZE);
+ random_entropy_file_read = 1;
+ os_free(buf);
+ wpa_printf(MSG_DEBUG, "random: Added entropy from %s "
+ "(own_pool_ready=%u)",
+ random_entropy_file, own_pool_ready);
+}
+
+
+static void random_write_entropy(void)
+{
+ char buf[RANDOM_ENTROPY_SIZE];
+ FILE *f;
+ u8 opr;
+ int fail = 0;
+
+ if (!random_entropy_file)
+ return;
+
+ if (random_get_bytes(buf, RANDOM_ENTROPY_SIZE) < 0)
+ return;
+
+ f = fopen(random_entropy_file, "wb");
+ if (f == NULL) {
+ wpa_printf(MSG_ERROR, "random: Could not open entropy file %s "
+ "for writing", random_entropy_file);
+ return;
+ }
+
+ opr = own_pool_ready > 0xff ? 0xff : own_pool_ready;
+ if (fwrite(&opr, 1, 1, f) != 1 ||
+ fwrite(buf, RANDOM_ENTROPY_SIZE, 1, f) != 1)
+ fail = 1;
+ fclose(f);
+ if (fail) {
+ wpa_printf(MSG_ERROR, "random: Could not write entropy data "
+ "to %s", random_entropy_file);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "random: Updated entropy file %s "
+ "(own_pool_ready=%u)",
+ random_entropy_file, own_pool_ready);
+}
+
+
+void random_init(const char *entropy_file)
+{
+ os_free(random_entropy_file);
+ if (entropy_file)
+ random_entropy_file = os_strdup(entropy_file);
+ else
+ random_entropy_file = NULL;
+ random_read_entropy();
+
+#ifdef __linux__
+ if (random_fd >= 0)
+ return;
+
+ random_fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
+ if (random_fd < 0) {
+#ifndef CONFIG_NO_STDOUT_DEBUG
+ int error = errno;
+ perror("open(/dev/random)");
+ wpa_printf(MSG_ERROR, "random: Cannot open /dev/random: %s",
+ strerror(error));
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "random: Trying to read entropy from "
+ "/dev/random");
+
+ eloop_register_read_sock(random_fd, random_read_fd, NULL, NULL);
+#endif /* __linux__ */
+
+ random_write_entropy();
+}
+
+
+void random_deinit(void)
+{
+#ifdef __linux__
+ random_close_fd();
+#endif /* __linux__ */
+ random_write_entropy();
+ os_free(random_entropy_file);
+ random_entropy_file = NULL;
+}
--- /dev/null
+/*
+ * Random number generator
+ * Copyright (c) 2010-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef RANDOM_H
+#define RANDOM_H
+
+#ifdef CONFIG_NO_RANDOM_POOL
+#define random_init(e) do { } while (0)
+#define random_deinit() do { } while (0)
+#define random_add_randomness(b, l) do { } while (0)
+#define random_get_bytes(b, l) os_get_random((b), (l))
+#define random_pool_ready() 1
+#define random_mark_pool_ready() do { } while (0)
+#else /* CONFIG_NO_RANDOM_POOL */
+void random_init(const char *entropy_file);
+void random_deinit(void);
+void random_add_randomness(const void *buf, size_t len);
+int random_get_bytes(void *buf, size_t len);
+int random_pool_ready(void);
+void random_mark_pool_ready(void);
+#endif /* CONFIG_NO_RANDOM_POOL */
+
+#endif /* RANDOM_H */
/*
* SHA-256 hash implementation and interface functions
- * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2011, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
#include "sha256.h"
#include "crypto.h"
+#define SHA256_BLOCK_SIZE 64
+
struct sha256_state {
u64 length;
u32 state[8], curlen;
- u8 buf[64];
+ u8 buf[SHA256_BLOCK_SIZE];
};
static void sha256_init(struct sha256_state *md);
unsigned long inlen)
{
unsigned long n;
-#define block_size 64
- if (md->curlen > sizeof(md->buf))
+ if (md->curlen >= sizeof(md->buf))
return -1;
while (inlen > 0) {
- if (md->curlen == 0 && inlen >= block_size) {
+ if (md->curlen == 0 && inlen >= SHA256_BLOCK_SIZE) {
if (sha256_compress(md, (unsigned char *) in) < 0)
return -1;
- md->length += block_size * 8;
- in += block_size;
- inlen -= block_size;
+ md->length += SHA256_BLOCK_SIZE * 8;
+ in += SHA256_BLOCK_SIZE;
+ inlen -= SHA256_BLOCK_SIZE;
} else {
- n = MIN(inlen, (block_size - md->curlen));
+ n = MIN(inlen, (SHA256_BLOCK_SIZE - md->curlen));
os_memcpy(md->buf + md->curlen, in, n);
md->curlen += n;
in += n;
inlen -= n;
- if (md->curlen == block_size) {
+ if (md->curlen == SHA256_BLOCK_SIZE) {
if (sha256_compress(md, md->buf) < 0)
return -1;
- md->length += 8 * block_size;
+ md->length += 8 * SHA256_BLOCK_SIZE;
md->curlen = 0;
}
}
* encoding like normal.
*/
if (md->curlen > 56) {
- while (md->curlen < 64) {
+ while (md->curlen < SHA256_BLOCK_SIZE) {
md->buf[md->curlen++] = (unsigned char) 0;
}
sha256_compress(md, md->buf);
md->curlen = 0;
}
- /* pad upto 56 bytes of zeroes */
+ /* pad up to 56 bytes of zeroes */
while (md->curlen < 56) {
md->buf[md->curlen++] = (unsigned char) 0;
}
size_t client_random_len;
const u8 *server_random;
size_t server_random_len;
- const u8 *inner_secret; /* TLS/IA inner secret */
- size_t inner_secret_len;
};
enum tls_event {
const char *pkcs11_engine_path;
const char *pkcs11_module_path;
int fips_mode;
+ int cert_in_cb;
void (*event_cb)(void *ctx, enum tls_event ev,
union tls_event_data *data);
* specific for now)
* @cert_id: the certificate's id when using engine
* @ca_cert_id: the CA certificate's id when using engine
- * @tls_ia: Whether to enable TLS/IA (for EAP-TTLSv1)
* @flags: Parameter options (TLS_CONN_*)
*
* TLS connection parameters to be configured with tls_connection_set_params()
const char *dh_file;
const u8 *dh_blob;
size_t dh_blob_len;
- int tls_ia;
/* OpenSSL specific variables */
int engine;
int verify_peer);
/**
- * tls_connection_set_ia - Set TLS/IA parameters
- * @tls_ctx: TLS context data from tls_init()
- * @conn: Connection context data from tls_connection_init()
- * @tls_ia: 1 = enable TLS/IA
- * Returns: 0 on success, -1 on failure
- *
- * This function is used to configure TLS/IA in server mode where
- * tls_connection_set_params() is not used.
- */
-int __must_check tls_connection_set_ia(void *tls_ctx,
- struct tls_connection *conn,
- int tls_ia);
-
-/**
* tls_connection_get_keys - Get master key and random data from TLS connection
* @tls_ctx: TLS context data from tls_init()
* @conn: Connection context data from tls_connection_init()
int tls_connection_get_keyblock_size(void *tls_ctx,
struct tls_connection *conn);
-#define TLS_CAPABILITY_IA 0x0001 /* TLS Inner Application (TLS/IA) */
/**
* tls_capabilities - Get supported TLS capabilities
* @tls_ctx: TLS context data from tls_init()
*/
unsigned int tls_capabilities(void *tls_ctx);
-/**
- * tls_connection_ia_send_phase_finished - Send a TLS/IA PhaseFinished message
- * @tls_ctx: TLS context data from tls_init()
- * @conn: Connection context data from tls_connection_init()
- * @final: 1 = FinalPhaseFinished, 0 = IntermediatePhaseFinished
- * Returns: Encrypted TLS/IA data, %NULL on failure
- *
- * This function is used to send the TLS/IA end phase message, e.g., when the
- * EAP server completes EAP-TTLSv1.
- */
-struct wpabuf * tls_connection_ia_send_phase_finished(
- void *tls_ctx, struct tls_connection *conn, int final);
-
-/**
- * tls_connection_ia_final_phase_finished - Has final phase been completed
- * @tls_ctx: TLS context data from tls_init()
- * @conn: Connection context data from tls_connection_init()
- * Returns: 1 if valid FinalPhaseFinished has been received, 0 if not, or -1
- * on failure
- */
-int __must_check tls_connection_ia_final_phase_finished(
- void *tls_ctx, struct tls_connection *conn);
-
-/**
- * tls_connection_ia_permute_inner_secret - Permute TLS/IA inner secret
- * @tls_ctx: TLS context data from tls_init()
- * @conn: Connection context data from tls_connection_init()
- * @key: Session key material (session_key vectors with 2-octet length), or
- * %NULL if no session key was generating in the current phase
- * @key_len: Length of session key material
- * Returns: 0 on success, -1 on failure
- */
-int __must_check tls_connection_ia_permute_inner_secret(
- void *tls_ctx, struct tls_connection *conn,
- const u8 *key, size_t key_len);
-
typedef int (*tls_session_ticket_cb)
(void *ctx, const u8 *ticket, size_t len, const u8 *client_random,
const u8 *server_random, u8 *master_secret);
/*
* SSL/TLS interface functions for GnuTLS
- * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
#include <gnutls/pkcs12.h>
#endif /* PKCS12_FUNCS */
-#ifdef CONFIG_GNUTLS_EXTRA
-#if LIBGNUTLS_VERSION_NUMBER >= 0x010302
-#define GNUTLS_IA
-#include <gnutls/extra.h>
-#if LIBGNUTLS_VERSION_NUMBER == 0x010302
-/* This function is not included in the current gnutls/extra.h even though it
- * should be, so define it here as a workaround for the time being. */
-int gnutls_ia_verify_endphase(gnutls_session_t session, char *checksum);
-#endif /* LIBGNUTLS_VERSION_NUMBER == 0x010302 */
-#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */
-#endif /* CONFIG_GNUTLS_EXTRA */
-
#include "common.h"
#include "tls.h"
-#ifndef TLS_RANDOM_SIZE
-#define TLS_RANDOM_SIZE 32
-#endif
-#ifndef TLS_MASTER_SIZE
-#define TLS_MASTER_SIZE 48
-#endif
+#define WPA_TLS_RANDOM_SIZE 32
+#define WPA_TLS_MASTER_SIZE 48
#if LIBGNUTLS_VERSION_NUMBER < 0x010302
gnutls_mac_algorithm_t write_mac_algorithm;
gnutls_compression_method_t write_compression_algorithm;
cipher_suite_st current_cipher_suite;
- opaque master_secret[TLS_MASTER_SIZE];
- opaque client_random[TLS_RANDOM_SIZE];
- opaque server_random[TLS_RANDOM_SIZE];
+ opaque master_secret[WPA_TLS_MASTER_SIZE];
+ opaque client_random[WPA_TLS_RANDOM_SIZE];
+ opaque server_random[WPA_TLS_RANDOM_SIZE];
/* followed by stuff we are not interested in */
} security_parameters_st;
int params_set;
gnutls_certificate_credentials_t xcred;
-
- int tls_ia;
- int final_phase_finished;
-
-#ifdef GNUTLS_IA
- gnutls_ia_server_credentials_t iacred_srv;
- gnutls_ia_client_credentials_t iacred_cli;
-
- /* Session keys generated in the current phase for inner secret
- * permutation before generating/verifying PhaseFinished. */
- u8 *session_keys;
- size_t session_keys_len;
-
- u8 inner_secret[TLS_MASTER_SIZE];
-#endif /* GNUTLS_IA */
};
static int tls_gnutls_init_session(struct tls_global *global,
struct tls_connection *conn)
{
+#if LIBGNUTLS_VERSION_NUMBER >= 0x020200
+ const char *err;
+#else /* LIBGNUTLS_VERSION_NUMBER >= 0x020200 */
const int cert_types[2] = { GNUTLS_CRT_X509, 0 };
const int protos[2] = { GNUTLS_TLS1, 0 };
+#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020200 */
int ret;
ret = gnutls_init(&conn->session,
if (ret < 0)
goto fail;
+#if LIBGNUTLS_VERSION_NUMBER >= 0x020200
+ ret = gnutls_priority_set_direct(conn->session, "NORMAL:-VERS-SSL3.0",
+ &err);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR, "GnuTLS: Priority string failure at "
+ "'%s'", err);
+ goto fail;
+ }
+#else /* LIBGNUTLS_VERSION_NUMBER >= 0x020200 */
ret = gnutls_certificate_type_set_priority(conn->session, cert_types);
if (ret < 0)
goto fail;
ret = gnutls_protocol_set_priority(conn->session, protos);
if (ret < 0)
goto fail;
+#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020200 */
gnutls_transport_set_pull_function(conn->session, tls_pull_func);
gnutls_transport_set_push_function(conn->session, tls_push_func);
if (conn == NULL)
return;
-#ifdef GNUTLS_IA
- if (conn->iacred_srv)
- gnutls_ia_free_server_credentials(conn->iacred_srv);
- if (conn->iacred_cli)
- gnutls_ia_free_client_credentials(conn->iacred_cli);
- if (conn->session_keys) {
- os_memset(conn->session_keys, 0, conn->session_keys_len);
- os_free(conn->session_keys);
- }
-#endif /* GNUTLS_IA */
-
gnutls_certificate_free_credentials(conn->xcred);
gnutls_deinit(conn->session);
os_free(conn->pre_shared_secret);
wpabuf_free(conn->push_buf);
conn->push_buf = NULL;
conn->established = 0;
- conn->final_phase_finished = 0;
-#ifdef GNUTLS_IA
- if (conn->session_keys) {
- os_memset(conn->session_keys, 0, conn->session_keys_len);
- os_free(conn->session_keys);
- }
- conn->session_keys_len = 0;
-#endif /* GNUTLS_IA */
gnutls_deinit(conn->session);
if (tls_gnutls_init_session(global, conn)) {
conn->xcred, GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5);
}
+#if LIBGNUTLS_VERSION_NUMBER >= 0x020800
if (params->flags & TLS_CONN_DISABLE_TIME_CHECKS) {
gnutls_certificate_set_verify_flags(
conn->xcred,
GNUTLS_VERIFY_DISABLE_TIME_CHECKS);
}
+#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020800 */
}
if (params->client_cert && params->private_key) {
}
}
- conn->tls_ia = params->tls_ia;
conn->params_set = 1;
ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE,
gnutls_strerror(ret));
}
-#ifdef GNUTLS_IA
- if (conn->iacred_cli)
- gnutls_ia_free_client_credentials(conn->iacred_cli);
-
- ret = gnutls_ia_allocate_client_credentials(&conn->iacred_cli);
- if (ret) {
- wpa_printf(MSG_DEBUG, "Failed to allocate IA credentials: %s",
- gnutls_strerror(ret));
- return -1;
- }
-
- ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_IA,
- conn->iacred_cli);
- if (ret) {
- wpa_printf(MSG_DEBUG, "Failed to configure IA credentials: %s",
- gnutls_strerror(ret));
- gnutls_ia_free_client_credentials(conn->iacred_cli);
- conn->iacred_cli = NULL;
- return -1;
- }
-#endif /* GNUTLS_IE */
-
return ret;
}
GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5);
}
+#if LIBGNUTLS_VERSION_NUMBER >= 0x020800
if (params->flags & TLS_CONN_DISABLE_TIME_CHECKS) {
gnutls_certificate_set_verify_flags(
global->xcred,
GNUTLS_VERIFY_DISABLE_TIME_CHECKS);
}
+#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020800 */
}
if (params->client_cert && params->private_key) {
os_memset(keys, 0, sizeof(*keys));
+#if LIBGNUTLS_VERSION_NUMBER < 0x020c00
#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK
sec = &conn->session->security_parameters;
keys->master_key = sec->master_secret;
- keys->master_key_len = TLS_MASTER_SIZE;
+ keys->master_key_len = WPA_TLS_MASTER_SIZE;
keys->client_random = sec->client_random;
keys->server_random = sec->server_random;
#else /* GNUTLS_INTERNAL_STRUCTURE_HACK */
(u8 *) gnutls_session_get_server_random(conn->session);
/* No access to master_secret */
#endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */
+#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020c00 */
-#ifdef GNUTLS_IA
- gnutls_ia_extract_inner_secret(conn->session,
- (char *) conn->inner_secret);
- keys->inner_secret = conn->inner_secret;
- keys->inner_secret_len = TLS_MASTER_SIZE;
-#endif /* GNUTLS_IA */
-
- keys->client_random_len = TLS_RANDOM_SIZE;
- keys->server_random_len = TLS_RANDOM_SIZE;
+#if LIBGNUTLS_VERSION_NUMBER < 0x020c00
+ keys->client_random_len = WPA_TLS_RANDOM_SIZE;
+ keys->server_random_len = WPA_TLS_RANDOM_SIZE;
+#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020c00 */
return 0;
}
if (conn->verify_peer && (status & GNUTLS_CERT_INVALID)) {
wpa_printf(MSG_INFO, "TLS: Peer certificate not trusted");
+ *err = GNUTLS_A_INTERNAL_ERROR;
if (status & GNUTLS_CERT_INSECURE_ALGORITHM) {
wpa_printf(MSG_INFO, "TLS: Certificate uses insecure "
"algorithm");
*err = GNUTLS_A_INSUFFICIENT_SECURITY;
}
+#if LIBGNUTLS_VERSION_NUMBER >= 0x020800
if (status & GNUTLS_CERT_NOT_ACTIVATED) {
wpa_printf(MSG_INFO, "TLS: Certificate not yet "
"activated");
wpa_printf(MSG_INFO, "TLS: Certificate expired");
*err = GNUTLS_A_CERTIFICATE_EXPIRED;
}
+#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020800 */
return -1;
}
wpabuf_size(ad));
wpa_printf(MSG_DEBUG, "GnuTLS: gnutls_record_recv: %d", res);
if (res < 0) {
- wpa_printf(MSG_DEBUG, "%s - gnutls_ia_recv failed: %d "
+ wpa_printf(MSG_DEBUG, "%s - gnutls_record_recv failed: %d "
"(%s)", __func__, (int) res,
gnutls_strerror(res));
wpabuf_free(ad);
goto out;
}
-#ifdef CONFIG_GNUTLS_EXTRA
- if (conn->tls_ia && !gnutls_ia_handshake_p(conn->session)) {
- wpa_printf(MSG_INFO, "TLS: No TLS/IA negotiation");
- conn->failed++;
- return NULL;
- }
-#endif /* CONFIG_GNUTLS_EXTRA */
-
- if (conn->tls_ia)
- wpa_printf(MSG_DEBUG, "TLS: Start TLS/IA handshake");
- else {
- wpa_printf(MSG_DEBUG, "TLS: Handshake completed "
- "successfully");
- }
+ wpa_printf(MSG_DEBUG, "TLS: Handshake completed successfully");
conn->established = 1;
if (conn->push_buf == NULL) {
/* Need to return something to get final TLS ACK. */
ssize_t res;
struct wpabuf *buf;
-#ifdef GNUTLS_IA
- if (conn->tls_ia)
- res = gnutls_ia_send(conn->session, wpabuf_head(in_data),
- wpabuf_len(in_data));
- else
-#endif /* GNUTLS_IA */
res = gnutls_record_send(conn->session, wpabuf_head(in_data),
wpabuf_len(in_data));
if (res < 0) {
if (out == NULL)
return NULL;
-#ifdef GNUTLS_IA
- if (conn->tls_ia) {
- res = gnutls_ia_recv(conn->session, wpabuf_mhead(out),
- wpabuf_size(out));
- if (res == GNUTLS_E_WARNING_IA_IPHF_RECEIVED ||
- res == GNUTLS_E_WARNING_IA_FPHF_RECEIVED) {
- int final = res == GNUTLS_E_WARNING_IA_FPHF_RECEIVED;
- wpa_printf(MSG_DEBUG, "%s: Received %sPhaseFinished",
- __func__, final ? "Final" : "Intermediate");
-
- res = gnutls_ia_permute_inner_secret(
- conn->session, conn->session_keys_len,
- (char *) conn->session_keys);
- if (conn->session_keys) {
- os_memset(conn->session_keys, 0,
- conn->session_keys_len);
- os_free(conn->session_keys);
- }
- conn->session_keys = NULL;
- conn->session_keys_len = 0;
- if (res) {
- wpa_printf(MSG_DEBUG, "%s: Failed to permute "
- "inner secret: %s",
- __func__, gnutls_strerror(res));
- wpabuf_free(out);
- return NULL;
- }
-
- res = gnutls_ia_verify_endphase(conn->session,
- wpabuf_head(out));
- if (res == 0) {
- wpa_printf(MSG_DEBUG, "%s: Correct endphase "
- "checksum", __func__);
- } else {
- wpa_printf(MSG_INFO, "%s: Endphase "
- "verification failed: %s",
- __func__, gnutls_strerror(res));
- wpabuf_free(out);
- return NULL;
- }
-
- if (final)
- conn->final_phase_finished = 1;
-
- return out;
- }
-
- if (res < 0) {
- wpa_printf(MSG_DEBUG, "%s - gnutls_ia_recv failed: %d "
- "(%s)", __func__, (int) res,
- gnutls_strerror(res));
- wpabuf_free(out);
- return NULL;
- }
- wpabuf_put(out, res);
- return out;
- }
-#endif /* GNUTLS_IA */
-
res = gnutls_record_recv(conn->session, wpabuf_mhead(out),
wpabuf_size(out));
if (res < 0) {
unsigned int tls_capabilities(void *tls_ctx)
{
- unsigned int capa = 0;
-
-#ifdef GNUTLS_IA
- capa |= TLS_CAPABILITY_IA;
-#endif /* GNUTLS_IA */
-
- return capa;
-}
-
-
-int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn,
- int tls_ia)
-{
-#ifdef GNUTLS_IA
- int ret;
-
- if (conn == NULL)
- return -1;
-
- conn->tls_ia = tls_ia;
- if (!tls_ia)
- return 0;
-
- ret = gnutls_ia_allocate_server_credentials(&conn->iacred_srv);
- if (ret) {
- wpa_printf(MSG_DEBUG, "Failed to allocate IA credentials: %s",
- gnutls_strerror(ret));
- return -1;
- }
-
- ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_IA,
- conn->iacred_srv);
- if (ret) {
- wpa_printf(MSG_DEBUG, "Failed to configure IA credentials: %s",
- gnutls_strerror(ret));
- gnutls_ia_free_server_credentials(conn->iacred_srv);
- conn->iacred_srv = NULL;
- return -1;
- }
-
- return 0;
-#else /* GNUTLS_IA */
- return -1;
-#endif /* GNUTLS_IA */
-}
-
-
-struct wpabuf * tls_connection_ia_send_phase_finished(
- void *tls_ctx, struct tls_connection *conn, int final)
-{
-#ifdef GNUTLS_IA
- int ret;
- struct wpabuf *buf;
-
- if (conn == NULL || conn->session == NULL || !conn->tls_ia)
- return NULL;
-
- ret = gnutls_ia_permute_inner_secret(conn->session,
- conn->session_keys_len,
- (char *) conn->session_keys);
- if (conn->session_keys) {
- os_memset(conn->session_keys, 0, conn->session_keys_len);
- os_free(conn->session_keys);
- }
- conn->session_keys = NULL;
- conn->session_keys_len = 0;
- if (ret) {
- wpa_printf(MSG_DEBUG, "%s: Failed to permute inner secret: %s",
- __func__, gnutls_strerror(ret));
- return NULL;
- }
-
- ret = gnutls_ia_endphase_send(conn->session, final);
- if (ret) {
- wpa_printf(MSG_DEBUG, "%s: Failed to send endphase: %s",
- __func__, gnutls_strerror(ret));
- return NULL;
- }
-
- buf = conn->push_buf;
- conn->push_buf = NULL;
- return buf;
-#else /* GNUTLS_IA */
- return NULL;
-#endif /* GNUTLS_IA */
-}
-
-
-int tls_connection_ia_final_phase_finished(void *tls_ctx,
- struct tls_connection *conn)
-{
- if (conn == NULL)
- return -1;
-
- return conn->final_phase_finished;
-}
-
-
-int tls_connection_ia_permute_inner_secret(void *tls_ctx,
- struct tls_connection *conn,
- const u8 *key, size_t key_len)
-{
-#ifdef GNUTLS_IA
- if (conn == NULL || !conn->tls_ia)
- return -1;
-
- if (conn->session_keys) {
- os_memset(conn->session_keys, 0, conn->session_keys_len);
- os_free(conn->session_keys);
- }
- conn->session_keys_len = 0;
-
- if (key) {
- conn->session_keys = os_malloc(key_len);
- if (conn->session_keys == NULL)
- return -1;
- os_memcpy(conn->session_keys, key, key_len);
- conn->session_keys_len = key_len;
- } else {
- conn->session_keys = NULL;
- conn->session_keys_len = 0;
- }
-
return 0;
-#else /* GNUTLS_IA */
- return -1;
-#endif /* GNUTLS_IA */
}
/*
* TLS interface functions and an internal TLS implementation
- * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
return -1;
}
+ tlsv1_client_set_time_checks(
+ conn->client, !(params->flags & TLS_CONN_DISABLE_TIME_CHECKS));
+
return 0;
#else /* CONFIG_TLS_INTERNAL_CLIENT */
return -1;
}
-int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn,
- int tls_ia)
-{
- return -1;
-}
-
-
int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn,
struct tls_keys *keys)
{
}
-struct wpabuf * tls_connection_ia_send_phase_finished(
- void *tls_ctx, struct tls_connection *conn, int final)
-{
- return NULL;
-}
-
-
-int tls_connection_ia_final_phase_finished(void *tls_ctx,
- struct tls_connection *conn)
-{
- return -1;
-}
-
-
-int tls_connection_ia_permute_inner_secret(void *tls_ctx,
- struct tls_connection *conn,
- const u8 *key, size_t key_len)
-{
- return -1;
-}
-
-
int tls_connection_set_session_ticket_cb(void *tls_ctx,
struct tls_connection *conn,
tls_session_ticket_cb cb,
}
-int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn,
- int tls_ia)
-{
- return -1;
-}
-
-
int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn,
struct tls_keys *keys)
{
{
return 0;
}
-
-
-struct wpabuf * tls_connection_ia_send_phase_finished(
- void *tls_ctx, struct tls_connection *conn, int final)
-{
- return NULL;
-}
-
-
-int tls_connection_ia_final_phase_finished(void *tls_ctx,
- struct tls_connection *conn)
-{
- return -1;
-}
-
-
-int tls_connection_ia_permute_inner_secret(void *tls_ctx,
- struct tls_connection *conn,
- const u8 *key, size_t key_len)
-{
- return -1;
-}
}
-int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn,
- int tls_ia)
-{
- return -1;
-}
-
-
int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn,
struct tls_keys *keys)
{
}
-struct wpabuf * tls_connection_ia_send_phase_finished(
- void *tls_ctx, struct tls_connection *conn, int final)
-{
- return NULL;
-}
-
-
-int tls_connection_ia_final_phase_finished(void *tls_ctx,
- struct tls_connection *conn)
-{
- return -1;
-}
-
-
-int tls_connection_ia_permute_inner_secret(void *tls_ctx,
- struct tls_connection *conn,
- const u8 *key, size_t key_len)
-{
- return -1;
-}
-
-
int tls_connection_set_session_ticket_cb(void *tls_ctx,
struct tls_connection *conn,
tls_session_ticket_cb cb,
/*
* SSL/TLS interface functions for OpenSSL
- * Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
#include <openssl/engine.h>
#endif /* OPENSSL_NO_ENGINE */
+#ifdef ANDROID
+#include <openssl/pem.h>
+#include "keystore_get.h"
+#endif /* ANDROID */
+
#include "common.h"
#include "crypto.h"
#include "tls.h"
void (*event_cb)(void *ctx, enum tls_event ev,
union tls_event_data *data);
void *cb_ctx;
+ int cert_in_cb;
};
static struct tls_global *tls_global = NULL;
unsigned int server_cert_only:1;
u8 srv_cert_hash[32];
+
+ unsigned int flags;
};
if (conf) {
tls_global->event_cb = conf->event_cb;
tls_global->cb_ctx = conf->cb_ctx;
+ tls_global->cert_in_cb = conf->cert_in_cb;
}
#ifdef CONFIG_FIPS
#endif /* CONFIG_FIPS */
SSL_load_error_strings();
SSL_library_init();
-#ifndef OPENSSL_NO_SHA256
+#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256)
EVP_add_digest(EVP_sha256());
#endif /* OPENSSL_NO_SHA256 */
/* TODO: if /dev/urandom is available, PRNG is seeded
return;
os_memset(&ev, 0, sizeof(ev));
- if (conn->cert_probe) {
+ if (conn->cert_probe || tls_global->cert_in_cb) {
cert = get_x509_cert(err_cert);
ev.peer_cert.cert = cert;
}
X509_NAME_oneline(X509_get_subject_name(err_cert), buf, sizeof(buf));
conn = SSL_get_app_data(ssl);
- match = conn ? conn->subject_match : NULL;
- altmatch = conn ? conn->altsubject_match : NULL;
+ if (conn == NULL)
+ return 0;
+ match = conn->subject_match;
+ altmatch = conn->altsubject_match;
if (!preverify_ok && !conn->ca_cert_verify)
preverify_ok = 1;
if (!preverify_ok && depth > 0 && conn->server_cert_only)
preverify_ok = 1;
+ if (!preverify_ok && (conn->flags & TLS_CONN_DISABLE_TIME_CHECKS) &&
+ (err == X509_V_ERR_CERT_HAS_EXPIRED ||
+ err == X509_V_ERR_CERT_NOT_YET_VALID)) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: Ignore certificate validity "
+ "time mismatch");
+ preverify_ok = 1;
+ }
err_str = X509_verify_cert_error_string(err);
#endif /* OPENSSL_NO_STDIO */
+#ifdef ANDROID
+static BIO * BIO_from_keystore(const char *key)
+{
+ BIO *bio = NULL;
+ char value[KEYSTORE_MESSAGE_SIZE];
+ int length = keystore_get(key, strlen(key), value);
+ if (length != -1 && (bio = BIO_new(BIO_s_mem())) != NULL)
+ BIO_write(bio, value, length);
+ return bio;
+}
+#endif /* ANDROID */
+
+
static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn,
const char *ca_cert, const u8 *ca_cert_blob,
size_t ca_cert_blob_len, const char *ca_path)
return 0;
}
+#ifdef ANDROID
+ if (ca_cert && os_strncmp("keystore://", ca_cert, 11) == 0) {
+ BIO *bio = BIO_from_keystore(&ca_cert[11]);
+ STACK_OF(X509_INFO) *stack = NULL;
+ int i;
+
+ if (bio) {
+ stack = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL);
+ BIO_free(bio);
+ }
+ if (!stack)
+ return -1;
+
+ for (i = 0; i < sk_X509_INFO_num(stack); ++i) {
+ X509_INFO *info = sk_X509_INFO_value(stack, i);
+ if (info->x509) {
+ X509_STORE_add_cert(ssl_ctx->cert_store,
+ info->x509);
+ }
+ if (info->crl) {
+ X509_STORE_add_crl(ssl_ctx->cert_store,
+ info->crl);
+ }
+ }
+ sk_X509_INFO_pop_free(stack, X509_INFO_free);
+ SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
+ return 0;
+ }
+#endif /* ANDROID */
+
#ifdef CONFIG_NATIVE_WINDOWS
if (ca_cert && tls_cryptoapi_ca_cert(ssl_ctx, conn->ssl, ca_cert) ==
0) {
if (client_cert == NULL)
return -1;
+#ifdef ANDROID
+ if (os_strncmp("keystore://", client_cert, 11) == 0) {
+ BIO *bio = BIO_from_keystore(&client_cert[11]);
+ X509 *x509 = NULL;
+ int ret = -1;
+ if (bio) {
+ x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
+ BIO_free(bio);
+ }
+ if (x509) {
+ if (SSL_use_certificate(conn->ssl, x509) == 1)
+ ret = 0;
+ X509_free(x509);
+ }
+ return ret;
+ }
+#endif /* ANDROID */
+
#ifndef OPENSSL_NO_STDIO
if (SSL_use_certificate_file(conn->ssl, client_cert,
SSL_FILETYPE_ASN1) == 1) {
wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_file (DER)"
" --> OK");
return 0;
- } else {
- tls_show_errors(MSG_DEBUG, __func__,
- "SSL_use_certificate_file (DER) failed");
}
if (SSL_use_certificate_file(conn->ssl, client_cert,
SSL_FILETYPE_PEM) == 1) {
+ ERR_clear_error();
wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_file (PEM)"
" --> OK");
return 0;
- } else {
- tls_show_errors(MSG_DEBUG, __func__,
- "SSL_use_certificate_file (PEM) failed");
}
+
+ tls_show_errors(MSG_DEBUG, __func__,
+ "SSL_use_certificate_file failed");
#else /* OPENSSL_NO_STDIO */
wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", __func__);
#endif /* OPENSSL_NO_STDIO */
"ASN1(EVP_PKEY_RSA) --> OK");
ok = 1;
break;
- } else {
- tls_show_errors(MSG_DEBUG, __func__,
- "SSL_use_PrivateKey_ASN1(EVP_PKEY_RSA)"
- " failed");
}
if (SSL_use_PrivateKey_ASN1(EVP_PKEY_DSA, conn->ssl,
"ASN1(EVP_PKEY_DSA) --> OK");
ok = 1;
break;
- } else {
- tls_show_errors(MSG_DEBUG, __func__,
- "SSL_use_PrivateKey_ASN1(EVP_PKEY_DSA)"
- " failed");
}
if (SSL_use_RSAPrivateKey_ASN1(conn->ssl,
"SSL_use_RSAPrivateKey_ASN1 --> OK");
ok = 1;
break;
- } else {
- tls_show_errors(MSG_DEBUG, __func__,
- "SSL_use_RSAPrivateKey_ASN1 failed");
}
if (tls_read_pkcs12_blob(ssl_ctx, conn->ssl, private_key_blob,
break;
}
+#ifdef ANDROID
+ if (!ok && private_key &&
+ os_strncmp("keystore://", private_key, 11) == 0) {
+ BIO *bio = BIO_from_keystore(&private_key[11]);
+ EVP_PKEY *pkey = NULL;
+ if (bio) {
+ pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
+ BIO_free(bio);
+ }
+ if (pkey) {
+ if (SSL_use_PrivateKey(conn->ssl, pkey) == 1) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: Private key "
+ "from keystore");
+ ok = 1;
+ }
+ EVP_PKEY_free(pkey);
+ }
+ }
+#endif /* ANDROID */
+
while (!ok && private_key) {
#ifndef OPENSSL_NO_STDIO
if (SSL_use_PrivateKey_file(conn->ssl, private_key,
"SSL_use_PrivateKey_File (DER) --> OK");
ok = 1;
break;
- } else {
- tls_show_errors(MSG_DEBUG, __func__,
- "SSL_use_PrivateKey_File (DER) "
- "failed");
}
if (SSL_use_PrivateKey_file(conn->ssl, private_key,
"SSL_use_PrivateKey_File (PEM) --> OK");
ok = 1;
break;
- } else {
- tls_show_errors(MSG_DEBUG, __func__,
- "SSL_use_PrivateKey_File (PEM) "
- "failed");
}
#else /* OPENSSL_NO_STDIO */
wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO",
}
if (!ok) {
- wpa_printf(MSG_INFO, "OpenSSL: Failed to load private key");
+ tls_show_errors(MSG_INFO, __func__,
+ "Failed to load private key");
os_free(passwd);
- ERR_clear_error();
return -1;
}
ERR_clear_error();
return -1;
}
+ conn->flags = params->flags;
+
tls_get_errors(tls_ctx);
return 0;
}
-int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn,
- int tls_ia)
-{
- return -1;
-}
-
-
-struct wpabuf * tls_connection_ia_send_phase_finished(
- void *tls_ctx, struct tls_connection *conn, int final)
-{
- return NULL;
-}
-
-
-int tls_connection_ia_final_phase_finished(void *tls_ctx,
- struct tls_connection *conn)
-{
- return -1;
-}
-
-
-int tls_connection_ia_permute_inner_secret(void *tls_ctx,
- struct tls_connection *conn,
- const u8 *key, size_t key_len)
-{
- return -1;
-}
-
-
#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
/* Pre-shared secred requires a patch to openssl, so this function is
* commented out unless explicitly needed for EAP-FAST in order to be able to
{
return 0;
}
-
-
-int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn,
- int tls_ia)
-{
- return -1;
-}
-
-
-struct wpabuf * tls_connection_ia_send_phase_finished(
- void *tls_ctx, struct tls_connection *conn, int final);
-{
- return NULL;
-}
-
-
-int tls_connection_ia_final_phase_finished(void *tls_ctx,
- struct tls_connection *conn)
-{
- return -1;
-}
-
-
-int tls_connection_ia_permute_inner_secret(void *tls_ctx,
- struct tls_connection *conn,
- const u8 *key, size_t key_len)
-{
- return -1;
-}
#define HOSTAPD_CHAN_PASSIVE_SCAN 0x00000002
#define HOSTAPD_CHAN_NO_IBSS 0x00000004
#define HOSTAPD_CHAN_RADAR 0x00000008
+#define HOSTAPD_CHAN_HT40PLUS 0x00000010
+#define HOSTAPD_CHAN_HT40MINUS 0x00000020
+#define HOSTAPD_CHAN_HT40 0x00000040
/**
* struct hostapd_channel_data - Channel information
u8 max_tx_power;
};
+#define HOSTAPD_MODE_FLAG_HT_INFO_KNOWN BIT(0)
+
/**
* struct hostapd_hw_modes - Supported hardware mode information
*/
* a_mpdu_params - A-MPDU (IEEE 802.11n) parameters
*/
u8 a_mpdu_params;
+
+ unsigned int flags; /* HOSTAPD_MODE_FLAG_* */
};
const char *drv_name;
};
-#define WPAS_MAX_SCAN_SSIDS 4
+#define WPAS_MAX_SCAN_SSIDS 16
/**
* struct wpa_driver_scan_params - Scan parameters
* num_filter_ssids - Number of entries in filter_ssids array
*/
size_t num_filter_ssids;
+
+ /**
+ * p2p_probe - Used to disable CCK (802.11b) rates for P2P probes
+ *
+ * When set, the driver is expected to remove rates 1, 2, 5.5, and 11
+ * Mbps from the support rates element(s) in the Probe Request frames
+ * and not to transmit the frames at any of those rates.
+ */
+ u8 p2p_probe;
};
/**
size_t wep_key_len[4];
int wep_tx_keyidx;
int local_state_change;
+
+ /**
+ * p2p - Whether this connection is a P2P group
+ */
+ int p2p;
+
+};
+
+enum wps_mode {
+ WPS_MODE_NONE /* no WPS provisioning being used */,
+ WPS_MODE_OPEN /* WPS provisioning with AP that is in open mode */,
+ WPS_MODE_PRIVACY /* WPS provisioning with AP that is using protection
+ */
};
/**
size_t wpa_ie_len;
/**
+ * wpa_proto - Bitfield of WPA_PROTO_* values to indicate WPA/WPA2
+ */
+ unsigned int wpa_proto;
+
+ /**
* pairwise_suite - Selected pairwise cipher suite
*
* This is usually ignored if @wpa_ie is used.
* association.
*/
const u8 *prev_bssid;
+
+ /**
+ * wps - WPS mode
+ *
+ * If the driver needs to do special configuration for WPS association,
+ * this variable provides more information on what type of association
+ * is being requested. Most drivers should not need ot use this.
+ */
+ enum wps_mode wps;
+
+ /**
+ * p2p - Whether this connection is a P2P group
+ */
+ int p2p;
+
+ /**
+ * uapsd - UAPSD parameters for the network
+ * -1 = do not change defaults
+ * AP mode: 1 = enabled, 0 = disabled
+ * STA mode: bits 0..3 UAPSD enabled for VO,VI,BK,BE
+ */
+ int uapsd;
+};
+
+enum hide_ssid {
+ NO_SSID_HIDING,
+ HIDDEN_SSID_ZERO_LEN,
+ HIDDEN_SSID_ZERO_CONTENTS
+};
+
+struct wpa_driver_ap_params {
+ /**
+ * head - Beacon head from IEEE 802.11 header to IEs before TIM IE
+ */
+ const u8 *head;
+
+ /**
+ * head_len - Length of the head buffer in octets
+ */
+ size_t head_len;
+
+ /**
+ * tail - Beacon tail following TIM IE
+ */
+ const u8 *tail;
+
+ /**
+ * tail_len - Length of the tail buffer in octets
+ */
+ size_t tail_len;
+
+ /**
+ * dtim_period - DTIM period
+ */
+ int dtim_period;
+
+ /**
+ * beacon_int - Beacon interval
+ */
+ int beacon_int;
+
+ /**
+ * ssid - The SSID to use in Beacon/Probe Response frames
+ */
+ const u8 *ssid;
+
+ /**
+ * ssid_len - Length of the SSID (1..32)
+ */
+ size_t ssid_len;
+
+ /**
+ * hide_ssid - Whether to hide the SSID
+ */
+ enum hide_ssid hide_ssid;
+
+ /**
+ * pairwise_ciphers - WPA_CIPHER_* bitfield
+ */
+ unsigned int pairwise_ciphers;
+
+ /**
+ * group_cipher - WPA_CIPHER_*
+ */
+ unsigned int group_cipher;
+
+ /**
+ * key_mgmt_suites - WPA_KEY_MGMT_* bitfield
+ */
+ unsigned int key_mgmt_suites;
+
+ /**
+ * auth_algs - WPA_AUTH_ALG_* bitfield
+ */
+ unsigned int auth_algs;
+
+ /**
+ * wpa_version - WPA_PROTO_* bitfield
+ */
+ unsigned int wpa_version;
+
+ /**
+ * privacy - Whether privacy is used in the BSS
+ */
+ int privacy;
+
+ /**
+ * beacon_ies - WPS/P2P IE(s) for Beacon frames
+ *
+ * This is used to add IEs like WPS IE and P2P IE by drivers that do
+ * not use the full Beacon template.
+ */
+ const struct wpabuf *beacon_ies;
+
+ /**
+ * proberesp_ies - P2P/WPS IE(s) for Probe Response frames
+ *
+ * This is used to add IEs like WPS IE and P2P IE by drivers that
+ * reply to Probe Request frames internally.
+ */
+ const struct wpabuf *proberesp_ies;
+
+ /**
+ * assocresp_ies - WPS IE(s) for (Re)Association Response frames
+ *
+ * This is used to add IEs like WPS IE by drivers that reply to
+ * (Re)Association Request frames internally.
+ */
+ const struct wpabuf *assocresp_ies;
+
+ /**
+ * isolate - Whether to isolate frames between associated stations
+ *
+ * If this is non-zero, the AP is requested to disable forwarding of
+ * frames between association stations.
+ */
+ int isolate;
+
+ /**
+ * cts_protect - Whether CTS protection is enabled
+ */
+ int cts_protect;
+
+ /**
+ * preamble - Whether short preamble is enabled
+ */
+ int preamble;
+
+ /**
+ * short_slot_time - Whether short slot time is enabled
+ *
+ * 0 = short slot time disable, 1 = short slot time enabled, -1 = do
+ * not set (e.g., when 802.11g mode is not in use)
+ */
+ int short_slot_time;
+
+ /**
+ * ht_opmode - HT operation mode or -1 if HT not in use
+ */
+ int ht_opmode;
+
+ /**
+ * interworking - Whether Interworking is enabled
+ */
+ int interworking;
+
+ /**
+ * hessid - Homogeneous ESS identifier or %NULL if not set
+ */
+ const u8 *hessid;
+
+ /**
+ * access_network_type - Access Network Type (0..15)
+ *
+ * This is used for filtering Probe Request frames when Interworking is
+ * enabled.
+ */
+ u8 access_network_type;
};
/**
#define WPA_DRIVER_FLAGS_DRIVER_IE 0x00000001
/* Driver needs static WEP key setup after association command */
#define WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC 0x00000002
-#define WPA_DRIVER_FLAGS_USER_SPACE_MLME 0x00000004
+/* unused: 0x00000004 */
/* Driver takes care of RSN 4-way handshake internally; PMK is configured with
* struct wpa_driver_ops::set_key using alg = WPA_ALG_PMK */
#define WPA_DRIVER_FLAGS_4WAY_HANDSHAKE 0x00000008
#define WPA_DRIVER_FLAGS_AP 0x00000040
/* Driver needs static WEP key setup after association has been completed */
#define WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE 0x00000080
+/* Driver takes care of P2P management operations */
+#define WPA_DRIVER_FLAGS_P2P_MGMT 0x00000100
+/* Driver supports concurrent P2P operations */
+#define WPA_DRIVER_FLAGS_P2P_CONCURRENT 0x00000200
+/*
+ * Driver uses the initial interface as a dedicated management interface, i.e.,
+ * it cannot be used for P2P group operations or non-P2P purposes.
+ */
+#define WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE 0x00000400
+/* This interface is P2P capable (P2P Device, GO, or P2P Client */
+#define WPA_DRIVER_FLAGS_P2P_CAPABLE 0x00000800
+/* Driver supports concurrent operations on multiple channels */
+#define WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT 0x00001000
+/*
+ * Driver uses the initial interface for P2P management interface and non-P2P
+ * purposes (e.g., connect to infra AP), but this interface cannot be used for
+ * P2P group operations.
+ */
+#define WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P 0x00002000
+/*
+ * Driver is known to use sane error codes, i.e., when it indicates that
+ * something (e.g., association) fails, there was indeed a failure and the
+ * operation does not end up getting completed successfully later.
+ */
+#define WPA_DRIVER_FLAGS_SANE_ERROR_CODES 0x00004000
+/* Driver supports off-channel TX */
+#define WPA_DRIVER_FLAGS_OFFCHANNEL_TX 0x00008000
+/* Driver indicates TX status events for EAPOL Data frames */
+#define WPA_DRIVER_FLAGS_EAPOL_TX_STATUS 0x00010000
+/* Driver indicates TX status events for Deauth/Disassoc frames */
+#define WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS 0x00020000
+/* Driver supports roaming (BSS selection) in firmware */
+#define WPA_DRIVER_FLAGS_BSS_SELECTION 0x00040000
+/* Driver supports operating as a TDLS peer */
+#define WPA_DRIVER_FLAGS_TDLS_SUPPORT 0x00080000
+/* Driver requires external TDLS setup/teardown/discovery */
+#define WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP 0x00100000
unsigned int flags;
int max_scan_ssids;
+ int max_sched_scan_ssids;
+ int sched_scan_supported;
+ int max_match_sets;
/**
* max_remain_on_chan - Maximum remain-on-channel duration in msec
*/
unsigned int max_remain_on_chan;
+
+ /**
+ * max_stations - Maximum number of associated stations the driver
+ * supports in AP mode
+ */
+ unsigned int max_stations;
};
size_t supp_rates_len;
u16 listen_interval;
const struct ieee80211_ht_capabilities *ht_capabilities;
+ u32 flags; /* bitmask of WPA_STA_* flags */
+ int set; /* Set STA parameters instead of add */
};
struct hostapd_freq_params {
* This interface has its own address and Beacon frame.
*/
WPA_IF_AP_BSS,
+
+ /**
+ * WPA_IF_P2P_GO - P2P Group Owner
+ */
+ WPA_IF_P2P_GO,
+
+ /**
+ * WPA_IF_P2P_CLIENT - P2P Client
+ */
+ WPA_IF_P2P_CLIENT,
+
+ /**
+ * WPA_IF_P2P_GROUP - P2P Group interface (will become either
+ * WPA_IF_P2P_GO or WPA_IF_P2P_CLIENT, but the role is not yet known)
+ */
+ WPA_IF_P2P_GROUP
};
struct wpa_init_params {
+ void *global_priv;
const u8 *bssid;
const char *ifname;
const u8 *ssid;
int wpa_pairwise;
int wpa_key_mgmt;
int rsn_preauth;
+ enum mfp_options ieee80211w;
};
#define WPA_STA_AUTHORIZED BIT(0)
#define WPA_STA_WMM BIT(1)
#define WPA_STA_SHORT_PREAMBLE BIT(2)
#define WPA_STA_MFP BIT(3)
+#define WPA_STA_TDLS_PEER BIT(4)
+
+/**
+ * struct p2p_params - P2P parameters for driver-based P2P management
+ */
+struct p2p_params {
+ const char *dev_name;
+ u8 pri_dev_type[8];
+#define DRV_MAX_SEC_DEV_TYPES 5
+ u8 sec_dev_type[DRV_MAX_SEC_DEV_TYPES][8];
+ size_t num_sec_dev_types;
+};
+
+enum tdls_oper {
+ TDLS_DISCOVERY_REQ,
+ TDLS_SETUP,
+ TDLS_TEARDOWN,
+ TDLS_ENABLE_LINK,
+ TDLS_DISABLE_LINK,
+ TDLS_ENABLE,
+ TDLS_DISABLE
+};
+
+/**
+ * struct wpa_signal_info - Information about channel signal quality
+ */
+struct wpa_signal_info {
+ u32 frequency;
+ int above_threshold;
+ int current_signal;
+ int current_noise;
+ int current_txrate;
+};
/**
* struct wpa_driver_ops - Driver interface API definition
* @alg: encryption algorithm (%WPA_ALG_NONE, %WPA_ALG_WEP,
* %WPA_ALG_TKIP, %WPA_ALG_CCMP, %WPA_ALG_IGTK, %WPA_ALG_PMK);
* %WPA_ALG_NONE clears the key.
- * @addr: address of the peer STA or ff:ff:ff:ff:ff:ff for
- * broadcast/default keys
+ * @addr: Address of the peer STA (BSSID of the current AP when setting
+ * pairwise key in station mode), ff:ff:ff:ff:ff:ff for
+ * broadcast keys, %NULL for default keys that are used both for
+ * broadcast and unicast; when clearing keys, %NULL is used to
+ * indicate that both the broadcast-only and default key of the
+ * specified key index is to be cleared
* @key_idx: key index (0..3), usually 0 for unicast keys; 0..4095 for
* IGTK
* @set_tx: configure this key as the default Tx key (only used when
* @seq: sequence number/packet number, seq_len octets, the next
* packet number to be used for in replay protection; configured
* for Rx keys (in most cases, this is only used with broadcast
- * keys and set to zero for unicast keys)
+ * keys and set to zero for unicast keys); %NULL if not set
* @seq_len: length of the seq, depends on the algorithm:
* TKIP: 6 octets, CCMP: 6 octets, IGTK: 6 octets
* @key: key buffer; TKIP: 16-byte temporal key, 8-byte Tx Mic key,
* Please note that TKIP keys include separate TX and RX MIC keys and
* some drivers may expect them in different order than wpa_supplicant
* is using. If the TX/RX keys are swapped, all TKIP encrypted packets
- * will tricker Michael MIC errors. This can be fixed by changing the
+ * will trigger Michael MIC errors. This can be fixed by changing the
* order of MIC keys by swapping te bytes 16..23 and 24..31 of the key
* in driver_*.c set_key() implementation, see driver_ndis.c for an
* example on how this can be done.
* flags: Variable for returning hardware feature flags
* Returns: Pointer to allocated hardware data on success or %NULL on
* failure. Caller is responsible for freeing this.
- *
- * This function is only needed for drivers that export MLME
- * (management frame processing) to %wpa_supplicant or hostapd.
*/
struct hostapd_hw_modes * (*get_hw_feature_data)(void *priv,
u16 *num_modes,
u16 *flags);
/**
- * set_channel - Set channel
- * @priv: Private driver interface data
- * @phymode: HOSTAPD_MODE_IEEE80211B, ..
- * @chan: IEEE 802.11 channel number
- * @freq: Frequency of the channel in MHz
- * Returns: 0 on success, -1 on failure
- *
- * This function is only needed for drivers that export MLME
- * (management frame processing) to wpa_supplicant.
- */
- int (*set_channel)(void *priv, enum hostapd_hw_mode phymode, int chan,
- int freq);
-
- /**
- * set_ssid - Set SSID
- * @priv: Private driver interface data
- * @ssid: SSID
- * @ssid_len: SSID length
- * Returns: 0 on success, -1 on failure
- *
- * This function is only needed for drivers that export MLME
- * (management frame processing) to wpa_supplicant.
- */
- int (*set_ssid)(void *priv, const u8 *ssid, size_t ssid_len);
-
- /**
- * set_bssid - Set BSSID
- * @priv: Private driver interface data
- * @bssid: BSSID
- * Returns: 0 on success, -1 on failure
- *
- * This function is only needed for drivers that export MLME
- * (management frame processing) to wpa_supplicant.
- */
- int (*set_bssid)(void *priv, const u8 *bssid);
-
- /**
* send_mlme - Send management frame from MLME
* @priv: Private driver interface data
* @data: IEEE 802.11 management frame with IEEE 802.11 header
* @data_len: Size of the management frame
* Returns: 0 on success, -1 on failure
- *
- * This function is only needed for drivers that export MLME
- * (management frame processing) to wpa_supplicant.
*/
int (*send_mlme)(void *priv, const u8 *data, size_t data_len);
/**
- * mlme_add_sta - Add a STA entry into the driver/netstack
- * @priv: Private driver interface data
- * @addr: MAC address of the STA (e.g., BSSID of the AP)
- * @supp_rates: Supported rate set (from (Re)AssocResp); in IEEE 802.11
- * format (one octet per rate, 1 = 0.5 Mbps)
- * @supp_rates_len: Number of entries in supp_rates
- * Returns: 0 on success, -1 on failure
- *
- * This function is only needed for drivers that export MLME
- * (management frame processing) to wpa_supplicant. When the MLME code
- * completes association with an AP, this function is called to
- * configure the driver/netstack with a STA entry for data frame
- * processing (TX rate control, encryption/decryption).
- */
- int (*mlme_add_sta)(void *priv, const u8 *addr, const u8 *supp_rates,
- size_t supp_rates_len);
-
- /**
- * mlme_remove_sta - Remove a STA entry from the driver/netstack
- * @priv: Private driver interface data
- * @addr: MAC address of the STA (e.g., BSSID of the AP)
- * Returns: 0 on success, -1 on failure
- *
- * This function is only needed for drivers that export MLME
- * (management frame processing) to wpa_supplicant.
- */
- int (*mlme_remove_sta)(void *priv, const u8 *addr);
-
- /**
* update_ft_ies - Update FT (IEEE 802.11r) IEs
* @priv: Private driver interface data
* @md: Mobility domain (2 octets) (also included inside ies)
struct wpa_driver_auth_params *params);
/**
- * set_beacon - Set Beacon frame template
+ * set_ap - Set Beacon and Probe Response information for AP mode
* @priv: Private driver interface data
- * @head: Beacon head from IEEE 802.11 header to IEs before TIM IE
- * @head_len: Length of the head buffer in octets
- * @tail: Beacon tail following TIM IE
- * @tail_len: Length of the tail buffer in octets
- * @dtim_period: DTIM period
- * @beacon_int: Beacon interval
- * Returns: 0 on success, -1 on failure
+ * @params: Parameters to use in AP mode
*
- * This function is used to configure Beacon template for the driver in
+ * This function is used to configure Beacon template and/or extra IEs
+ * to add for Beacon and Probe Response frames for the driver in
* AP mode. The driver is responsible for building the full Beacon
* frame by concatenating the head part with TIM IE generated by the
- * driver/firmware and finishing with the tail part.
+ * driver/firmware and finishing with the tail part. Depending on the
+ * driver architectue, this can be done either by using the full
+ * template or the set of additional IEs (e.g., WPS and P2P IE).
+ * Similarly, Probe Response processing depends on the driver design.
+ * If the driver (or firmware) takes care of replying to Probe Request
+ * frames, the extra IEs provided here needs to be added to the Probe
+ * Response frames.
+ *
+ * Returns: 0 on success, -1 on failure
*/
- int (*set_beacon)(void *priv, const u8 *head, size_t head_len,
- const u8 *tail, size_t tail_len, int dtim_period,
- int beacon_int);
+ int (*set_ap)(void *priv, struct wpa_driver_ap_params *params);
/**
* hapd_init - Initialize driver interface (hostapd only)
* Returns: Pointer to private data, %NULL on failure
*
* This function is used instead of init() or init2() when the driver
- * wrapper is used withh hostapd.
+ * wrapper is used with hostapd.
*/
void * (*hapd_init)(struct hostapd_data *hapd,
struct wpa_init_params *params);
* This is an optional function to configure the kernel driver to
* enable/disable IEEE 802.1X support and set WPA/WPA2 parameters. This
* can be left undefined (set to %NULL) if IEEE 802.1X support is
- * always enabled and the driver uses set_beacon() to set WPA/RSN IE
+ * always enabled and the driver uses set_ap() to set WPA/RSN IE
* for Beacon frames.
+ *
+ * DEPRECATED - use set_ap() instead
*/
int (*set_ieee8021x)(void *priv, struct wpa_bss_params *params);
*
* This is an optional function to configure privacy field in the
* kernel driver for Beacon frames. This can be left undefined (set to
- * %NULL) if the driver uses the Beacon template from set_beacon().
+ * %NULL) if the driver uses the Beacon template from set_ap().
+ *
+ * DEPRECATED - use set_ap() instead
*/
int (*set_privacy)(void *priv, int enabled);
* This is an optional function to add information elements in the
* kernel driver for Beacon and Probe Response frames. This can be left
* undefined (set to %NULL) if the driver uses the Beacon template from
- * set_beacon().
+ * set_ap().
+ *
+ * DEPRECATED - use set_ap() instead
*/
int (*set_generic_elem)(void *priv, const u8 *elem, size_t elem_len);
* @data_len: Length of the EAPOL packet in octets
* @encrypt: Whether the frame should be encrypted
* @own_addr: Source MAC address
+ * @flags: WPA_STA_* flags for the destination station
*
* Returns: 0 on success, -1 on failure
*/
int (*hapd_send_eapol)(void *priv, const u8 *addr, const u8 *data,
size_t data_len, int encrypt,
- const u8 *own_addr);
+ const u8 *own_addr, u32 flags);
/**
* sta_deauth - Deauthenticate a station (AP only)
* Returns: Length of the SSID on success, -1 on failure
*
* This function need not be implemented if the driver uses Beacon
- * template from set_beacon() and does not reply to Probe Request
- * frames.
+ * template from set_ap() and does not reply to Probe Request frames.
*/
int (*hapd_get_ssid)(void *priv, u8 *buf, int len);
* @buf: SSID
* @len: Length of the SSID in octets
* Returns: 0 on success, -1 on failure
+ *
+ * DEPRECATED - use set_ap() instead
*/
int (*hapd_set_ssid)(void *priv, const u8 *buf, int len);
* This function is used to add a station entry to the driver once the
* station has completed association. This is only used if the driver
* does not take care of association processing.
+ *
+ * With TDLS, this function is also used to add or set (params->set 1)
+ * TDLS peer entries.
*/
int (*sta_add)(void *priv, struct hostapd_sta_add_params *params);
int mode);
/**
- * set_cts_protect - Set CTS protection mode (AP only)
- * @priv: Private driver interface data
- * @value: Whether CTS protection is enabled
- * Returns: 0 on success, -1 on failure
- */
- int (*set_cts_protect)(void *priv, int value);
-
- /**
- * set_preamble - Set preamble mode (AP only)
- * @priv: Private driver interface data
- * @value: Whether short preamble is enabled
- * Returns: 0 on success, -1 on failure
- */
- int (*set_preamble)(void *priv, int value);
-
- /**
- * set_short_slot_time - Set short slot time (AP only)
- * @priv: Private driver interface data
- * @value: Whether short slot time is enabled
- * Returns: 0 on success, -1 on failure
- */
- int (*set_short_slot_time)(void *priv, int value);
-
- /**
* set_tx_queue_params - Set TX queue parameters
* @priv: Private driver interface data
- * @queue: Queue number
+ * @queue: Queue number (0 = VO, 1 = VI, 2 = BE, 3 = BK)
* @aifs: AIFS
* @cw_min: cwMin
* @cw_max: cwMax
int cw_max, int burst_time);
/**
- * valid_bss_mask - Validate BSSID mask
- * @priv: Private driver interface data
- * @addr: Address
- * @mask: Mask
- * Returns: 0 if mask is valid, -1 if mask is not valid, 1 if mask can
- * be used, but the main interface address must be the first address in
- * the block if mask is applied
- */
- int (*valid_bss_mask)(void *priv, const u8 *addr, const u8 *mask);
-
- /**
* if_add - Add a virtual interface
* @priv: Private driver interface data
* @type: Interface type
* @if_addr: Buffer for returning the allocated interface address
* (this may differ from the requested addr if the driver cannot
* change interface address)
+ * @bridge: Bridge interface to use or %NULL if no bridge configured
* Returns: 0 on success, -1 on failure
*/
int (*if_add)(void *priv, enum wpa_driver_if_type type,
const char *ifname, const u8 *addr, void *bss_ctx,
- void **drv_priv, char *force_ifname, u8 *if_addr);
+ void **drv_priv, char *force_ifname, u8 *if_addr,
+ const char *bridge);
/**
* if_remove - Remove a virtual interface
int (*set_radius_acl_expire)(void *priv, const u8 *mac);
/**
- * set_ht_params - Set HT parameters (AP only)
- * @priv: Private driver interface data
- * @ht_capab: HT Capabilities IE
- * @ht_capab_len: Length of ht_capab in octets
- * @ht_oper: HT Operation IE
- * @ht_oper_len: Length of ht_oper in octets
- * Returns: 0 on success, -1 on failure
- */
- int (*set_ht_params)(void *priv,
- const u8 *ht_capab, size_t ht_capab_len,
- const u8 *ht_oper, size_t ht_oper_len);
-
- /**
* set_ap_wps_ie - Add WPS IE(s) into Beacon/Probe Response frames (AP)
* @priv: Private driver interface data
* @beacon: WPS IE(s) for Beacon frames or %NULL to remove extra IE(s)
* @proberesp: WPS IE(s) for Probe Response frames or %NULL to remove
* extra IE(s)
+ * @assocresp: WPS IE(s) for (Re)Association Response frames or %NULL
+ * to remove extra IE(s)
* Returns: 0 on success, -1 on failure
*
* This is an optional function to add WPS IE in the kernel driver for
* Beacon and Probe Response frames. This can be left undefined (set
- * to %NULL) if the driver uses the Beacon template from set_beacon()
- * and does not process Probe Request frames.
+ * to %NULL) if the driver uses the Beacon template from set_ap()
+ * and does not process Probe Request frames. If the driver takes care
+ * of (Re)Association frame processing, the assocresp buffer includes
+ * WPS IE(s) that need to be added to (Re)Association Response frames
+ * whenever a (Re)Association Request frame indicated use of WPS.
+ *
+ * This will also be used to add P2P IE(s) into Beacon/Probe Response
+ * frames when operating as a GO. The driver is responsible for adding
+ * timing related attributes (e.g., NoA) in addition to the IEs
+ * included here by appending them after these buffers. This call is
+ * also used to provide Probe Response IEs for P2P Listen state
+ * operations for drivers that generate the Probe Response frames
+ * internally.
+ *
+ * DEPRECATED - use set_ap() instead
*/
int (*set_ap_wps_ie)(void *priv, const struct wpabuf *beacon,
- const struct wpabuf *proberesp);
+ const struct wpabuf *proberesp,
+ const struct wpabuf *assocresp);
/**
* set_supp_port - Set IEEE 802.1X Supplicant Port status
* @addr: MAC address of the associated station
* @aid: Association ID
* @val: 1 = bind to 4-address WDS; 0 = unbind
+ * @bridge_ifname: Bridge interface to use for the WDS station or %NULL
+ * to indicate that bridge is not to be used
* Returns: 0 on success, -1 on failure
*/
- int (*set_wds_sta)(void *priv, const u8 *addr, int aid, int val);
+ int (*set_wds_sta)(void *priv, const u8 *addr, int aid, int val,
+ const char *bridge_ifname);
/**
* send_action - Transmit an Action frame
* @priv: Private driver interface data
* @freq: Frequency (in MHz) of the channel
+ * @wait: Time to wait off-channel for a response (in ms), or zero
* @dst: Destination MAC address (Address 1)
* @src: Source MAC address (Address 2)
* @bssid: BSSID (Address 3)
* @data: Frame body
* @data_len: data length in octets
+ @ @no_cck: Whether CCK rates must not be used to transmit this frame
* Returns: 0 on success, -1 on failure
*
* This command can be used to request the driver to transmit an action
- * frame to the specified destination. If a remain-on-channel duration
- * is in progress, the frame is transmitted on that channel. Otherwise,
- * the frame is transmitted on the current operational channel if in
- * associated state in station mode or if operating as an AP. If none
- * of these conditions is in effect, send_action() cannot be used.
+ * frame to the specified destination.
+ *
+ * If the %WPA_DRIVER_FLAGS_OFFCHANNEL_TX flag is set, the frame will
+ * be transmitted on the given channel and the device will wait for a
+ * response on that channel for the given wait time.
+ *
+ * If the flag is not set, the wait time will be ignored. In this case,
+ * if a remain-on-channel duration is in progress, the frame must be
+ * transmitted on that channel; alternatively the frame may be sent on
+ * the current operational channel (if in associated state in station
+ * mode or while operating as an AP.)
*/
- int (*send_action)(void *priv, unsigned int freq,
+ int (*send_action)(void *priv, unsigned int freq, unsigned int wait,
const u8 *dst, const u8 *src, const u8 *bssid,
- const u8 *data, size_t data_len);
+ const u8 *data, size_t data_len, int no_cck);
+
+ /**
+ * send_action_cancel_wait - Cancel action frame TX wait
+ * @priv: Private driver interface data
+ *
+ * This command cancels the wait time associated with sending an action
+ * frame. It is only available when %WPA_DRIVER_FLAGS_OFFCHANNEL_TX is
+ * set in the driver flags.
+ */
+ void (*send_action_cancel_wait)(void *priv);
/**
* remain_on_channel - Remain awake on a channel
int (*probe_req_report)(void *priv, int report);
/**
- * disable_11b_rates - Set whether IEEE 802.11b rates are used for TX
+ * deinit_ap - Deinitialize AP mode
* @priv: Private driver interface data
- * @disabled: Whether IEEE 802.11b rates are disabled
* Returns: 0 on success, -1 on failure (or if not supported)
*
- * This command is used to disable IEEE 802.11b rates (1, 2, 5.5, and
- * 11 Mbps) as TX rates for data and management frames. This can be
- * used to optimize channel use when there is no need to support IEEE
- * 802.11b-only devices.
+ * This optional function can be used to disable AP mode related
+ * configuration and change the driver mode to station mode to allow
+ * normal station operations like scanning to be completed.
*/
- int (*disable_11b_rates)(void *priv, int disabled);
+ int (*deinit_ap)(void *priv);
/**
- * deinit_ap - Deinitialize AP mode
+ * deinit_p2p_cli - Deinitialize P2P client mode
* @priv: Private driver interface data
* Returns: 0 on success, -1 on failure (or if not supported)
*
- * This optional function can be used to disable AP mode related
- * configuration and change the driver mode to station mode to allow
- * normal station operations like scanning to be completed.
+ * This optional function can be used to disable P2P client mode. It
+ * can be used to change the interface type back to station mode.
*/
- int (*deinit_ap)(void *priv);
+ int (*deinit_p2p_cli)(void *priv);
/**
* suspend - Notification on system suspend/hibernate event
*/
int (*send_frame)(void *priv, const u8 *data, size_t data_len,
int encrypt);
-};
-
-/**
- * enum wpa_event_type - Event type for wpa_supplicant_event() calls
- */
-enum wpa_event_type {
/**
- * EVENT_ASSOC - Association completed
+ * shared_freq - Get operating frequency of shared interface(s)
+ * @priv: Private driver interface data
+ * Returns: Operating frequency in MHz, 0 if no shared operation in
+ * use, or -1 on failure
*
- * This event needs to be delivered when the driver completes IEEE
- * 802.11 association or reassociation successfully.
- * wpa_driver_ops::get_bssid() is expected to provide the current BSSID
- * after this event has been generated. In addition, optional
- * EVENT_ASSOCINFO may be generated just before EVENT_ASSOC to provide
- * more information about the association. If the driver interface gets
- * both of these events at the same time, it can also include the
- * assoc_info data in EVENT_ASSOC call.
+ * This command can be used to request the current operating frequency
+ * of any virtual interface that shares the same radio to provide
+ * information for channel selection for other virtual interfaces.
*/
- EVENT_ASSOC,
+ int (*shared_freq)(void *priv);
/**
- * EVENT_DISASSOC - Association lost
+ * get_noa - Get current Notice of Absence attribute payload
+ * @priv: Private driver interface data
+ * @buf: Buffer for returning NoA
+ * @buf_len: Buffer length in octets
+ * Returns: Number of octets used in buf, 0 to indicate no NoA is being
+ * advertized, or -1 on failure
*
- * This event should be called when association is lost either due to
- * receiving deauthenticate or disassociate frame from the AP or when
- * sending either of these frames to the current AP. If the driver
- * supports separate deauthentication event, EVENT_DISASSOC should only
- * be used for disassociation and EVENT_DEAUTH for deauthentication.
- * In AP mode, union wpa_event_data::disassoc_info is required.
+ * This function is used to fetch the current Notice of Absence
+ * attribute value from GO.
*/
- EVENT_DISASSOC,
+ int (*get_noa)(void *priv, u8 *buf, size_t buf_len);
/**
- * EVENT_MICHAEL_MIC_FAILURE - Michael MIC (TKIP) detected
+ * set_noa - Set Notice of Absence parameters for GO (testing)
+ * @priv: Private driver interface data
+ * @count: Count
+ * @start: Start time in ms from next TBTT
+ * @duration: Duration in ms
+ * Returns: 0 on success or -1 on failure
*
- * This event must be delivered when a Michael MIC error is detected by
- * the local driver. Additional data for event processing is
- * provided with union wpa_event_data::michael_mic_failure. This
- * information is used to request new encyption key and to initiate
+ * This function is used to set Notice of Absence parameters for GO. It
+ * is used only for testing. To disable NoA, all parameters are set to
+ * 0.
+ */
+ int (*set_noa)(void *priv, u8 count, int start, int duration);
+
+ /**
+ * set_p2p_powersave - Set P2P power save options
+ * @priv: Private driver interface data
+ * @legacy_ps: 0 = disable, 1 = enable, 2 = maximum PS, -1 = no change
+ * @opp_ps: 0 = disable, 1 = enable, -1 = no change
+ * @ctwindow: 0.. = change (msec), -1 = no change
+ * Returns: 0 on success or -1 on failure
+ */
+ int (*set_p2p_powersave)(void *priv, int legacy_ps, int opp_ps,
+ int ctwindow);
+
+ /**
+ * ampdu - Enable/disable aggregation
+ * @priv: Private driver interface data
+ * @ampdu: 1/0 = enable/disable A-MPDU aggregation
+ * Returns: 0 on success or -1 on failure
+ */
+ int (*ampdu)(void *priv, int ampdu);
+
+ /**
+ * get_radio_name - Get physical radio name for the device
+ * @priv: Private driver interface data
+ * Returns: Radio name or %NULL if not known
+ *
+ * The returned data must not be modified by the caller. It is assumed
+ * that any interface that has the same radio name as another is
+ * sharing the same physical radio. This information can be used to
+ * share scan results etc. information between the virtual interfaces
+ * to speed up various operations.
+ */
+ const char * (*get_radio_name)(void *priv);
+
+ /**
+ * p2p_find - Start P2P Device Discovery
+ * @priv: Private driver interface data
+ * @timeout: Timeout for find operation in seconds or 0 for no timeout
+ * @type: Device Discovery type (enum p2p_discovery_type)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used if the driver implements P2P management,
+ * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
+ * struct wpa_driver_capa.
+ */
+ int (*p2p_find)(void *priv, unsigned int timeout, int type);
+
+ /**
+ * p2p_stop_find - Stop P2P Device Discovery
+ * @priv: Private driver interface data
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used if the driver implements P2P management,
+ * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
+ * struct wpa_driver_capa.
+ */
+ int (*p2p_stop_find)(void *priv);
+
+ /**
+ * p2p_listen - Start P2P Listen state for specified duration
+ * @priv: Private driver interface data
+ * @timeout: Listen state duration in milliseconds
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to request the P2P module to keep the
+ * device discoverable on the listen channel for an extended set of
+ * time. At least in its current form, this is mainly used for testing
+ * purposes and may not be of much use for normal P2P operations.
+ *
+ * This function is only used if the driver implements P2P management,
+ * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
+ * struct wpa_driver_capa.
+ */
+ int (*p2p_listen)(void *priv, unsigned int timeout);
+
+ /**
+ * p2p_connect - Start P2P group formation (GO negotiation)
+ * @priv: Private driver interface data
+ * @peer_addr: MAC address of the peer P2P client
+ * @wps_method: enum p2p_wps_method value indicating config method
+ * @go_intent: Local GO intent value (1..15)
+ * @own_interface_addr: Intended interface address to use with the
+ * group
+ * @force_freq: The only allowed channel frequency in MHz or 0
+ * @persistent_group: Whether to create persistent group
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used if the driver implements P2P management,
+ * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
+ * struct wpa_driver_capa.
+ */
+ int (*p2p_connect)(void *priv, const u8 *peer_addr, int wps_method,
+ int go_intent, const u8 *own_interface_addr,
+ unsigned int force_freq, int persistent_group);
+
+ /**
+ * wps_success_cb - Report successfully completed WPS provisioning
+ * @priv: Private driver interface data
+ * @peer_addr: Peer address
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to report successfully completed WPS
+ * provisioning during group formation in both GO/Registrar and
+ * client/Enrollee roles.
+ *
+ * This function is only used if the driver implements P2P management,
+ * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
+ * struct wpa_driver_capa.
+ */
+ int (*wps_success_cb)(void *priv, const u8 *peer_addr);
+
+ /**
+ * p2p_group_formation_failed - Report failed WPS provisioning
+ * @priv: Private driver interface data
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to report failed group formation. This can
+ * happen either due to failed WPS provisioning or due to 15 second
+ * timeout during the provisioning phase.
+ *
+ * This function is only used if the driver implements P2P management,
+ * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
+ * struct wpa_driver_capa.
+ */
+ int (*p2p_group_formation_failed)(void *priv);
+
+ /**
+ * p2p_set_params - Set P2P parameters
+ * @priv: Private driver interface data
+ * @params: P2P parameters
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used if the driver implements P2P management,
+ * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
+ * struct wpa_driver_capa.
+ */
+ int (*p2p_set_params)(void *priv, const struct p2p_params *params);
+
+ /**
+ * p2p_prov_disc_req - Send Provision Discovery Request
+ * @priv: Private driver interface data
+ * @peer_addr: MAC address of the peer P2P client
+ * @config_methods: WPS Config Methods value (only one bit set)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to request a discovered P2P peer to
+ * display a PIN (config_methods = WPS_CONFIG_DISPLAY) or be prepared
+ * to enter a PIN from us (config_methods = WPS_CONFIG_KEYPAD). The
+ * Provision Discovery Request frame is transmitted once immediately
+ * and if no response is received, the frame will be sent again
+ * whenever the target device is discovered during device dsicovery
+ * (start with a p2p_find() call). Response from the peer is indicated
+ * with the EVENT_P2P_PROV_DISC_RESPONSE event.
+ *
+ * This function is only used if the driver implements P2P management,
+ * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
+ * struct wpa_driver_capa.
+ */
+ int (*p2p_prov_disc_req)(void *priv, const u8 *peer_addr,
+ u16 config_methods, int join);
+
+ /**
+ * p2p_sd_request - Schedule a service discovery query
+ * @priv: Private driver interface data
+ * @dst: Destination peer or %NULL to apply for all peers
+ * @tlvs: P2P Service Query TLV(s)
+ * Returns: Reference to the query or 0 on failure
+ *
+ * Response to the query is indicated with the
+ * EVENT_P2P_SD_RESPONSE driver event.
+ *
+ * This function is only used if the driver implements P2P management,
+ * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
+ * struct wpa_driver_capa.
+ */
+ u64 (*p2p_sd_request)(void *priv, const u8 *dst,
+ const struct wpabuf *tlvs);
+
+ /**
+ * p2p_sd_cancel_request - Cancel a pending service discovery query
+ * @priv: Private driver interface data
+ * @req: Query reference from p2p_sd_request()
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used if the driver implements P2P management,
+ * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
+ * struct wpa_driver_capa.
+ */
+ int (*p2p_sd_cancel_request)(void *priv, u64 req);
+
+ /**
+ * p2p_sd_response - Send response to a service discovery query
+ * @priv: Private driver interface data
+ * @freq: Frequency from EVENT_P2P_SD_REQUEST event
+ * @dst: Destination address from EVENT_P2P_SD_REQUEST event
+ * @dialog_token: Dialog token from EVENT_P2P_SD_REQUEST event
+ * @resp_tlvs: P2P Service Response TLV(s)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is called as a response to the request indicated with
+ * the EVENT_P2P_SD_REQUEST driver event.
+ *
+ * This function is only used if the driver implements P2P management,
+ * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
+ * struct wpa_driver_capa.
+ */
+ int (*p2p_sd_response)(void *priv, int freq, const u8 *dst,
+ u8 dialog_token,
+ const struct wpabuf *resp_tlvs);
+
+ /**
+ * p2p_service_update - Indicate a change in local services
+ * @priv: Private driver interface data
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function needs to be called whenever there is a change in
+ * availability of the local services. This will increment the
+ * Service Update Indicator value which will be used in SD Request and
+ * Response frames.
+ *
+ * This function is only used if the driver implements P2P management,
+ * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
+ * struct wpa_driver_capa.
+ */
+ int (*p2p_service_update)(void *priv);
+
+ /**
+ * p2p_reject - Reject peer device (explicitly block connections)
+ * @priv: Private driver interface data
+ * @addr: MAC address of the peer
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*p2p_reject)(void *priv, const u8 *addr);
+
+ /**
+ * p2p_invite - Invite a P2P Device into a group
+ * @priv: Private driver interface data
+ * @peer: Device Address of the peer P2P Device
+ * @role: Local role in the group
+ * @bssid: Group BSSID or %NULL if not known
+ * @ssid: Group SSID
+ * @ssid_len: Length of ssid in octets
+ * @go_dev_addr: Forced GO Device Address or %NULL if none
+ * @persistent_group: Whether this is to reinvoke a persistent group
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*p2p_invite)(void *priv, const u8 *peer, int role,
+ const u8 *bssid, const u8 *ssid, size_t ssid_len,
+ const u8 *go_dev_addr, int persistent_group);
+
+ /**
+ * send_tdls_mgmt - for sending TDLS management packets
+ * @priv: private driver interface data
+ * @dst: Destination (peer) MAC address
+ * @action_code: TDLS action code for the mssage
+ * @dialog_token: Dialog Token to use in the message (if needed)
+ * @status_code: Status Code or Reason Code to use (if needed)
+ * @buf: TDLS IEs to add to the message
+ * @len: Length of buf in octets
+ * Returns: 0 on success, negative (<0) on failure
+ *
+ * This optional function can be used to send packet to driver which is
+ * responsible for receiving and sending all TDLS packets.
+ */
+ int (*send_tdls_mgmt)(void *priv, const u8 *dst, u8 action_code,
+ u8 dialog_token, u16 status_code,
+ const u8 *buf, size_t len);
+
+ /**
+ * tdls_oper - Ask the driver to perform high-level TDLS operations
+ * @priv: Private driver interface data
+ * @oper: TDLS high-level operation. See %enum tdls_oper
+ * @peer: Destination (peer) MAC address
+ * Returns: 0 on success, negative (<0) on failure
+ *
+ * This optional function can be used to send high-level TDLS commands
+ * to the driver.
+ */
+ int (*tdls_oper)(void *priv, enum tdls_oper oper, const u8 *peer);
+
+ /**
+ * signal_poll - Get current connection information
+ * @priv: Private driver interface data
+ * @signal_info: Connection info structure
+ */
+ int (*signal_poll)(void *priv, struct wpa_signal_info *signal_info);
+
+ /**
+ * set_authmode - Set authentication algorithm(s) for static WEP
+ * @priv: Private driver interface data
+ * @authmode: 1=Open System, 2=Shared Key, 3=both
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to set authentication algorithms for AP
+ * mode when static WEP is used. If the driver uses user space MLME/SME
+ * implementation, there is no need to implement this function.
+ *
+ * DEPRECATED - use set_ap() instead
+ */
+ int (*set_authmode)(void *priv, int authmode);
+
+ /**
+ * set_rekey_info - Set rekey information
+ * @priv: Private driver interface data
+ * @kek: Current KEK
+ * @kck: Current KCK
+ * @replay_ctr: Current EAPOL-Key Replay Counter
+ *
+ * This optional function can be used to provide information for the
+ * driver/firmware to process EAPOL-Key frames in Group Key Handshake
+ * while the host (including wpa_supplicant) is sleeping.
+ */
+ void (*set_rekey_info)(void *priv, const u8 *kek, const u8 *kck,
+ const u8 *replay_ctr);
+
+ /**
+ * sta_assoc - Station association indication
+ * @priv: Private driver interface data
+ * @own_addr: Source address and BSSID for association frame
+ * @addr: MAC address of the station to associate
+ * @reassoc: flag to indicate re-association
+ * @status: association response status code
+ * @ie: assoc response ie buffer
+ * @len: ie buffer length
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function indicates the driver to send (Re)Association
+ * Response frame to the station.
+ */
+ int (*sta_assoc)(void *priv, const u8 *own_addr, const u8 *addr,
+ int reassoc, u16 status, const u8 *ie, size_t len);
+
+ /**
+ * sta_auth - Station authentication indication
+ * @priv: Private driver interface data
+ * @own_addr: Source address and BSSID for authentication frame
+ * @addr: MAC address of the station to associate
+ * @seq: authentication sequence number
+ * @status: authentication response status code
+ * @ie: authentication frame ie buffer
+ * @len: ie buffer length
+ *
+ * This function indicates the driver to send Authentication frame
+ * to the station.
+ */
+ int (*sta_auth)(void *priv, const u8 *own_addr, const u8 *addr,
+ u16 seq, u16 status, const u8 *ie, size_t len);
+
+ /**
+ * add_tspec - Add traffic stream
+ * @priv: Private driver interface data
+ * @addr: MAC address of the station to associate
+ * @tspec_ie: tspec ie buffer
+ * @tspec_ielen: tspec ie length
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function adds the traffic steam for the station
+ * and fills the medium_time in tspec_ie.
+ */
+ int (*add_tspec)(void *priv, const u8 *addr, u8 *tspec_ie,
+ size_t tspec_ielen);
+
+ /**
+ * add_sta_node - Add a station node in the driver
+ * @priv: Private driver interface data
+ * @addr: MAC address of the station to add
+ * @auth_alg: authentication algorithm used by the station
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function adds the station node in the driver, when
+ * the station gets added by FT-over-DS.
+ */
+ int (*add_sta_node)(void *priv, const u8 *addr, u16 auth_alg);
+
+ /**
+ * sched_scan - Request the driver to initiate scheduled scan
+ * @priv: Private driver interface data
+ * @params: Scan parameters
+ * @interval: Interval between scan cycles in milliseconds
+ * Returns: 0 on success, -1 on failure
+ *
+ * This operation should be used for scheduled scan offload to
+ * the hardware. Every time scan results are available, the
+ * driver should report scan results event for wpa_supplicant
+ * which will eventually request the results with
+ * wpa_driver_get_scan_results2(). This operation is optional
+ * and if not provided or if it returns -1, we fall back to
+ * normal host-scheduled scans.
+ */
+ int (*sched_scan)(void *priv, struct wpa_driver_scan_params *params,
+ u32 interval);
+
+ /**
+ * stop_sched_scan - Request the driver to stop a scheduled scan
+ * @priv: Private driver interface data
+ * Returns: 0 on success, -1 on failure
+ *
+ * This should cause the scheduled scan to be stopped and
+ * results should stop being sent. Must be supported if
+ * sched_scan is supported.
+ */
+ int (*stop_sched_scan)(void *priv);
+
+ /**
+ * poll_client - Probe (null data or such) the given station
+ * @priv: Private driver interface data
+ * @own_addr: MAC address of sending interface
+ * @addr: MAC address of the station to probe
+ * @qos: Indicates whether station is QoS station
+ *
+ * This function is used to verify whether an associated station is
+ * still present. This function does not need to be implemented if the
+ * driver provides such inactivity polling mechanism.
+ */
+ void (*poll_client)(void *priv, const u8 *own_addr,
+ const u8 *addr, int qos);
+};
+
+
+/**
+ * enum wpa_event_type - Event type for wpa_supplicant_event() calls
+ */
+enum wpa_event_type {
+ /**
+ * EVENT_ASSOC - Association completed
+ *
+ * This event needs to be delivered when the driver completes IEEE
+ * 802.11 association or reassociation successfully.
+ * wpa_driver_ops::get_bssid() is expected to provide the current BSSID
+ * after this event has been generated. In addition, optional
+ * EVENT_ASSOCINFO may be generated just before EVENT_ASSOC to provide
+ * more information about the association. If the driver interface gets
+ * both of these events at the same time, it can also include the
+ * assoc_info data in EVENT_ASSOC call.
+ */
+ EVENT_ASSOC,
+
+ /**
+ * EVENT_DISASSOC - Association lost
+ *
+ * This event should be called when association is lost either due to
+ * receiving deauthenticate or disassociate frame from the AP or when
+ * sending either of these frames to the current AP. If the driver
+ * supports separate deauthentication event, EVENT_DISASSOC should only
+ * be used for disassociation and EVENT_DEAUTH for deauthentication.
+ * In AP mode, union wpa_event_data::disassoc_info is required.
+ */
+ EVENT_DISASSOC,
+
+ /**
+ * EVENT_MICHAEL_MIC_FAILURE - Michael MIC (TKIP) detected
+ *
+ * This event must be delivered when a Michael MIC error is detected by
+ * the local driver. Additional data for event processing is
+ * provided with union wpa_event_data::michael_mic_failure. This
+ * information is used to request new encyption key and to initiate
* TKIP countermeasures if needed.
*/
EVENT_MICHAEL_MIC_FAILURE,
EVENT_STKSTART,
/**
+ * EVENT_TDLS - Request TDLS operation
+ *
+ * This event can be used to request a TDLS operation to be performed.
+ */
+ EVENT_TDLS,
+
+ /**
* EVENT_FT_RESPONSE - Report FT (IEEE 802.11r) response IEs
*
* The driver is expected to report the received FT IEs from
* EVENT_ASSOC_REJECT - Association rejected
*
* This event should be called when (re)association attempt has been
- * rejected by the AP. Information about authentication result is
+ * rejected by the AP. Information about the association response is
* included in union wpa_event_data::assoc_reject.
*/
EVENT_ASSOC_REJECT,
* observed in frames received from the current AP if signal strength
* monitoring has been enabled with signal_monitor().
*/
- EVENT_SIGNAL_CHANGE
+ EVENT_SIGNAL_CHANGE,
+
+ /**
+ * EVENT_INTERFACE_ENABLED - Notify that interface was enabled
+ *
+ * This event is used to indicate that the interface was enabled after
+ * having been previously disabled, e.g., due to rfkill.
+ */
+ EVENT_INTERFACE_ENABLED,
+
+ /**
+ * EVENT_INTERFACE_DISABLED - Notify that interface was disabled
+ *
+ * This event is used to indicate that the interface was disabled,
+ * e.g., due to rfkill.
+ */
+ EVENT_INTERFACE_DISABLED,
+
+ /**
+ * EVENT_CHANNEL_LIST_CHANGED - Channel list changed
+ *
+ * This event is used to indicate that the channel list has changed,
+ * e.g., because of a regulatory domain change triggered by scan
+ * results including an AP advertising a country code.
+ */
+ EVENT_CHANNEL_LIST_CHANGED,
+
+ /**
+ * EVENT_INTERFACE_UNAVAILABLE - Notify that interface is unavailable
+ *
+ * This event is used to indicate that the driver cannot maintain this
+ * interface in its operation mode anymore. The most likely use for
+ * this is to indicate that AP mode operation is not available due to
+ * operating channel would need to be changed to a DFS channel when
+ * the driver does not support radar detection and another virtual
+ * interfaces caused the operating channel to change. Other similar
+ * resource conflicts could also trigger this for station mode
+ * interfaces.
+ */
+ EVENT_INTERFACE_UNAVAILABLE,
+
+ /**
+ * EVENT_BEST_CHANNEL
+ *
+ * Driver generates this event whenever it detects a better channel
+ * (e.g., based on RSSI or channel use). This information can be used
+ * to improve channel selection for a new AP/P2P group.
+ */
+ EVENT_BEST_CHANNEL,
+
+ /**
+ * EVENT_UNPROT_DEAUTH - Unprotected Deauthentication frame received
+ *
+ * This event should be called when a Deauthentication frame is dropped
+ * due to it not being protected (MFP/IEEE 802.11w).
+ * union wpa_event_data::unprot_deauth is required to provide more
+ * details of the frame.
+ */
+ EVENT_UNPROT_DEAUTH,
+
+ /**
+ * EVENT_UNPROT_DISASSOC - Unprotected Disassociation frame received
+ *
+ * This event should be called when a Disassociation frame is dropped
+ * due to it not being protected (MFP/IEEE 802.11w).
+ * union wpa_event_data::unprot_disassoc is required to provide more
+ * details of the frame.
+ */
+ EVENT_UNPROT_DISASSOC,
+
+ /**
+ * EVENT_STATION_LOW_ACK
+ *
+ * Driver generates this event whenever it detected that a particular
+ * station was lost. Detection can be through massive transmission
+ * failures for example.
+ */
+ EVENT_STATION_LOW_ACK,
+
+ /**
+ * EVENT_P2P_DEV_FOUND - Report a discovered P2P device
+ *
+ * This event is used only if the driver implements P2P management
+ * internally. Event data is stored in
+ * union wpa_event_data::p2p_dev_found.
+ */
+ EVENT_P2P_DEV_FOUND,
+
+ /**
+ * EVENT_P2P_GO_NEG_REQ_RX - Report reception of GO Negotiation Request
+ *
+ * This event is used only if the driver implements P2P management
+ * internally. Event data is stored in
+ * union wpa_event_data::p2p_go_neg_req_rx.
+ */
+ EVENT_P2P_GO_NEG_REQ_RX,
+
+ /**
+ * EVENT_P2P_GO_NEG_COMPLETED - Report completion of GO Negotiation
+ *
+ * This event is used only if the driver implements P2P management
+ * internally. Event data is stored in
+ * union wpa_event_data::p2p_go_neg_completed.
+ */
+ EVENT_P2P_GO_NEG_COMPLETED,
+
+ EVENT_P2P_PROV_DISC_REQUEST,
+ EVENT_P2P_PROV_DISC_RESPONSE,
+ EVENT_P2P_SD_REQUEST,
+ EVENT_P2P_SD_RESPONSE,
+
+ /**
+ * EVENT_IBSS_PEER_LOST - IBSS peer not reachable anymore
+ */
+ EVENT_IBSS_PEER_LOST,
+
+ /**
+ * EVENT_DRIVER_GTK_REKEY - Device/driver did GTK rekey
+ *
+ * This event carries the new replay counter to notify wpa_supplicant
+ * of the current EAPOL-Key Replay Counter in case the driver/firmware
+ * completed Group Key Handshake while the host (including
+ * wpa_supplicant was sleeping).
+ */
+ EVENT_DRIVER_GTK_REKEY,
+
+ /**
+ * EVENT_SCHED_SCAN_STOPPED - Scheduled scan was stopped
+ */
+ EVENT_SCHED_SCAN_STOPPED,
+
+ /**
+ * EVENT_DRIVER_CLIENT_POLL_OK - Station responded to poll
+ *
+ * This event indicates that the station responded to the poll
+ * initiated with @poll_client.
+ */
+ EVENT_DRIVER_CLIENT_POLL_OK
};
*/
struct assoc_info {
/**
+ * reassoc - Flag to indicate association or reassociation
+ */
+ int reassoc;
+
+ /**
* req_ies - (Re)Association Request IEs
*
* If the driver generates WPA/RSN IE, this event data must be
* Deauthentication frame
*/
u16 reason_code;
+
+ /**
+ * ie - Optional IE(s) in Disassociation frame
+ */
+ const u8 *ie;
+
+ /**
+ * ie_len - Length of ie buffer in octets
+ */
+ size_t ie_len;
} disassoc_info;
/**
* Deauthentication frame
*/
u16 reason_code;
+
+ /**
+ * ie - Optional IE(s) in Deauthentication frame
+ */
+ const u8 *ie;
+
+ /**
+ * ie_len - Length of ie buffer in octets
+ */
+ size_t ie_len;
} deauth_info;
/**
} stkstart;
/**
+ * struct tdls - Data for EVENT_TDLS
+ */
+ struct tdls {
+ u8 peer[ETH_ALEN];
+ enum {
+ TDLS_REQUEST_SETUP,
+ TDLS_REQUEST_TEARDOWN
+ } oper;
+ u16 reason_code; /* for teardown */
+ } tdls;
+
+ /**
* struct ft_ies - FT information elements (EVENT_FT_RESPONSE)
*
* During FT (IEEE 802.11r) authentication sequence, the driver is
*/
struct auth_info {
u8 peer[ETH_ALEN];
+ u8 bssid[ETH_ALEN];
u16 auth_type;
+ u16 auth_transaction;
u16 status_code;
const u8 *ies;
size_t ies_len;
*/
struct assoc_reject {
/**
+ * bssid - BSSID of the AP that rejected association
+ */
+ const u8 *bssid;
+
+ /**
* resp_ies - (Re)Association Response IEs
*
* Optional association data from the driver. This data is not
* This should start with the first IE (fixed fields before IEs
* are not included).
*/
- u8 *resp_ies;
+ const u8 *resp_ies;
/**
* resp_ies_len - Length of resp_ies in bytes
* struct rx_from_unknown - Data for EVENT_RX_FROM_UNKNOWN events
*/
struct rx_from_unknown {
- const u8 *frame;
- size_t len;
+ const u8 *bssid;
+ const u8 *addr;
+ int wds;
} rx_from_unknown;
/**
const u8 *sa;
/**
+ * da - Destination address of the received Probe Request frame
+ * or %NULL if not available
+ */
+ const u8 *da;
+
+ /**
+ * bssid - BSSID of the received Probe Request frame or %NULL
+ * if not available
+ */
+ const u8 *bssid;
+
+ /**
* ie - IEs from the Probe Request body
*/
const u8 *ie;
} eapol_rx;
/**
- * struct signal_change - Data for EVENT_SIGNAL_CHANGE events
+ * signal_change - Data for EVENT_SIGNAL_CHANGE events
+ */
+ struct wpa_signal_info signal_change;
+
+ /**
+ * struct best_channel - Data for EVENT_BEST_CHANNEL events
+ * @freq_24: Best 2.4 GHz band channel frequency in MHz
+ * @freq_5: Best 5 GHz band channel frequency in MHz
+ * @freq_overall: Best channel frequency in MHz
+ *
+ * 0 can be used to indicate no preference in either band.
+ */
+ struct best_channel {
+ int freq_24;
+ int freq_5;
+ int freq_overall;
+ } best_chan;
+
+ struct unprot_deauth {
+ const u8 *sa;
+ const u8 *da;
+ u16 reason_code;
+ } unprot_deauth;
+
+ struct unprot_disassoc {
+ const u8 *sa;
+ const u8 *da;
+ u16 reason_code;
+ } unprot_disassoc;
+
+ /**
+ * struct low_ack - Data for EVENT_STATION_LOW_ACK events
+ * @addr: station address
*/
- struct signal_change {
- int above_threshold;
- } signal_change;
+ struct low_ack {
+ u8 addr[ETH_ALEN];
+ } low_ack;
+
+ /**
+ * struct p2p_dev_found - Data for EVENT_P2P_DEV_FOUND
+ */
+ struct p2p_dev_found {
+ const u8 *addr;
+ const u8 *dev_addr;
+ const u8 *pri_dev_type;
+ const char *dev_name;
+ u16 config_methods;
+ u8 dev_capab;
+ u8 group_capab;
+ } p2p_dev_found;
+
+ /**
+ * struct p2p_go_neg_req_rx - Data for EVENT_P2P_GO_NEG_REQ_RX
+ */
+ struct p2p_go_neg_req_rx {
+ const u8 *src;
+ u16 dev_passwd_id;
+ } p2p_go_neg_req_rx;
+
+ /**
+ * struct p2p_go_neg_completed - Data for EVENT_P2P_GO_NEG_COMPLETED
+ */
+ struct p2p_go_neg_completed {
+ struct p2p_go_neg_results *res;
+ } p2p_go_neg_completed;
+
+ struct p2p_prov_disc_req {
+ const u8 *peer;
+ u16 config_methods;
+ const u8 *dev_addr;
+ const u8 *pri_dev_type;
+ const char *dev_name;
+ u16 supp_config_methods;
+ u8 dev_capab;
+ u8 group_capab;
+ } p2p_prov_disc_req;
+
+ struct p2p_prov_disc_resp {
+ const u8 *peer;
+ u16 config_methods;
+ } p2p_prov_disc_resp;
+
+ struct p2p_sd_req {
+ int freq;
+ const u8 *sa;
+ u8 dialog_token;
+ u16 update_indic;
+ const u8 *tlvs;
+ size_t tlvs_len;
+ } p2p_sd_req;
+
+ struct p2p_sd_resp {
+ const u8 *sa;
+ u16 update_indic;
+ const u8 *tlvs;
+ size_t tlvs_len;
+ } p2p_sd_resp;
+
+ /**
+ * struct ibss_peer_lost - Data for EVENT_IBSS_PEER_LOST
+ */
+ struct ibss_peer_lost {
+ u8 peer[ETH_ALEN];
+ } ibss_peer_lost;
+
+ /**
+ * struct driver_gtk_rekey - Data for EVENT_DRIVER_GTK_REKEY
+ */
+ struct driver_gtk_rekey {
+ const u8 *bssid;
+ const u8 *replay_ctr;
+ } driver_gtk_rekey;
+
+ /**
+ * struct client_poll - Data for EVENT_DRIVER_CLIENT_POLL_OK events
+ * @addr: station address
+ */
+ struct client_poll {
+ u8 addr[ETH_ALEN];
+ } client_poll;
};
/**
*/
static inline void drv_event_assoc(void *ctx, const u8 *addr, const u8 *ie,
- size_t ielen)
+ size_t ielen, int reassoc)
{
union wpa_event_data event;
os_memset(&event, 0, sizeof(event));
+ event.assoc_info.reassoc = reassoc;
event.assoc_info.req_ies = ie;
event.assoc_info.req_ies_len = ielen;
event.assoc_info.addr = addr;
wpa_supplicant_event(ctx, EVENT_EAPOL_RX, &event);
}
+/* driver_common.c */
+void wpa_scan_results_free(struct wpa_scan_results *res);
+
+/* Convert wpa_event_type to a string for logging */
+const char * event_to_string(enum wpa_event_type event);
+
#endif /* DRIVER_H */
*/
#define ATH_WPS_IE
-#include "os/linux/include/ieee80211_external.h"
+#include "ieee80211_external.h"
#ifdef CONFIG_WPS
#include "linux_ioctl.h"
-struct madwifi_driver_data {
+struct atheros_driver_data {
struct hostapd_data *hapd; /* back pointer */
char iface[IFNAMSIZ + 1];
struct hostap_sta_driver_data acct_data;
struct l2_packet_data *sock_raw; /* raw 802.11 management frames */
+ struct wpabuf *wpa_ie;
+ struct wpabuf *wps_beacon_ie;
+ struct wpabuf *wps_probe_resp_ie;
};
-static int madwifi_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
+static int atheros_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
int reason_code);
-static int madwifi_set_privacy(void *priv, int enabled);
+static int atheros_set_privacy(void *priv, int enabled);
static const char * athr_get_ioctl_name(int op)
{
return "FILTERFRAME";
case IEEE80211_IOCTL_SET_RTPARAMS:
return "SET_RTPARAMS";
- case IEEE80211_IOCTL_SENDADDBA:
- return "SENDADDBA";
- case IEEE80211_IOCTL_GETADDBASTATUS:
- return "GETADDBASTATUS";
- case IEEE80211_IOCTL_SENDDELBA:
- return "SENDDELBA";
case IEEE80211_IOCTL_SET_MEDENYENTRY:
return "SET_MEDENYENTRY";
- case IEEE80211_IOCTL_SET_ADDBARESP:
- return "SET_ADDBARESP";
case IEEE80211_IOCTL_GET_MACADDR:
return "GET_MACADDR";
case IEEE80211_IOCTL_SET_HBRPARAMS:
static int
-set80211priv(struct madwifi_driver_data *drv, int op, void *data, int len)
+set80211priv(struct atheros_driver_data *drv, int op, void *data, int len)
{
struct iwreq iwr;
int do_inline = len < IFNAMSIZ;
}
static int
-set80211param(struct madwifi_driver_data *drv, int op, int arg)
+set80211param(struct atheros_driver_data *drv, int op, int arg)
{
struct iwreq iwr;
* Configure WPA parameters.
*/
static int
-madwifi_configure_wpa(struct madwifi_driver_data *drv,
+atheros_configure_wpa(struct atheros_driver_data *drv,
struct wpa_bss_params *params)
{
int v;
v = 0;
if (params->rsn_preauth)
v |= BIT(0);
+#ifdef CONFIG_IEEE80211W
+ if (params->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+ v |= BIT(7);
+ if (params->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED)
+ v |= BIT(6);
+ }
+#endif /* CONFIG_IEEE80211W */
+
wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x",
__func__, params->rsn_preauth);
if (set80211param(drv, IEEE80211_PARAM_RSNCAPS, v)) {
}
static int
-madwifi_set_ieee8021x(void *priv, struct wpa_bss_params *params)
+atheros_set_ieee8021x(void *priv, struct wpa_bss_params *params)
{
- struct madwifi_driver_data *drv = priv;
+ struct atheros_driver_data *drv = priv;
wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, params->enabled);
IEEE80211_AUTH_AUTO) < 0)
return -1;
/* IEEE80211_AUTH_AUTO ends up enabling Privacy; clear that */
- return madwifi_set_privacy(drv, 0);
+ return atheros_set_privacy(drv, 0);
}
if (!params->wpa && !params->ieee802_1x) {
hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER,
HOSTAPD_LEVEL_WARNING, "No 802.1X or WPA enabled!");
return -1;
}
- if (params->wpa && madwifi_configure_wpa(drv, params) != 0) {
+ if (params->wpa && atheros_configure_wpa(drv, params) != 0) {
hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER,
HOSTAPD_LEVEL_WARNING, "Error configuring WPA state!");
return -1;
}
static int
-madwifi_set_privacy(void *priv, int enabled)
+atheros_set_privacy(void *priv, int enabled)
{
- struct madwifi_driver_data *drv = priv;
+ struct atheros_driver_data *drv = priv;
wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled);
}
static int
-madwifi_set_sta_authorized(void *priv, const u8 *addr, int authorized)
+atheros_set_sta_authorized(void *priv, const u8 *addr, int authorized)
{
- struct madwifi_driver_data *drv = priv;
+ struct atheros_driver_data *drv = priv;
struct ieee80211req_mlme mlme;
int ret;
}
static int
-madwifi_sta_set_flags(void *priv, const u8 *addr,
+atheros_sta_set_flags(void *priv, const u8 *addr,
int total_flags, int flags_or, int flags_and)
{
/* For now, only support setting Authorized flag */
if (flags_or & WPA_STA_AUTHORIZED)
- return madwifi_set_sta_authorized(priv, addr, 1);
+ return atheros_set_sta_authorized(priv, addr, 1);
if (!(flags_and & WPA_STA_AUTHORIZED))
- return madwifi_set_sta_authorized(priv, addr, 0);
+ return atheros_set_sta_authorized(priv, addr, 0);
return 0;
}
static int
-madwifi_del_key(void *priv, const u8 *addr, int key_idx)
+atheros_del_key(void *priv, const u8 *addr, int key_idx)
{
- struct madwifi_driver_data *drv = priv;
+ struct atheros_driver_data *drv = priv;
struct ieee80211req_del_key wk;
int ret;
}
static int
-madwifi_set_key(const char *ifname, void *priv, enum wpa_alg alg,
+atheros_set_key(const char *ifname, void *priv, enum wpa_alg alg,
const u8 *addr, int key_idx, int set_tx, const u8 *seq,
size_t seq_len, const u8 *key, size_t key_len)
{
- struct madwifi_driver_data *drv = priv;
+ struct atheros_driver_data *drv = priv;
struct ieee80211req_key wk;
u_int8_t cipher;
int ret;
if (alg == WPA_ALG_NONE)
- return madwifi_del_key(drv, addr, key_idx);
+ return atheros_del_key(drv, addr, key_idx);
wpa_printf(MSG_DEBUG, "%s: alg=%d addr=%s key_idx=%d",
__func__, alg, ether_sprintf(addr), key_idx);
case WPA_ALG_CCMP:
cipher = IEEE80211_CIPHER_AES_CCM;
break;
+#ifdef CONFIG_IEEE80211W
+ case WPA_ALG_IGTK:
+ cipher = IEEE80211_CIPHER_AES_CMAC;
+ break;
+#endif /* CONFIG_IEEE80211W */
default:
printf("%s: unknown/unsupported algorithm %d\n",
__func__, alg);
memset(&wk, 0, sizeof(wk));
wk.ik_type = cipher;
wk.ik_flags = IEEE80211_KEY_RECV | IEEE80211_KEY_XMIT;
- if (addr == NULL) {
+ if (addr == NULL || is_broadcast_ether_addr(addr)) {
memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN);
wk.ik_keyix = key_idx;
- wk.ik_flags |= IEEE80211_KEY_DEFAULT;
+ if (set_tx)
+ wk.ik_flags |= IEEE80211_KEY_DEFAULT;
} else {
memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN);
wk.ik_keyix = IEEE80211_KEYIX_NONE;
static int
-madwifi_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx,
+atheros_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx,
u8 *seq)
{
- struct madwifi_driver_data *drv = priv;
+ struct atheros_driver_data *drv = priv;
struct ieee80211req_key wk;
wpa_printf(MSG_DEBUG, "%s: addr=%s idx=%d",
static int
-madwifi_flush(void *priv)
+atheros_flush(void *priv)
{
u8 allsta[IEEE80211_ADDR_LEN];
memset(allsta, 0xff, IEEE80211_ADDR_LEN);
- return madwifi_sta_deauth(priv, NULL, allsta,
+ return atheros_sta_deauth(priv, NULL, allsta,
IEEE80211_REASON_AUTH_LEAVE);
}
static int
-madwifi_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data,
+atheros_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data,
const u8 *addr)
{
- struct madwifi_driver_data *drv = priv;
+ struct atheros_driver_data *drv = priv;
struct ieee80211req_sta_stats stats;
memset(data, 0, sizeof(*data));
static int
-madwifi_sta_clear_stats(void *priv, const u8 *addr)
+atheros_sta_clear_stats(void *priv, const u8 *addr)
{
- struct madwifi_driver_data *drv = priv;
+ struct atheros_driver_data *drv = priv;
struct ieee80211req_mlme mlme;
int ret;
static int
-madwifi_set_opt_ie(void *priv, const u8 *ie, size_t ie_len)
+atheros_set_opt_ie(void *priv, const u8 *ie, size_t ie_len)
{
- /*
- * Do nothing; we setup parameters at startup that define the
- * contents of the beacon information element.
- */
+ struct atheros_driver_data *drv = priv;
+ u8 buf[512];
+ struct ieee80211req_getset_appiebuf *app_ie;
+
+ wpa_printf(MSG_DEBUG, "%s buflen = %lu", __func__,
+ (unsigned long) ie_len);
+ wpa_hexdump(MSG_DEBUG, "atheros: set_generic_elem", ie, ie_len);
+
+ wpabuf_free(drv->wpa_ie);
+ drv->wpa_ie = wpabuf_alloc_copy(ie, ie_len);
+
+ app_ie = (struct ieee80211req_getset_appiebuf *) buf;
+ os_memcpy(&(app_ie->app_buf[0]), ie, ie_len);
+ app_ie->app_buflen = ie_len;
+
+ app_ie->app_frmtype = IEEE80211_APPIE_FRAME_BEACON;
+
+ /* append WPS IE for Beacon */
+ if (drv->wps_beacon_ie != NULL) {
+ os_memcpy(&(app_ie->app_buf[ie_len]),
+ wpabuf_head(drv->wps_beacon_ie),
+ wpabuf_len(drv->wps_beacon_ie));
+ app_ie->app_buflen = ie_len + wpabuf_len(drv->wps_beacon_ie);
+ }
+ wpa_hexdump(MSG_DEBUG, "atheros: SET_APPIEBUF(Beacon)",
+ app_ie->app_buf, app_ie->app_buflen);
+ set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, app_ie,
+ sizeof(struct ieee80211req_getset_appiebuf) +
+ app_ie->app_buflen);
+
+ /* append WPS IE for Probe Response */
+ app_ie->app_frmtype = IEEE80211_APPIE_FRAME_PROBE_RESP;
+ if (drv->wps_probe_resp_ie != NULL) {
+ os_memcpy(&(app_ie->app_buf[ie_len]),
+ wpabuf_head(drv->wps_probe_resp_ie),
+ wpabuf_len(drv->wps_probe_resp_ie));
+ app_ie->app_buflen = ie_len +
+ wpabuf_len(drv->wps_probe_resp_ie);
+ } else
+ app_ie->app_buflen = ie_len;
+ wpa_hexdump(MSG_DEBUG, "atheros: SET_APPIEBUF(ProbeResp)",
+ app_ie->app_buf, app_ie->app_buflen);
+ set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, app_ie,
+ sizeof(struct ieee80211req_getset_appiebuf) +
+ app_ie->app_buflen);
return 0;
}
static int
-madwifi_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
+atheros_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
int reason_code)
{
- struct madwifi_driver_data *drv = priv;
+ struct atheros_driver_data *drv = priv;
struct ieee80211req_mlme mlme;
int ret;
}
static int
-madwifi_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
+atheros_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
int reason_code)
{
- struct madwifi_driver_data *drv = priv;
+ struct atheros_driver_data *drv = priv;
struct ieee80211req_mlme mlme;
int ret;
}
#ifdef CONFIG_WPS
-static void madwifi_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf,
+static void atheros_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf,
size_t len)
{
- struct madwifi_driver_data *drv = ctx;
+ struct atheros_driver_data *drv = ctx;
const struct ieee80211_mgmt *mgmt;
u16 fc;
union wpa_event_data event;
os_memset(&event, 0, sizeof(event));
event.rx_probe_req.sa = mgmt->sa;
+ event.rx_probe_req.da = mgmt->da;
+ event.rx_probe_req.bssid = mgmt->bssid;
event.rx_probe_req.ie = mgmt->u.probe_req.variable;
event.rx_probe_req.ie_len =
len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req));
}
#endif /* CONFIG_WPS */
-static int madwifi_receive_probe_req(struct madwifi_driver_data *drv)
+static int atheros_receive_probe_req(struct atheros_driver_data *drv)
{
int ret = 0;
#ifdef CONFIG_WPS
return ret;
drv->sock_raw = l2_packet_init(drv->iface, NULL, ETH_P_80211_RAW,
- madwifi_raw_receive, drv, 1);
+ atheros_raw_receive, drv, 1);
if (drv->sock_raw == NULL)
return -1;
#endif /* CONFIG_WPS */
#ifdef CONFIG_WPS
static int
-madwifi_set_wps_ie(void *priv, const u8 *ie, size_t len, u32 frametype)
+atheros_set_wps_ie(void *priv, const u8 *ie, size_t len, u32 frametype)
{
- struct madwifi_driver_data *drv = priv;
- u8 buf[256];
+ struct atheros_driver_data *drv = priv;
+ u8 buf[512];
struct ieee80211req_getset_appiebuf *beac_ie;
- wpa_printf(MSG_DEBUG, "%s buflen = %lu", __func__,
- (unsigned long) len);
+ wpa_printf(MSG_DEBUG, "%s buflen = %lu frametype=%u", __func__,
+ (unsigned long) len, frametype);
+ wpa_hexdump(MSG_DEBUG, "atheros: IE", ie, len);
beac_ie = (struct ieee80211req_getset_appiebuf *) buf;
beac_ie->app_frmtype = frametype;
beac_ie->app_buflen = len;
- memcpy(&(beac_ie->app_buf[0]), ie, len);
+ os_memcpy(&(beac_ie->app_buf[0]), ie, len);
+
+ /* append the WPA/RSN IE if it is set already */
+ if (((frametype == IEEE80211_APPIE_FRAME_BEACON) ||
+ (frametype == IEEE80211_APPIE_FRAME_PROBE_RESP)) &&
+ (drv->wpa_ie != NULL)) {
+ wpa_hexdump_buf(MSG_DEBUG, "atheros: Append WPA/RSN IE",
+ drv->wpa_ie);
+ os_memcpy(&(beac_ie->app_buf[len]), wpabuf_head(drv->wpa_ie),
+ wpabuf_len(drv->wpa_ie));
+ beac_ie->app_buflen += wpabuf_len(drv->wpa_ie);
+ }
+ wpa_hexdump(MSG_DEBUG, "atheros: SET_APPIEBUF",
+ beac_ie->app_buf, beac_ie->app_buflen);
return set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, beac_ie,
- sizeof(struct ieee80211req_getset_appiebuf) + len);
+ sizeof(struct ieee80211req_getset_appiebuf) +
+ beac_ie->app_buflen);
}
static int
-madwifi_set_ap_wps_ie(void *priv, const struct wpabuf *beacon,
- const struct wpabuf *proberesp)
+atheros_set_ap_wps_ie(void *priv, const struct wpabuf *beacon,
+ const struct wpabuf *proberesp,
+ const struct wpabuf *assocresp)
{
- if (madwifi_set_wps_ie(priv, beacon ? wpabuf_head(beacon) : NULL,
+ struct atheros_driver_data *drv = priv;
+
+ wpa_hexdump_buf(MSG_DEBUG, "atheros: set_ap_wps_ie - beacon", beacon);
+ wpa_hexdump_buf(MSG_DEBUG, "atheros: set_ap_wps_ie - proberesp",
+ proberesp);
+ wpa_hexdump_buf(MSG_DEBUG, "atheros: set_ap_wps_ie - assocresp",
+ assocresp);
+ wpabuf_free(drv->wps_beacon_ie);
+ drv->wps_beacon_ie = beacon ? wpabuf_dup(beacon) : NULL;
+ wpabuf_free(drv->wps_probe_resp_ie);
+ drv->wps_probe_resp_ie = proberesp ? wpabuf_dup(proberesp) : NULL;
+
+ atheros_set_wps_ie(priv, assocresp ? wpabuf_head(assocresp) : NULL,
+ assocresp ? wpabuf_len(assocresp) : 0,
+ IEEE80211_APPIE_FRAME_ASSOC_RESP);
+ if (atheros_set_wps_ie(priv, beacon ? wpabuf_head(beacon) : NULL,
beacon ? wpabuf_len(beacon) : 0,
IEEE80211_APPIE_FRAME_BEACON))
return -1;
- return madwifi_set_wps_ie(priv,
+ return atheros_set_wps_ie(priv,
proberesp ? wpabuf_head(proberesp) : NULL,
proberesp ? wpabuf_len(proberesp): 0,
IEEE80211_APPIE_FRAME_PROBE_RESP);
}
#else /* CONFIG_WPS */
-#define madwifi_set_ap_wps_ie NULL
+#define atheros_set_ap_wps_ie NULL
#endif /* CONFIG_WPS */
static void
-madwifi_new_sta(struct madwifi_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN])
+atheros_new_sta(struct atheros_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN])
{
struct hostapd_data *hapd = drv->hapd;
struct ieee80211req_wpaie ie;
__func__, strerror(errno));
goto no_ie;
}
- wpa_hexdump(MSG_MSGDUMP, "madwifi req WPA IE",
+ wpa_hexdump(MSG_MSGDUMP, "atheros req WPA IE",
ie.wpa_ie, IEEE80211_MAX_OPT_IE);
- wpa_hexdump(MSG_MSGDUMP, "madwifi req RSN IE",
+ wpa_hexdump(MSG_MSGDUMP, "atheros req RSN IE",
ie.rsn_ie, IEEE80211_MAX_OPT_IE);
+#ifdef ATH_WPS_IE
+ wpa_hexdump(MSG_MSGDUMP, "atheros req WPS IE",
+ ie.wps_ie, IEEE80211_MAX_OPT_IE);
+#endif /* ATH_WPS_IE */
iebuf = ie.wpa_ie;
- /* madwifi seems to return some random data if WPA/RSN IE is not set.
+ /* atheros seems to return some random data if WPA/RSN IE is not set.
* Assume the IE was not included if the IE type is unknown. */
if (iebuf[0] != WLAN_EID_VENDOR_SPECIFIC)
iebuf[1] = 0;
if (iebuf[1] == 0 && ie.rsn_ie[1] > 0) {
- /* madwifi-ng svn #1453 added rsn_ie. Use it, if wpa_ie was not
+ /* atheros-ng svn #1453 added rsn_ie. Use it, if wpa_ie was not
* set. This is needed for WPA2. */
iebuf = ie.rsn_ie;
if (iebuf[0] != WLAN_EID_RSN)
}
ielen = iebuf[1];
+
+#ifdef ATH_WPS_IE
+ /* if WPS IE is present, preference is given to WPS */
+ if (ie.wps_ie &&
+ (ie.wps_ie[1] > 0 && (ie.wps_ie[0] == WLAN_EID_VENDOR_SPECIFIC))) {
+ iebuf = ie.wps_ie;
+ ielen = ie.wps_ie[1];
+ }
+#endif /* ATH_WPS_IE */
+
if (ielen == 0)
iebuf = NULL;
else
ielen += 2;
no_ie:
- drv_event_assoc(hapd, addr, iebuf, ielen);
+ drv_event_assoc(hapd, addr, iebuf, ielen, 0);
if (memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) {
/* Cached accounting data is not valid anymore. */
}
static void
-madwifi_wireless_event_wireless_custom(struct madwifi_driver_data *drv,
+atheros_wireless_event_wireless_custom(struct atheros_driver_data *drv,
char *custom, char *end)
{
wpa_printf(MSG_DEBUG, "Custom wireless event: '%s'", custom);
"length %d", len);
return;
}
- madwifi_raw_receive(drv, NULL,
+ atheros_raw_receive(drv, NULL,
(u8 *) custom + WPS_FRAM_TAG_SIZE, len);
#endif /* CONFIG_WPS */
}
}
static void
-madwifi_wireless_event_wireless(struct madwifi_driver_data *drv,
+atheros_wireless_event_wireless(struct atheros_driver_data *drv,
char *data, int len)
{
struct iw_event iwe_buf, *iwe = &iwe_buf;
(u8 *) iwe->u.addr.sa_data);
break;
case IWEVREGISTERED:
- madwifi_new_sta(drv, (u8 *) iwe->u.addr.sa_data);
+ atheros_new_sta(drv, (u8 *) iwe->u.addr.sa_data);
break;
case IWEVASSOCREQIE:
/* Driver hack.. Use IWEVASSOCREQIE to bypass
return; /* XXX */
memcpy(buf, custom, iwe->u.data.length);
buf[iwe->u.data.length] = '\0';
- madwifi_wireless_event_wireless_custom(
+ atheros_wireless_event_wireless_custom(
drv, buf, buf + iwe->u.data.length);
free(buf);
break;
static void
-madwifi_wireless_event_rtm_newlink(void *ctx,
+atheros_wireless_event_rtm_newlink(void *ctx,
struct ifinfomsg *ifi, u8 *buf, size_t len)
{
- struct madwifi_driver_data *drv = ctx;
+ struct atheros_driver_data *drv = ctx;
int attrlen, rta_len;
struct rtattr *attr;
rta_len = RTA_ALIGN(sizeof(struct rtattr));
while (RTA_OK(attr, attrlen)) {
if (attr->rta_type == IFLA_WIRELESS) {
- madwifi_wireless_event_wireless(
+ atheros_wireless_event_wireless(
drv, ((char *) attr) + rta_len,
attr->rta_len - rta_len);
}
static int
-madwifi_get_we_version(struct madwifi_driver_data *drv)
+atheros_get_we_version(struct atheros_driver_data *drv)
{
struct iw_range *range;
struct iwreq iwr;
drv->we_version = range->we_version_compiled;
}
- free(range);
+ os_free(range);
return 0;
}
static int
-madwifi_wireless_event_init(struct madwifi_driver_data *drv)
+atheros_wireless_event_init(struct atheros_driver_data *drv)
{
struct netlink_config *cfg;
- madwifi_get_we_version(drv);
+ atheros_get_we_version(drv);
cfg = os_zalloc(sizeof(*cfg));
if (cfg == NULL)
return -1;
cfg->ctx = drv;
- cfg->newlink_cb = madwifi_wireless_event_rtm_newlink;
+ cfg->newlink_cb = atheros_wireless_event_rtm_newlink;
drv->netlink = netlink_init(cfg);
if (drv->netlink == NULL) {
os_free(cfg);
static int
-madwifi_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len,
- int encrypt, const u8 *own_addr)
+atheros_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len,
+ int encrypt, const u8 *own_addr, u32 flags)
{
- struct madwifi_driver_data *drv = priv;
+ struct atheros_driver_data *drv = priv;
unsigned char buf[3000];
unsigned char *bp = buf;
struct l2_ethhdr *eth;
static void
handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len)
{
- struct madwifi_driver_data *drv = ctx;
+ struct atheros_driver_data *drv = ctx;
drv_event_eapol_rx(drv->hapd, src_addr, buf + sizeof(struct l2_ethhdr),
len - sizeof(struct l2_ethhdr));
}
static void *
-madwifi_init(struct hostapd_data *hapd, struct wpa_init_params *params)
+atheros_init(struct hostapd_data *hapd, struct wpa_init_params *params)
{
- struct madwifi_driver_data *drv;
+ struct atheros_driver_data *drv;
struct ifreq ifr;
struct iwreq iwr;
char brname[IFNAMSIZ];
- drv = os_zalloc(sizeof(struct madwifi_driver_data));
+ drv = os_zalloc(sizeof(struct atheros_driver_data));
if (drv == NULL) {
- printf("Could not allocate memory for madwifi driver data\n");
+ printf("Could not allocate memory for atheros driver data\n");
return NULL;
}
/* mark down during setup */
linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0);
- madwifi_set_privacy(drv, 0); /* default to no privacy */
+ atheros_set_privacy(drv, 0); /* default to no privacy */
- madwifi_receive_probe_req(drv);
+ atheros_receive_probe_req(drv);
- if (madwifi_wireless_event_init(drv))
+ if (atheros_wireless_event_init(drv))
goto bad;
return drv;
static void
-madwifi_deinit(void *priv)
+atheros_deinit(void *priv)
{
- struct madwifi_driver_data *drv = priv;
+ struct atheros_driver_data *drv = priv;
netlink_deinit(drv->netlink);
(void) linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0);
l2_packet_deinit(drv->sock_xmit);
if (drv->sock_raw)
l2_packet_deinit(drv->sock_raw);
+ wpabuf_free(drv->wpa_ie);
+ wpabuf_free(drv->wps_beacon_ie);
+ wpabuf_free(drv->wps_probe_resp_ie);
free(drv);
}
static int
-madwifi_set_ssid(void *priv, const u8 *buf, int len)
+atheros_set_ssid(void *priv, const u8 *buf, int len)
{
- struct madwifi_driver_data *drv = priv;
+ struct atheros_driver_data *drv = priv;
struct iwreq iwr;
memset(&iwr, 0, sizeof(iwr));
}
static int
-madwifi_get_ssid(void *priv, u8 *buf, int len)
+atheros_get_ssid(void *priv, u8 *buf, int len)
{
- struct madwifi_driver_data *drv = priv;
+ struct atheros_driver_data *drv = priv;
struct iwreq iwr;
int ret = 0;
os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
iwr.u.essid.pointer = (caddr_t) buf;
iwr.u.essid.length = len;
+ iwr.u.essid.length = (len > IW_ESSID_MAX_SIZE) ?
+ IW_ESSID_MAX_SIZE : len;
if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) {
perror("ioctl[SIOCGIWESSID]");
}
static int
-madwifi_set_countermeasures(void *priv, int enabled)
+atheros_set_countermeasures(void *priv, int enabled)
{
- struct madwifi_driver_data *drv = priv;
+ struct atheros_driver_data *drv = priv;
wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled);
return set80211param(drv, IEEE80211_PARAM_COUNTERMEASURES, enabled);
}
static int
-madwifi_commit(void *priv)
+atheros_commit(void *priv)
{
- struct madwifi_driver_data *drv = priv;
+ struct atheros_driver_data *drv = priv;
return linux_set_iface_flags(drv->ioctl_sock, drv->iface, 1);
}
+static int atheros_set_authmode(void *priv, int auth_algs)
+{
+ int authmode;
+
+ if ((auth_algs & WPA_AUTH_ALG_OPEN) &&
+ (auth_algs & WPA_AUTH_ALG_SHARED))
+ authmode = IEEE80211_AUTH_AUTO;
+ else if (auth_algs & WPA_AUTH_ALG_OPEN)
+ authmode = IEEE80211_AUTH_OPEN;
+ else if (auth_algs & WPA_AUTH_ALG_SHARED)
+ authmode = IEEE80211_AUTH_SHARED;
+ else
+ return -1;
+
+ return set80211param(priv, IEEE80211_PARAM_AUTHMODE, authmode);
+}
+
+static int atheros_set_ap(void *priv, struct wpa_driver_ap_params *params)
+{
+ /*
+ * TODO: Use this to replace set_authmode, set_privacy, set_ieee8021x,
+ * set_generic_elem, and hapd_set_ssid.
+ */
+
+ wpa_printf(MSG_DEBUG, "atheros: set_ap - pairwise_ciphers=0x%x "
+ "group_cipher=0x%x key_mgmt_suites=0x%x auth_algs=0x%x "
+ "wpa_version=0x%x privacy=%d interworking=%d",
+ params->pairwise_ciphers, params->group_cipher,
+ params->key_mgmt_suites, params->auth_algs,
+ params->wpa_version, params->privacy, params->interworking);
+ wpa_hexdump_ascii(MSG_DEBUG, "atheros: SSID",
+ params->ssid, params->ssid_len);
+ if (params->hessid)
+ wpa_printf(MSG_DEBUG, "atheros: HESSID " MACSTR,
+ MAC2STR(params->hessid));
+ wpa_hexdump_buf(MSG_DEBUG, "atheros: beacon_ies",
+ params->beacon_ies);
+ wpa_hexdump_buf(MSG_DEBUG, "atheros: proberesp_ies",
+ params->proberesp_ies);
+ wpa_hexdump_buf(MSG_DEBUG, "atheros: assocresp_ies",
+ params->assocresp_ies);
+
+ return 0;
+}
+
const struct wpa_driver_ops wpa_driver_atheros_ops = {
.name = "atheros",
- .hapd_init = madwifi_init,
- .hapd_deinit = madwifi_deinit,
- .set_ieee8021x = madwifi_set_ieee8021x,
- .set_privacy = madwifi_set_privacy,
- .set_key = madwifi_set_key,
- .get_seqnum = madwifi_get_seqnum,
- .flush = madwifi_flush,
- .set_generic_elem = madwifi_set_opt_ie,
- .sta_set_flags = madwifi_sta_set_flags,
- .read_sta_data = madwifi_read_sta_driver_data,
- .hapd_send_eapol = madwifi_send_eapol,
- .sta_disassoc = madwifi_sta_disassoc,
- .sta_deauth = madwifi_sta_deauth,
- .hapd_set_ssid = madwifi_set_ssid,
- .hapd_get_ssid = madwifi_get_ssid,
- .set_countermeasures = madwifi_set_countermeasures,
- .sta_clear_stats = madwifi_sta_clear_stats,
- .commit = madwifi_commit,
- .set_ap_wps_ie = madwifi_set_ap_wps_ie,
+ .hapd_init = atheros_init,
+ .hapd_deinit = atheros_deinit,
+ .set_ieee8021x = atheros_set_ieee8021x,
+ .set_privacy = atheros_set_privacy,
+ .set_key = atheros_set_key,
+ .get_seqnum = atheros_get_seqnum,
+ .flush = atheros_flush,
+ .set_generic_elem = atheros_set_opt_ie,
+ .sta_set_flags = atheros_sta_set_flags,
+ .read_sta_data = atheros_read_sta_driver_data,
+ .hapd_send_eapol = atheros_send_eapol,
+ .sta_disassoc = atheros_sta_disassoc,
+ .sta_deauth = atheros_sta_deauth,
+ .hapd_set_ssid = atheros_set_ssid,
+ .hapd_get_ssid = atheros_get_ssid,
+ .set_countermeasures = atheros_set_countermeasures,
+ .sta_clear_stats = atheros_sta_clear_stats,
+ .commit = atheros_commit,
+ .set_ap_wps_ie = atheros_set_ap_wps_ie,
+ .set_authmode = atheros_set_authmode,
+ .set_ap = atheros_set_ap,
};
+++ /dev/null
-/*
- * WPA Supplicant - Driver interaction with Atmel Wireless LAN drivers
- * Copyright (c) 2000-2005, ATMEL Corporation
- * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
- */
-
-/******************************************************************************
- Copyright 2000-2001 ATMEL Corporation.
-
- WPA Supplicant - driver interaction with Atmel Wireless lan drivers.
-
- This is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with Atmel wireless lan drivers; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-
-******************************************************************************/
-
-/*
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- */
-
-#include "includes.h"
-#include <sys/ioctl.h>
-
-#include "wireless_copy.h"
-#include "common.h"
-#include "driver.h"
-#include "driver_wext.h"
-
-struct wpa_driver_atmel_data {
- void *wext; /* private data for driver_wext */
- void *ctx;
- char ifname[IFNAMSIZ + 1];
- int sock;
-};
-
-
-#define ATMEL_WPA_IOCTL (SIOCIWFIRSTPRIV + 2)
-#define ATMEL_WPA_IOCTL_PARAM (SIOCIWFIRSTPRIV + 3)
-#define ATMEL_WPA_IOCTL_GET_PARAM (SIOCIWFIRSTPRIV + 4)
-
-
-/* ATMEL_WPA_IOCTL ioctl() cmd: */
-enum {
- SET_WPA_ENCRYPTION = 1,
- SET_CIPHER_SUITES = 2,
- MLME_STA_DEAUTH = 3,
- MLME_STA_DISASSOC = 4
-};
-
-/* ATMEL_WPA_IOCTL_PARAM ioctl() cmd: */
-enum {
- ATMEL_PARAM_WPA = 1,
- ATMEL_PARAM_PRIVACY_INVOKED = 2,
- ATMEL_PARAM_WPA_TYPE = 3
-};
-
-#define MAX_KEY_LENGTH 40
-
-struct atmel_param{
- unsigned char sta_addr[6];
- int cmd;
- u8 alg;
- u8 key_idx;
- u8 set_tx;
- u8 seq[8];
- u8 seq_len;
- u16 key_len;
- u8 key[MAX_KEY_LENGTH];
- struct{
- int reason_code;
- u8 state;
- }mlme;
- u8 pairwise_suite;
- u8 group_suite;
- u8 key_mgmt_suite;
-};
-
-
-
-static int atmel_ioctl(struct wpa_driver_atmel_data *drv,
- struct atmel_param *param,
- int len, int show_err)
-{
- struct iwreq iwr;
-
- os_memset(&iwr, 0, sizeof(iwr));
- os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
- iwr.u.data.pointer = (caddr_t) param;
- iwr.u.data.length = len;
-
- if (ioctl(drv->sock, ATMEL_WPA_IOCTL, &iwr) < 0) {
- int ret;
- ret = errno;
- if (show_err)
- perror("ioctl[ATMEL_WPA_IOCTL]");
- return ret;
- }
-
- return 0;
-}
-
-
-static int atmel2param(struct wpa_driver_atmel_data *drv, int param, int value)
-{
- struct iwreq iwr;
- int *i, ret = 0;
-
- os_memset(&iwr, 0, sizeof(iwr));
- os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
- i = (int *) iwr.u.name;
- *i++ = param;
- *i++ = value;
-
- if (ioctl(drv->sock, ATMEL_WPA_IOCTL_PARAM, &iwr) < 0) {
- perror("ioctl[ATMEL_WPA_IOCTL_PARAM]");
- ret = -1;
- }
- return ret;
-}
-
-
-#if 0
-static int wpa_driver_atmel_set_wpa_ie(struct wpa_driver_atmel_data *drv,
- const char *wpa_ie, size_t wpa_ie_len)
-{
- struct atmel_param *param;
- int res;
- size_t blen = ATMEL_GENERIC_ELEMENT_HDR_LEN + wpa_ie_len;
- if (blen < sizeof(*param))
- blen = sizeof(*param);
-
- param = os_zalloc(blen);
- if (param == NULL)
- return -1;
-
- param->cmd = ATMEL_SET_GENERIC_ELEMENT;
- param->u.generic_elem.len = wpa_ie_len;
- os_memcpy(param->u.generic_elem.data, wpa_ie, wpa_ie_len);
- res = atmel_ioctl(drv, param, blen, 1);
-
- os_free(param);
-
- return res;
-}
-#endif
-
-
-static int wpa_driver_atmel_set_wpa(void *priv, int enabled)
-{
- struct wpa_driver_atmel_data *drv = priv;
- int ret = 0;
-
- printf("wpa_driver_atmel_set_wpa %s\n", drv->ifname);
-
- wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled);
-
-#if 0
- if (!enabled && wpa_driver_atmel_set_wpa_ie(drv, NULL, 0) < 0)
- ret = -1;
-#endif
- if (atmel2param(drv, ATMEL_PARAM_PRIVACY_INVOKED, enabled) < 0)
- ret = -1;
- if (atmel2param(drv, ATMEL_PARAM_WPA, enabled) < 0)
- ret = -1;
-
- return ret;
-}
-
-
-static int wpa_driver_atmel_set_key(const char *ifname, void *priv,
- enum wpa_alg alg, const u8 *addr,
- int key_idx, int set_tx,
- const u8 *seq, size_t seq_len,
- const u8 *key, size_t key_len)
-{
- struct wpa_driver_atmel_data *drv = priv;
- int ret = 0;
- struct atmel_param *param;
- u8 *buf;
- u8 alg_type;
-
- size_t blen;
- char *alg_name;
-
- switch (alg) {
- case WPA_ALG_NONE:
- alg_name = "none";
- alg_type = 0;
- break;
- case WPA_ALG_WEP:
- alg_name = "WEP";
- alg_type = 1;
- break;
- case WPA_ALG_TKIP:
- alg_name = "TKIP";
- alg_type = 2;
- break;
- case WPA_ALG_CCMP:
- alg_name = "CCMP";
- alg_type = 3;
- break;
- default:
- return -1;
- }
-
- wpa_printf(MSG_DEBUG, "%s: alg=%s key_idx=%d set_tx=%d seq_len=%lu "
- "key_len=%lu", __FUNCTION__, alg_name, key_idx, set_tx,
- (unsigned long) seq_len, (unsigned long) key_len);
-
- if (seq_len > 8)
- return -2;
-
- blen = sizeof(*param) + key_len;
- buf = os_zalloc(blen);
- if (buf == NULL)
- return -1;
-
- param = (struct atmel_param *) buf;
-
- param->cmd = SET_WPA_ENCRYPTION;
-
- if (addr == NULL)
- os_memset(param->sta_addr, 0xff, ETH_ALEN);
- else
- os_memcpy(param->sta_addr, addr, ETH_ALEN);
-
- param->alg = alg_type;
- param->key_idx = key_idx;
- param->set_tx = set_tx;
- os_memcpy(param->seq, seq, seq_len);
- param->seq_len = seq_len;
- param->key_len = key_len;
- os_memcpy((u8 *)param->key, key, key_len);
-
- if (atmel_ioctl(drv, param, blen, 1)) {
- wpa_printf(MSG_WARNING, "Failed to set encryption.");
- /* TODO: show key error*/
- ret = -1;
- }
- os_free(buf);
-
- return ret;
-}
-
-
-static int wpa_driver_atmel_set_countermeasures(void *priv,
- int enabled)
-{
- /* FIX */
- printf("wpa_driver_atmel_set_countermeasures - not yet "
- "implemented\n");
- return 0;
-}
-
-
-static int wpa_driver_atmel_mlme(void *priv, const u8 *addr, int cmd,
- int reason_code)
-{
- struct wpa_driver_atmel_data *drv = priv;
- struct atmel_param param;
- int ret;
- int mgmt_error = 0xaa;
-
- os_memset(¶m, 0, sizeof(param));
- os_memcpy(param.sta_addr, addr, ETH_ALEN);
- param.cmd = cmd;
- param.mlme.reason_code = reason_code;
- param.mlme.state = mgmt_error;
- ret = atmel_ioctl(drv, ¶m, sizeof(param), 1);
- return ret;
-}
-
-
-#if 0
-static int wpa_driver_atmel_set_suites(struct wpa_driver_atmel_data *drv,
- u8 pairwise_suite, u8 group_suite,
- u8 key_mgmt_suite)
-{
- struct atmel_param param;
- int ret;
-
- os_memset(¶m, 0, sizeof(param));
- param.cmd = SET_CIPHER_SUITES;
- param.pairwise_suite = pairwise_suite;
- param.group_suite = group_suite;
- param.key_mgmt_suite = key_mgmt_suite;
-
- ret = atmel_ioctl(drv, ¶m, sizeof(param), 1);
- return ret;
-}
-#endif
-
-
-static int wpa_driver_atmel_deauthenticate(void *priv, const u8 *addr,
- int reason_code)
-{
- struct wpa_driver_atmel_data *drv = priv;
- printf("wpa_driver_atmel_deauthenticate\n");
- wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
- return wpa_driver_atmel_mlme(drv, addr, MLME_STA_DEAUTH,
- reason_code);
-
-}
-
-
-static int wpa_driver_atmel_disassociate(void *priv, const u8 *addr,
- int reason_code)
-{
- struct wpa_driver_atmel_data *drv = priv;
- printf("wpa_driver_atmel_disassociate\n");
- wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
- return wpa_driver_atmel_mlme(drv, addr, MLME_STA_DISASSOC,
- reason_code);
-
-}
-
-
-#if 0
-/* Atmel driver uses specific values for each cipher suite */
-static int convertSuiteToDriver(enum wpa_cipher suite)
-{
- u8 suite_type;
-
- switch(suite) {
- case CIPHER_NONE:
- suite_type = 0;
- break;
- case CIPHER_WEP40:
- suite_type = 1;
- break;
- case CIPHER_TKIP:
- suite_type = 2;
- break;
- case CIPHER_WEP104:
- suite_type = 5;
- break;
- case CIPHER_CCMP:
- suite_type = 3;
- break;
- default:
- suite_type = 2;
- }
-
- return suite_type;
-
-}
-#endif
-
-static int
-wpa_driver_atmel_associate(void *priv,
- struct wpa_driver_associate_params *params)
-{
- struct wpa_driver_atmel_data *drv = priv;
- int ret = 0;
-#if 0
- u8 pairwise_suite_driver;
- u8 group_suite_driver;
- u8 key_mgmt_suite_driver;
-
- pairwise_suite_driver = convertSuiteToDriver(params->pairwise_suite);
- group_suite_driver = convertSuiteToDriver(params->group_suite);
- key_mgmt_suite_driver = convertSuiteToDriver(params->key_mgmt_suite);
-
- if (wpa_driver_atmel_set_suites(drv, pairwise_suite_driver,
- group_suite_driver,
- key_mgmt_suite_driver) < 0){
- printf("wpa_driver_atmel_set_suites.\n");
- ret = -1;
- }
- if (wpa_driver_wext_set_freq(drv->wext, params->freq) < 0) {
- printf("wpa_driver_atmel_set_freq.\n");
- ret = -1;
- }
-#endif
- if (wpa_driver_wext_set_ssid(drv->wext, params->ssid, params->ssid_len)
- < 0) {
- printf("FAILED : wpa_driver_atmel_set_ssid.\n");
- ret = -1;
- }
- if (wpa_driver_wext_set_bssid(drv->wext, params->bssid) < 0) {
- printf("FAILED : wpa_driver_atmel_set_bssid.\n");
- ret = -1;
- }
-
- return ret;
-}
-
-
-static int wpa_driver_atmel_get_bssid(void *priv, u8 *bssid)
-{
- struct wpa_driver_atmel_data *drv = priv;
- return wpa_driver_wext_get_bssid(drv->wext, bssid);
-}
-
-
-static int wpa_driver_atmel_get_ssid(void *priv, u8 *ssid)
-{
- struct wpa_driver_atmel_data *drv = priv;
- return wpa_driver_wext_get_ssid(drv->wext, ssid);
-}
-
-
-static int wpa_driver_atmel_scan(void *priv,
- struct wpa_driver_scan_params *params)
-{
- struct wpa_driver_atmel_data *drv = priv;
- return wpa_driver_wext_scan(drv->wext, params);
-}
-
-
-static struct wpa_scan_results * wpa_driver_atmel_get_scan_results(void *priv)
-{
- struct wpa_driver_atmel_data *drv = priv;
- return wpa_driver_wext_get_scan_results(drv->wext);
-}
-
-
-static int wpa_driver_atmel_set_operstate(void *priv, int state)
-{
- struct wpa_driver_atmel_data *drv = priv;
- return wpa_driver_wext_set_operstate(drv->wext, state);
-}
-
-
-static void * wpa_driver_atmel_init(void *ctx, const char *ifname)
-{
- struct wpa_driver_atmel_data *drv;
-
- drv = os_zalloc(sizeof(*drv));
- if (drv == NULL)
- return NULL;
- drv->wext = wpa_driver_wext_init(ctx, ifname);
- if (drv->wext == NULL) {
- os_free(drv);
- return NULL;
- }
-
- drv->ctx = ctx;
- os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
- drv->sock = socket(PF_INET, SOCK_DGRAM, 0);
- if (drv->sock < 0) {
- wpa_driver_wext_deinit(drv->wext);
- os_free(drv);
- return NULL;
- }
-
- wpa_driver_atmel_set_wpa(drv, 1);
-
- return drv;
-}
-
-
-static void wpa_driver_atmel_deinit(void *priv)
-{
- struct wpa_driver_atmel_data *drv = priv;
- wpa_driver_atmel_set_wpa(drv, 0);
- wpa_driver_wext_deinit(drv->wext);
- close(drv->sock);
- os_free(drv);
-}
-
-
-const struct wpa_driver_ops wpa_driver_atmel_ops = {
- .name = "atmel",
- .desc = "ATMEL AT76C5XXx (USB, PCMCIA)",
- .get_bssid = wpa_driver_atmel_get_bssid,
- .get_ssid = wpa_driver_atmel_get_ssid,
- .set_key = wpa_driver_atmel_set_key,
- .init = wpa_driver_atmel_init,
- .deinit = wpa_driver_atmel_deinit,
- .set_countermeasures = wpa_driver_atmel_set_countermeasures,
- .scan2 = wpa_driver_atmel_scan,
- .get_scan_results2 = wpa_driver_atmel_get_scan_results,
- .deauthenticate = wpa_driver_atmel_deauthenticate,
- .disassociate = wpa_driver_atmel_disassociate,
- .associate = wpa_driver_atmel_associate,
- .set_operstate = wpa_driver_atmel_set_operstate,
-};
#include "includes.h"
#include <sys/ioctl.h>
+#include <sys/sysctl.h>
#include "common.h"
#include "driver.h"
#include "eloop.h"
#include "common/ieee802_11_defs.h"
+#include "common/wpa_common.h"
#include <net/if.h>
#include <net/if_media.h>
if (alg == WPA_ALG_NONE) {
#ifndef HOSTAPD
- if (addr == NULL ||
- os_memcmp(addr, "\xff\xff\xff\xff\xff\xff",
- IEEE80211_ADDR_LEN) == 0)
+ if (addr == NULL || is_broadcast_ether_addr(addr))
return bsd_del_key(priv, NULL, key_idx);
else
#endif /* HOSTAPD */
* the address (yech). Note also that we can only mark global
* keys default; doing this for a unicast key is an error.
*/
- if (os_memcmp(addr, "\xff\xff\xff\xff\xff\xff",
- IEEE80211_ADDR_LEN) == 0) {
+ if (is_broadcast_ether_addr(addr)) {
wk.ik_flags |= IEEE80211_KEY_GROUP;
wk.ik_keyix = key_idx;
} else {
if (wk.ik_keyix != IEEE80211_KEYIX_NONE && set_tx)
wk.ik_flags |= IEEE80211_KEY_DEFAULT;
wk.ik_keylen = key_len;
- os_memcpy(&wk.ik_keyrsc, seq, seq_len);
+ if (seq) {
+#ifdef WORDS_BIGENDIAN
+ /*
+ * wk.ik_keyrsc is in host byte order (big endian), need to
+ * swap it to match with the byte order used in WPA.
+ */
+ int i;
+ u8 *keyrsc = (u8 *) &wk.ik_keyrsc;
+ for (i = 0; i < seq_len; i++)
+ keyrsc[WPA_KEY_RSC_LEN - i - 1] = seq[i];
+#else /* WORDS_BIGENDIAN */
+ os_memcpy(&wk.ik_keyrsc, seq, seq_len);
+#endif /* WORDS_BIGENDIAN */
+ }
os_memcpy(wk.ik_keydata, key, key_len);
return set80211var(priv, IEEE80211_IOC_WPAKEY, &wk, sizeof(wk));
ielen += 2;
no_ie:
- drv_event_assoc(ctx, addr, iebuf, ielen);
+ drv_event_assoc(ctx, addr, iebuf, ielen, 0);
}
static int
bsd_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len,
- int encrypt, const u8 *own_addr)
+ int encrypt, const u8 *own_addr, u32 flags)
{
struct bsd_driver_data *drv = priv;
}
static int
-bsd_set_freq(void *priv, u16 channel)
+bsd_set_freq(void *priv, struct hostapd_freq_params *freq)
{
struct bsd_driver_data *drv = priv;
#ifdef SIOCS80211CHANNEL
struct ieee80211chanreq creq;
#endif /* SIOCS80211CHANNEL */
u32 mode;
-
- if (channel < 14)
- mode = IFM_IEEE80211_11G;
- else if (channel == 14)
+ int channel = freq->channel;
+
+ if (channel < 14) {
+ mode =
+#ifdef CONFIG_IEEE80211N
+ freq->ht_enabled ? IFM_IEEE80211_11NG :
+#endif /* CONFIG_IEEE80211N */
+ IFM_IEEE80211_11G;
+ } else if (channel == 14) {
mode = IFM_IEEE80211_11B;
- else
- mode = IFM_IEEE80211_11A;
+ } else {
+ mode =
+#ifdef CONFIG_IEEE80211N
+ freq->ht_enabled ? IFM_IEEE80211_11NA :
+#endif /* CONFIG_IEEE80211N */
+ IFM_IEEE80211_11A;
+ }
if (bsd_set_mediaopt(drv, IFM_MMASK, mode) < 0) {
wpa_printf(MSG_ERROR, "%s: failed to set modulation mode",
__func__);
#ifdef SIOCS80211CHANNEL
os_memset(&creq, 0, sizeof(creq));
os_strlcpy(creq.i_name, drv->ifname, sizeof(creq.i_name));
- creq.i_channel = channel;
+ creq.i_channel = (u_int16_t)channel;
return ioctl(drv->sock, SIOCS80211CHANNEL, &creq);
#else /* SIOCS80211CHANNEL */
return set80211param(priv, IEEE80211_IOC_CHANNEL, channel);
return 0;
}
+static int
+rtbuf_len(void)
+{
+ size_t len;
+
+ int mib[6] = {CTL_NET, AF_ROUTE, 0, AF_INET, NET_RT_DUMP, 0};
+
+ if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {
+ wpa_printf(MSG_WARNING, "%s failed: %s\n", __func__,
+ strerror(errno));
+ len = 2048;
+ }
+
+ return len;
+}
#ifdef HOSTAPD
bsd_wireless_event_receive(int sock, void *ctx, void *sock_ctx)
{
struct bsd_driver_data *drv = ctx;
- char buf[2048];
+ char *buf;
struct if_announcemsghdr *ifan;
struct rt_msghdr *rtm;
struct ieee80211_michael_event *mic;
struct ieee80211_join_event *join;
struct ieee80211_leave_event *leave;
- int n;
+ int n, len;
union wpa_event_data data;
- n = read(sock, buf, sizeof(buf));
+ len = rtbuf_len();
+
+ buf = os_malloc(len);
+ if (buf == NULL) {
+ wpa_printf(MSG_ERROR, "%s os_malloc() failed\n", __func__);
+ return;
+ }
+
+ n = read(sock, buf, len);
if (n < 0) {
if (errno != EINTR && errno != EAGAIN)
- perror("read(PF_ROUTE)");
+ wpa_printf(MSG_ERROR, "%s read() failed: %s\n",
+ __func__, strerror(errno));
+ os_free(buf);
return;
}
rtm = (struct rt_msghdr *) buf;
if (rtm->rtm_version != RTM_VERSION) {
- wpa_printf(MSG_DEBUG, "Routing message version %d not "
- "understood\n", rtm->rtm_version);
+ wpa_printf(MSG_DEBUG, "Invalid routing message version=%d",
+ rtm->rtm_version);
+ os_free(buf);
return;
}
ifan = (struct if_announcemsghdr *) rtm;
}
break;
}
+ os_free(buf);
}
static void
drv_event_eapol_rx(drv->hapd, src_addr, buf, len);
}
-static int
-hostapd_bsd_set_freq(void *priv, struct hostapd_freq_params *freq)
-{
- return bsd_set_freq(priv, freq->channel);
-}
-
static void *
bsd_init(struct hostapd_data *hapd, struct wpa_init_params *params)
{
struct bsd_driver_data *drv = priv;
struct ieee80211req_mlme mlme;
u32 mode;
- u16 channel;
int privacy;
int ret = 0;
}
if (params->mode == IEEE80211_MODE_AP) {
- if (params->freq >= 2412 && params->freq <= 2472)
- channel = (params->freq - 2407) / 5;
- else if (params->freq == 2484)
- channel = 14;
- else if ((params->freq >= 5180 && params->freq <= 5240) ||
- (params->freq >= 5745 && params->freq <= 5825))
- channel = (params->freq - 5000) / 5;
- else
- channel = 0;
- if (bsd_set_freq(drv, channel) < 0)
- return -1;
-
drv->sock_xmit = l2_packet_init(drv->ifname, NULL, ETH_P_EAPOL,
handle_read, drv, 0);
if (drv->sock_xmit == NULL)
wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx)
{
struct bsd_driver_data *drv = sock_ctx;
- char buf[2048];
+ char *buf;
struct if_announcemsghdr *ifan;
struct if_msghdr *ifm;
struct rt_msghdr *rtm;
struct ieee80211_michael_event *mic;
struct ieee80211_leave_event *leave;
struct ieee80211_join_event *join;
- int n;
+ int n, len;
+
+ len = rtbuf_len();
+
+ buf = os_malloc(len);
+ if (buf == NULL) {
+ wpa_printf(MSG_ERROR, "%s os_malloc() failed\n", __func__);
+ return;
+ }
- n = read(sock, buf, sizeof(buf));
+ n = read(sock, buf, len);
if (n < 0) {
if (errno != EINTR && errno != EAGAIN)
- perror("read(PF_ROUTE)");
+ wpa_printf(MSG_ERROR, "%s read() failed: %s\n",
+ __func__, strerror(errno));
+ os_free(buf);
return;
}
rtm = (struct rt_msghdr *) buf;
if (rtm->rtm_version != RTM_VERSION) {
- wpa_printf(MSG_DEBUG, "Routing message version %d not "
- "understood\n", rtm->rtm_version);
+ wpa_printf(MSG_DEBUG, "Invalid routing message version=%d",
+ rtm->rtm_version);
+ os_free(buf);
return;
}
os_memset(&event, 0, sizeof(event));
case IFAN_DEPARTURE:
event.interface_status.ievent = EVENT_INTERFACE_REMOVED;
default:
+ os_free(buf);
return;
}
wpa_printf(MSG_DEBUG, "RTM_IFANNOUNCE: Interface '%s' %s",
}
break;
}
+ os_free(buf);
}
static void
.read_sta_data = bsd_read_sta_driver_data,
.sta_disassoc = bsd_sta_disassoc,
.sta_deauth = bsd_sta_deauth,
- .set_freq = hostapd_bsd_set_freq,
#else /* HOSTAPD */
.init = wpa_driver_bsd_init,
.deinit = wpa_driver_bsd_deinit,
.associate = wpa_driver_bsd_associate,
.get_capa = wpa_driver_bsd_get_capa,
#endif /* HOSTAPD */
+ .set_freq = bsd_set_freq,
.set_key = bsd_set_key,
.set_ieee8021x = bsd_set_ieee8021x,
.hapd_set_ssid = bsd_set_ssid,
--- /dev/null
+/*
+ * Common driver-related functions
+ * Copyright (c) 2003-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#include "utils/common.h"
+#include "driver.h"
+
+void wpa_scan_results_free(struct wpa_scan_results *res)
+{
+ size_t i;
+
+ if (res == NULL)
+ return;
+
+ for (i = 0; i < res->num; i++)
+ os_free(res->res[i]);
+ os_free(res->res);
+ os_free(res);
+}
+
+
+const char * event_to_string(enum wpa_event_type event)
+{
+#define E2S(n) case EVENT_ ## n: return #n
+ switch (event) {
+ E2S(ASSOC);
+ E2S(DISASSOC);
+ E2S(MICHAEL_MIC_FAILURE);
+ E2S(SCAN_RESULTS);
+ E2S(ASSOCINFO);
+ E2S(INTERFACE_STATUS);
+ E2S(PMKID_CANDIDATE);
+ E2S(STKSTART);
+ E2S(TDLS);
+ E2S(FT_RESPONSE);
+ E2S(IBSS_RSN_START);
+ E2S(AUTH);
+ E2S(DEAUTH);
+ E2S(ASSOC_REJECT);
+ E2S(AUTH_TIMED_OUT);
+ E2S(ASSOC_TIMED_OUT);
+ E2S(FT_RRB_RX);
+ E2S(WPS_BUTTON_PUSHED);
+ E2S(TX_STATUS);
+ E2S(RX_FROM_UNKNOWN);
+ E2S(RX_MGMT);
+ E2S(RX_ACTION);
+ E2S(REMAIN_ON_CHANNEL);
+ E2S(CANCEL_REMAIN_ON_CHANNEL);
+ E2S(MLME_RX);
+ E2S(RX_PROBE_REQ);
+ E2S(NEW_STA);
+ E2S(EAPOL_RX);
+ E2S(SIGNAL_CHANGE);
+ E2S(INTERFACE_ENABLED);
+ E2S(INTERFACE_DISABLED);
+ E2S(CHANNEL_LIST_CHANGED);
+ E2S(INTERFACE_UNAVAILABLE);
+ E2S(BEST_CHANNEL);
+ E2S(UNPROT_DEAUTH);
+ E2S(UNPROT_DISASSOC);
+ E2S(STATION_LOW_ACK);
+ E2S(P2P_DEV_FOUND);
+ E2S(P2P_GO_NEG_REQ_RX);
+ E2S(P2P_GO_NEG_COMPLETED);
+ E2S(P2P_PROV_DISC_REQUEST);
+ E2S(P2P_PROV_DISC_RESPONSE);
+ E2S(P2P_SD_REQUEST);
+ E2S(P2P_SD_RESPONSE);
+ E2S(IBSS_PEER_LOST);
+ E2S(DRIVER_GTK_REKEY);
+ E2S(SCHED_SCAN_STOPPED);
+ E2S(DRIVER_CLIENT_POLL_OK);
+ }
+
+ return "UNKNOWN";
+#undef E2S
+}
#include "netlink.h"
#include "linux_ioctl.h"
#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
/* MTU to be set for the wlan#ap device; this is mainly needed for IEEE 802.1X
sa = hdr->addr2;
os_memset(&event, 0, sizeof(event));
- event.rx_from_unknown.frame = buf;
- event.rx_from_unknown.len = len;
+ event.rx_from_unknown.bssid = get_hdr_bssid(hdr, len);
+ event.rx_from_unknown.addr = sa;
wpa_supplicant_event(drv->hapd, EVENT_RX_FROM_UNKNOWN, &event);
pos = (u8 *) (hdr + 1);
{
struct ieee80211_hdr *hdr;
u16 fc, extra_len, type, stype;
- unsigned char *extra = NULL;
size_t data_len = len;
int ver;
union wpa_event_data event;
return;
}
len -= extra_len + 2;
- extra = buf + len;
} else if (ver == 1 || ver == 2) {
handle_tx_callback(drv, buf, data_len, ver == 2 ? 1 : 0);
return;
static int hostap_send_eapol(void *priv, const u8 *addr, const u8 *data,
- size_t data_len, int encrypt, const u8 *own_addr)
+ size_t data_len, int encrypt, const u8 *own_addr,
+ u32 flags)
{
struct hostap_driver_data *drv = priv;
struct ieee80211_hdr *hdr;
static int hostap_set_ap_wps_ie(void *priv, const struct wpabuf *beacon,
- const struct wpabuf *proberesp)
+ const struct wpabuf *proberesp,
+ const struct wpabuf *assocresp)
{
struct hostap_driver_data *drv = priv;
struct hostap_driver_data *drv = priv;
struct ieee80211_mgmt mgmt;
+ if (is_broadcast_ether_addr(addr)) {
+ /*
+ * New Prism2.5/3 STA firmware versions seem to have issues
+ * with this broadcast deauth frame. This gets the firmware in
+ * odd state where nothing works correctly, so let's skip
+ * sending this for the hostap driver.
+ */
+ return 0;
+ }
+
memset(&mgmt, 0, sizeof(mgmt));
mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
WLAN_FC_STYPE_DEAUTH);
}
+static int hostap_set_freq(void *priv, struct hostapd_freq_params *freq)
+{
+ struct hostap_driver_data *drv = priv;
+ struct iwreq iwr;
+
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+ iwr.u.freq.m = freq->channel;
+ iwr.u.freq.e = 0;
+
+ if (ioctl(drv->ioctl_sock, SIOCSIWFREQ, &iwr) < 0) {
+ perror("ioctl[SIOCSIWFREQ]");
+ return -1;
+ }
+
+ return 0;
+}
+
+
static int hostap_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
int reason)
{
return mode;
}
+
+static void wpa_driver_hostap_poll_client(void *priv, const u8 *own_addr,
+ const u8 *addr, int qos)
+{
+ struct ieee80211_hdr hdr;
+
+ os_memset(&hdr, 0, sizeof(hdr));
+
+ /*
+ * WLAN_FC_STYPE_NULLFUNC would be more appropriate,
+ * but it is apparently not retried so TX Exc events
+ * are not received for it.
+ * This is the reason the driver overrides the default
+ * handling.
+ */
+ hdr.frame_control = IEEE80211_FC(WLAN_FC_TYPE_DATA,
+ WLAN_FC_STYPE_DATA);
+
+ hdr.frame_control |=
+ host_to_le16(WLAN_FC_FROMDS);
+ os_memcpy(hdr.IEEE80211_DA_FROMDS, addr, ETH_ALEN);
+ os_memcpy(hdr.IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN);
+ os_memcpy(hdr.IEEE80211_SA_FROMDS, own_addr, ETH_ALEN);
+
+ hostap_send_mlme(priv, (u8 *)&hdr, sizeof(hdr));
+}
+
#else /* HOSTAPD */
struct wpa_driver_hostap_data {
HOSTAP_CRYPT_ALG_NAME_LEN);
param->u.crypt.flags = set_tx ? HOSTAP_CRYPT_FLAG_SET_TX_KEY : 0;
param->u.crypt.idx = key_idx;
- os_memcpy(param->u.crypt.seq, seq, seq_len);
+ if (seq)
+ os_memcpy(param->u.crypt.seq, seq, seq_len);
param->u.crypt.key_len = key_len;
os_memcpy((u8 *) (param + 1), key, key_len);
.sta_clear_stats = hostap_sta_clear_stats,
.get_hw_feature_data = hostap_get_hw_feature_data,
.set_ap_wps_ie = hostap_set_ap_wps_ie,
+ .set_freq = hostap_set_freq,
+ .poll_client = wpa_driver_hostap_poll_client,
#else /* HOSTAPD */
.get_bssid = wpa_driver_hostap_get_bssid,
.get_ssid = wpa_driver_hostap_get_ssid,
+++ /dev/null
-/*
- * WPA Supplicant - driver interaction with Linux ipw2100/2200 drivers
- * Copyright (c) 2005 Zhu Yi <yi.zhu@intel.com>
- * Copyright (c) 2004 Lubomir Gelo <lgelo@cnc.sk>
- * Copyright (c) 2003-2004, Jouni Malinen <j@w1.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
- *
- * Please note that ipw2100/2200 drivers change to use generic Linux wireless
- * extensions if the kernel includes support for WE-18 or newer (Linux 2.6.13
- * or newer). driver_wext.c should be used in those cases.
- */
-
-#include "includes.h"
-#include <sys/ioctl.h>
-
-#include "wireless_copy.h"
-#include "common.h"
-#include "driver.h"
-#include "driver_wext.h"
-
-struct wpa_driver_ipw_data {
- void *wext; /* private data for driver_wext */
- void *ctx;
- char ifname[IFNAMSIZ + 1];
- int sock;
-};
-
-/* following definitions must be kept in sync with ipw2100.c and ipw2200.c */
-
-#define IPW_IOCTL_WPA_SUPPLICANT SIOCIWFIRSTPRIV+30
-
-#define IPW_CMD_SET_WPA_PARAM 1
-#define IPW_CMD_SET_WPA_IE 2
-#define IPW_CMD_SET_ENCRYPTION 3
-#define IPW_CMD_MLME 4
-
-#define IPW_PARAM_WPA_ENABLED 1
-#define IPW_PARAM_TKIP_COUNTERMEASURES 2
-#define IPW_PARAM_DROP_UNENCRYPTED 3
-#define IPW_PARAM_PRIVACY_INVOKED 4
-#define IPW_PARAM_AUTH_ALGS 5
-#define IPW_PARAM_IEEE_802_1X 6
-
-#define IPW_MLME_STA_DEAUTH 1
-#define IPW_MLME_STA_DISASSOC 2
-
-#define IPW_CRYPT_ERR_UNKNOWN_ALG 2
-#define IPW_CRYPT_ERR_UNKNOWN_ADDR 3
-#define IPW_CRYPT_ERR_CRYPT_INIT_FAILED 4
-#define IPW_CRYPT_ERR_KEY_SET_FAILED 5
-#define IPW_CRYPT_ERR_TX_KEY_SET_FAILED 6
-#define IPW_CRYPT_ERR_CARD_CONF_FAILED 7
-
-#define IPW_CRYPT_ALG_NAME_LEN 16
-
-struct ipw_param {
- u32 cmd;
- u8 sta_addr[ETH_ALEN];
- union {
- struct {
- u8 name;
- u32 value;
- } wpa_param;
- struct {
- u32 len;
- u8 reserved[32];
- u8 data[0];
- } wpa_ie;
- struct{
- u32 command;
- u32 reason_code;
- } mlme;
- struct {
- u8 alg[IPW_CRYPT_ALG_NAME_LEN];
- u8 set_tx;
- u32 err;
- u8 idx;
- u8 seq[8];
- u16 key_len;
- u8 key[0];
- } crypt;
-
- } u;
-};
-
-/* end of ipw2100.c and ipw2200.c code */
-
-static int wpa_driver_ipw_set_auth_alg(void *priv, int auth_alg);
-
-static int ipw_ioctl(struct wpa_driver_ipw_data *drv,
- struct ipw_param *param, int len, int show_err)
-{
- struct iwreq iwr;
-
- os_memset(&iwr, 0, sizeof(iwr));
- os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
- iwr.u.data.pointer = (caddr_t) param;
- iwr.u.data.length = len;
-
- if (ioctl(drv->sock, IPW_IOCTL_WPA_SUPPLICANT, &iwr) < 0) {
- int ret = errno;
- if (show_err)
- perror("ioctl[IPW_IOCTL_WPA_SUPPLICANT]");
- return ret;
- }
-
- return 0;
-}
-
-
-static void ipw_show_set_key_error(struct ipw_param *param)
-{
- switch (param->u.crypt.err) {
- case IPW_CRYPT_ERR_UNKNOWN_ALG:
- wpa_printf(MSG_INFO, "Unknown algorithm '%s'.",
- param->u.crypt.alg);
- wpa_printf(MSG_INFO, "You may need to load kernel module to "
- "register that algorithm.");
- wpa_printf(MSG_INFO, "E.g., 'modprobe ieee80211_crypt_wep' for"
- " WEP.");
- break;
- case IPW_CRYPT_ERR_UNKNOWN_ADDR:
- wpa_printf(MSG_INFO, "Unknown address " MACSTR ".",
- MAC2STR(param->sta_addr));
- break;
- case IPW_CRYPT_ERR_CRYPT_INIT_FAILED:
- wpa_printf(MSG_INFO, "Crypt algorithm initialization failed.");
- break;
- case IPW_CRYPT_ERR_KEY_SET_FAILED:
- wpa_printf(MSG_INFO, "Key setting failed.");
- break;
- case IPW_CRYPT_ERR_TX_KEY_SET_FAILED:
- wpa_printf(MSG_INFO, "TX key index setting failed.");
- break;
- case IPW_CRYPT_ERR_CARD_CONF_FAILED:
- wpa_printf(MSG_INFO, "Card configuration failed.");
- break;
- }
-}
-
-
-static int ipw_set_wpa_ie(struct wpa_driver_ipw_data *drv,
- const u8 *wpa_ie, size_t wpa_ie_len)
-{
- struct ipw_param *param;
- int ret;
- size_t blen = sizeof(*param) + wpa_ie_len;
-
- param = os_zalloc(blen);
- if (param == NULL)
- return -1;
-
- param->cmd = IPW_CMD_SET_WPA_IE;
- param->u.wpa_ie.len = wpa_ie_len;
- os_memcpy(param->u.wpa_ie.data, wpa_ie, wpa_ie_len);
-
- ret = ipw_ioctl(drv, param, blen, 1);
-
- os_free(param);
- return ret;
-}
-
-
-static int ipw_set_wpa_param(struct wpa_driver_ipw_data *drv, u8 name,
- u32 value)
-{
- struct ipw_param param;
-
- os_memset(¶m, 0, sizeof(param));
- param.cmd = IPW_CMD_SET_WPA_PARAM;
- param.u.wpa_param.name = name;
- param.u.wpa_param.value = value;
-
- return ipw_ioctl(drv, ¶m, sizeof(param), 1);
-}
-
-
-static int ipw_mlme(struct wpa_driver_ipw_data *drv, const u8 *addr,
- int cmd, int reason)
-{
- struct ipw_param param;
-
- os_memset(¶m, 0, sizeof(param));
- os_memcpy(param.sta_addr, addr, ETH_ALEN);
- param.cmd = IPW_CMD_MLME;
- param.u.mlme.command = cmd;
- param.u.mlme.reason_code = reason;
-
- return ipw_ioctl(drv, ¶m, sizeof(param), 1);
-}
-
-
-static int wpa_driver_ipw_set_wpa(void *priv, int enabled)
-{
- struct wpa_driver_ipw_data *drv = priv;
- int ret = 0;
-
- wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled);
-
- if (!enabled && ipw_set_wpa_ie(drv, NULL, 0) < 0)
- ret = -1;
-
- if (ipw_set_wpa_param(drv, IPW_PARAM_WPA_ENABLED, enabled) < 0)
- ret = -1;
-
- return ret;
-}
-
-
-static int wpa_driver_ipw_set_key(const char *ifname, void *priv,
- enum wpa_alg alg, const u8 *addr,
- int key_idx, int set_tx,
- const u8 *seq, size_t seq_len,
- const u8 *key, size_t key_len)
-{
- struct wpa_driver_ipw_data *drv = priv;
- struct ipw_param *param;
- u8 *buf;
- size_t blen;
- int ret = 0;
- char *alg_name;
-
- switch (alg) {
- case WPA_ALG_NONE:
- alg_name = "none";
- break;
- case WPA_ALG_WEP:
- alg_name = "WEP";
- break;
- case WPA_ALG_TKIP:
- alg_name = "TKIP";
- break;
- case WPA_ALG_CCMP:
- alg_name = "CCMP";
- break;
- default:
- return -1;
- }
-
- wpa_printf(MSG_DEBUG, "%s: alg=%s key_idx=%d set_tx=%d seq_len=%lu "
- "key_len=%lu", __FUNCTION__, alg_name, key_idx, set_tx,
- (unsigned long) seq_len, (unsigned long) key_len);
-
- if (seq_len > 8)
- return -2;
-
- blen = sizeof(*param) + key_len;
- buf = os_zalloc(blen);
- if (buf == NULL)
- return -1;
-
- param = (struct ipw_param *) buf;
- param->cmd = IPW_CMD_SET_ENCRYPTION;
- os_memset(param->sta_addr, 0xff, ETH_ALEN);
- os_strlcpy((char *) param->u.crypt.alg, alg_name,
- IPW_CRYPT_ALG_NAME_LEN);
- param->u.crypt.set_tx = set_tx ? 1 : 0;
- param->u.crypt.idx = key_idx;
- os_memcpy(param->u.crypt.seq, seq, seq_len);
- param->u.crypt.key_len = key_len;
- os_memcpy((u8 *) (param + 1), key, key_len);
-
- if (ipw_ioctl(drv, param, blen, 1)) {
- wpa_printf(MSG_WARNING, "Failed to set encryption.");
- ipw_show_set_key_error(param);
- ret = -1;
- }
- os_free(buf);
-
- return ret;
-}
-
-
-static int wpa_driver_ipw_set_countermeasures(void *priv, int enabled)
-{
- struct wpa_driver_ipw_data *drv = priv;
- wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled);
- return ipw_set_wpa_param(drv, IPW_PARAM_TKIP_COUNTERMEASURES,
- enabled);
-
-}
-
-
-static int wpa_driver_ipw_set_drop_unencrypted(void *priv, int enabled)
-{
- struct wpa_driver_ipw_data *drv = priv;
- wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled);
- return ipw_set_wpa_param(drv, IPW_PARAM_DROP_UNENCRYPTED,
- enabled);
-}
-
-
-static int wpa_driver_ipw_deauthenticate(void *priv, const u8 *addr,
- int reason_code)
-{
- struct wpa_driver_ipw_data *drv = priv;
- return ipw_mlme(drv, addr, IPW_MLME_STA_DEAUTH, reason_code);
-}
-
-
-static int wpa_driver_ipw_disassociate(void *priv, const u8 *addr,
- int reason_code)
-{
- struct wpa_driver_ipw_data *drv = priv;
- return ipw_mlme(drv, addr, IPW_MLME_STA_DISASSOC, reason_code);
-}
-
-
-static int
-wpa_driver_ipw_associate(void *priv, struct wpa_driver_associate_params *params)
-{
- struct wpa_driver_ipw_data *drv = priv;
- int ret = 0;
- int unencrypted_eapol;
-
- if (wpa_driver_ipw_set_auth_alg(drv, params->auth_alg) < 0)
- ret = -1;
- if (wpa_driver_ipw_set_drop_unencrypted(drv, params->drop_unencrypted)
- < 0)
- ret = -1;
- if (ipw_set_wpa_ie(drv, params->wpa_ie, params->wpa_ie_len) < 0)
- ret = -1;
- if (wpa_driver_wext_set_ssid(drv->wext, params->ssid,
- params->ssid_len) < 0)
- ret = -1;
- if (wpa_driver_wext_set_bssid(drv->wext, params->bssid) < 0)
- ret = -1;
-
- if (params->key_mgmt_suite == KEY_MGMT_802_1X ||
- params->key_mgmt_suite == KEY_MGMT_PSK)
- unencrypted_eapol = 0;
- else
- unencrypted_eapol = 1;
-
- if (ipw_set_wpa_param(drv, IPW_PARAM_IEEE_802_1X,
- unencrypted_eapol) < 0) {
- wpa_printf(MSG_DEBUG, "ipw: Failed to configure "
- "ieee_802_1x param");
- }
-
- return ret;
-}
-
-
-static int wpa_driver_ipw_set_auth_alg(void *priv, int auth_alg)
-{
- struct wpa_driver_ipw_data *drv = priv;
- int algs = 0;
-
- if (auth_alg & WPA_AUTH_ALG_OPEN)
- algs |= 1;
- if (auth_alg & WPA_AUTH_ALG_SHARED)
- algs |= 2;
- if (auth_alg & WPA_AUTH_ALG_LEAP)
- algs |= 4;
- if (algs == 0)
- algs = 1; /* at least one algorithm should be set */
-
- wpa_printf(MSG_DEBUG, "%s: auth_alg=0x%x", __FUNCTION__, algs);
- return ipw_set_wpa_param(drv, IPW_PARAM_AUTH_ALGS, algs);
-}
-
-
-static int wpa_driver_ipw_get_bssid(void *priv, u8 *bssid)
-{
- struct wpa_driver_ipw_data *drv = priv;
- return wpa_driver_wext_get_bssid(drv->wext, bssid);
-}
-
-
-static int wpa_driver_ipw_get_ssid(void *priv, u8 *ssid)
-{
- struct wpa_driver_ipw_data *drv = priv;
- return wpa_driver_wext_get_ssid(drv->wext, ssid);
-}
-
-
-static int wpa_driver_ipw_scan(void *priv,
- struct wpa_driver_scan_params *params)
-{
- struct wpa_driver_ipw_data *drv = priv;
- return wpa_driver_wext_scan(drv->wext, params);
-}
-
-
-static struct wpa_scan_results * wpa_driver_ipw_get_scan_results(void *priv)
-{
- struct wpa_driver_ipw_data *drv = priv;
- return wpa_driver_wext_get_scan_results(drv->wext);
-}
-
-
-static int wpa_driver_ipw_set_operstate(void *priv, int state)
-{
- struct wpa_driver_ipw_data *drv = priv;
- return wpa_driver_wext_set_operstate(drv->wext, state);
-}
-
-
-static void * wpa_driver_ipw_init(void *ctx, const char *ifname)
-{
- struct wpa_driver_ipw_data *drv;
- int ver;
-
- wpa_printf(MSG_DEBUG, "%s is called", __FUNCTION__);
- drv = os_zalloc(sizeof(*drv));
- if (drv == NULL)
- return NULL;
- drv->wext = wpa_driver_wext_init(ctx, ifname);
- if (drv->wext == NULL) {
- os_free(drv);
- return NULL;
- }
-
- ver = wpa_driver_wext_get_version(drv->wext);
- if (ver >= 18) {
- wpa_printf(MSG_WARNING, "Linux wireless extensions version %d "
- "detected.", ver);
- wpa_printf(MSG_WARNING, "ipw2x00 driver uses driver_wext "
- "(-Dwext) instead of driver_ipw.");
- }
-
- drv->ctx = ctx;
- os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
- drv->sock = socket(PF_INET, SOCK_DGRAM, 0);
- if (drv->sock < 0) {
- wpa_driver_wext_deinit(drv->wext);
- os_free(drv);
- return NULL;
- }
-
- wpa_driver_ipw_set_wpa(drv, 1);
-
- return drv;
-}
-
-
-static void wpa_driver_ipw_deinit(void *priv)
-{
- struct wpa_driver_ipw_data *drv = priv;
- wpa_driver_ipw_set_wpa(drv, 0);
- wpa_driver_wext_deinit(drv->wext);
- close(drv->sock);
- os_free(drv);
-}
-
-
-const struct wpa_driver_ops wpa_driver_ipw_ops = {
- .name = "ipw",
- .desc = "Intel ipw2100/2200 driver (old; use wext with Linux 2.6.13 "
- "or newer)",
- .get_bssid = wpa_driver_ipw_get_bssid,
- .get_ssid = wpa_driver_ipw_get_ssid,
- .set_key = wpa_driver_ipw_set_key,
- .set_countermeasures = wpa_driver_ipw_set_countermeasures,
- .scan2 = wpa_driver_ipw_scan,
- .get_scan_results2 = wpa_driver_ipw_get_scan_results,
- .deauthenticate = wpa_driver_ipw_deauthenticate,
- .disassociate = wpa_driver_ipw_disassociate,
- .associate = wpa_driver_ipw_associate,
- .init = wpa_driver_ipw_init,
- .deinit = wpa_driver_ipw_deinit,
- .set_operstate = wpa_driver_ipw_set_operstate,
-};
#define MADWIFI_NG
#endif /* IEEE80211_IOCTL_SETWMMPARAMS */
+#define WPA_KEY_RSC_LEN 8
#ifdef HOSTAPD
memset(&wk, 0, sizeof(wk));
wk.ik_type = cipher;
wk.ik_flags = IEEE80211_KEY_RECV | IEEE80211_KEY_XMIT;
- if (addr == NULL) {
+ if (addr == NULL || is_broadcast_ether_addr(addr)) {
memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN);
wk.ik_keyix = key_idx;
wk.ik_flags |= IEEE80211_KEY_DEFAULT;
os_memset(&event, 0, sizeof(event));
event.rx_probe_req.sa = mgmt->sa;
+ event.rx_probe_req.da = mgmt->da;
+ event.rx_probe_req.bssid = mgmt->bssid;
event.rx_probe_req.ie = mgmt->u.probe_req.variable;
event.rx_probe_req.ie_len =
len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req));
static int
madwifi_set_ap_wps_ie(void *priv, const struct wpabuf *beacon,
- const struct wpabuf *proberesp)
+ const struct wpabuf *proberesp,
+ const struct wpabuf *assocresp)
{
if (madwifi_set_wps_ie(priv, beacon ? wpabuf_head(beacon) : NULL,
beacon ? wpabuf_len(beacon) : 0,
#define madwifi_set_ap_wps_ie NULL
#endif /* CONFIG_WPS */
+static int madwifi_set_freq(void *priv, struct hostapd_freq_params *freq)
+{
+ struct madwifi_driver_data *drv = priv;
+ struct iwreq iwr;
+
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+ iwr.u.freq.m = freq->channel;
+ iwr.u.freq.e = 0;
+
+ if (ioctl(drv->ioctl_sock, SIOCSIWFREQ, &iwr) < 0) {
+ perror("ioctl[SIOCSIWFREQ]");
+ return -1;
+ }
+
+ return 0;
+}
+
static void
madwifi_new_sta(struct madwifi_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN])
{
ielen += 2;
no_ie:
- drv_event_assoc(hapd, addr, iebuf, ielen);
+ drv_event_assoc(hapd, addr, iebuf, ielen, 0);
if (memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) {
/* Cached accounting data is not valid anymore. */
static int
madwifi_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len,
- int encrypt, const u8 *own_addr)
+ int encrypt, const u8 *own_addr, u32 flags)
{
struct madwifi_driver_data *drv = priv;
unsigned char buf[3000];
wk.ik_keyix = key_idx;
wk.ik_keylen = key_len;
#ifdef WORDS_BIGENDIAN
-#define WPA_KEY_RSC_LEN 8
- {
+ if (seq) {
size_t i;
u8 tmp[WPA_KEY_RSC_LEN];
os_memset(tmp, 0, sizeof(tmp));
os_memcpy(&wk.ik_keyrsc, tmp, WPA_KEY_RSC_LEN);
}
#else /* WORDS_BIGENDIAN */
- os_memcpy(&wk.ik_keyrsc, seq, seq_len);
+ if (seq)
+ os_memcpy(&wk.ik_keyrsc, seq, seq_len);
#endif /* WORDS_BIGENDIAN */
os_memcpy(wk.ik_keydata, key, key_len);
.sta_clear_stats = madwifi_sta_clear_stats,
.commit = madwifi_commit,
.set_ap_wps_ie = madwifi_set_ap_wps_ie,
+ .set_freq = madwifi_set_freq,
#else /* HOSTAPD */
.get_bssid = wpa_driver_madwifi_get_bssid,
.get_ssid = wpa_driver_madwifi_get_ssid,
int res, pairwise;
u8 bssid[ETH_ALEN];
- if (addr == NULL || os_memcmp(addr, "\xff\xff\xff\xff\xff\xff",
- ETH_ALEN) == 0) {
+ if (addr == NULL || is_broadcast_ether_addr(addr)) {
/* Group Key */
pairwise = 0;
if (wpa_driver_ndis_get_bssid(drv, bssid) < 0)
{
struct wpa_driver_ndis_data *drv = priv;
u32 auth_mode, encr, priv_mode, mode;
+ u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
drv->mode = params->mode;
if (params->key_mgmt_suite == KEY_MGMT_NONE ||
params->key_mgmt_suite == KEY_MGMT_802_1X_NO_WPA) {
/* Re-set WEP keys if static WEP configuration is used. */
- u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
int i;
for (i = 0; i < 4; i++) {
if (!params->wep_key[i])
} else if (params->key_mgmt_suite == KEY_MGMT_WPS) {
auth_mode = Ndis802_11AuthModeOpen;
priv_mode = Ndis802_11PrivFilterAcceptAll;
+ if (params->wps == WPS_MODE_PRIVACY) {
+ u8 dummy_key[5] = { 0x11, 0x22, 0x33, 0x44, 0x55 };
+ /*
+ * Some NDIS drivers refuse to associate in open mode
+ * configuration due to Privacy field mismatch, so use
+ * a workaround to make the configuration look like
+ * matching one for WPS provisioning.
+ */
+ wpa_printf(MSG_DEBUG, "NDIS: Set dummy WEP key as a "
+ "workaround to allow driver to associate "
+ "for WPS");
+ wpa_driver_ndis_set_key(drv->ifname, drv, WPA_ALG_WEP,
+ bcast, 0, 1,
+ NULL, 0, dummy_key,
+ sizeof(dummy_key));
+ }
#endif /* CONFIG_WPS */
} else {
priv_mode = Ndis802_11PrivFilter8021xWEP;
encr = Ndis802_11Encryption1Enabled;
break;
case CIPHER_NONE:
+#ifdef CONFIG_WPS
+ if (params->wps == WPS_MODE_PRIVACY) {
+ encr = Ndis802_11Encryption1Enabled;
+ break;
+ }
+#endif /* CONFIG_WPS */
if (params->group_suite == CIPHER_CCMP)
encr = Ndis802_11Encryption3Enabled;
else if (params->group_suite == CIPHER_TKIP)
encr = Ndis802_11EncryptionDisabled;
break;
default:
+#ifdef CONFIG_WPS
+ if (params->wps == WPS_MODE_PRIVACY) {
+ encr = Ndis802_11Encryption1Enabled;
+ break;
+ }
+#endif /* CONFIG_WPS */
encr = Ndis802_11EncryptionDisabled;
+ break;
};
if (ndis_set_oid(drv, OID_802_11_PRIVACY_FILTER,
}
-const struct wpa_driver_ops wpa_driver_ndis_ops = {
- "ndis",
- "Windows NDIS driver",
- wpa_driver_ndis_get_bssid,
- wpa_driver_ndis_get_ssid,
- wpa_driver_ndis_set_key,
- wpa_driver_ndis_init,
- wpa_driver_ndis_deinit,
- NULL /* set_param */,
- NULL /* set_countermeasures */,
- wpa_driver_ndis_deauthenticate,
- wpa_driver_ndis_disassociate,
- wpa_driver_ndis_associate,
- wpa_driver_ndis_add_pmkid,
- wpa_driver_ndis_remove_pmkid,
- wpa_driver_ndis_flush_pmkid,
- wpa_driver_ndis_get_capa,
- wpa_driver_ndis_poll,
- wpa_driver_ndis_get_ifname,
- wpa_driver_ndis_get_mac_addr,
- NULL /* send_eapol */,
- NULL /* set_operstate */,
- NULL /* mlme_setprotection */,
- NULL /* get_hw_feature_data */,
- NULL /* set_channel */,
- NULL /* set_ssid */,
- NULL /* set_bssid */,
- NULL /* send_mlme */,
- NULL /* mlme_add_sta */,
- NULL /* mlme_remove_sta */,
- NULL /* update_ft_ies */,
- NULL /* send_ft_action */,
- wpa_driver_ndis_get_scan_results,
- NULL /* set_country */,
- NULL /* global_init */,
- NULL /* global_deinit */,
- NULL /* init2 */,
- wpa_driver_ndis_get_interfaces,
- wpa_driver_ndis_scan,
- NULL /* authenticate */,
- NULL /* set_beacon */,
- NULL /* hapd_init */,
- NULL /* hapd_deinit */,
- NULL /* set_ieee8021x */,
- NULL /* set_privacy */,
- NULL /* get_seqnum */,
- NULL /* flush */,
- NULL /* set_generic_elem */,
- NULL /* read_sta_data */,
- NULL /* hapd_send_eapol */,
- NULL /* sta_deauth */,
- NULL /* sta_disassoc */,
- NULL /* sta_remove */,
- NULL /* hapd_get_ssid */,
- NULL /* hapd_set_ssid */,
- NULL /* hapd_set_countermeasures */,
- NULL /* sta_add */,
- NULL /* get_inact_sec */,
- NULL /* sta_clear_stats */,
- NULL /* set_freq */,
- NULL /* set_rts */,
- NULL /* set_frag */,
- NULL /* sta_set_flags */,
- NULL /* set_rate_sets */,
- NULL /* set_cts_protect */,
- NULL /* set_preamble */,
- NULL /* set_short_slot_time */,
- NULL /* set_tx_queue_params */,
- NULL /* valid_bss_mask */,
- NULL /* if_add */,
- NULL /* if_remove */,
- NULL /* set_sta_vlan */,
- NULL /* commit */,
- NULL /* send_ether */,
- NULL /* set_radius_acl_auth */,
- NULL /* set_radius_acl_expire */,
- NULL /* set_ht_params */,
- NULL /* set_ap_wps_ie */,
- NULL /* set_supp_port */,
- NULL /* set_wds_sta */,
- NULL /* send_action */,
- NULL /* remain_on_channel */,
- NULL /* cancel_remain_on_channel */,
- NULL /* probe_req_report */,
- NULL /* disable_11b_rates */,
- NULL /* deinit_ap */,
- NULL /* suspend */,
- NULL /* resume */,
- NULL /* signal_monitor */,
- NULL /* send_frame */
-};
+static const char *ndis_drv_name = "ndis";
+static const char *ndis_drv_desc = "Windows NDIS driver";
+
+struct wpa_driver_ops wpa_driver_ndis_ops;
+
+void driver_ndis_init_ops(void)
+{
+ os_memset(&wpa_driver_ndis_ops, 0, sizeof(wpa_driver_ndis_ops));
+ wpa_driver_ndis_ops.name = ndis_drv_name;
+ wpa_driver_ndis_ops.desc = ndis_drv_desc;
+ wpa_driver_ndis_ops.get_bssid = wpa_driver_ndis_get_bssid;
+ wpa_driver_ndis_ops.get_ssid = wpa_driver_ndis_get_ssid;
+ wpa_driver_ndis_ops.set_key = wpa_driver_ndis_set_key;
+ wpa_driver_ndis_ops.init = wpa_driver_ndis_init;
+ wpa_driver_ndis_ops.deinit = wpa_driver_ndis_deinit;
+ wpa_driver_ndis_ops.deauthenticate = wpa_driver_ndis_deauthenticate;
+ wpa_driver_ndis_ops.disassociate = wpa_driver_ndis_disassociate;
+ wpa_driver_ndis_ops.associate = wpa_driver_ndis_associate;
+ wpa_driver_ndis_ops.add_pmkid = wpa_driver_ndis_add_pmkid;
+ wpa_driver_ndis_ops.remove_pmkid = wpa_driver_ndis_remove_pmkid;
+ wpa_driver_ndis_ops.flush_pmkid = wpa_driver_ndis_flush_pmkid;
+ wpa_driver_ndis_ops.get_capa = wpa_driver_ndis_get_capa;
+ wpa_driver_ndis_ops.poll = wpa_driver_ndis_poll;
+ wpa_driver_ndis_ops.get_ifname = wpa_driver_ndis_get_ifname;
+ wpa_driver_ndis_ops.get_mac_addr = wpa_driver_ndis_get_mac_addr;
+ wpa_driver_ndis_ops.get_scan_results2 =
+ wpa_driver_ndis_get_scan_results;
+ wpa_driver_ndis_ops.get_interfaces = wpa_driver_ndis_get_interfaces;
+ wpa_driver_ndis_ops.scan2 = wpa_driver_ndis_scan;
+}
+++ /dev/null
-/*
- * WPA Supplicant - driver interaction with Linux ndiswrapper
- * Copyright (c) 2004-2006, Giridhar Pemmasani <giri@lmc.cs.sunysb.edu>
- * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
- *
- * Please note that ndiswrapper supports WPA configuration via Linux wireless
- * extensions and if the kernel includes support for this, driver_wext.c should
- * be used instead of this driver wrapper.
- */
-
-#include "includes.h"
-#include <sys/ioctl.h>
-
-#include "wireless_copy.h"
-#include "common.h"
-#include "driver.h"
-#include "driver_wext.h"
-
-struct wpa_driver_ndiswrapper_data {
- void *wext; /* private data for driver_wext */
- void *ctx;
- char ifname[IFNAMSIZ + 1];
- int sock;
-};
-
-
-struct wpa_key {
- enum wpa_alg alg;
- const u8 *addr;
- int key_index;
- int set_tx;
- const u8 *seq;
- size_t seq_len;
- const u8 *key;
- size_t key_len;
-};
-
-struct wpa_assoc_info {
- const u8 *bssid;
- const u8 *ssid;
- size_t ssid_len;
- int freq;
- const u8 *wpa_ie;
- size_t wpa_ie_len;
- enum wpa_cipher pairwise_suite;
- enum wpa_cipher group_suite;
- enum wpa_key_mgmt key_mgmt_suite;
- int auth_alg;
- int mode;
-};
-
-#define PRIV_RESET SIOCIWFIRSTPRIV+0
-#define WPA_SET_WPA SIOCIWFIRSTPRIV+1
-#define WPA_SET_KEY SIOCIWFIRSTPRIV+2
-#define WPA_ASSOCIATE SIOCIWFIRSTPRIV+3
-#define WPA_DISASSOCIATE SIOCIWFIRSTPRIV+4
-#define WPA_DROP_UNENCRYPTED SIOCIWFIRSTPRIV+5
-#define WPA_SET_COUNTERMEASURES SIOCIWFIRSTPRIV+6
-#define WPA_DEAUTHENTICATE SIOCIWFIRSTPRIV+7
-#define WPA_SET_AUTH_ALG SIOCIWFIRSTPRIV+8
-#define WPA_INIT SIOCIWFIRSTPRIV+9
-#define WPA_DEINIT SIOCIWFIRSTPRIV+10
-#define WPA_GET_CAPA SIOCIWFIRSTPRIV+11
-
-static int wpa_ndiswrapper_set_auth_alg(void *priv, int auth_alg);
-
-static int get_socket(void)
-{
- static const int families[] = {
- AF_INET, AF_IPX, AF_AX25, AF_APPLETALK
- };
- unsigned int i;
- int sock;
-
- for (i = 0; i < sizeof(families) / sizeof(int); ++i) {
- sock = socket(families[i], SOCK_DGRAM, 0);
- if (sock >= 0)
- return sock;
- }
-
- return -1;
-}
-
-static int iw_set_ext(struct wpa_driver_ndiswrapper_data *drv, int request,
- struct iwreq *pwrq)
-{
- os_strlcpy(pwrq->ifr_name, drv->ifname, IFNAMSIZ);
- return ioctl(drv->sock, request, pwrq);
-}
-
-static int wpa_ndiswrapper_set_wpa(void *priv, int enabled)
-{
- struct wpa_driver_ndiswrapper_data *drv = priv;
- struct iwreq priv_req;
- int ret = 0;
-
- os_memset(&priv_req, 0, sizeof(priv_req));
-
- priv_req.u.data.flags = enabled;
- if (iw_set_ext(drv, WPA_SET_WPA, &priv_req) < 0)
- ret = -1;
- return ret;
-}
-
-static int wpa_ndiswrapper_set_key(const char *ifname, void *priv,
- enum wpa_alg alg, const u8 *addr,
- int key_idx, int set_tx,
- const u8 *seq, size_t seq_len,
- const u8 *key, size_t key_len)
-{
- struct wpa_driver_ndiswrapper_data *drv = priv;
- struct wpa_key wpa_key;
- int ret = 0;
- struct iwreq priv_req;
-
- os_memset(&priv_req, 0, sizeof(priv_req));
-
- wpa_key.alg = alg;
- wpa_key.addr = addr;
- wpa_key.key_index = key_idx;
- wpa_key.set_tx = set_tx;
- wpa_key.seq = seq;
- wpa_key.seq_len = seq_len;
- wpa_key.key = key;
- wpa_key.key_len = key_len;
-
- priv_req.u.data.pointer = (void *)&wpa_key;
- priv_req.u.data.length = sizeof(wpa_key);
-
- if (iw_set_ext(drv, WPA_SET_KEY, &priv_req) < 0)
- ret = -1;
-
- if (alg == WPA_ALG_NONE) {
- /*
- * ndiswrapper did not seem to be clearing keys properly in
- * some cases with WPA_SET_KEY. For example, roaming from WPA
- * enabled AP to plaintext one seemed to fail since the driver
- * did not associate. Try to make sure the keys are cleared so
- * that plaintext APs can be used in all cases.
- */
- wpa_driver_wext_set_key(ifname, drv->wext, alg, addr, key_idx,
- set_tx, seq, seq_len, key, key_len);
- }
-
- return ret;
-}
-
-static int wpa_ndiswrapper_set_countermeasures(void *priv, int enabled)
-{
- struct wpa_driver_ndiswrapper_data *drv = priv;
- int ret = 0;
- struct iwreq priv_req;
-
- os_memset(&priv_req, 0, sizeof(priv_req));
-
- priv_req.u.param.value = enabled;
- if (iw_set_ext(drv, WPA_SET_COUNTERMEASURES, &priv_req) < 0)
- ret = -1;
-
- return ret;
-}
-
-static int wpa_ndiswrapper_set_drop_unencrypted(void *priv,
- int enabled)
-{
- struct wpa_driver_ndiswrapper_data *drv = priv;
- int ret = 0;
- struct iwreq priv_req;
-
- os_memset(&priv_req, 0, sizeof(priv_req));
-
- priv_req.u.param.value = enabled;
- if (iw_set_ext(drv, WPA_DROP_UNENCRYPTED, &priv_req) < 0)
- ret = -1;
- return ret;
-}
-
-static int wpa_ndiswrapper_deauthenticate(void *priv, const u8 *addr,
- int reason_code)
-{
- struct wpa_driver_ndiswrapper_data *drv = priv;
- int ret = 0;
- struct iwreq priv_req;
-
- os_memset(&priv_req, 0, sizeof(priv_req));
-
- priv_req.u.param.value = reason_code;
- os_memcpy(&priv_req.u.ap_addr.sa_data, addr, ETH_ALEN);
- if (iw_set_ext(drv, WPA_DEAUTHENTICATE, &priv_req) < 0)
- ret = -1;
- return ret;
-}
-
-static int wpa_ndiswrapper_disassociate(void *priv, const u8 *addr,
- int reason_code)
-{
- struct wpa_driver_ndiswrapper_data *drv = priv;
- int ret = 0;
- struct iwreq priv_req;
-
- os_memset(&priv_req, 0, sizeof(priv_req));
-
- os_memcpy(&priv_req.u.ap_addr.sa_data, addr, ETH_ALEN);
- if (iw_set_ext(drv, WPA_DISASSOCIATE, &priv_req) < 0)
- ret = -1;
- return ret;
-}
-
-static int
-wpa_ndiswrapper_associate(void *priv,
- struct wpa_driver_associate_params *params)
-{
- struct wpa_driver_ndiswrapper_data *drv = priv;
- int ret = 0;
- struct wpa_assoc_info wpa_assoc_info;
- struct iwreq priv_req;
-
- if (wpa_ndiswrapper_set_drop_unencrypted(drv,
- params->drop_unencrypted) < 0)
- ret = -1;
- if (wpa_ndiswrapper_set_auth_alg(drv, params->auth_alg) < 0)
- ret = -1;
-
- os_memset(&priv_req, 0, sizeof(priv_req));
- os_memset(&wpa_assoc_info, 0, sizeof(wpa_assoc_info));
-
- wpa_assoc_info.bssid = params->bssid;
- wpa_assoc_info.ssid = params->ssid;
- wpa_assoc_info.ssid_len = params->ssid_len;
- wpa_assoc_info.freq = params->freq;
- wpa_assoc_info.wpa_ie = params->wpa_ie;
- wpa_assoc_info.wpa_ie_len = params->wpa_ie_len;
- wpa_assoc_info.pairwise_suite = params->pairwise_suite;
- wpa_assoc_info.group_suite = params->group_suite;
- wpa_assoc_info.key_mgmt_suite = params->key_mgmt_suite;
- wpa_assoc_info.auth_alg = params->auth_alg;
- wpa_assoc_info.mode = params->mode;
-
- priv_req.u.data.pointer = (void *)&wpa_assoc_info;
- priv_req.u.data.length = sizeof(wpa_assoc_info);
-
- if (iw_set_ext(drv, WPA_ASSOCIATE, &priv_req) < 0)
- ret = -1;
- return ret;
-}
-
-static int wpa_ndiswrapper_set_auth_alg(void *priv, int auth_alg)
-{
- struct wpa_driver_ndiswrapper_data *drv = priv;
- int ret = 0;
- struct iwreq priv_req;
-
- os_memset(&priv_req, 0, sizeof(priv_req));
-
- priv_req.u.param.value = auth_alg;
- if (iw_set_ext(drv, WPA_SET_AUTH_ALG, &priv_req) < 0)
- ret = -1;
- return ret;
-}
-
-static int wpa_ndiswrapper_get_bssid(void *priv, u8 *bssid)
-{
- struct wpa_driver_ndiswrapper_data *drv = priv;
- return wpa_driver_wext_get_bssid(drv->wext, bssid);
-}
-
-
-static int wpa_ndiswrapper_get_ssid(void *priv, u8 *ssid)
-{
- struct wpa_driver_ndiswrapper_data *drv = priv;
- return wpa_driver_wext_get_ssid(drv->wext, ssid);
-}
-
-
-static int wpa_ndiswrapper_scan(void *priv,
- struct wpa_driver_scan_params *params)
-{
- struct wpa_driver_ndiswrapper_data *drv = priv;
- return wpa_driver_wext_scan(drv->wext, params);
-}
-
-
-static struct wpa_scan_results * wpa_ndiswrapper_get_scan_results(void *priv)
-{
- struct wpa_driver_ndiswrapper_data *drv = priv;
- return wpa_driver_wext_get_scan_results(drv->wext);
-}
-
-
-static int wpa_ndiswrapper_get_capa(void *priv, struct wpa_driver_capa *capa)
-{
- struct wpa_driver_ndiswrapper_data *drv = priv;
- int ret = 0;
- struct iwreq priv_req;
-
- os_memset(&priv_req, 0, sizeof(priv_req));
-
- priv_req.u.data.pointer = (void *) capa;
- priv_req.u.data.length = sizeof(*capa);
- if (iw_set_ext(drv, WPA_GET_CAPA, &priv_req) < 0)
- ret = -1;
- return ret;
-
-}
-
-
-static int wpa_ndiswrapper_set_operstate(void *priv, int state)
-{
- struct wpa_driver_ndiswrapper_data *drv = priv;
- return wpa_driver_wext_set_operstate(drv->wext, state);
-}
-
-
-static void * wpa_ndiswrapper_init(void *ctx, const char *ifname)
-{
- struct wpa_driver_ndiswrapper_data *drv;
-
- drv = os_zalloc(sizeof(*drv));
- if (drv == NULL)
- return NULL;
- drv->wext = wpa_driver_wext_init(ctx, ifname);
- if (drv->wext == NULL) {
- os_free(drv);
- return NULL;
- }
-
- drv->ctx = ctx;
- os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
- drv->sock = get_socket();
- if (drv->sock < 0) {
- wpa_driver_wext_deinit(drv->wext);
- os_free(drv);
- return NULL;
- }
-
- wpa_ndiswrapper_set_wpa(drv, 1);
-
- return drv;
-}
-
-
-static void wpa_ndiswrapper_deinit(void *priv)
-{
- struct wpa_driver_ndiswrapper_data *drv = priv;
- wpa_ndiswrapper_set_wpa(drv, 0);
- wpa_driver_wext_deinit(drv->wext);
- close(drv->sock);
- os_free(drv);
-}
-
-
-const struct wpa_driver_ops wpa_driver_ndiswrapper_ops = {
- .name = "ndiswrapper",
- .desc = "Linux ndiswrapper (deprecated; use wext)",
- .set_key = wpa_ndiswrapper_set_key,
- .set_countermeasures = wpa_ndiswrapper_set_countermeasures,
- .deauthenticate = wpa_ndiswrapper_deauthenticate,
- .disassociate = wpa_ndiswrapper_disassociate,
- .associate = wpa_ndiswrapper_associate,
-
- .get_bssid = wpa_ndiswrapper_get_bssid,
- .get_ssid = wpa_ndiswrapper_get_ssid,
- .scan2 = wpa_ndiswrapper_scan,
- .get_scan_results2 = wpa_ndiswrapper_get_scan_results,
- .init = wpa_ndiswrapper_init,
- .deinit = wpa_ndiswrapper_deinit,
- .get_capa = wpa_ndiswrapper_get_capa,
- .set_operstate = wpa_ndiswrapper_set_operstate,
-};
#include "includes.h"
#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
#include <net/if.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/family.h>
#include <netlink/genl/ctrl.h>
+#include <linux/rtnetlink.h>
#include <netpacket/packet.h>
#include <linux/filter.h>
#include "nl80211_copy.h"
#include "common.h"
#include "eloop.h"
+#include "utils/list.h"
#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "l2_packet/l2_packet.h"
#include "netlink.h"
#include "linux_ioctl.h"
#include "radiotap.h"
#include "radiotap_iter.h"
+#include "rfkill.h"
#include "driver.h"
#ifdef CONFIG_LIBNL20
/* libnl 2.0 compatibility code */
#define nl_handle nl_sock
-#define nl_handle_alloc_cb nl_socket_alloc_cb
-#define nl_handle_destroy nl_socket_free
+#define nl80211_handle_alloc nl_socket_alloc_cb
+#define nl80211_handle_destroy nl_socket_free
+#else
+/*
+ * libnl 1.1 has a bug, it tries to allocate socket numbers densely
+ * but when you free a socket again it will mess up its bitmap and
+ * and use the wrong number the next time it needs a socket ID.
+ * Therefore, we wrap the handle alloc/destroy and add our own pid
+ * accounting.
+ */
+static uint32_t port_bitmap[32] = { 0 };
+
+static struct nl_handle *nl80211_handle_alloc(void *cb)
+{
+ struct nl_handle *handle;
+ uint32_t pid = getpid() & 0x3FFFFF;
+ int i;
+
+ handle = nl_handle_alloc_cb(cb);
+
+ for (i = 0; i < 1024; i++) {
+ if (port_bitmap[i / 32] & (1 << (i % 32)))
+ continue;
+ port_bitmap[i / 32] |= 1 << (i % 32);
+ pid += i << 22;
+ break;
+ }
+
+ nl_socket_set_local_port(handle, pid);
+
+ return handle;
+}
+
+static void nl80211_handle_destroy(struct nl_handle *handle)
+{
+ uint32_t port = nl_socket_get_local_port(handle);
+
+ port >>= 22;
+ port_bitmap[port / 32] &= ~(1 << (port % 32));
+
+ nl_handle_destroy(handle);
+}
+
+static inline int __genl_ctrl_alloc_cache(struct nl_handle *h,
+ struct nl_cache **cache)
+{
+ struct nl_cache *tmp = genl_ctrl_alloc_cache(h);
+ if (!tmp)
+ return -ENOMEM;
+ *cache = tmp;
+ return 0;
+}
+#define genl_ctrl_alloc_cache __genl_ctrl_alloc_cache
#endif /* CONFIG_LIBNL20 */
+struct nl80211_handles {
+ struct nl_handle *handle;
+ struct nl_cache *cache;
+};
+
+
+static int nl_create_handles(struct nl80211_handles *handles, struct nl_cb *cb,
+ const char *dbg)
+{
+ if (!handles)
+ return -1;
+
+ handles->handle = nl80211_handle_alloc(cb);
+ if (handles->handle == NULL) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink "
+ "callbacks (%s)", dbg);
+ return -1;
+ }
+
+ if (genl_connect(handles->handle)) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to connect to generic "
+ "netlink (%s)", dbg);
+ goto err;
+ }
+
+ if (genl_ctrl_alloc_cache(handles->handle, &handles->cache) < 0) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic "
+ "netlink cache (%s)", dbg);
+ goto err;
+ }
+
+ return 0;
+err:
+ nl80211_handle_destroy(handles->handle);
+ return -1;
+}
+
+
+static void nl_destroy_handles(struct nl80211_handles *handles)
+{
+ if (handles->handle == NULL)
+ return;
+ nl_cache_free(handles->cache);
+ nl80211_handle_destroy(handles->handle);
+ handles->handle = NULL;
+}
+
+
#ifndef IFF_LOWER_UP
#define IFF_LOWER_UP 0x10000 /* driver signals L1 up */
#endif
#define IF_OPER_UP 6
#endif
+struct nl80211_global {
+ struct dl_list interfaces;
+ int if_add_ifindex;
+ struct netlink_data *netlink;
+ struct nl_cb *nl_cb;
+ struct nl80211_handles nl;
+ struct genl_family *nl80211;
+ int ioctl_sock; /* socket for ioctl() use */
+};
+
+static void nl80211_global_deinit(void *priv);
+static void wpa_driver_nl80211_deinit(void *priv);
+
struct i802_bss {
struct wpa_driver_nl80211_data *drv;
struct i802_bss *next;
int ifindex;
char ifname[IFNAMSIZ + 1];
+ char brname[IFNAMSIZ];
unsigned int beacon_set:1;
+ unsigned int added_if_into_bridge:1;
+ unsigned int added_bridge:1;
};
struct wpa_driver_nl80211_data {
+ struct nl80211_global *global;
+ struct dl_list list;
+ u8 addr[ETH_ALEN];
+ char phyname[32];
void *ctx;
- struct netlink_data *netlink;
- int ioctl_sock; /* socket for ioctl() use */
- char brname[IFNAMSIZ];
int ifindex;
int if_removed;
+ int if_disabled;
+ int ignore_if_down_event;
+ struct rfkill_data *rfkill;
struct wpa_driver_capa capa;
int has_capability;
int scan_complete_events;
- struct nl_handle *nl_handle;
- struct nl_handle *nl_handle_event;
- struct nl_cache *nl_cache;
- struct nl_cache *nl_cache_event;
- struct nl_cb *nl_cb;
- struct genl_family *nl80211;
+ struct nl80211_handles nl_event, nl_preq;
u8 auth_bssid[ETH_ALEN];
u8 bssid[ETH_ALEN];
int associated;
u8 ssid[32];
size_t ssid_len;
- int nlmode;
- int ap_scan_as_station;
+ enum nl80211_iftype nlmode;
+ enum nl80211_iftype ap_scan_as_station;
unsigned int assoc_freq;
int monitor_sock;
int monitor_ifidx;
- int probe_req_report;
- int disable_11b_rates;
+ int no_monitor_iface_capab;
+ unsigned int disabled_11b_rates:1;
unsigned int pending_remain_on_chan:1;
- unsigned int added_bridge:1;
- unsigned int added_if_into_bridge:1;
+ unsigned int in_interface_list:1;
u64 remain_on_chan_cookie;
u64 send_action_cookie;
+ unsigned int last_mgmt_freq;
+ unsigned int ap_oper_freq;
+
struct wpa_driver_scan_filter *filter_ssids;
size_t num_filter_ssids;
struct i802_bss first_bss;
+#ifdef CONFIG_AP
+ struct l2_packet_data *l2;
+#endif /* CONFIG_AP */
+
#ifdef HOSTAPD
int eapol_sock; /* socket for EAPOL frames */
static void wpa_driver_nl80211_scan_timeout(void *eloop_ctx,
void *timeout_ctx);
-static int wpa_driver_nl80211_set_mode(void *priv, int mode);
+static int wpa_driver_nl80211_set_mode(struct i802_bss *bss,
+ enum nl80211_iftype nlmode);
static int
wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv);
static int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv,
int local_state_change);
static void nl80211_remove_monitor_interface(
struct wpa_driver_nl80211_data *drv);
+static int nl80211_send_frame_cmd(struct wpa_driver_nl80211_data *drv,
+ unsigned int freq, unsigned int wait,
+ const u8 *buf, size_t buf_len, u64 *cookie,
+ int no_cck);
+static int wpa_driver_nl80211_probe_req_report(void *priv, int report);
#ifdef HOSTAPD
static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx);
enum wpa_driver_if_type type,
const char *ifname);
#else /* HOSTAPD */
-static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
+static inline void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
+{
+}
+
+static inline void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
+{
+}
+
+static inline int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
{
return 0;
}
#endif /* HOSTAPD */
static int i802_set_freq(void *priv, struct hostapd_freq_params *freq);
-static void wpa_driver_nl80211_probe_req_report_timeout(void *eloop_ctx,
- void *timeout_ctx);
static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv,
int ifindex, int disabled);
+static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv);
+
+
+static int is_ap_interface(enum nl80211_iftype nlmode)
+{
+ return (nlmode == NL80211_IFTYPE_AP ||
+ nlmode == NL80211_IFTYPE_P2P_GO);
+}
+
+
+static int is_sta_interface(enum nl80211_iftype nlmode)
+{
+ return (nlmode == NL80211_IFTYPE_STATION ||
+ nlmode == NL80211_IFTYPE_P2P_CLIENT);
+}
+
+
+static int is_p2p_interface(enum nl80211_iftype nlmode)
+{
+ return (nlmode == NL80211_IFTYPE_P2P_CLIENT ||
+ nlmode == NL80211_IFTYPE_P2P_GO);
+}
+
+
+struct nl80211_bss_info_arg {
+ struct wpa_driver_nl80211_data *drv;
+ struct wpa_scan_results *res;
+ unsigned int assoc_freq;
+ u8 assoc_bssid[ETH_ALEN];
+};
+
+static int bss_info_handler(struct nl_msg *msg, void *arg);
+
/* nl80211 code */
static int ack_handler(struct nl_msg *msg, void *arg)
struct nl_cb *cb;
int err = -ENOMEM;
- cb = nl_cb_clone(drv->nl_cb);
+ cb = nl_cb_clone(drv->global->nl_cb);
if (!cb)
goto out;
int (*valid_handler)(struct nl_msg *, void *),
void *valid_data)
{
- return send_and_recv(drv, drv->nl_handle, msg, valid_handler,
+ return send_and_recv(drv, drv->global->nl.handle, msg, valid_handler,
valid_data);
}
msg = nlmsg_alloc();
if (!msg)
return -ENOMEM;
- genlmsg_put(msg, 0, 0, genl_ctrl_resolve(drv->nl_handle, "nlctrl"),
+ genlmsg_put(msg, 0, 0,
+ genl_ctrl_resolve(drv->global->nl.handle, "nlctrl"),
0, 0, CTRL_CMD_GETFAMILY, 0);
NLA_PUT_STRING(msg, CTRL_ATTR_FAMILY_NAME, family);
}
+static void * nl80211_cmd(struct wpa_driver_nl80211_data *drv,
+ struct nl_msg *msg, int flags, uint8_t cmd)
+{
+ return genlmsg_put(msg, 0, 0, genl_family_get_id(drv->global->nl80211),
+ 0, flags, cmd, 0);
+}
+
+
static int wpa_driver_nl80211_get_bssid(void *priv, u8 *bssid)
{
struct i802_bss *bss = priv;
del ? "removed" : "added");
if (os_strcmp(drv->first_bss.ifname, event.interface_status.ifname) == 0) {
- if (del)
+ if (del) {
+ if (drv->if_removed) {
+ wpa_printf(MSG_DEBUG, "nl80211: if_removed "
+ "already set - ignore event");
+ return;
+ }
drv->if_removed = 1;
- else
+ } else {
+ if (if_nametoindex(drv->first_bss.ifname) == 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: Interface %s "
+ "does not exist - ignore "
+ "RTM_NEWLINK",
+ drv->first_bss.ifname);
+ return;
+ }
+ if (!drv->if_removed) {
+ wpa_printf(MSG_DEBUG, "nl80211: if_removed "
+ "already cleared - ignore event");
+ return;
+ }
drv->if_removed = 0;
+ }
}
wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
}
+static struct wpa_driver_nl80211_data *
+nl80211_find_drv(struct nl80211_global *global, int idx, u8 *buf, size_t len)
+{
+ struct wpa_driver_nl80211_data *drv;
+ dl_list_for_each(drv, &global->interfaces,
+ struct wpa_driver_nl80211_data, list) {
+ if (wpa_driver_nl80211_own_ifindex(drv, idx, buf, len) ||
+ have_ifidx(drv, idx))
+ return drv;
+ }
+ return NULL;
+}
+
+
static void wpa_driver_nl80211_event_rtm_newlink(void *ctx,
struct ifinfomsg *ifi,
u8 *buf, size_t len)
{
- struct wpa_driver_nl80211_data *drv = ctx;
+ struct nl80211_global *global = ctx;
+ struct wpa_driver_nl80211_data *drv;
int attrlen, rta_len;
struct rtattr *attr;
u32 brid = 0;
+ char namebuf[IFNAMSIZ];
- if (!wpa_driver_nl80211_own_ifindex(drv, ifi->ifi_index, buf, len) &&
- !have_ifidx(drv, ifi->ifi_index)) {
+ drv = nl80211_find_drv(global, ifi->ifi_index, buf, len);
+ if (!drv) {
wpa_printf(MSG_DEBUG, "nl80211: Ignore event for foreign "
"ifindex %d", ifi->ifi_index);
return;
(ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "",
(ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "",
(ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
+
+ if (!drv->if_disabled && !(ifi->ifi_flags & IFF_UP)) {
+ if (if_indextoname(ifi->ifi_index, namebuf) &&
+ linux_iface_up(drv->global->ioctl_sock,
+ drv->first_bss.ifname) > 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: Ignore interface down "
+ "event since interface %s is up", namebuf);
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "nl80211: Interface down");
+ if (drv->ignore_if_down_event) {
+ wpa_printf(MSG_DEBUG, "nl80211: Ignore interface down "
+ "event generated by mode change");
+ drv->ignore_if_down_event = 0;
+ } else {
+ drv->if_disabled = 1;
+ wpa_supplicant_event(drv->ctx,
+ EVENT_INTERFACE_DISABLED, NULL);
+ }
+ }
+
+ if (drv->if_disabled && (ifi->ifi_flags & IFF_UP)) {
+ if (if_indextoname(ifi->ifi_index, namebuf) &&
+ linux_iface_up(drv->global->ioctl_sock,
+ drv->first_bss.ifname) == 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up "
+ "event since interface %s is down",
+ namebuf);
+ } else if (if_nametoindex(drv->first_bss.ifname) == 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up "
+ "event since interface %s does not exist",
+ drv->first_bss.ifname);
+ } else if (drv->if_removed) {
+ wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up "
+ "event since interface %s is marked "
+ "removed", drv->first_bss.ifname);
+ } else {
+ wpa_printf(MSG_DEBUG, "nl80211: Interface up");
+ drv->if_disabled = 0;
+ wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED,
+ NULL);
+ }
+ }
+
/*
* Some drivers send the association event before the operup event--in
* this case, lifting operstate in wpa_driver_nl80211_set_operstate()
if (drv->operstate == 1 &&
(ifi->ifi_flags & (IFF_LOWER_UP | IFF_DORMANT)) == IFF_LOWER_UP &&
!(ifi->ifi_flags & IFF_RUNNING))
- netlink_send_oper_ifla(drv->netlink, drv->ifindex,
+ netlink_send_oper_ifla(drv->global->netlink, drv->ifindex,
-1, IF_OPER_UP);
attrlen = len;
attr = RTA_NEXT(attr, attrlen);
}
-#ifdef HOSTAPD
if (ifi->ifi_family == AF_BRIDGE && brid) {
/* device has been added to bridge */
- char namebuf[IFNAMSIZ];
if_indextoname(brid, namebuf);
wpa_printf(MSG_DEBUG, "nl80211: Add ifindex %u for bridge %s",
brid, namebuf);
add_ifidx(drv, brid);
}
-#endif /* HOSTAPD */
}
struct ifinfomsg *ifi,
u8 *buf, size_t len)
{
- struct wpa_driver_nl80211_data *drv = ctx;
+ struct nl80211_global *global = ctx;
+ struct wpa_driver_nl80211_data *drv;
int attrlen, rta_len;
struct rtattr *attr;
u32 brid = 0;
+ drv = nl80211_find_drv(global, ifi->ifi_index, buf, len);
+ if (!drv) {
+ wpa_printf(MSG_DEBUG, "nl80211: Ignore dellink event for "
+ "foreign ifindex %d", ifi->ifi_index);
+ return;
+ }
+
attrlen = len;
attr = (struct rtattr *) buf;
attr = RTA_NEXT(attr, attrlen);
}
-#ifdef HOSTAPD
if (ifi->ifi_family == AF_BRIDGE && brid) {
/* device has been removed from bridge */
char namebuf[IFNAMSIZ];
"%s", brid, namebuf);
del_ifidx(drv, brid);
}
-#endif /* HOSTAPD */
}
}
+static unsigned int nl80211_get_assoc_freq(struct wpa_driver_nl80211_data *drv)
+{
+ struct nl_msg *msg;
+ int ret;
+ struct nl80211_bss_info_arg arg;
+
+ os_memset(&arg, 0, sizeof(arg));
+ msg = nlmsg_alloc();
+ if (!msg)
+ goto nla_put_failure;
+
+ nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SCAN);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+
+ arg.drv = drv;
+ ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg);
+ msg = NULL;
+ if (ret == 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: Operating frequency for the "
+ "associated BSS from scan results: %u MHz",
+ arg.assoc_freq);
+ return arg.assoc_freq ? arg.assoc_freq : drv->assoc_freq;
+ }
+ wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d "
+ "(%s)", ret, strerror(-ret));
+nla_put_failure:
+ nlmsg_free(msg);
+ return drv->assoc_freq;
+}
+
+
static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv,
const u8 *frame, size_t len)
{
status = le_to_host16(mgmt->u.assoc_resp.status_code);
if (status != WLAN_STATUS_SUCCESS) {
os_memset(&event, 0, sizeof(event));
+ event.assoc_reject.bssid = mgmt->bssid;
if (len > 24 + sizeof(mgmt->u.assoc_resp)) {
event.assoc_reject.resp_ies =
(u8 *) mgmt->u.assoc_resp.variable;
os_memset(&event, 0, sizeof(event));
if (cmd == NL80211_CMD_CONNECT &&
nla_get_u16(status) != WLAN_STATUS_SUCCESS) {
+ if (addr)
+ event.assoc_reject.bssid = nla_data(addr);
if (resp_ie) {
event.assoc_reject.resp_ies = nla_data(resp_ie);
event.assoc_reject.resp_ies_len = nla_len(resp_ie);
event.assoc_info.resp_ies_len = nla_len(resp_ie);
}
+ event.assoc_info.freq = nl80211_get_assoc_freq(drv);
+
wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
}
+static void mlme_event_disconnect(struct wpa_driver_nl80211_data *drv,
+ struct nlattr *reason, struct nlattr *addr)
+{
+ union wpa_event_data data;
+
+ if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) {
+ /*
+ * Avoid reporting two disassociation events that could
+ * confuse the core code.
+ */
+ wpa_printf(MSG_DEBUG, "nl80211: Ignore disconnect "
+ "event when using userspace SME");
+ return;
+ }
+
+ drv->associated = 0;
+ os_memset(&data, 0, sizeof(data));
+ if (reason)
+ data.disassoc_info.reason_code = nla_get_u16(reason);
+ wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, &data);
+}
+
+
static void mlme_timeout_event(struct wpa_driver_nl80211_data *drv,
enum nl80211_commands cmd, struct nlattr *addr)
{
}
-static void mlme_event_action(struct wpa_driver_nl80211_data *drv,
- struct nlattr *freq, const u8 *frame, size_t len)
+static void mlme_event_mgmt(struct wpa_driver_nl80211_data *drv,
+ struct nlattr *freq, const u8 *frame, size_t len)
{
const struct ieee80211_mgmt *mgmt;
union wpa_event_data event;
stype = WLAN_FC_GET_STYPE(fc);
os_memset(&event, 0, sizeof(event));
- event.rx_action.da = mgmt->da;
- event.rx_action.sa = mgmt->sa;
- event.rx_action.bssid = mgmt->bssid;
- event.rx_action.category = mgmt->u.action.category;
- event.rx_action.data = &mgmt->u.action.category + 1;
- event.rx_action.len = frame + len - event.rx_action.data;
- if (freq)
+ if (freq) {
event.rx_action.freq = nla_get_u32(freq);
- wpa_supplicant_event(drv->ctx, EVENT_RX_ACTION, &event);
+ drv->last_mgmt_freq = event.rx_action.freq;
+ }
+ if (stype == WLAN_FC_STYPE_ACTION) {
+ event.rx_action.da = mgmt->da;
+ event.rx_action.sa = mgmt->sa;
+ event.rx_action.bssid = mgmt->bssid;
+ event.rx_action.category = mgmt->u.action.category;
+ event.rx_action.data = &mgmt->u.action.category + 1;
+ event.rx_action.len = frame + len - event.rx_action.data;
+ wpa_supplicant_event(drv->ctx, EVENT_RX_ACTION, &event);
+ } else {
+ event.rx_mgmt.frame = frame;
+ event.rx_mgmt.frame_len = len;
+ wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);
+ }
}
return;
cookie_val = nla_get_u64(cookie);
- wpa_printf(MSG_DEBUG, "nl80211: Action TX status: cookie=0%llx%s",
+ wpa_printf(MSG_DEBUG, "nl80211: Action TX status: cookie=0%llx%s "
+ "(ack=%d)",
(long long unsigned int) cookie_val,
cookie_val == drv->send_action_cookie ?
- " (match)" : " (unknown)");
+ " (match)" : " (unknown)", ack != NULL);
if (cookie_val != drv->send_action_cookie)
return;
if (type == EVENT_DISASSOC) {
event.disassoc_info.addr = bssid;
event.disassoc_info.reason_code = reason_code;
+ if (frame + len > mgmt->u.disassoc.variable) {
+ event.disassoc_info.ie = mgmt->u.disassoc.variable;
+ event.disassoc_info.ie_len = frame + len -
+ mgmt->u.disassoc.variable;
+ }
} else {
event.deauth_info.addr = bssid;
event.deauth_info.reason_code = reason_code;
+ if (frame + len > mgmt->u.deauth.variable) {
+ event.deauth_info.ie = mgmt->u.deauth.variable;
+ event.deauth_info.ie_len = frame + len -
+ mgmt->u.deauth.variable;
+ }
+ }
+
+ wpa_supplicant_event(drv->ctx, type, &event);
+}
+
+
+static void mlme_event_unprot_disconnect(struct wpa_driver_nl80211_data *drv,
+ enum wpa_event_type type,
+ const u8 *frame, size_t len)
+{
+ const struct ieee80211_mgmt *mgmt;
+ union wpa_event_data event;
+ u16 reason_code = 0;
+
+ if (len < 24)
+ return;
+
+ mgmt = (const struct ieee80211_mgmt *) frame;
+
+ os_memset(&event, 0, sizeof(event));
+ /* Note: Same offset for Reason Code in both frame subtypes */
+ if (len >= 24 + sizeof(mgmt->u.deauth))
+ reason_code = le_to_host16(mgmt->u.deauth.reason_code);
+
+ if (type == EVENT_UNPROT_DISASSOC) {
+ event.unprot_disassoc.sa = mgmt->sa;
+ event.unprot_disassoc.da = mgmt->da;
+ event.unprot_disassoc.reason_code = reason_code;
+ } else {
+ event.unprot_deauth.sa = mgmt->sa;
+ event.unprot_deauth.da = mgmt->da;
+ event.unprot_deauth.reason_code = reason_code;
}
wpa_supplicant_event(drv->ctx, type, &event);
mlme_event_deauth_disassoc(drv, EVENT_DISASSOC,
nla_data(frame), nla_len(frame));
break;
- case NL80211_CMD_ACTION:
- mlme_event_action(drv, freq, nla_data(frame), nla_len(frame));
+ case NL80211_CMD_FRAME:
+ mlme_event_mgmt(drv, freq, nla_data(frame), nla_len(frame));
break;
- case NL80211_CMD_ACTION_TX_STATUS:
+ case NL80211_CMD_FRAME_TX_STATUS:
mlme_event_action_tx_status(drv, cookie, nla_data(frame),
nla_len(frame), ack);
break;
+ case NL80211_CMD_UNPROT_DEAUTHENTICATE:
+ mlme_event_unprot_disconnect(drv, EVENT_UNPROT_DEAUTH,
+ nla_data(frame), nla_len(frame));
+ break;
+ case NL80211_CMD_UNPROT_DISASSOCIATE:
+ mlme_event_unprot_disconnect(drv, EVENT_UNPROT_DISASSOC,
+ nla_data(frame), nla_len(frame));
+ break;
default:
break;
}
if (cookie != drv->remain_on_chan_cookie)
return; /* not for us */
- drv->pending_remain_on_chan = !cancel_event;
+ if (cancel_event)
+ drv->pending_remain_on_chan = 0;
os_memset(&data, 0, sizeof(data));
data.remain_on_channel.freq = freq;
}
-static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv,
- struct nlattr *tb[])
+static int get_link_signal(struct nl_msg *msg, void *arg)
{
- static struct nla_policy cqm_policy[NL80211_ATTR_CQM_MAX + 1] = {
- [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 },
- [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U8 },
- [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 },
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1];
+ static struct nla_policy policy[NL80211_STA_INFO_MAX + 1] = {
+ [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 },
};
- struct nlattr *cqm[NL80211_ATTR_CQM_MAX + 1];
- enum nl80211_cqm_rssi_threshold_event event;
- union wpa_event_data ed;
-
- if (tb[NL80211_ATTR_CQM] == NULL ||
- nla_parse_nested(cqm, NL80211_ATTR_CQM_MAX, tb[NL80211_ATTR_CQM],
- cqm_policy)) {
- wpa_printf(MSG_DEBUG, "nl80211: Ignore invalid CQM event");
- return;
- }
-
- if (cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] == NULL)
- return;
- event = nla_get_u32(cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT]);
+ struct nlattr *rinfo[NL80211_RATE_INFO_MAX + 1];
+ static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = {
+ [NL80211_RATE_INFO_BITRATE] = { .type = NLA_U16 },
+ [NL80211_RATE_INFO_MCS] = { .type = NLA_U8 },
+ [NL80211_RATE_INFO_40_MHZ_WIDTH] = { .type = NLA_FLAG },
+ [NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG },
+ };
+ struct wpa_signal_info *sig_change = arg;
- os_memset(&ed, 0, sizeof(ed));
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+ if (!tb[NL80211_ATTR_STA_INFO] ||
+ nla_parse_nested(sinfo, NL80211_STA_INFO_MAX,
+ tb[NL80211_ATTR_STA_INFO], policy))
+ return NL_SKIP;
+ if (!sinfo[NL80211_STA_INFO_SIGNAL])
+ return NL_SKIP;
- if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH) {
- wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor "
- "event: RSSI high");
- ed.signal_change.above_threshold = 1;
- } else if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW) {
- wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor "
- "event: RSSI low");
- ed.signal_change.above_threshold = 0;
- } else
- return;
+ sig_change->current_signal =
+ (s8) nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]);
+
+ if (sinfo[NL80211_STA_INFO_TX_BITRATE]) {
+ if (nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX,
+ sinfo[NL80211_STA_INFO_TX_BITRATE],
+ rate_policy)) {
+ sig_change->current_txrate = 0;
+ } else {
+ if (rinfo[NL80211_RATE_INFO_BITRATE]) {
+ sig_change->current_txrate =
+ nla_get_u16(rinfo[
+ NL80211_RATE_INFO_BITRATE]) * 100;
+ }
+ }
+ }
- wpa_supplicant_event(drv->ctx, EVENT_SIGNAL_CHANGE, &ed);
+ return NL_SKIP;
}
-static int process_event(struct nl_msg *msg, void *arg)
+static int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv,
+ struct wpa_signal_info *sig)
{
- struct wpa_driver_nl80211_data *drv = arg;
- struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
- struct nlattr *tb[NL80211_ATTR_MAX + 1];
- union wpa_event_data data;
+ struct nl_msg *msg;
- nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
- genlmsg_attrlen(gnlh, 0), NULL);
+ sig->current_signal = -9999;
+ sig->current_txrate = 0;
- if (tb[NL80211_ATTR_IFINDEX]) {
- int ifindex = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
- if (ifindex != drv->ifindex && !have_ifidx(drv, ifindex)) {
- wpa_printf(MSG_DEBUG, "nl80211: Ignored event (cmd=%d)"
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_STATION);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+ NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, drv->bssid);
+
+ return send_and_recv_msgs(drv, msg, get_link_signal, sig);
+ nla_put_failure:
+ nlmsg_free(msg);
+ return -ENOBUFS;
+}
+
+
+static int get_link_noise(struct nl_msg *msg, void *arg)
+{
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1];
+ static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = {
+ [NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 },
+ [NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 },
+ };
+ struct wpa_signal_info *sig_change = arg;
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ if (!tb[NL80211_ATTR_SURVEY_INFO]) {
+ wpa_printf(MSG_DEBUG, "nl80211: survey data missing!");
+ return NL_SKIP;
+ }
+
+ if (nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX,
+ tb[NL80211_ATTR_SURVEY_INFO],
+ survey_policy)) {
+ wpa_printf(MSG_DEBUG, "nl80211: failed to parse nested "
+ "attributes!");
+ return NL_SKIP;
+ }
+
+ if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY])
+ return NL_SKIP;
+
+ if (nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]) !=
+ sig_change->frequency)
+ return NL_SKIP;
+
+ if (!sinfo[NL80211_SURVEY_INFO_NOISE])
+ return NL_SKIP;
+
+ sig_change->current_noise =
+ (s8) nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]);
+
+ return NL_SKIP;
+}
+
+
+static int nl80211_get_link_noise(struct wpa_driver_nl80211_data *drv,
+ struct wpa_signal_info *sig_change)
+{
+ struct nl_msg *msg;
+
+ sig_change->current_noise = 9999;
+ sig_change->frequency = drv->assoc_freq;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+
+ return send_and_recv_msgs(drv, msg, get_link_noise, sig_change);
+ nla_put_failure:
+ nlmsg_free(msg);
+ return -ENOBUFS;
+}
+
+
+static int get_noise_for_scan_results(struct nl_msg *msg, void *arg)
+{
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1];
+ static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = {
+ [NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 },
+ [NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 },
+ };
+ struct wpa_scan_results *scan_results = arg;
+ struct wpa_scan_res *scan_res;
+ size_t i;
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ if (!tb[NL80211_ATTR_SURVEY_INFO]) {
+ wpa_printf(MSG_DEBUG, "nl80211: Survey data missing");
+ return NL_SKIP;
+ }
+
+ if (nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX,
+ tb[NL80211_ATTR_SURVEY_INFO],
+ survey_policy)) {
+ wpa_printf(MSG_DEBUG, "nl80211: Failed to parse nested "
+ "attributes");
+ return NL_SKIP;
+ }
+
+ if (!sinfo[NL80211_SURVEY_INFO_NOISE])
+ return NL_SKIP;
+
+ if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY])
+ return NL_SKIP;
+
+ for (i = 0; i < scan_results->num; ++i) {
+ scan_res = scan_results->res[i];
+ if (!scan_res)
+ continue;
+ if ((int) nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]) !=
+ scan_res->freq)
+ continue;
+ if (!(scan_res->flags & WPA_SCAN_NOISE_INVALID))
+ continue;
+ scan_res->noise = (s8)
+ nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]);
+ scan_res->flags &= ~WPA_SCAN_NOISE_INVALID;
+ }
+
+ return NL_SKIP;
+}
+
+
+static int nl80211_get_noise_for_scan_results(
+ struct wpa_driver_nl80211_data *drv,
+ struct wpa_scan_results *scan_res)
+{
+ struct nl_msg *msg;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+
+ return send_and_recv_msgs(drv, msg, get_noise_for_scan_results,
+ scan_res);
+ nla_put_failure:
+ nlmsg_free(msg);
+ return -ENOBUFS;
+}
+
+
+static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv,
+ struct nlattr *tb[])
+{
+ static struct nla_policy cqm_policy[NL80211_ATTR_CQM_MAX + 1] = {
+ [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 },
+ [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U8 },
+ [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 },
+ [NL80211_ATTR_CQM_PKT_LOSS_EVENT] = { .type = NLA_U32 },
+ };
+ struct nlattr *cqm[NL80211_ATTR_CQM_MAX + 1];
+ enum nl80211_cqm_rssi_threshold_event event;
+ union wpa_event_data ed;
+ struct wpa_signal_info sig;
+ int res;
+
+ if (tb[NL80211_ATTR_CQM] == NULL ||
+ nla_parse_nested(cqm, NL80211_ATTR_CQM_MAX, tb[NL80211_ATTR_CQM],
+ cqm_policy)) {
+ wpa_printf(MSG_DEBUG, "nl80211: Ignore invalid CQM event");
+ return;
+ }
+
+ os_memset(&ed, 0, sizeof(ed));
+
+ if (cqm[NL80211_ATTR_CQM_PKT_LOSS_EVENT]) {
+ if (!tb[NL80211_ATTR_MAC])
+ return;
+ os_memcpy(ed.low_ack.addr, nla_data(tb[NL80211_ATTR_MAC]),
+ ETH_ALEN);
+ wpa_supplicant_event(drv->ctx, EVENT_STATION_LOW_ACK, &ed);
+ return;
+ }
+
+ if (cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] == NULL)
+ return;
+ event = nla_get_u32(cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT]);
+
+ if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH) {
+ wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor "
+ "event: RSSI high");
+ ed.signal_change.above_threshold = 1;
+ } else if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW) {
+ wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor "
+ "event: RSSI low");
+ ed.signal_change.above_threshold = 0;
+ } else
+ return;
+
+ res = nl80211_get_link_signal(drv, &sig);
+ if (res == 0) {
+ ed.signal_change.current_signal = sig.current_signal;
+ ed.signal_change.current_txrate = sig.current_txrate;
+ wpa_printf(MSG_DEBUG, "nl80211: Signal: %d dBm txrate: %d",
+ sig.current_signal, sig.current_txrate);
+ }
+
+ res = nl80211_get_link_noise(drv, &sig);
+ if (res == 0) {
+ ed.signal_change.current_noise = sig.current_noise;
+ wpa_printf(MSG_DEBUG, "nl80211: Noise: %d dBm",
+ sig.current_noise);
+ }
+
+ wpa_supplicant_event(drv->ctx, EVENT_SIGNAL_CHANGE, &ed);
+}
+
+
+static void nl80211_new_station_event(struct wpa_driver_nl80211_data *drv,
+ struct nlattr **tb)
+{
+ u8 *addr;
+ union wpa_event_data data;
+
+ if (tb[NL80211_ATTR_MAC] == NULL)
+ return;
+ addr = nla_data(tb[NL80211_ATTR_MAC]);
+ wpa_printf(MSG_DEBUG, "nl80211: New station " MACSTR, MAC2STR(addr));
+
+ if (is_ap_interface(drv->nlmode) && drv->no_monitor_iface_capab) {
+ u8 *ies = NULL;
+ size_t ies_len = 0;
+ if (tb[NL80211_ATTR_IE]) {
+ ies = nla_data(tb[NL80211_ATTR_IE]);
+ ies_len = nla_len(tb[NL80211_ATTR_IE]);
+ }
+ wpa_hexdump(MSG_DEBUG, "nl80211: Assoc Req IEs", ies, ies_len);
+ drv_event_assoc(drv->ctx, addr, ies, ies_len, 0);
+ return;
+ }
+
+ if (drv->nlmode != NL80211_IFTYPE_ADHOC)
+ return;
+
+ os_memset(&data, 0, sizeof(data));
+ os_memcpy(data.ibss_rsn_start.peer, addr, ETH_ALEN);
+ wpa_supplicant_event(drv->ctx, EVENT_IBSS_RSN_START, &data);
+}
+
+
+static void nl80211_del_station_event(struct wpa_driver_nl80211_data *drv,
+ struct nlattr **tb)
+{
+ u8 *addr;
+ union wpa_event_data data;
+
+ if (tb[NL80211_ATTR_MAC] == NULL)
+ return;
+ addr = nla_data(tb[NL80211_ATTR_MAC]);
+ wpa_printf(MSG_DEBUG, "nl80211: Delete station " MACSTR,
+ MAC2STR(addr));
+
+ if (is_ap_interface(drv->nlmode) && drv->no_monitor_iface_capab) {
+ drv_event_disassoc(drv->ctx, addr);
+ return;
+ }
+
+ if (drv->nlmode != NL80211_IFTYPE_ADHOC)
+ return;
+
+ os_memset(&data, 0, sizeof(data));
+ os_memcpy(data.ibss_peer_lost.peer, addr, ETH_ALEN);
+ wpa_supplicant_event(drv->ctx, EVENT_IBSS_PEER_LOST, &data);
+}
+
+
+static void nl80211_rekey_offload_event(struct wpa_driver_nl80211_data *drv,
+ struct nlattr **tb)
+{
+ struct nlattr *rekey_info[NUM_NL80211_REKEY_DATA];
+ static struct nla_policy rekey_policy[NUM_NL80211_REKEY_DATA] = {
+ [NL80211_REKEY_DATA_KEK] = {
+ .minlen = NL80211_KEK_LEN,
+ .maxlen = NL80211_KEK_LEN,
+ },
+ [NL80211_REKEY_DATA_KCK] = {
+ .minlen = NL80211_KCK_LEN,
+ .maxlen = NL80211_KCK_LEN,
+ },
+ [NL80211_REKEY_DATA_REPLAY_CTR] = {
+ .minlen = NL80211_REPLAY_CTR_LEN,
+ .maxlen = NL80211_REPLAY_CTR_LEN,
+ },
+ };
+ union wpa_event_data data;
+
+ if (!tb[NL80211_ATTR_MAC])
+ return;
+ if (!tb[NL80211_ATTR_REKEY_DATA])
+ return;
+ if (nla_parse_nested(rekey_info, MAX_NL80211_REKEY_DATA,
+ tb[NL80211_ATTR_REKEY_DATA], rekey_policy))
+ return;
+ if (!rekey_info[NL80211_REKEY_DATA_REPLAY_CTR])
+ return;
+
+ os_memset(&data, 0, sizeof(data));
+ data.driver_gtk_rekey.bssid = nla_data(tb[NL80211_ATTR_MAC]);
+ wpa_printf(MSG_DEBUG, "nl80211: Rekey offload event for BSSID " MACSTR,
+ MAC2STR(data.driver_gtk_rekey.bssid));
+ data.driver_gtk_rekey.replay_ctr =
+ nla_data(rekey_info[NL80211_REKEY_DATA_REPLAY_CTR]);
+ wpa_hexdump(MSG_DEBUG, "nl80211: Rekey offload - Replay Counter",
+ data.driver_gtk_rekey.replay_ctr, NL80211_REPLAY_CTR_LEN);
+ wpa_supplicant_event(drv->ctx, EVENT_DRIVER_GTK_REKEY, &data);
+}
+
+
+static void nl80211_pmksa_candidate_event(struct wpa_driver_nl80211_data *drv,
+ struct nlattr **tb)
+{
+ struct nlattr *cand[NUM_NL80211_PMKSA_CANDIDATE];
+ static struct nla_policy cand_policy[NUM_NL80211_PMKSA_CANDIDATE] = {
+ [NL80211_PMKSA_CANDIDATE_INDEX] = { .type = NLA_U32 },
+ [NL80211_PMKSA_CANDIDATE_BSSID] = {
+ .minlen = ETH_ALEN,
+ .maxlen = ETH_ALEN,
+ },
+ [NL80211_PMKSA_CANDIDATE_PREAUTH] = { .type = NLA_FLAG },
+ };
+ union wpa_event_data data;
+
+ if (!tb[NL80211_ATTR_PMKSA_CANDIDATE])
+ return;
+ if (nla_parse_nested(cand, MAX_NL80211_PMKSA_CANDIDATE,
+ tb[NL80211_ATTR_PMKSA_CANDIDATE], cand_policy))
+ return;
+ if (!cand[NL80211_PMKSA_CANDIDATE_INDEX] ||
+ !cand[NL80211_PMKSA_CANDIDATE_BSSID])
+ return;
+
+ os_memset(&data, 0, sizeof(data));
+ os_memcpy(data.pmkid_candidate.bssid,
+ nla_data(cand[NL80211_PMKSA_CANDIDATE_BSSID]), ETH_ALEN);
+ data.pmkid_candidate.index =
+ nla_get_u32(cand[NL80211_PMKSA_CANDIDATE_INDEX]);
+ data.pmkid_candidate.preauth =
+ cand[NL80211_PMKSA_CANDIDATE_PREAUTH] != NULL;
+ wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE, &data);
+}
+
+
+static int process_event(struct nl_msg *msg, void *arg)
+{
+ struct wpa_driver_nl80211_data *drv = arg;
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ if (tb[NL80211_ATTR_IFINDEX]) {
+ int ifindex = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
+ if (ifindex != drv->ifindex && !have_ifidx(drv, ifindex)) {
+ wpa_printf(MSG_DEBUG, "nl80211: Ignored event (cmd=%d)"
" for foreign interface (ifindex %d)",
gnlh->cmd, ifindex);
return NL_SKIP;
}
}
- if (drv->ap_scan_as_station &&
+ if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED &&
(gnlh->cmd == NL80211_CMD_NEW_SCAN_RESULTS ||
gnlh->cmd == NL80211_CMD_SCAN_ABORTED)) {
wpa_driver_nl80211_set_mode(&drv->first_bss,
- IEEE80211_MODE_AP);
- drv->ap_scan_as_station = 0;
+ drv->ap_scan_as_station);
+ drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED;
}
switch (gnlh->cmd) {
case NL80211_CMD_TRIGGER_SCAN:
wpa_printf(MSG_DEBUG, "nl80211: Scan trigger");
break;
+ case NL80211_CMD_START_SCHED_SCAN:
+ wpa_printf(MSG_DEBUG, "nl80211: Sched scan started");
+ break;
+ case NL80211_CMD_SCHED_SCAN_STOPPED:
+ wpa_printf(MSG_DEBUG, "nl80211: Sched scan stopped");
+ wpa_supplicant_event(drv->ctx, EVENT_SCHED_SCAN_STOPPED, NULL);
+ break;
case NL80211_CMD_NEW_SCAN_RESULTS:
wpa_printf(MSG_DEBUG, "nl80211: New scan results available");
drv->scan_complete_events = 1;
drv->ctx);
send_scan_event(drv, 0, tb);
break;
+ case NL80211_CMD_SCHED_SCAN_RESULTS:
+ wpa_printf(MSG_DEBUG,
+ "nl80211: New sched scan results available");
+ send_scan_event(drv, 0, tb);
+ break;
case NL80211_CMD_SCAN_ABORTED:
wpa_printf(MSG_DEBUG, "nl80211: Scan aborted");
/*
case NL80211_CMD_ASSOCIATE:
case NL80211_CMD_DEAUTHENTICATE:
case NL80211_CMD_DISASSOCIATE:
- case NL80211_CMD_ACTION:
- case NL80211_CMD_ACTION_TX_STATUS:
+ case NL80211_CMD_FRAME:
+ case NL80211_CMD_FRAME_TX_STATUS:
+ case NL80211_CMD_UNPROT_DEAUTHENTICATE:
+ case NL80211_CMD_UNPROT_DISASSOCIATE:
mlme_event(drv, gnlh->cmd, tb[NL80211_ATTR_FRAME],
tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT],
tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK],
tb[NL80211_ATTR_RESP_IE]);
break;
case NL80211_CMD_DISCONNECT:
- if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) {
- /*
- * Avoid reporting two disassociation events that could
- * confuse the core code.
- */
- wpa_printf(MSG_DEBUG, "nl80211: Ignore disconnect "
- "event when using userspace SME");
- break;
- }
- drv->associated = 0;
- os_memset(&data, 0, sizeof(data));
- if (tb[NL80211_ATTR_REASON_CODE])
- data.disassoc_info.reason_code =
- nla_get_u16(tb[NL80211_ATTR_REASON_CODE]);
- wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, &data);
+ mlme_event_disconnect(drv, tb[NL80211_ATTR_REASON_CODE],
+ tb[NL80211_ATTR_MAC]);
break;
case NL80211_CMD_MICHAEL_MIC_FAILURE:
mlme_event_michael_mic_failure(drv, tb);
case NL80211_CMD_NOTIFY_CQM:
nl80211_cqm_event(drv, tb);
break;
+ case NL80211_CMD_REG_CHANGE:
+ wpa_printf(MSG_DEBUG, "nl80211: Regulatory domain change");
+ wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED,
+ NULL);
+ break;
+ case NL80211_CMD_REG_BEACON_HINT:
+ wpa_printf(MSG_DEBUG, "nl80211: Regulatory beacon hint");
+ wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED,
+ NULL);
+ break;
+ case NL80211_CMD_NEW_STATION:
+ nl80211_new_station_event(drv, tb);
+ break;
+ case NL80211_CMD_DEL_STATION:
+ nl80211_del_station_event(drv, tb);
+ break;
+ case NL80211_CMD_SET_REKEY_OFFLOAD:
+ nl80211_rekey_offload_event(drv, tb);
+ break;
+ case NL80211_CMD_PMKSA_CANDIDATE:
+ nl80211_pmksa_candidate_event(drv, tb);
+ break;
default:
wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event "
"(cmd=%d)", gnlh->cmd);
static void wpa_driver_nl80211_event_receive(int sock, void *eloop_ctx,
- void *sock_ctx)
+ void *handle)
{
struct nl_cb *cb;
struct wpa_driver_nl80211_data *drv = eloop_ctx;
wpa_printf(MSG_DEBUG, "nl80211: Event message available");
- cb = nl_cb_clone(drv->nl_cb);
+ cb = nl_cb_clone(drv->global->nl_cb);
if (!cb)
return;
nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL);
nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, process_event, drv);
- nl_recvmsgs(drv->nl_handle_event, cb);
+ nl_recvmsgs(handle, cb);
nl_cb_put(cb);
}
alpha2[1] = alpha2_arg[1];
alpha2[2] = '\0';
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
- 0, NL80211_CMD_REQ_SET_REG, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_REQ_SET_REG);
NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, alpha2);
if (send_and_recv_msgs(drv, msg, NULL, NULL))
return -EINVAL;
return 0;
nla_put_failure:
+ nlmsg_free(msg);
return -EINVAL;
}
-#ifndef HOSTAPD
struct wiphy_info_data {
- int max_scan_ssids;
- int ap_supported;
- int auth_supported;
- int connect_supported;
+ struct wpa_driver_capa *capa;
+
+ unsigned int error:1;
};
struct nlattr *tb[NL80211_ATTR_MAX + 1];
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
struct wiphy_info_data *info = arg;
+ int p2p_go_supported = 0, p2p_client_supported = 0;
+ int p2p_concurrent = 0;
+ int auth_supported = 0, connect_supported = 0;
+ struct wpa_driver_capa *capa = info->capa;
+ static struct nla_policy
+ iface_combination_policy[NUM_NL80211_IFACE_COMB] = {
+ [NL80211_IFACE_COMB_LIMITS] = { .type = NLA_NESTED },
+ [NL80211_IFACE_COMB_MAXNUM] = { .type = NLA_U32 },
+ [NL80211_IFACE_COMB_STA_AP_BI_MATCH] = { .type = NLA_FLAG },
+ [NL80211_IFACE_COMB_NUM_CHANNELS] = { .type = NLA_U32 },
+ },
+ iface_limit_policy[NUM_NL80211_IFACE_LIMIT] = {
+ [NL80211_IFACE_LIMIT_TYPES] = { .type = NLA_NESTED },
+ [NL80211_IFACE_LIMIT_MAX] = { .type = NLA_U32 },
+ };
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
if (tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS])
- info->max_scan_ssids =
+ capa->max_scan_ssids =
nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]);
+ if (tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS])
+ capa->max_sched_scan_ssids =
+ nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS]);
+
+ if (tb[NL80211_ATTR_MAX_MATCH_SETS])
+ capa->max_match_sets =
+ nla_get_u8(tb[NL80211_ATTR_MAX_MATCH_SETS]);
+
if (tb[NL80211_ATTR_SUPPORTED_IFTYPES]) {
struct nlattr *nl_mode;
int i;
nla_for_each_nested(nl_mode,
tb[NL80211_ATTR_SUPPORTED_IFTYPES], i) {
- if (nl_mode->nla_type == NL80211_IFTYPE_AP) {
- info->ap_supported = 1;
+ switch (nla_type(nl_mode)) {
+ case NL80211_IFTYPE_AP:
+ capa->flags |= WPA_DRIVER_FLAGS_AP;
+ break;
+ case NL80211_IFTYPE_P2P_GO:
+ p2p_go_supported = 1;
+ break;
+ case NL80211_IFTYPE_P2P_CLIENT:
+ p2p_client_supported = 1;
break;
}
}
}
+ if (tb[NL80211_ATTR_INTERFACE_COMBINATIONS]) {
+ struct nlattr *nl_combi;
+ int rem_combi;
+
+ nla_for_each_nested(nl_combi,
+ tb[NL80211_ATTR_INTERFACE_COMBINATIONS],
+ rem_combi) {
+ struct nlattr *tb_comb[NUM_NL80211_IFACE_COMB];
+ struct nlattr *tb_limit[NUM_NL80211_IFACE_LIMIT];
+ struct nlattr *nl_limit, *nl_mode;
+ int err, rem_limit, rem_mode;
+ int combination_has_p2p = 0, combination_has_mgd = 0;
+
+ err = nla_parse_nested(tb_comb, MAX_NL80211_IFACE_COMB,
+ nl_combi,
+ iface_combination_policy);
+ if (err || !tb_comb[NL80211_IFACE_COMB_LIMITS] ||
+ !tb_comb[NL80211_IFACE_COMB_MAXNUM] ||
+ !tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS])
+ goto broken_combination;
+
+ nla_for_each_nested(nl_limit,
+ tb_comb[NL80211_IFACE_COMB_LIMITS],
+ rem_limit) {
+ err = nla_parse_nested(tb_limit,
+ MAX_NL80211_IFACE_LIMIT,
+ nl_limit,
+ iface_limit_policy);
+ if (err ||
+ !tb_limit[NL80211_IFACE_LIMIT_TYPES])
+ goto broken_combination;
+
+ nla_for_each_nested(
+ nl_mode,
+ tb_limit[NL80211_IFACE_LIMIT_TYPES],
+ rem_mode) {
+ int ift = nla_type(nl_mode);
+ if (ift == NL80211_IFTYPE_P2P_GO ||
+ ift == NL80211_IFTYPE_P2P_CLIENT)
+ combination_has_p2p = 1;
+ if (ift == NL80211_IFTYPE_STATION)
+ combination_has_mgd = 1;
+ }
+ if (combination_has_p2p && combination_has_mgd)
+ break;
+ }
+
+ if (combination_has_p2p && combination_has_mgd) {
+ p2p_concurrent = 1;
+ break;
+ }
+
+broken_combination:
+ ;
+ }
+ }
+
if (tb[NL80211_ATTR_SUPPORTED_COMMANDS]) {
struct nlattr *nl_cmd;
int i;
nla_for_each_nested(nl_cmd,
tb[NL80211_ATTR_SUPPORTED_COMMANDS], i) {
- u32 cmd = nla_get_u32(nl_cmd);
- if (cmd == NL80211_CMD_AUTHENTICATE)
- info->auth_supported = 1;
- else if (cmd == NL80211_CMD_CONNECT)
- info->connect_supported = 1;
+ switch (nla_get_u32(nl_cmd)) {
+ case NL80211_CMD_AUTHENTICATE:
+ auth_supported = 1;
+ break;
+ case NL80211_CMD_CONNECT:
+ connect_supported = 1;
+ break;
+ case NL80211_CMD_START_SCHED_SCAN:
+ /*
+ * Disabled for 1.x for now as it is
+ * broken there due to the way it ends
+ * up getting used.
+ capa->sched_scan_supported = 1;
+ */
+ break;
+ }
+ }
+ }
+
+ if (tb[NL80211_ATTR_OFFCHANNEL_TX_OK]) {
+ wpa_printf(MSG_DEBUG, "nl80211: Using driver-based "
+ "off-channel TX");
+ capa->flags |= WPA_DRIVER_FLAGS_OFFCHANNEL_TX;
+ }
+
+ if (tb[NL80211_ATTR_ROAM_SUPPORT]) {
+ wpa_printf(MSG_DEBUG, "nl80211: Using driver-based roaming");
+ capa->flags |= WPA_DRIVER_FLAGS_BSS_SELECTION;
+ }
+
+ /* default to 5000 since early versions of mac80211 don't set it */
+ capa->max_remain_on_chan = 5000;
+
+ if (tb[NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION])
+ capa->max_remain_on_chan =
+ nla_get_u32(tb[NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION]);
+
+ if (auth_supported)
+ capa->flags |= WPA_DRIVER_FLAGS_SME;
+ else if (!connect_supported) {
+ wpa_printf(MSG_INFO, "nl80211: Driver does not support "
+ "authentication/association or connect commands");
+ info->error = 1;
+ }
+
+ if (p2p_go_supported && p2p_client_supported)
+ capa->flags |= WPA_DRIVER_FLAGS_P2P_CAPABLE;
+ if (p2p_concurrent) {
+ wpa_printf(MSG_DEBUG, "nl80211: Use separate P2P group "
+ "interface (driver advertised support)");
+ capa->flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT;
+ capa->flags |= WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P;
+ }
+
+ if (tb[NL80211_ATTR_TDLS_SUPPORT]) {
+ wpa_printf(MSG_DEBUG, "nl80211: TDLS supported");
+ capa->flags |= WPA_DRIVER_FLAGS_TDLS_SUPPORT;
+
+ if (tb[NL80211_ATTR_TDLS_EXTERNAL_SETUP]) {
+ wpa_printf(MSG_DEBUG, "nl80211: TDLS external setup");
+ capa->flags |=
+ WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP;
}
}
struct nl_msg *msg;
os_memset(info, 0, sizeof(*info));
+ info->capa = &drv->capa;
+
msg = nlmsg_alloc();
if (!msg)
return -1;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
- 0, NL80211_CMD_GET_WIPHY, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_WIPHY);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->first_bss.ifindex);
struct wiphy_info_data info;
if (wpa_driver_nl80211_get_info(drv, &info))
return -1;
+
+ if (info.error)
+ return -1;
+
drv->has_capability = 1;
/* For now, assume TKIP, CCMP, WPA, WPA2 are supported */
drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA |
WPA_DRIVER_AUTH_SHARED |
WPA_DRIVER_AUTH_LEAP;
- drv->capa.max_scan_ssids = info.max_scan_ssids;
- if (info.ap_supported)
- drv->capa.flags |= WPA_DRIVER_FLAGS_AP;
-
- if (info.auth_supported)
- drv->capa.flags |= WPA_DRIVER_FLAGS_SME;
- else if (!info.connect_supported) {
- wpa_printf(MSG_INFO, "nl80211: Driver does not support "
- "authentication/association or connect commands");
- return -1;
- }
-
+ drv->capa.flags |= WPA_DRIVER_FLAGS_SANE_ERROR_CODES;
drv->capa.flags |= WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE;
- drv->capa.max_remain_on_chan = 5000;
+ drv->capa.flags |= WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
+ drv->capa.flags |= WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS;
return 0;
}
-#endif /* HOSTAPD */
-static int wpa_driver_nl80211_init_nl(struct wpa_driver_nl80211_data *drv,
- void *ctx)
+#ifdef ANDROID
+static int android_genl_ctrl_resolve(struct nl_handle *handle,
+ const char *name)
{
- int ret;
-
- /* Initialize generic netlink and nl80211 */
+ /*
+ * Android ICS has very minimal genl_ctrl_resolve() implementation, so
+ * need to work around that.
+ */
+ struct nl_cache *cache = NULL;
+ struct genl_family *nl80211 = NULL;
+ int id = -1;
- drv->nl_cb = nl_cb_alloc(NL_CB_DEFAULT);
- if (drv->nl_cb == NULL) {
- wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink "
- "callbacks");
- goto err1;
+ if (genl_ctrl_alloc_cache(handle, &cache) < 0) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic "
+ "netlink cache");
+ goto fail;
}
- drv->nl_handle = nl_handle_alloc_cb(drv->nl_cb);
- if (drv->nl_handle == NULL) {
- wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink "
- "callbacks");
- goto err2;
- }
+ nl80211 = genl_ctrl_search_by_name(cache, name);
+ if (nl80211 == NULL)
+ goto fail;
- drv->nl_handle_event = nl_handle_alloc_cb(drv->nl_cb);
- if (drv->nl_handle_event == NULL) {
- wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink "
- "callbacks (event)");
- goto err2b;
- }
+ id = genl_family_get_id(nl80211);
- if (genl_connect(drv->nl_handle)) {
- wpa_printf(MSG_ERROR, "nl80211: Failed to connect to generic "
- "netlink");
- goto err3;
- }
+fail:
+ if (nl80211)
+ genl_family_put(nl80211);
+ if (cache)
+ nl_cache_free(cache);
- if (genl_connect(drv->nl_handle_event)) {
- wpa_printf(MSG_ERROR, "nl80211: Failed to connect to generic "
- "netlink (event)");
- goto err3;
- }
+ return id;
+}
+#define genl_ctrl_resolve android_genl_ctrl_resolve
+#endif /* ANDROID */
-#ifdef CONFIG_LIBNL20
- if (genl_ctrl_alloc_cache(drv->nl_handle, &drv->nl_cache) < 0) {
- wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic "
- "netlink cache");
- goto err3;
- }
- if (genl_ctrl_alloc_cache(drv->nl_handle_event, &drv->nl_cache_event) <
- 0) {
- wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic "
- "netlink cache (event)");
- goto err3b;
- }
-#else /* CONFIG_LIBNL20 */
- drv->nl_cache = genl_ctrl_alloc_cache(drv->nl_handle);
- if (drv->nl_cache == NULL) {
- wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic "
- "netlink cache");
- goto err3;
- }
- drv->nl_cache_event = genl_ctrl_alloc_cache(drv->nl_handle_event);
- if (drv->nl_cache_event == NULL) {
- wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic "
- "netlink cache (event)");
- goto err3b;
+
+static int wpa_driver_nl80211_init_nl_global(struct nl80211_global *global)
+{
+ global->nl_cb = nl_cb_alloc(NL_CB_DEFAULT);
+ if (global->nl_cb == NULL) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink "
+ "callbacks");
+ return -1;
}
-#endif /* CONFIG_LIBNL20 */
- drv->nl80211 = genl_ctrl_search_by_name(drv->nl_cache, "nl80211");
- if (drv->nl80211 == NULL) {
+ if (nl_create_handles(&global->nl, global->nl_cb, "nl"))
+ return -1;
+
+ global->nl80211 = genl_ctrl_search_by_name(global->nl.cache,
+ "nl80211");
+ if (global->nl80211 == NULL) {
wpa_printf(MSG_ERROR, "nl80211: 'nl80211' generic netlink not "
"found");
- goto err4;
+ return -1;
}
+ return 0;
+}
+
+
+static int wpa_driver_nl80211_init_nl(struct wpa_driver_nl80211_data *drv)
+{
+ struct nl80211_global *global = drv->global;
+ int ret;
+
+ /* Initialize generic netlink and nl80211 */
+
+ if (nl_create_handles(&drv->nl_event, global->nl_cb, "event"))
+ goto err3;
+
ret = nl_get_multicast_id(drv, "nl80211", "scan");
if (ret >= 0)
- ret = nl_socket_add_membership(drv->nl_handle_event, ret);
+ ret = nl_socket_add_membership(drv->nl_event.handle, ret);
if (ret < 0) {
wpa_printf(MSG_ERROR, "nl80211: Could not add multicast "
"membership for scan events: %d (%s)",
ret = nl_get_multicast_id(drv, "nl80211", "mlme");
if (ret >= 0)
- ret = nl_socket_add_membership(drv->nl_handle_event, ret);
+ ret = nl_socket_add_membership(drv->nl_event.handle, ret);
if (ret < 0) {
wpa_printf(MSG_ERROR, "nl80211: Could not add multicast "
"membership for mlme events: %d (%s)",
goto err4;
}
- eloop_register_read_sock(nl_socket_get_fd(drv->nl_handle_event),
- wpa_driver_nl80211_event_receive, drv, ctx);
+ ret = nl_get_multicast_id(drv, "nl80211", "regulatory");
+ if (ret >= 0)
+ ret = nl_socket_add_membership(drv->nl_event.handle, ret);
+ if (ret < 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: Could not add multicast "
+ "membership for regulatory events: %d (%s)",
+ ret, strerror(-ret));
+ /* Continue without regulatory events */
+ }
+
+ eloop_register_read_sock(nl_socket_get_fd(drv->nl_event.handle),
+ wpa_driver_nl80211_event_receive, drv,
+ drv->nl_event.handle);
return 0;
err4:
- nl_cache_free(drv->nl_cache_event);
-err3b:
- nl_cache_free(drv->nl_cache);
+ nl_destroy_handles(&drv->nl_event);
err3:
- nl_handle_destroy(drv->nl_handle_event);
-err2b:
- nl_handle_destroy(drv->nl_handle);
-err2:
- nl_cb_put(drv->nl_cb);
-err1:
return -1;
}
+static void wpa_driver_nl80211_rfkill_blocked(void *ctx)
+{
+ wpa_printf(MSG_DEBUG, "nl80211: RFKILL blocked");
+ /*
+ * This may be for any interface; use ifdown event to disable
+ * interface.
+ */
+}
+
+
+static void wpa_driver_nl80211_rfkill_unblocked(void *ctx)
+{
+ struct wpa_driver_nl80211_data *drv = ctx;
+ wpa_printf(MSG_DEBUG, "nl80211: RFKILL unblocked");
+ if (linux_set_iface_flags(drv->global->ioctl_sock,
+ drv->first_bss.ifname, 1)) {
+ wpa_printf(MSG_DEBUG, "nl80211: Could not set interface UP "
+ "after rfkill unblock");
+ return;
+ }
+ /* rtnetlink ifup handler will report interface as enabled */
+}
+
+
+static void nl80211_get_phy_name(struct wpa_driver_nl80211_data *drv)
+{
+ /* Find phy (radio) to which this interface belongs */
+ char buf[90], *pos;
+ int f, rv;
+
+ drv->phyname[0] = '\0';
+ snprintf(buf, sizeof(buf) - 1, "/sys/class/net/%s/phy80211/name",
+ drv->first_bss.ifname);
+ f = open(buf, O_RDONLY);
+ if (f < 0) {
+ wpa_printf(MSG_DEBUG, "Could not open file %s: %s",
+ buf, strerror(errno));
+ return;
+ }
+
+ rv = read(f, drv->phyname, sizeof(drv->phyname) - 1);
+ close(f);
+ if (rv < 0) {
+ wpa_printf(MSG_DEBUG, "Could not read file %s: %s",
+ buf, strerror(errno));
+ return;
+ }
+
+ drv->phyname[rv] = '\0';
+ pos = os_strchr(drv->phyname, '\n');
+ if (pos)
+ *pos = '\0';
+ wpa_printf(MSG_DEBUG, "nl80211: interface %s in phy %s",
+ drv->first_bss.ifname, drv->phyname);
+}
+
+
+#ifdef CONFIG_AP
+static void nl80211_l2_read(void *ctx, const u8 *src_addr, const u8 *buf,
+ size_t len)
+{
+ wpa_printf(MSG_DEBUG, "nl80211: l2_packet read %u",
+ (unsigned int) len);
+}
+#endif /* CONFIG_AP */
+
+
/**
* wpa_driver_nl80211_init - Initialize nl80211 driver interface
* @ctx: context to be used when calling wpa_supplicant functions,
* e.g., wpa_supplicant_event()
* @ifname: interface name, e.g., wlan0
+ * @global_priv: private driver global data from global_init()
* Returns: Pointer to private data, %NULL on failure
*/
-static void * wpa_driver_nl80211_init(void *ctx, const char *ifname)
+static void * wpa_driver_nl80211_init(void *ctx, const char *ifname,
+ void *global_priv)
{
struct wpa_driver_nl80211_data *drv;
- struct netlink_config *cfg;
+ struct rfkill_config *rcfg;
struct i802_bss *bss;
+ if (global_priv == NULL)
+ return NULL;
drv = os_zalloc(sizeof(*drv));
if (drv == NULL)
return NULL;
+ drv->global = global_priv;
drv->ctx = ctx;
bss = &drv->first_bss;
bss->drv = drv;
os_strlcpy(bss->ifname, ifname, sizeof(bss->ifname));
drv->monitor_ifidx = -1;
drv->monitor_sock = -1;
- drv->ioctl_sock = -1;
+ drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED;
- if (wpa_driver_nl80211_init_nl(drv, ctx)) {
+ if (wpa_driver_nl80211_init_nl(drv)) {
os_free(drv);
return NULL;
}
- drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
- if (drv->ioctl_sock < 0) {
- perror("socket(PF_INET,SOCK_DGRAM)");
- goto failed;
- }
+ nl80211_get_phy_name(drv);
- cfg = os_zalloc(sizeof(*cfg));
- if (cfg == NULL)
- goto failed;
- cfg->ctx = drv;
- cfg->newlink_cb = wpa_driver_nl80211_event_rtm_newlink;
- cfg->dellink_cb = wpa_driver_nl80211_event_rtm_dellink;
- drv->netlink = netlink_init(cfg);
- if (drv->netlink == NULL) {
- os_free(cfg);
+ rcfg = os_zalloc(sizeof(*rcfg));
+ if (rcfg == NULL)
goto failed;
+ rcfg->ctx = drv;
+ os_strlcpy(rcfg->ifname, ifname, sizeof(rcfg->ifname));
+ rcfg->blocked_cb = wpa_driver_nl80211_rfkill_blocked;
+ rcfg->unblocked_cb = wpa_driver_nl80211_rfkill_unblocked;
+ drv->rfkill = rfkill_init(rcfg);
+ if (drv->rfkill == NULL) {
+ wpa_printf(MSG_DEBUG, "nl80211: RFKILL status not available");
+ os_free(rcfg);
}
+
if (wpa_driver_nl80211_finish_drv_init(drv))
goto failed;
- return bss;
+#ifdef CONFIG_AP
+ drv->l2 = l2_packet_init(ifname, NULL, ETH_P_EAPOL,
+ nl80211_l2_read, drv, 0);
+#endif /* CONFIG_AP */
-failed:
- netlink_deinit(drv->netlink);
- if (drv->ioctl_sock >= 0)
- close(drv->ioctl_sock);
+ if (drv->global) {
+ dl_list_add(&drv->global->interfaces, &drv->list);
+ drv->in_interface_list = 1;
+ }
- genl_family_put(drv->nl80211);
- nl_cache_free(drv->nl_cache);
- nl_handle_destroy(drv->nl_handle);
- nl_cb_put(drv->nl_cb);
- eloop_unregister_read_sock(nl_socket_get_fd(drv->nl_handle_event));
+ return bss;
- os_free(drv);
+failed:
+ wpa_driver_nl80211_deinit(bss);
return NULL;
}
-static int nl80211_register_action_frame(struct wpa_driver_nl80211_data *drv,
- const u8 *match, size_t match_len)
+static int nl80211_register_frame(struct wpa_driver_nl80211_data *drv,
+ struct nl_handle *nl_handle,
+ u16 type, const u8 *match, size_t match_len)
{
struct nl_msg *msg;
int ret = -1;
if (!msg)
return -1;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
- NL80211_CMD_REGISTER_ACTION, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_REGISTER_ACTION);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+ NLA_PUT_U16(msg, NL80211_ATTR_FRAME_TYPE, type);
NLA_PUT(msg, NL80211_ATTR_FRAME_MATCH, match_len, match);
- ret = send_and_recv(drv, drv->nl_handle_event, msg, NULL, NULL);
+ ret = send_and_recv(drv, nl_handle, msg, NULL, NULL);
msg = NULL;
if (ret) {
- wpa_printf(MSG_DEBUG, "nl80211: Register Action command "
- "failed: ret=%d (%s)", ret, strerror(-ret));
- wpa_hexdump(MSG_DEBUG, "nl80211: Register Action match",
+ wpa_printf(MSG_DEBUG, "nl80211: Register frame command "
+ "failed (type=%u): ret=%d (%s)",
+ type, ret, strerror(-ret));
+ wpa_hexdump(MSG_DEBUG, "nl80211: Register frame match",
match, match_len);
goto nla_put_failure;
}
}
+static int nl80211_register_action_frame(struct wpa_driver_nl80211_data *drv,
+ const u8 *match, size_t match_len)
+{
+ u16 type = (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_ACTION << 4);
+ return nl80211_register_frame(drv, drv->nl_event.handle,
+ type, match, match_len);
+}
+
+
static int nl80211_register_action_frames(struct wpa_driver_nl80211_data *drv)
{
+#if defined(CONFIG_P2P) || defined(CONFIG_INTERWORKING)
+ /* GAS Initial Request */
+ if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0a", 2) < 0)
+ return -1;
+ /* GAS Initial Response */
+ if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0b", 2) < 0)
+ return -1;
+ /* GAS Comeback Request */
+ if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0c", 2) < 0)
+ return -1;
+ /* GAS Comeback Response */
+ if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0d", 2) < 0)
+ return -1;
+#endif /* CONFIG_P2P || CONFIG_INTERWORKING */
+#ifdef CONFIG_P2P
+ /* P2P Public Action */
+ if (nl80211_register_action_frame(drv,
+ (u8 *) "\x04\x09\x50\x6f\x9a\x09",
+ 6) < 0)
+ return -1;
+ /* P2P Action */
+ if (nl80211_register_action_frame(drv,
+ (u8 *) "\x7f\x50\x6f\x9a\x09",
+ 5) < 0)
+ return -1;
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_IEEE80211W
+ /* SA Query Response */
+ if (nl80211_register_action_frame(drv, (u8 *) "\x08\x01", 2) < 0)
+ return -1;
+#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_TDLS
+ if ((drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)) {
+ /* TDLS Discovery Response */
+ if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0e", 2) <
+ 0)
+ return -1;
+ }
+#endif /* CONFIG_TDLS */
+
/* FT Action frames */
if (nl80211_register_action_frame(drv, (u8 *) "\x06", 1) < 0)
return -1;
drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT |
WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK;
+ /* WNM - BSS Transition Management Request */
+ if (nl80211_register_action_frame(drv, (u8 *) "\x0a\x07", 2) < 0)
+ return -1;
+
return 0;
}
+static void wpa_driver_nl80211_send_rfkill(void *eloop_ctx, void *timeout_ctx)
+{
+ wpa_supplicant_event(timeout_ctx, EVENT_INTERFACE_DISABLED, NULL);
+}
+
+
static int
wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv)
{
struct i802_bss *bss = &drv->first_bss;
+ int send_rfkill_event = 0;
drv->ifindex = if_nametoindex(bss->ifname);
drv->first_bss.ifindex = drv->ifindex;
#ifndef HOSTAPD
- if (wpa_driver_nl80211_set_mode(bss, IEEE80211_MODE_INFRA) < 0) {
- wpa_printf(MSG_DEBUG, "nl80211: Could not configure driver to "
+ /*
+ * Make sure the interface starts up in station mode unless this is a
+ * dynamically added interface (e.g., P2P) that was already configured
+ * with proper iftype.
+ */
+ if (drv->ifindex != drv->global->if_add_ifindex &&
+ wpa_driver_nl80211_set_mode(bss, NL80211_IFTYPE_STATION) < 0) {
+ wpa_printf(MSG_ERROR, "nl80211: Could not configure driver to "
"use managed mode");
+ return -1;
}
- if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 1)) {
- wpa_printf(MSG_ERROR, "Could not set interface '%s' UP",
- bss->ifname);
- return -1;
+ if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1)) {
+ if (rfkill_is_blocked(drv->rfkill)) {
+ wpa_printf(MSG_DEBUG, "nl80211: Could not yet enable "
+ "interface '%s' due to rfkill",
+ bss->ifname);
+ drv->if_disabled = 1;
+ send_rfkill_event = 1;
+ } else {
+ wpa_printf(MSG_ERROR, "nl80211: Could not set "
+ "interface '%s' UP", bss->ifname);
+ return -1;
+ }
}
+ netlink_send_oper_ifla(drv->global->netlink, drv->ifindex,
+ 1, IF_OPER_DORMANT);
+#endif /* HOSTAPD */
+
if (wpa_driver_nl80211_capa(drv))
return -1;
- netlink_send_oper_ifla(drv->netlink, drv->ifindex,
- 1, IF_OPER_DORMANT);
-#endif /* HOSTAPD */
+ if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
+ drv->addr))
+ return -1;
if (nl80211_register_action_frames(drv) < 0) {
wpa_printf(MSG_DEBUG, "nl80211: Failed to register Action "
*/
}
+ if (send_rfkill_event) {
+ eloop_register_timeout(0, 0, wpa_driver_nl80211_send_rfkill,
+ drv, drv->ctx);
+ }
+
return 0;
}
if (!msg)
return -ENOMEM;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
- 0, NL80211_CMD_DEL_BEACON, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_BEACON);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
return send_and_recv_msgs(drv, msg, NULL, NULL);
nla_put_failure:
+ nlmsg_free(msg);
return -ENOBUFS;
}
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
- if (drv->added_if_into_bridge) {
- if (linux_br_del_if(drv->ioctl_sock, drv->brname, bss->ifname)
- < 0)
+#ifdef CONFIG_AP
+ if (drv->l2)
+ l2_packet_deinit(drv->l2);
+#endif /* CONFIG_AP */
+
+ if (drv->nl_preq.handle)
+ wpa_driver_nl80211_probe_req_report(bss, 0);
+ if (bss->added_if_into_bridge) {
+ if (linux_br_del_if(drv->global->ioctl_sock, bss->brname,
+ bss->ifname) < 0)
wpa_printf(MSG_INFO, "nl80211: Failed to remove "
"interface %s from bridge %s: %s",
- bss->ifname, drv->brname, strerror(errno));
+ bss->ifname, bss->brname, strerror(errno));
}
- if (drv->added_bridge) {
- if (linux_br_del(drv->ioctl_sock, drv->brname) < 0)
+ if (bss->added_bridge) {
+ if (linux_br_del(drv->global->ioctl_sock, bss->brname) < 0)
wpa_printf(MSG_INFO, "nl80211: Failed to remove "
"bridge %s: %s",
- drv->brname, strerror(errno));
+ bss->brname, strerror(errno));
}
nl80211_remove_monitor_interface(drv);
- if (drv->nlmode == NL80211_IFTYPE_AP)
+ if (is_ap_interface(drv->nlmode))
wpa_driver_nl80211_del_beacon(drv);
#ifdef HOSTAPD
os_free(drv->if_indices);
#endif /* HOSTAPD */
- if (drv->disable_11b_rates)
+ if (drv->disabled_11b_rates)
nl80211_disable_11b_rates(drv, drv->ifindex, 0);
- netlink_send_oper_ifla(drv->netlink, drv->ifindex, 0, IF_OPER_UP);
- netlink_deinit(drv->netlink);
+ netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, 0,
+ IF_OPER_UP);
+ rfkill_deinit(drv->rfkill);
eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx);
- (void) linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 0);
- wpa_driver_nl80211_set_mode(bss, IEEE80211_MODE_INFRA);
-
- if (drv->ioctl_sock >= 0)
- close(drv->ioctl_sock);
+ (void) linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0);
+ wpa_driver_nl80211_set_mode(bss, NL80211_IFTYPE_STATION);
- eloop_unregister_read_sock(nl_socket_get_fd(drv->nl_handle_event));
- genl_family_put(drv->nl80211);
- nl_cache_free(drv->nl_cache);
- nl_cache_free(drv->nl_cache_event);
- nl_handle_destroy(drv->nl_handle);
- nl_handle_destroy(drv->nl_handle_event);
- nl_cb_put(drv->nl_cb);
-
- eloop_cancel_timeout(wpa_driver_nl80211_probe_req_report_timeout,
- drv, NULL);
+ eloop_unregister_read_sock(nl_socket_get_fd(drv->nl_event.handle));
+ nl_destroy_handles(&drv->nl_event);
os_free(drv->filter_ssids);
+ if (drv->in_interface_list)
+ dl_list_del(&drv->list);
+
os_free(drv);
}
static void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_driver_nl80211_data *drv = eloop_ctx;
- if (drv->ap_scan_as_station) {
+ if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED) {
wpa_driver_nl80211_set_mode(&drv->first_bss,
- IEEE80211_MODE_AP);
- drv->ap_scan_as_station = 0;
+ drv->ap_scan_as_station);
+ drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED;
}
wpa_printf(MSG_DEBUG, "Scan timeout - try to get results");
wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
int ret = 0, timeout;
- struct nl_msg *msg, *ssids, *freqs;
+ struct nl_msg *msg, *ssids, *freqs, *rates;
size_t i;
msg = nlmsg_alloc();
ssids = nlmsg_alloc();
freqs = nlmsg_alloc();
- if (!msg || !ssids || !freqs) {
+ rates = nlmsg_alloc();
+ if (!msg || !ssids || !freqs || !rates) {
nlmsg_free(msg);
nlmsg_free(ssids);
nlmsg_free(freqs);
+ nlmsg_free(rates);
return -1;
}
params->filter_ssids = NULL;
drv->num_filter_ssids = params->num_filter_ssids;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
- NL80211_CMD_TRIGGER_SCAN, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_TRIGGER_SCAN);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
nla_put_nested(msg, NL80211_ATTR_SCAN_FREQUENCIES, freqs);
}
+ if (params->p2p_probe) {
+ /*
+ * Remove 2.4 GHz rates 1, 2, 5.5, 11 Mbps from supported rates
+ * by masking out everything else apart from the OFDM rates 6,
+ * 9, 12, 18, 24, 36, 48, 54 Mbps from non-MCS rates. All 5 GHz
+ * rates are left enabled.
+ */
+ NLA_PUT(rates, NL80211_BAND_2GHZ, 8,
+ "\x0c\x12\x18\x24\x30\x48\x60\x6c");
+ nla_put_nested(msg, NL80211_ATTR_SCAN_SUPP_RATES, rates);
+
+ NLA_PUT_FLAG(msg, NL80211_ATTR_TX_NO_CCK_RATE);
+ }
+
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
msg = NULL;
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Scan trigger failed: ret=%d "
"(%s)", ret, strerror(-ret));
#ifdef HOSTAPD
- if (drv->nlmode == NL80211_IFTYPE_AP) {
+ if (is_ap_interface(drv->nlmode)) {
/*
* mac80211 does not allow scan requests in AP mode, so
* try to do this in station mode.
*/
- if (wpa_driver_nl80211_set_mode(bss,
- IEEE80211_MODE_INFRA))
+ if (wpa_driver_nl80211_set_mode(
+ bss, NL80211_IFTYPE_STATION))
goto nla_put_failure;
if (wpa_driver_nl80211_scan(drv, params)) {
- wpa_driver_nl80211_set_mode(bss,
- IEEE80211_MODE_AP);
+ wpa_driver_nl80211_set_mode(bss, drv->nlmode);
goto nla_put_failure;
}
/* Restore AP mode when processing scan results */
- drv->ap_scan_as_station = 1;
+ drv->ap_scan_as_station = drv->nlmode;
ret = 0;
} else
goto nla_put_failure;
nlmsg_free(ssids);
nlmsg_free(msg);
nlmsg_free(freqs);
+ nlmsg_free(rates);
+ return ret;
+}
+
+
+/**
+ * wpa_driver_nl80211_sched_scan - Initiate a scheduled scan
+ * @priv: Pointer to private driver data from wpa_driver_nl80211_init()
+ * @params: Scan parameters
+ * @interval: Interval between scan cycles in milliseconds
+ * Returns: 0 on success, -1 on failure or if not supported
+ */
+static int wpa_driver_nl80211_sched_scan(void *priv,
+ struct wpa_driver_scan_params *params,
+ u32 interval)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ int ret = 0;
+ struct nl_msg *msg, *ssids, *freqs, *match_set_ssid, *match_sets;
+ size_t i;
+
+ msg = nlmsg_alloc();
+ ssids = nlmsg_alloc();
+ freqs = nlmsg_alloc();
+ if (!msg || !ssids || !freqs) {
+ nlmsg_free(msg);
+ nlmsg_free(ssids);
+ nlmsg_free(freqs);
+ return -1;
+ }
+
+ os_free(drv->filter_ssids);
+ drv->filter_ssids = params->filter_ssids;
+ params->filter_ssids = NULL;
+ drv->num_filter_ssids = params->num_filter_ssids;
+
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_START_SCHED_SCAN);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL, interval);
+
+ if (drv->num_filter_ssids) {
+ match_sets = nlmsg_alloc();
+
+ for (i = 0; i < drv->num_filter_ssids; i++) {
+ wpa_hexdump_ascii(MSG_MSGDUMP,
+ "nl80211: Sched scan filter SSID",
+ drv->filter_ssids[i].ssid,
+ drv->filter_ssids[i].ssid_len);
+
+ match_set_ssid = nlmsg_alloc();
+ nla_put(match_set_ssid,
+ NL80211_ATTR_SCHED_SCAN_MATCH_SSID,
+ drv->filter_ssids[i].ssid_len,
+ drv->filter_ssids[i].ssid);
+
+ nla_put_nested(match_sets, i + 1, match_set_ssid);
+
+ nlmsg_free(match_set_ssid);
+ }
+
+ nla_put_nested(msg, NL80211_ATTR_SCHED_SCAN_MATCH,
+ match_sets);
+ nlmsg_free(match_sets);
+ }
+
+ for (i = 0; i < params->num_ssids; i++) {
+ wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Sched scan SSID",
+ params->ssids[i].ssid,
+ params->ssids[i].ssid_len);
+ NLA_PUT(ssids, i + 1, params->ssids[i].ssid_len,
+ params->ssids[i].ssid);
+ }
+ if (params->num_ssids)
+ nla_put_nested(msg, NL80211_ATTR_SCAN_SSIDS, ssids);
+
+ if (params->extra_ies) {
+ wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Sched scan extra IEs",
+ params->extra_ies, params->extra_ies_len);
+ NLA_PUT(msg, NL80211_ATTR_IE, params->extra_ies_len,
+ params->extra_ies);
+ }
+
+ if (params->freqs) {
+ for (i = 0; params->freqs[i]; i++) {
+ wpa_printf(MSG_MSGDUMP, "nl80211: Scan frequency %u "
+ "MHz", params->freqs[i]);
+ NLA_PUT_U32(freqs, i + 1, params->freqs[i]);
+ }
+ nla_put_nested(msg, NL80211_ATTR_SCAN_FREQUENCIES, freqs);
+ }
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+
+ /* TODO: if we get an error here, we should fall back to normal scan */
+
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: Sched scan start failed: "
+ "ret=%d (%s)", ret, strerror(-ret));
+ goto nla_put_failure;
+ }
+
+ wpa_printf(MSG_DEBUG, "nl80211: Sched scan requested (ret=%d) - "
+ "scan interval %d msec", ret, interval);
+
+nla_put_failure:
+ nlmsg_free(ssids);
+ nlmsg_free(msg);
+ nlmsg_free(freqs);
+ return ret;
+}
+
+
+/**
+ * wpa_driver_nl80211_stop_sched_scan - Stop a scheduled scan
+ * @priv: Pointer to private driver data from wpa_driver_nl80211_init()
+ * Returns: 0 on success, -1 on failure or if not supported
+ */
+static int wpa_driver_nl80211_stop_sched_scan(void *priv)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ int ret = 0;
+ struct nl_msg *msg;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -1;
+
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_STOP_SCHED_SCAN);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: Sched scan stop failed: "
+ "ret=%d (%s)", ret, strerror(-ret));
+ goto nla_put_failure;
+ }
+
+ wpa_printf(MSG_DEBUG, "nl80211: Sched scan stop sent (ret=%d)", ret);
+
+nla_put_failure:
+ nlmsg_free(msg);
return ret;
}
}
-struct nl80211_bss_info_arg {
- struct wpa_driver_nl80211_data *drv;
- struct wpa_scan_results *res;
-};
-
static int bss_info_handler(struct nl_msg *msg, void *arg)
{
struct nlattr *tb[NL80211_ATTR_MAX + 1];
const u8 *ie, *beacon_ie;
size_t ie_len, beacon_ie_len;
u8 *pos;
+ size_t i;
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
if (nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS],
bss_policy))
return NL_SKIP;
+ if (bss[NL80211_BSS_STATUS]) {
+ enum nl80211_bss_status status;
+ status = nla_get_u32(bss[NL80211_BSS_STATUS]);
+ if (status == NL80211_BSS_STATUS_ASSOCIATED &&
+ bss[NL80211_BSS_FREQUENCY]) {
+ _arg->assoc_freq =
+ nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
+ wpa_printf(MSG_DEBUG, "nl80211: Associated on %u MHz",
+ _arg->assoc_freq);
+ }
+ if (status == NL80211_BSS_STATUS_ASSOCIATED &&
+ bss[NL80211_BSS_BSSID]) {
+ os_memcpy(_arg->assoc_bssid,
+ nla_data(bss[NL80211_BSS_BSSID]), ETH_ALEN);
+ wpa_printf(MSG_DEBUG, "nl80211: Associated with "
+ MACSTR, MAC2STR(_arg->assoc_bssid));
+ }
+ }
+ if (!res)
+ return NL_SKIP;
if (bss[NL80211_BSS_INFORMATION_ELEMENTS]) {
ie = nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
ie_len = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
r->flags |= WPA_SCAN_LEVEL_DBM | WPA_SCAN_QUAL_INVALID;
} else if (bss[NL80211_BSS_SIGNAL_UNSPEC]) {
r->level = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]);
- r->flags |= WPA_SCAN_LEVEL_INVALID;
+ r->flags |= WPA_SCAN_QUAL_INVALID;
} else
r->flags |= WPA_SCAN_LEVEL_INVALID | WPA_SCAN_QUAL_INVALID;
if (bss[NL80211_BSS_TSF])
}
}
+ /*
+ * cfg80211 maintains separate BSS table entries for APs if the same
+ * BSSID,SSID pair is seen on multiple channels. wpa_supplicant does
+ * not use frequency as a separate key in the BSS table, so filter out
+ * duplicated entries. Prefer associated BSS entry in such a case in
+ * order to get the correct frequency into the BSS table.
+ */
+ for (i = 0; i < res->num; i++) {
+ const u8 *s1, *s2;
+ if (os_memcmp(res->res[i]->bssid, r->bssid, ETH_ALEN) != 0)
+ continue;
+
+ s1 = nl80211_get_ie((u8 *) (res->res[i] + 1),
+ res->res[i]->ie_len, WLAN_EID_SSID);
+ s2 = nl80211_get_ie((u8 *) (r + 1), r->ie_len, WLAN_EID_SSID);
+ if (s1 == NULL || s2 == NULL || s1[1] != s2[1] ||
+ os_memcmp(s1, s2, 2 + s1[1]) != 0)
+ continue;
+
+ /* Same BSSID,SSID was already included in scan results */
+ wpa_printf(MSG_DEBUG, "nl80211: Remove duplicated scan result "
+ "for " MACSTR, MAC2STR(r->bssid));
+
+ if ((r->flags & WPA_SCAN_ASSOCIATED) &&
+ !(res->res[i]->flags & WPA_SCAN_ASSOCIATED)) {
+ os_free(res->res[i]);
+ res->res[i] = r;
+ } else
+ os_free(r);
+ return NL_SKIP;
+ }
+
tmp = os_realloc(res->res,
(res->num + 1) * sizeof(struct wpa_scan_res *));
if (tmp == NULL) {
"indicates BSS status with " MACSTR
" as authenticated",
MAC2STR(r->bssid));
- if (drv->nlmode == NL80211_IFTYPE_STATION &&
+ if (is_sta_interface(drv->nlmode) &&
os_memcmp(r->bssid, drv->bssid, ETH_ALEN) != 0 &&
os_memcmp(r->bssid, drv->auth_bssid, ETH_ALEN) !=
0) {
"indicate BSS status with " MACSTR
" as associated",
MAC2STR(r->bssid));
- if (drv->nlmode == NL80211_IFTYPE_STATION &&
+ if (is_sta_interface(drv->nlmode) &&
!drv->associated) {
wpa_printf(MSG_DEBUG, "nl80211: Local state "
"(not associated) does not match "
"with BSS state");
clear_state_mismatch(drv, r->bssid);
- } else if (drv->nlmode == NL80211_IFTYPE_STATION &&
+ } else if (is_sta_interface(drv->nlmode) &&
os_memcmp(drv->bssid, r->bssid, ETH_ALEN) !=
0) {
wpa_printf(MSG_DEBUG, "nl80211: Local state "
}
-static void wpa_scan_results_free(struct wpa_scan_results *res)
-{
- size_t i;
-
- if (res == NULL)
- return;
-
- for (i = 0; i < res->num; i++)
- os_free(res->res[i]);
- os_free(res->res);
- os_free(res);
-}
-
-
static struct wpa_scan_results *
nl80211_get_scan_results(struct wpa_driver_nl80211_data *drv)
{
if (!msg)
goto nla_put_failure;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, NLM_F_DUMP,
- NL80211_CMD_GET_SCAN, 0);
+ nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SCAN);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
arg.drv = drv;
ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg);
msg = NULL;
if (ret == 0) {
- wpa_printf(MSG_DEBUG, "Received scan results (%lu BSSes)",
- (unsigned long) res->num);
+ wpa_printf(MSG_DEBUG, "nl80211: Received scan results (%lu "
+ "BSSes)", (unsigned long) res->num);
+ nl80211_get_noise_for_scan_results(drv, res);
return res;
}
wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d "
"set_tx=%d seq_len=%lu key_len=%lu",
__func__, ifindex, alg, addr, key_idx, set_tx,
(unsigned long) seq_len, (unsigned long) key_len);
+#ifdef CONFIG_TDLS
+ if (key_idx == -1)
+ key_idx = 0;
+#endif /* CONFIG_TDLS */
msg = nlmsg_alloc();
if (!msg)
return -ENOMEM;
if (alg == WPA_ALG_NONE) {
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
- 0, NL80211_CMD_DEL_KEY, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_KEY);
} else {
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
- 0, NL80211_CMD_NEW_KEY, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_NEW_KEY);
NLA_PUT(msg, NL80211_ATTR_KEY_DATA, key_len, key);
switch (alg) {
case WPA_ALG_WEP:
if (seq && seq_len)
NLA_PUT(msg, NL80211_ATTR_KEY_SEQ, seq_len, seq);
- if (addr && os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) != 0)
- {
+ if (addr && !is_broadcast_ether_addr(addr)) {
wpa_printf(MSG_DEBUG, " addr=" MACSTR, MAC2STR(addr));
NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
+
+ if (alg != WPA_ALG_WEP && key_idx && !set_tx) {
+ wpa_printf(MSG_DEBUG, " RSN IBSS RX GTK");
+ NLA_PUT_U32(msg, NL80211_ATTR_KEY_TYPE,
+ NL80211_KEYTYPE_GROUP);
+ }
+ } else if (addr && is_broadcast_ether_addr(addr)) {
+ struct nl_msg *types;
+ int err;
+ wpa_printf(MSG_DEBUG, " broadcast key");
+ types = nlmsg_alloc();
+ if (!types)
+ goto nla_put_failure;
+ NLA_PUT_FLAG(types, NL80211_KEY_DEFAULT_TYPE_MULTICAST);
+ err = nla_put_nested(msg, NL80211_ATTR_KEY_DEFAULT_TYPES,
+ types);
+ nlmsg_free(types);
+ if (err)
+ goto nla_put_failure;
}
NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
*/
if (ret || !set_tx || alg == WPA_ALG_NONE)
return ret;
-#ifdef HOSTAPD
- if (addr)
- return ret;
-#else /* HOSTAPD */
- if (drv->nlmode == NL80211_IFTYPE_AP && addr)
+ if (is_ap_interface(drv->nlmode) && addr &&
+ !is_broadcast_ether_addr(addr))
return ret;
-#endif /* HOSTAPD */
msg = nlmsg_alloc();
if (!msg)
return -ENOMEM;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
- 0, NL80211_CMD_SET_KEY, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_KEY);
NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
if (alg == WPA_ALG_IGTK)
NLA_PUT_FLAG(msg, NL80211_ATTR_KEY_DEFAULT_MGMT);
else
NLA_PUT_FLAG(msg, NL80211_ATTR_KEY_DEFAULT);
+ if (addr && is_broadcast_ether_addr(addr)) {
+ struct nl_msg *types;
+ int err;
+ types = nlmsg_alloc();
+ if (!types)
+ goto nla_put_failure;
+ NLA_PUT_FLAG(types, NL80211_KEY_DEFAULT_TYPE_MULTICAST);
+ err = nla_put_nested(msg, NL80211_ATTR_KEY_DEFAULT_TYPES,
+ types);
+ nlmsg_free(types);
+ if (err)
+ goto nla_put_failure;
+ } else if (addr) {
+ struct nl_msg *types;
+ int err;
+ types = nlmsg_alloc();
+ if (!types)
+ goto nla_put_failure;
+ NLA_PUT_FLAG(types, NL80211_KEY_DEFAULT_TYPE_UNICAST);
+ err = nla_put_nested(msg, NL80211_ATTR_KEY_DEFAULT_TYPES,
+ types);
+ nlmsg_free(types);
+ if (err)
+ goto nla_put_failure;
+ }
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
if (ret == -ENOENT)
return ret;
nla_put_failure:
+ nlmsg_free(msg);
return -ENOBUFS;
}
privacy = 1;
break;
}
+ if (params->wps == WPS_MODE_PRIVACY)
+ privacy = 1;
+ if (params->pairwise_suite &&
+ params->pairwise_suite != WPA_CIPHER_NONE)
+ privacy = 1;
+
if (!privacy)
return 0;
if (!msg)
return -1;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, cmd, 0);
+ nl80211_cmd(drv, msg, 0, cmd);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
NLA_PUT_U16(msg, NL80211_ATTR_REASON_CODE, reason_code);
static int wpa_driver_nl80211_disconnect(struct wpa_driver_nl80211_data *drv,
const u8 *addr, int reason_code)
{
- wpa_printf(MSG_DEBUG, "%s", __func__);
+ wpa_printf(MSG_DEBUG, "%s(addr=" MACSTR " reason_code=%d)",
+ __func__, MAC2STR(addr), reason_code);
drv->associated = 0;
return wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DISCONNECT,
reason_code, 0);
struct wpa_driver_nl80211_data *drv = bss->drv;
if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME))
return wpa_driver_nl80211_disconnect(drv, addr, reason_code);
- wpa_printf(MSG_DEBUG, "%s", __func__);
+ wpa_printf(MSG_DEBUG, "%s(addr=" MACSTR " reason_code=%d)",
+ __func__, MAC2STR(addr), reason_code);
drv->associated = 0;
+ if (drv->nlmode == NL80211_IFTYPE_ADHOC)
+ return nl80211_leave_ibss(drv);
return wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DEAUTHENTICATE,
reason_code, 0);
}
int ret = -1, i;
struct nl_msg *msg;
enum nl80211_auth_type type;
+ enum nl80211_iftype nlmode;
int count = 0;
drv->associated = 0;
os_memset(drv->auth_bssid, 0, ETH_ALEN);
/* FIX: IBSS mode */
- if (drv->nlmode != NL80211_IFTYPE_STATION)
- wpa_driver_nl80211_set_mode(priv, IEEE80211_MODE_INFRA);
-
- if (wpa_driver_nl80211_set_mode(priv, IEEE80211_MODE_INFRA) < 0)
+ nlmode = params->p2p ?
+ NL80211_IFTYPE_P2P_CLIENT : NL80211_IFTYPE_STATION;
+ if (drv->nlmode != nlmode &&
+ wpa_driver_nl80211_set_mode(priv, nlmode) < 0)
return -1;
retry:
wpa_printf(MSG_DEBUG, "nl80211: Authenticate (ifindex=%d)",
drv->ifindex);
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
- NL80211_CMD_AUTHENTICATE, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_AUTHENTICATE);
for (i = 0; i < 4; i++) {
if (!params->wep_key[i])
mode = &phy_info->modes[*(phy_info->num_modes)];
memset(mode, 0, sizeof(*mode));
+ mode->flags = HOSTAPD_MODE_FLAG_HT_INFO_KNOWN;
*(phy_info->num_modes) += 1;
nla_parse(tb_band, NL80211_BAND_ATTR_MAX, nla_data(nl_band),
}
+static void nl80211_set_ht40_mode(struct hostapd_hw_modes *mode, int start,
+ int end)
+{
+ int c;
+
+ for (c = 0; c < mode->num_channels; c++) {
+ struct hostapd_channel_data *chan = &mode->channels[c];
+ if (chan->freq - 10 >= start && chan->freq + 10 <= end)
+ chan->flag |= HOSTAPD_CHAN_HT40;
+ }
+}
+
+
+static void nl80211_set_ht40_mode_sec(struct hostapd_hw_modes *mode, int start,
+ int end)
+{
+ int c;
+
+ for (c = 0; c < mode->num_channels; c++) {
+ struct hostapd_channel_data *chan = &mode->channels[c];
+ if (!(chan->flag & HOSTAPD_CHAN_HT40))
+ continue;
+ if (chan->freq - 30 >= start && chan->freq - 10 <= end)
+ chan->flag |= HOSTAPD_CHAN_HT40MINUS;
+ if (chan->freq + 10 >= start && chan->freq + 30 <= end)
+ chan->flag |= HOSTAPD_CHAN_HT40PLUS;
+ }
+}
+
+
+static void nl80211_reg_rule_ht40(struct nlattr *tb[],
+ struct phy_info_arg *results)
+{
+ u32 start, end, max_bw;
+ u16 m;
+
+ if (tb[NL80211_ATTR_FREQ_RANGE_START] == NULL ||
+ tb[NL80211_ATTR_FREQ_RANGE_END] == NULL ||
+ tb[NL80211_ATTR_FREQ_RANGE_MAX_BW] == NULL)
+ return;
+
+ start = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]) / 1000;
+ end = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]) / 1000;
+ max_bw = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000;
+
+ wpa_printf(MSG_DEBUG, "nl80211: %u-%u @ %u MHz",
+ start, end, max_bw);
+ if (max_bw < 40)
+ return;
+
+ for (m = 0; m < *results->num_modes; m++) {
+ if (!(results->modes[m].ht_capab &
+ HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
+ continue;
+ nl80211_set_ht40_mode(&results->modes[m], start, end);
+ }
+}
+
+
+static void nl80211_reg_rule_sec(struct nlattr *tb[],
+ struct phy_info_arg *results)
+{
+ u32 start, end, max_bw;
+ u16 m;
+
+ if (tb[NL80211_ATTR_FREQ_RANGE_START] == NULL ||
+ tb[NL80211_ATTR_FREQ_RANGE_END] == NULL ||
+ tb[NL80211_ATTR_FREQ_RANGE_MAX_BW] == NULL)
+ return;
+
+ start = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]) / 1000;
+ end = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]) / 1000;
+ max_bw = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000;
+
+ if (max_bw < 20)
+ return;
+
+ for (m = 0; m < *results->num_modes; m++) {
+ if (!(results->modes[m].ht_capab &
+ HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
+ continue;
+ nl80211_set_ht40_mode_sec(&results->modes[m], start, end);
+ }
+}
+
+
+static int nl80211_get_reg(struct nl_msg *msg, void *arg)
+{
+ struct phy_info_arg *results = arg;
+ struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct nlattr *nl_rule;
+ struct nlattr *tb_rule[NL80211_FREQUENCY_ATTR_MAX + 1];
+ int rem_rule;
+ static struct nla_policy reg_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = {
+ [NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 },
+ [NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 },
+ [NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 },
+ [NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 },
+ [NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 },
+ [NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 },
+ };
+
+ nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+ if (!tb_msg[NL80211_ATTR_REG_ALPHA2] ||
+ !tb_msg[NL80211_ATTR_REG_RULES]) {
+ wpa_printf(MSG_DEBUG, "nl80211: No regulatory information "
+ "available");
+ return NL_SKIP;
+ }
+
+ wpa_printf(MSG_DEBUG, "nl80211: Regulatory information - country=%s",
+ (char *) nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2]));
+
+ nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule)
+ {
+ nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX,
+ nla_data(nl_rule), nla_len(nl_rule), reg_policy);
+ nl80211_reg_rule_ht40(tb_rule, results);
+ }
+
+ nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule)
+ {
+ nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX,
+ nla_data(nl_rule), nla_len(nl_rule), reg_policy);
+ nl80211_reg_rule_sec(tb_rule, results);
+ }
+
+ return NL_SKIP;
+}
+
+
+static int nl80211_set_ht40_flags(struct wpa_driver_nl80211_data *drv,
+ struct phy_info_arg *results)
+{
+ struct nl_msg *msg;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_REG);
+ return send_and_recv_msgs(drv, msg, nl80211_get_reg, results);
+}
+
+
static struct hostapd_hw_modes *
wpa_driver_nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags)
{
if (!msg)
return NULL;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
- 0, NL80211_CMD_GET_WIPHY, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_WIPHY);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
- if (send_and_recv_msgs(drv, msg, phy_info_handler, &result) == 0)
+ if (send_and_recv_msgs(drv, msg, phy_info_handler, &result) == 0) {
+ nl80211_set_ht40_flags(drv, &result);
return wpa_driver_nl80211_add_11b(result.modes, num_modes);
+ }
+ msg = NULL;
nla_put_failure:
+ nlmsg_free(msg);
return NULL;
}
.msg_controllen = 0,
.msg_flags = 0,
};
+ int res;
if (encrypt)
rtap_hdr[8] |= IEEE80211_RADIOTAP_F_WEP;
- return sendmsg(drv->monitor_sock, &msg, 0);
+ if (drv->monitor_sock < 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: No monitor socket available "
+ "for %s", __func__);
+ return -1;
+ }
+
+ res = sendmsg(drv->monitor_sock, &msg, 0);
+ if (res < 0) {
+ wpa_printf(MSG_INFO, "nl80211: sendmsg: %s", strerror(errno));
+ return -1;
+ }
+ return 0;
}
mgmt = (struct ieee80211_mgmt *) data;
fc = le_to_host16(mgmt->frame_control);
+ if (is_sta_interface(drv->nlmode) &&
+ WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
+ WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_RESP) {
+ /*
+ * The use of last_mgmt_freq is a bit of a hack,
+ * but it works due to the single-threaded nature
+ * of wpa_supplicant.
+ */
+ return nl80211_send_frame_cmd(drv, drv->last_mgmt_freq, 0,
+ data, data_len, NULL, 1);
+ }
+
+ if (drv->no_monitor_iface_capab && is_ap_interface(drv->nlmode)) {
+ return nl80211_send_frame_cmd(drv, drv->ap_oper_freq, 0,
+ data, data_len,
+ &drv->send_action_cookie, 0);
+ }
+
if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_AUTH) {
/*
}
-static int wpa_driver_nl80211_set_beacon(void *priv,
- const u8 *head, size_t head_len,
- const u8 *tail, size_t tail_len,
- int dtim_period, int beacon_int)
+static int nl80211_set_ap_isolate(struct i802_bss *bss, int enabled)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_BSS);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname));
+ NLA_PUT_U8(msg, NL80211_ATTR_AP_ISOLATE, enabled);
+
+ return send_and_recv_msgs(drv, msg, NULL, NULL);
+ nla_put_failure:
+ return -ENOBUFS;
+}
+
+
+static int nl80211_set_bss(struct i802_bss *bss, int cts, int preamble,
+ int slot, int ht_opmode)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_BSS);
+
+ if (cts >= 0)
+ NLA_PUT_U8(msg, NL80211_ATTR_BSS_CTS_PROT, cts);
+ if (preamble >= 0)
+ NLA_PUT_U8(msg, NL80211_ATTR_BSS_SHORT_PREAMBLE, preamble);
+ if (slot >= 0)
+ NLA_PUT_U8(msg, NL80211_ATTR_BSS_SHORT_SLOT_TIME, slot);
+ if (ht_opmode >= 0)
+ NLA_PUT_U16(msg, NL80211_ATTR_BSS_HT_OPMODE, ht_opmode);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname));
+
+ return send_and_recv_msgs(drv, msg, NULL, NULL);
+ nla_put_failure:
+ nlmsg_free(msg);
+ return -ENOBUFS;
+}
+
+
+static int wpa_driver_nl80211_set_ap(void *priv,
+ struct wpa_driver_ap_params *params)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
int ret;
int beacon_set;
int ifindex = if_nametoindex(bss->ifname);
+ int num_suites;
+ u32 suites[10];
+ u32 ver;
beacon_set = bss->beacon_set;
if (beacon_set)
cmd = NL80211_CMD_SET_BEACON;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
- 0, cmd, 0);
- NLA_PUT(msg, NL80211_ATTR_BEACON_HEAD, head_len, head);
- NLA_PUT(msg, NL80211_ATTR_BEACON_TAIL, tail_len, tail);
+ nl80211_cmd(drv, msg, 0, cmd);
+ NLA_PUT(msg, NL80211_ATTR_BEACON_HEAD, params->head_len, params->head);
+ NLA_PUT(msg, NL80211_ATTR_BEACON_TAIL, params->tail_len, params->tail);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
- NLA_PUT_U32(msg, NL80211_ATTR_BEACON_INTERVAL, beacon_int);
- NLA_PUT_U32(msg, NL80211_ATTR_DTIM_PERIOD, dtim_period);
+ NLA_PUT_U32(msg, NL80211_ATTR_BEACON_INTERVAL, params->beacon_int);
+ NLA_PUT_U32(msg, NL80211_ATTR_DTIM_PERIOD, params->dtim_period);
+ NLA_PUT(msg, NL80211_ATTR_SSID, params->ssid_len,
+ params->ssid);
+ switch (params->hide_ssid) {
+ case NO_SSID_HIDING:
+ NLA_PUT_U32(msg, NL80211_ATTR_HIDDEN_SSID,
+ NL80211_HIDDEN_SSID_NOT_IN_USE);
+ break;
+ case HIDDEN_SSID_ZERO_LEN:
+ NLA_PUT_U32(msg, NL80211_ATTR_HIDDEN_SSID,
+ NL80211_HIDDEN_SSID_ZERO_LEN);
+ break;
+ case HIDDEN_SSID_ZERO_CONTENTS:
+ NLA_PUT_U32(msg, NL80211_ATTR_HIDDEN_SSID,
+ NL80211_HIDDEN_SSID_ZERO_CONTENTS);
+ break;
+ }
+ if (params->privacy)
+ NLA_PUT_FLAG(msg, NL80211_ATTR_PRIVACY);
+ if ((params->auth_algs & (WPA_AUTH_ALG_OPEN | WPA_AUTH_ALG_SHARED)) ==
+ (WPA_AUTH_ALG_OPEN | WPA_AUTH_ALG_SHARED)) {
+ /* Leave out the attribute */
+ } else if (params->auth_algs & WPA_AUTH_ALG_SHARED)
+ NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE,
+ NL80211_AUTHTYPE_SHARED_KEY);
+ else
+ NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE,
+ NL80211_AUTHTYPE_OPEN_SYSTEM);
+
+ ver = 0;
+ if (params->wpa_version & WPA_PROTO_WPA)
+ ver |= NL80211_WPA_VERSION_1;
+ if (params->wpa_version & WPA_PROTO_RSN)
+ ver |= NL80211_WPA_VERSION_2;
+ if (ver)
+ NLA_PUT_U32(msg, NL80211_ATTR_WPA_VERSIONS, ver);
+
+ num_suites = 0;
+ if (params->key_mgmt_suites & WPA_KEY_MGMT_IEEE8021X)
+ suites[num_suites++] = WLAN_AKM_SUITE_8021X;
+ if (params->key_mgmt_suites & WPA_KEY_MGMT_PSK)
+ suites[num_suites++] = WLAN_AKM_SUITE_PSK;
+ if (num_suites) {
+ NLA_PUT(msg, NL80211_ATTR_AKM_SUITES,
+ num_suites * sizeof(u32), suites);
+ }
+
+ num_suites = 0;
+ if (params->pairwise_ciphers & WPA_CIPHER_CCMP)
+ suites[num_suites++] = WLAN_CIPHER_SUITE_CCMP;
+ if (params->pairwise_ciphers & WPA_CIPHER_TKIP)
+ suites[num_suites++] = WLAN_CIPHER_SUITE_TKIP;
+ if (params->pairwise_ciphers & WPA_CIPHER_WEP104)
+ suites[num_suites++] = WLAN_CIPHER_SUITE_WEP104;
+ if (params->pairwise_ciphers & WPA_CIPHER_WEP40)
+ suites[num_suites++] = WLAN_CIPHER_SUITE_WEP40;
+ if (num_suites) {
+ NLA_PUT(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE,
+ num_suites * sizeof(u32), suites);
+ }
+
+ switch (params->group_cipher) {
+ case WPA_CIPHER_CCMP:
+ NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP,
+ WLAN_CIPHER_SUITE_CCMP);
+ break;
+ case WPA_CIPHER_TKIP:
+ NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP,
+ WLAN_CIPHER_SUITE_TKIP);
+ break;
+ case WPA_CIPHER_WEP104:
+ NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP,
+ WLAN_CIPHER_SUITE_WEP104);
+ break;
+ case WPA_CIPHER_WEP40:
+ NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP,
+ WLAN_CIPHER_SUITE_WEP40);
+ break;
+ }
+
+ if (params->beacon_ies) {
+ NLA_PUT(msg, NL80211_ATTR_IE, wpabuf_len(params->beacon_ies),
+ wpabuf_head(params->beacon_ies));
+ }
+ if (params->proberesp_ies) {
+ NLA_PUT(msg, NL80211_ATTR_IE_PROBE_RESP,
+ wpabuf_len(params->proberesp_ies),
+ wpabuf_head(params->proberesp_ies));
+ }
+ if (params->assocresp_ies) {
+ NLA_PUT(msg, NL80211_ATTR_IE_ASSOC_RESP,
+ wpabuf_len(params->assocresp_ies),
+ wpabuf_head(params->assocresp_ies));
+ }
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
if (ret) {
ret, strerror(-ret));
} else {
bss->beacon_set = 1;
+ ret = nl80211_set_ap_isolate(bss, params->isolate);
+ if (!params->isolate && ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: Ignore AP isolation "
+ "configuration error since isolation is "
+ "not used");
+ ret = 0;
+ }
+
+ nl80211_set_bss(bss, params->cts_protect, params->preamble,
+ params->short_slot_time, params->ht_opmode);
}
return ret;
nla_put_failure:
+ nlmsg_free(msg);
return -ENOBUFS;
}
struct nl_msg *msg;
int ret;
+ wpa_printf(MSG_DEBUG, "nl80211: Set freq %d (ht_enabled=%d "
+ "sec_channel_offset=%d)",
+ freq, ht_enabled, sec_channel_offset);
msg = nlmsg_alloc();
if (!msg)
return -1;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
- NL80211_CMD_SET_WIPHY, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_WIPHY);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
}
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ msg = NULL;
if (ret == 0)
return 0;
wpa_printf(MSG_DEBUG, "nl80211: Failed to set channel (freq=%d): "
"%d (%s)", freq, ret, strerror(-ret));
nla_put_failure:
+ nlmsg_free(msg);
return -1;
}
+static u32 sta_flags_nl80211(int flags)
+{
+ u32 f = 0;
+
+ if (flags & WPA_STA_AUTHORIZED)
+ f |= BIT(NL80211_STA_FLAG_AUTHORIZED);
+ if (flags & WPA_STA_WMM)
+ f |= BIT(NL80211_STA_FLAG_WME);
+ if (flags & WPA_STA_SHORT_PREAMBLE)
+ f |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE);
+ if (flags & WPA_STA_MFP)
+ f |= BIT(NL80211_STA_FLAG_MFP);
+ if (flags & WPA_STA_TDLS_PEER)
+ f |= BIT(NL80211_STA_FLAG_TDLS_PEER);
+
+ return f;
+}
+
+
static int wpa_driver_nl80211_sta_add(void *priv,
struct hostapd_sta_add_params *params)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
+ struct nl80211_sta_flag_update upd;
int ret = -ENOBUFS;
+ if ((params->flags & WPA_STA_TDLS_PEER) &&
+ !(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT))
+ return -EOPNOTSUPP;
+
msg = nlmsg_alloc();
if (!msg)
return -ENOMEM;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
- 0, NL80211_CMD_NEW_STATION, 0);
+ nl80211_cmd(drv, msg, 0, params->set ? NL80211_CMD_SET_STATION :
+ NL80211_CMD_NEW_STATION);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname));
NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->addr);
- NLA_PUT_U16(msg, NL80211_ATTR_STA_AID, params->aid);
NLA_PUT(msg, NL80211_ATTR_STA_SUPPORTED_RATES, params->supp_rates_len,
params->supp_rates);
- NLA_PUT_U16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL,
- params->listen_interval);
+ if (!params->set) {
+ NLA_PUT_U16(msg, NL80211_ATTR_STA_AID, params->aid);
+ NLA_PUT_U16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL,
+ params->listen_interval);
+ }
if (params->ht_capabilities) {
NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY,
sizeof(*params->ht_capabilities),
params->ht_capabilities);
}
+ os_memset(&upd, 0, sizeof(upd));
+ upd.mask = sta_flags_nl80211(params->flags);
+ upd.set = upd.mask;
+ NLA_PUT(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd);
+
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ msg = NULL;
if (ret)
- wpa_printf(MSG_DEBUG, "nl80211: NL80211_CMD_NEW_STATION "
- "result: %d (%s)", ret, strerror(-ret));
+ wpa_printf(MSG_DEBUG, "nl80211: NL80211_CMD_%s_STATION "
+ "result: %d (%s)", params->set ? "SET" : "NEW", ret,
+ strerror(-ret));
if (ret == -EEXIST)
ret = 0;
nla_put_failure:
+ nlmsg_free(msg);
return ret;
}
if (!msg)
return -ENOMEM;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
- 0, NL80211_CMD_DEL_STATION, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_STATION);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX,
if_nametoindex(bss->ifname));
return 0;
return ret;
nla_put_failure:
+ nlmsg_free(msg);
return -ENOBUFS;
}
wpa_printf(MSG_DEBUG, "nl80211: Remove interface ifindex=%d", ifidx);
-#ifdef HOSTAPD
/* stop listening for EAPOL on this interface */
del_ifidx(drv, ifidx);
-#endif /* HOSTAPD */
msg = nlmsg_alloc();
if (!msg)
goto nla_put_failure;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
- 0, NL80211_CMD_DEL_INTERFACE, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_INTERFACE);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifidx);
if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0)
return;
+ msg = NULL;
nla_put_failure:
+ nlmsg_free(msg);
wpa_printf(MSG_ERROR, "Failed to remove interface (ifidx=%d)", ifidx);
}
+static const char * nl80211_iftype_str(enum nl80211_iftype mode)
+{
+ switch (mode) {
+ case NL80211_IFTYPE_ADHOC:
+ return "ADHOC";
+ case NL80211_IFTYPE_STATION:
+ return "STATION";
+ case NL80211_IFTYPE_AP:
+ return "AP";
+ case NL80211_IFTYPE_MONITOR:
+ return "MONITOR";
+ case NL80211_IFTYPE_P2P_CLIENT:
+ return "P2P_CLIENT";
+ case NL80211_IFTYPE_P2P_GO:
+ return "P2P_GO";
+ default:
+ return "unknown";
+ }
+}
+
+
static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv,
const char *ifname,
enum nl80211_iftype iftype,
int ifidx;
int ret = -ENOBUFS;
+ wpa_printf(MSG_DEBUG, "nl80211: Create interface iftype %d (%s)",
+ iftype, nl80211_iftype_str(iftype));
+
msg = nlmsg_alloc();
if (!msg)
return -1;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
- 0, NL80211_CMD_NEW_INTERFACE, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_NEW_INTERFACE);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, ifname);
NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, iftype);
}
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ msg = NULL;
if (ret) {
nla_put_failure:
+ nlmsg_free(msg);
wpa_printf(MSG_ERROR, "Failed to create interface %s: %d (%s)",
ifname, ret, strerror(-ret));
return ret;
if (ifidx <= 0)
return -1;
-#ifdef HOSTAPD
/* start listening for EAPOL on this interface */
add_ifidx(drv, ifidx);
-#endif /* HOSTAPD */
if (addr && iftype != NL80211_IFTYPE_MONITOR &&
- linux_set_ifhwaddr(drv->ioctl_sock, ifname, addr)) {
+ linux_set_ifhwaddr(drv->global->ioctl_sock, ifname, addr)) {
nl80211_remove_iface(drv, ifidx);
return -1;
}
ret = nl80211_create_iface_once(drv, ifname, iftype, addr, wds);
- /* if error occured and interface exists already */
+ /* if error occurred and interface exists already */
if (ret == -ENFILE && if_nametoindex(ifname)) {
wpa_printf(MSG_INFO, "Try to remove and re-create %s", ifname);
wds);
}
- if (ret >= 0 && drv->disable_11b_rates)
+ if (ret >= 0 && is_p2p_interface(iftype))
nl80211_disable_11b_rates(drv, ret, 1);
return ret;
static void from_unknown_sta(struct wpa_driver_nl80211_data *drv,
u8 *buf, size_t len)
{
+ struct ieee80211_hdr *hdr = (void *)buf;
+ u16 fc;
union wpa_event_data event;
+
+ if (len < sizeof(*hdr))
+ return;
+
+ fc = le_to_host16(hdr->frame_control);
+
os_memset(&event, 0, sizeof(event));
- event.rx_from_unknown.frame = buf;
- event.rx_from_unknown.len = len;
+ event.rx_from_unknown.bssid = get_hdr_bssid(hdr, len);
+ event.rx_from_unknown.addr = hdr->addr2;
+ event.rx_from_unknown.wds = (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) ==
+ (WLAN_FC_FROMDS | WLAN_FC_TODS);
wpa_supplicant_event(drv->ctx, EVENT_RX_FROM_UNKNOWN, &event);
}
return;
}
- if (drv->nlmode == NL80211_IFTYPE_STATION && !drv->probe_req_report) {
- wpa_printf(MSG_DEBUG, "nl80211: Ignore monitor interface "
- "frame since Probe Request reporting is disabled");
- return;
- }
-
if (ieee80211_radiotap_iterator_init(&iter, (void*)buf, len)) {
printf("received invalid radiotap frame\n");
return;
* add a filter here that filters on our DA and that flag
* to allow us to deauth frames to that bad station.
*
- * Not a regression -- we didn't do it before either.
+ * For now allow all To DS data frames through.
*/
+ /* load the IEEE 802.11 frame control field */
+ BPF_STMT(BPF_LD | BPF_H | BPF_IND, 0),
+ /* mask off frame type, version and DS status */
+ BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x0F03),
+ /* accept frame if version 0, type 2 and To DS, fall through otherwise
+ */
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x0801, PASS, 0),
#if 0
/*
int optval;
socklen_t optlen;
- snprintf(buf, IFNAMSIZ, "mon.%s", drv->first_bss.ifname);
+ if (os_strncmp(drv->first_bss.ifname, "p2p-", 4) == 0) {
+ /*
+ * P2P interface name is of the format p2p-%s-%d. For monitor
+ * interface name corresponding to P2P GO, replace "p2p-" with
+ * "mon-" to retain the same interface name length and to
+ * indicate that it is a monitor interface.
+ */
+ snprintf(buf, IFNAMSIZ, "mon-%s", drv->first_bss.ifname + 4);
+ } else {
+ /* Non-P2P interface with AP functionality. */
+ snprintf(buf, IFNAMSIZ, "mon.%s", drv->first_bss.ifname);
+ }
+
buf[IFNAMSIZ - 1] = '\0';
drv->monitor_ifidx =
nl80211_create_iface(drv, buf, NL80211_IFTYPE_MONITOR, NULL,
0);
+ if (drv->monitor_ifidx == -EOPNOTSUPP) {
+ wpa_printf(MSG_DEBUG, "nl80211: Driver does not support "
+ "monitor interface type - try to run without it");
+ drv->no_monitor_iface_capab = 1;
+ }
+
if (drv->monitor_ifidx < 0)
return -1;
- if (linux_set_iface_flags(drv->ioctl_sock, buf, 1))
+ if (linux_set_iface_flags(drv->global->ioctl_sock, buf, 1))
goto error;
memset(&ll, 0, sizeof(ll));
}
+#ifdef CONFIG_AP
+static int nl80211_send_eapol_data(struct i802_bss *bss,
+ const u8 *addr, const u8 *data,
+ size_t data_len, const u8 *own_addr)
+{
+ if (bss->drv->l2 == NULL) {
+ wpa_printf(MSG_DEBUG, "nl80211: No l2_packet to send EAPOL");
+ return -1;
+ }
+
+ if (l2_packet_send(bss->drv->l2, addr, ETH_P_EAPOL, data, data_len) <
+ 0)
+ return -1;
+ return 0;
+}
+#endif /* CONFIG_AP */
+
+
static const u8 rfc1042_header[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
static int wpa_driver_nl80211_hapd_send_eapol(
void *priv, const u8 *addr, const u8 *data,
- size_t data_len, int encrypt, const u8 *own_addr)
+ size_t data_len, int encrypt, const u8 *own_addr, u32 flags)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
size_t len;
u8 *pos;
int res;
-#if 0 /* FIX */
- int qos = sta->flags & WPA_STA_WMM;
-#else
- int qos = 0;
-#endif
+ int qos = flags & WPA_STA_WMM;
+
+#ifdef CONFIG_AP
+ if (drv->no_monitor_iface_capab)
+ return nl80211_send_eapol_data(bss, addr, data, data_len,
+ own_addr);
+#endif /* CONFIG_AP */
len = sizeof(*hdr) + (qos ? 2 : 0) + sizeof(rfc1042_header) + 2 +
data_len;
hdr->frame_control |= host_to_le16(WLAN_FC_FROMDS);
if (encrypt)
hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP);
-#if 0 /* To be enabled if qos determination is added above */
if (qos) {
hdr->frame_control |=
host_to_le16(WLAN_FC_STYPE_QOS_DATA << 4);
}
-#endif
memcpy(hdr->IEEE80211_DA_FROMDS, addr, ETH_ALEN);
memcpy(hdr->IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN);
memcpy(hdr->IEEE80211_SA_FROMDS, own_addr, ETH_ALEN);
pos = (u8 *) (hdr + 1);
-#if 0 /* To be enabled if qos determination is added above */
if (qos) {
/* add an empty QoS header if needed */
pos[0] = 0;
pos[1] = 0;
pos += 2;
}
-#endif
memcpy(pos, rfc1042_header, sizeof(rfc1042_header));
pos += sizeof(rfc1042_header);
"failed: %d (%s)",
(unsigned long) len, errno, strerror(errno));
}
- os_free(hdr);
-
- return res;
-}
-
-
-static u32 sta_flags_nl80211(int flags)
-{
- u32 f = 0;
-
- if (flags & WPA_STA_AUTHORIZED)
- f |= BIT(NL80211_STA_FLAG_AUTHORIZED);
- if (flags & WPA_STA_WMM)
- f |= BIT(NL80211_STA_FLAG_WME);
- if (flags & WPA_STA_SHORT_PREAMBLE)
- f |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE);
- if (flags & WPA_STA_MFP)
- f |= BIT(NL80211_STA_FLAG_MFP);
+ os_free(hdr);
- return f;
+ return res;
}
return -ENOMEM;
}
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
- 0, NL80211_CMD_SET_STATION, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_STATION);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX,
if_nametoindex(bss->ifname));
if (total_flags & WPA_STA_MFP)
NLA_PUT_FLAG(flags, NL80211_STA_FLAG_MFP);
+ if (total_flags & WPA_STA_TDLS_PEER)
+ NLA_PUT_FLAG(flags, NL80211_STA_FLAG_TDLS_PEER);
+
if (nla_put_nested(msg, NL80211_ATTR_STA_FLAGS, flags))
goto nla_put_failure;
return send_and_recv_msgs(drv, msg, NULL, NULL);
nla_put_failure:
+ nlmsg_free(msg);
nlmsg_free(flags);
return -ENOBUFS;
}
static int wpa_driver_nl80211_ap(struct wpa_driver_nl80211_data *drv,
struct wpa_driver_associate_params *params)
{
- if (wpa_driver_nl80211_set_mode(&drv->first_bss, params->mode) ||
+ enum nl80211_iftype nlmode;
+
+ if (params->p2p) {
+ wpa_printf(MSG_DEBUG, "nl80211: Setup AP operations for P2P "
+ "group (GO)");
+ nlmode = NL80211_IFTYPE_P2P_GO;
+ } else
+ nlmode = NL80211_IFTYPE_AP;
+
+ if (wpa_driver_nl80211_set_mode(&drv->first_bss, nlmode) ||
wpa_driver_nl80211_set_freq(drv, params->freq, 0, 0)) {
nl80211_remove_monitor_interface(drv);
return -1;
}
- /* TODO: setup monitor interface (and add code somewhere to remove this
- * when AP mode is stopped; associate with mode != 2 or drv_deinit) */
+ if (drv->no_monitor_iface_capab) {
+ if (wpa_driver_nl80211_probe_req_report(&drv->first_bss, 1) < 0)
+ {
+ wpa_printf(MSG_DEBUG, "nl80211: Failed to enable "
+ "Probe Request frame reporting in AP mode");
+ /* Try to survive without this */
+ }
+ }
+
+ drv->ap_oper_freq = params->freq;
return 0;
}
if (!msg)
return -1;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
- NL80211_CMD_LEAVE_IBSS, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_LEAVE_IBSS);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
msg = NULL;
wpa_printf(MSG_DEBUG, "nl80211: Join IBSS (ifindex=%d)", drv->ifindex);
- if (wpa_driver_nl80211_set_mode(&drv->first_bss, params->mode)) {
+ if (wpa_driver_nl80211_set_mode(&drv->first_bss,
+ NL80211_IFTYPE_ADHOC)) {
wpa_printf(MSG_INFO, "nl80211: Failed to set interface into "
"IBSS mode");
return -1;
if (!msg)
return -1;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
- NL80211_CMD_JOIN_IBSS, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_JOIN_IBSS);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
if (params->ssid == NULL || params->ssid_len > sizeof(drv->ssid))
}
+static unsigned int nl80211_get_assoc_bssid(struct wpa_driver_nl80211_data *drv,
+ u8 *bssid)
+{
+ struct nl_msg *msg;
+ int ret;
+ struct nl80211_bss_info_arg arg;
+
+ os_memset(&arg, 0, sizeof(arg));
+ msg = nlmsg_alloc();
+ if (!msg)
+ goto nla_put_failure;
+
+ nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SCAN);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+
+ arg.drv = drv;
+ ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg);
+ msg = NULL;
+ if (ret == 0) {
+ if (is_zero_ether_addr(arg.assoc_bssid))
+ return -ENOTCONN;
+ os_memcpy(bssid, arg.assoc_bssid, ETH_ALEN);
+ return 0;
+ }
+ wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d "
+ "(%s)", ret, strerror(-ret));
+nla_put_failure:
+ nlmsg_free(msg);
+ return drv->assoc_freq;
+}
+
+
+static int nl80211_disconnect(struct wpa_driver_nl80211_data *drv,
+ const u8 *bssid)
+{
+ u8 addr[ETH_ALEN];
+
+ if (bssid == NULL) {
+ int res = nl80211_get_assoc_bssid(drv, addr);
+ if (res)
+ return res;
+ bssid = addr;
+ }
+
+ return wpa_driver_nl80211_disconnect(drv, bssid,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+}
+
+
static int wpa_driver_nl80211_connect(
struct wpa_driver_nl80211_data *drv,
struct wpa_driver_associate_params *params)
struct nl_msg *msg;
enum nl80211_auth_type type;
int ret = 0;
+ int algs;
msg = nlmsg_alloc();
if (!msg)
return -1;
wpa_printf(MSG_DEBUG, "nl80211: Connect (ifindex=%d)", drv->ifindex);
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
- NL80211_CMD_CONNECT, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_CONNECT);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
if (params->bssid) {
NLA_PUT(msg, NL80211_ATTR_IE, params->wpa_ie_len,
params->wpa_ie);
+ algs = 0;
+ if (params->auth_alg & WPA_AUTH_ALG_OPEN)
+ algs++;
+ if (params->auth_alg & WPA_AUTH_ALG_SHARED)
+ algs++;
+ if (params->auth_alg & WPA_AUTH_ALG_LEAP)
+ algs++;
+ if (algs > 1) {
+ wpa_printf(MSG_DEBUG, " * Leave out Auth Type for automatic "
+ "selection");
+ goto skip_auth_type;
+ }
+
if (params->auth_alg & WPA_AUTH_ALG_OPEN)
type = NL80211_AUTHTYPE_OPEN_SYSTEM;
else if (params->auth_alg & WPA_AUTH_ALG_SHARED)
wpa_printf(MSG_DEBUG, " * Auth Type %d", type);
NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE, type);
- if (params->wpa_ie && params->wpa_ie_len) {
- enum nl80211_wpa_versions ver;
+skip_auth_type:
+ if (params->wpa_proto) {
+ enum nl80211_wpa_versions ver = 0;
- if (params->wpa_ie[0] == WLAN_EID_RSN)
- ver = NL80211_WPA_VERSION_2;
- else
- ver = NL80211_WPA_VERSION_1;
+ if (params->wpa_proto & WPA_PROTO_WPA)
+ ver |= NL80211_WPA_VERSION_1;
+ if (params->wpa_proto & WPA_PROTO_RSN)
+ ver |= NL80211_WPA_VERSION_2;
- wpa_printf(MSG_DEBUG, " * WPA Version %d", ver);
+ wpa_printf(MSG_DEBUG, " * WPA Versions 0x%x", ver);
NLA_PUT_U32(msg, NL80211_ATTR_WPA_VERSIONS, ver);
}
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: MLME connect failed: ret=%d "
"(%s)", ret, strerror(-ret));
+ /*
+ * cfg80211 does not currently accept new connection if we are
+ * already connected. As a workaround, force disconnection and
+ * try again once the driver indicates it completed
+ * disconnection.
+ */
+ if (ret == -EALREADY)
+ nl80211_disconnect(drv, params->bssid);
goto nla_put_failure;
}
ret = 0;
return wpa_driver_nl80211_ibss(drv, params);
if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) {
- if (wpa_driver_nl80211_set_mode(priv, params->mode) < 0)
+ enum nl80211_iftype nlmode = params->p2p ?
+ NL80211_IFTYPE_P2P_CLIENT : NL80211_IFTYPE_STATION;
+
+ if (wpa_driver_nl80211_set_mode(priv, nlmode) < 0)
return -1;
return wpa_driver_nl80211_connect(drv, params);
}
wpa_printf(MSG_DEBUG, "nl80211: Associate (ifindex=%d)",
drv->ifindex);
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
- NL80211_CMD_ASSOCIATE, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_ASSOCIATE);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
if (params->bssid) {
NLA_PUT(msg, NL80211_ATTR_IE, params->wpa_ie_len,
params->wpa_ie);
+ if (params->pairwise_suite != CIPHER_NONE) {
+ int cipher;
+
+ switch (params->pairwise_suite) {
+ case CIPHER_WEP40:
+ cipher = WLAN_CIPHER_SUITE_WEP40;
+ break;
+ case CIPHER_WEP104:
+ cipher = WLAN_CIPHER_SUITE_WEP104;
+ break;
+ case CIPHER_CCMP:
+ cipher = WLAN_CIPHER_SUITE_CCMP;
+ break;
+ case CIPHER_TKIP:
+ default:
+ cipher = WLAN_CIPHER_SUITE_TKIP;
+ break;
+ }
+ wpa_printf(MSG_DEBUG, " * pairwise=0x%x", cipher);
+ NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE, cipher);
+ }
+
+ if (params->group_suite != CIPHER_NONE) {
+ int cipher;
+
+ switch (params->group_suite) {
+ case CIPHER_WEP40:
+ cipher = WLAN_CIPHER_SUITE_WEP40;
+ break;
+ case CIPHER_WEP104:
+ cipher = WLAN_CIPHER_SUITE_WEP104;
+ break;
+ case CIPHER_CCMP:
+ cipher = WLAN_CIPHER_SUITE_CCMP;
+ break;
+ case CIPHER_TKIP:
+ default:
+ cipher = WLAN_CIPHER_SUITE_TKIP;
+ break;
+ }
+ wpa_printf(MSG_DEBUG, " * group=0x%x", cipher);
+ NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, cipher);
+ }
+
#ifdef CONFIG_IEEE80211W
if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED)
NLA_PUT_U32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED);
params->prev_bssid);
}
+ if (params->p2p)
+ wpa_printf(MSG_DEBUG, " * P2P group");
+
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
msg = NULL;
if (ret) {
static int nl80211_set_mode(struct wpa_driver_nl80211_data *drv,
- int ifindex, int mode)
+ int ifindex, enum nl80211_iftype mode)
{
struct nl_msg *msg;
int ret = -ENOBUFS;
+ wpa_printf(MSG_DEBUG, "nl80211: Set mode ifindex %d iftype %d (%s)",
+ ifindex, mode, nl80211_iftype_str(mode));
+
msg = nlmsg_alloc();
if (!msg)
return -ENOMEM;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
- 0, NL80211_CMD_SET_INTERFACE, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_INTERFACE);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, mode);
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ msg = NULL;
if (!ret)
return 0;
nla_put_failure:
+ nlmsg_free(msg);
wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface %d to mode %d:"
" %d (%s)", ifindex, mode, ret, strerror(-ret));
return ret;
}
-static int wpa_driver_nl80211_set_mode(void *priv, int mode)
+static int wpa_driver_nl80211_set_mode(struct i802_bss *bss,
+ enum nl80211_iftype nlmode)
{
- struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
int ret = -1;
- int nlmode;
-
- switch (mode) {
- case 0:
- nlmode = NL80211_IFTYPE_STATION;
- break;
- case 1:
- nlmode = NL80211_IFTYPE_ADHOC;
- break;
- case 2:
- nlmode = NL80211_IFTYPE_AP;
- break;
- default:
- return -1;
- }
+ int i;
+ int was_ap = is_ap_interface(drv->nlmode);
if (nl80211_set_mode(drv, drv->ifindex, nlmode) == 0) {
drv->nlmode = nlmode;
* take the device down, try to set the mode again, and bring the
* device back up.
*/
- if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 0) == 0) {
- /* Try to set the mode again while the interface is down */
- ret = nl80211_set_mode(drv, drv->ifindex, nlmode);
- if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 1))
- ret = -1;
+ wpa_printf(MSG_DEBUG, "nl80211: Try mode change after setting "
+ "interface down");
+ for (i = 0; i < 10; i++) {
+ int res;
+ res = linux_set_iface_flags(drv->global->ioctl_sock,
+ bss->ifname, 0);
+ if (res == -EACCES || res == -ENODEV)
+ break;
+ if (res == 0) {
+ /* Try to set the mode again while the interface is
+ * down */
+ ret = nl80211_set_mode(drv, drv->ifindex, nlmode);
+ if (ret == -EACCES)
+ break;
+ res = linux_set_iface_flags(drv->global->ioctl_sock,
+ bss->ifname, 1);
+ if (res && !ret)
+ ret = -1;
+ else if (ret != -EBUSY)
+ break;
+ } else
+ wpa_printf(MSG_DEBUG, "nl80211: Failed to set "
+ "interface down");
+ os_sleep(0, 100000);
}
if (!ret) {
wpa_printf(MSG_DEBUG, "nl80211: Mode change succeeded while "
"interface is down");
drv->nlmode = nlmode;
+ drv->ignore_if_down_event = 1;
}
done:
- if (!ret && nlmode == NL80211_IFTYPE_AP) {
+ if (!ret && is_ap_interface(nlmode)) {
/* Setup additional AP mode functionality if needed */
- if (drv->monitor_ifidx < 0 &&
- nl80211_create_monitor_interface(drv))
+ if (!drv->no_monitor_iface_capab && drv->monitor_ifidx < 0 &&
+ nl80211_create_monitor_interface(drv) &&
+ !drv->no_monitor_iface_capab)
return -1;
- } else if (!ret && nlmode != NL80211_IFTYPE_AP) {
+ } else if (!ret && !is_ap_interface(nlmode)) {
/* Remove additional AP mode functionality */
+ if (was_ap && drv->no_monitor_iface_capab)
+ wpa_driver_nl80211_probe_req_report(bss, 0);
nl80211_remove_monitor_interface(drv);
bss->beacon_set = 0;
}
+ if (!ret && is_p2p_interface(drv->nlmode)) {
+ nl80211_disable_11b_rates(drv, drv->ifindex, 1);
+ drv->disabled_11b_rates = 1;
+ } else if (!ret && drv->disabled_11b_rates) {
+ nl80211_disable_11b_rates(drv, drv->ifindex, 0);
+ drv->disabled_11b_rates = 0;
+ }
+
if (ret)
wpa_printf(MSG_DEBUG, "nl80211: Interface mode change to %d "
"from %d failed", nlmode, drv->nlmode);
wpa_printf(MSG_DEBUG, "%s: operstate %d->%d (%s)",
__func__, drv->operstate, state, state ? "UP" : "DORMANT");
drv->operstate = state;
- return netlink_send_oper_ifla(drv->netlink, drv->ifindex, -1,
+ return netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, -1,
state ? IF_OPER_UP : IF_OPER_DORMANT);
}
if (!msg)
return -ENOMEM;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
- 0, NL80211_CMD_SET_STATION, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_STATION);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX,
if_nametoindex(bss->ifname));
return send_and_recv_msgs(drv, msg, NULL, NULL);
nla_put_failure:
+ nlmsg_free(msg);
return -ENOBUFS;
}
-#ifdef HOSTAPD
-
-static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
-{
- int i;
- int *old;
-
- wpa_printf(MSG_DEBUG, "nl80211: Add own interface ifindex %d",
- ifidx);
- for (i = 0; i < drv->num_if_indices; i++) {
- if (drv->if_indices[i] == 0) {
- drv->if_indices[i] = ifidx;
- return;
- }
- }
-
- if (drv->if_indices != drv->default_if_indices)
- old = drv->if_indices;
- else
- old = NULL;
-
- drv->if_indices = os_realloc(old,
- sizeof(int) * (drv->num_if_indices + 1));
- if (!drv->if_indices) {
- if (!old)
- drv->if_indices = drv->default_if_indices;
- else
- drv->if_indices = old;
- wpa_printf(MSG_ERROR, "Failed to reallocate memory for "
- "interfaces");
- wpa_printf(MSG_ERROR, "Ignoring EAPOL on interface %d", ifidx);
- return;
- } else if (!old)
- os_memcpy(drv->if_indices, drv->default_if_indices,
- sizeof(drv->default_if_indices));
- drv->if_indices[drv->num_if_indices] = ifidx;
- drv->num_if_indices++;
-}
-
-
-static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
+/* Set kernel driver on given frequency (MHz) */
+static int i802_set_freq(void *priv, struct hostapd_freq_params *freq)
{
- int i;
-
- for (i = 0; i < drv->num_if_indices; i++) {
- if (drv->if_indices[i] == ifidx) {
- drv->if_indices[i] = 0;
- break;
- }
- }
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ return wpa_driver_nl80211_set_freq(drv, freq->freq, freq->ht_enabled,
+ freq->sec_channel_offset);
}
-static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
-{
- int i;
-
- for (i = 0; i < drv->num_if_indices; i++)
- if (drv->if_indices[i] == ifidx)
- return 1;
-
- return 0;
-}
-
+#if defined(HOSTAPD) || defined(CONFIG_AP)
static inline int min_int(int a, int b)
{
if (!msg)
return -ENOMEM;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
- 0, NL80211_CMD_GET_KEY, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_KEY);
if (addr)
NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
return send_and_recv_msgs(drv, msg, get_key_handler, seq);
nla_put_failure:
+ nlmsg_free(msg);
return -ENOBUFS;
}
if (!msg)
return -ENOMEM;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
- NL80211_CMD_SET_BSS, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_BSS);
for (i = 0; i < NL80211_MAX_SUPP_RATES && basic_rates[i] >= 0; i++)
rates[rates_len++] = basic_rates[i] / 5;
return -ENOBUFS;
}
-#endif /* HOSTAPD */
-
-
-/* Set kernel driver on given frequency (MHz) */
-static int i802_set_freq(void *priv, struct hostapd_freq_params *freq)
-{
- struct i802_bss *bss = priv;
- struct wpa_driver_nl80211_data *drv = bss->drv;
- return wpa_driver_nl80211_set_freq(drv, freq->freq, freq->ht_enabled,
- freq->sec_channel_offset);
-}
-
-
-#ifdef HOSTAPD
static int i802_set_rts(void *priv, int rts)
{
else
val = rts;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
- 0, NL80211_CMD_SET_WIPHY, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_WIPHY);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD, val);
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ msg = NULL;
if (!ret)
return 0;
nla_put_failure:
+ nlmsg_free(msg);
wpa_printf(MSG_DEBUG, "nl80211: Failed to set RTS threshold %d: "
"%d (%s)", rts, ret, strerror(-ret));
return ret;
else
val = frag;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
- 0, NL80211_CMD_SET_WIPHY, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_WIPHY);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD, val);
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ msg = NULL;
if (!ret)
return 0;
nla_put_failure:
+ nlmsg_free(msg);
wpa_printf(MSG_DEBUG, "nl80211: Failed to set fragmentation threshold "
"%d: %d (%s)", frag, ret, strerror(-ret));
return ret;
if (!msg)
return -1;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
- 0, NL80211_CMD_DEL_STATION, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_STATION);
/*
* XXX: FIX! this needs to flush all VLANs too
return send_and_recv_msgs(drv, msg, NULL, NULL);
nla_put_failure:
+ nlmsg_free(msg);
return -ENOBUFS;
}
if (!msg)
return -ENOMEM;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
- 0, NL80211_CMD_GET_STATION, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_STATION);
NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname));
return send_and_recv_msgs(drv, msg, get_sta_handler, data);
nla_put_failure:
+ nlmsg_free(msg);
return -ENOBUFS;
}
if (!msg)
return -1;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
- 0, NL80211_CMD_SET_WIPHY, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_WIPHY);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname));
if (!params)
goto nla_put_failure;
- NLA_PUT_U8(msg, NL80211_TXQ_ATTR_QUEUE, queue);
+ switch (queue) {
+ case 0:
+ NLA_PUT_U8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_VO);
+ break;
+ case 1:
+ NLA_PUT_U8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_VI);
+ break;
+ case 2:
+ NLA_PUT_U8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_BE);
+ break;
+ case 3:
+ NLA_PUT_U8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_BK);
+ break;
+ }
/* Burst time is configured in units of 0.1 msec and TXOP parameter in
* 32 usec, so need to convert the value here. */
NLA_PUT_U16(msg, NL80211_TXQ_ATTR_TXOP, (burst_time * 100 + 16) / 32);
NLA_PUT_U16(msg, NL80211_TXQ_ATTR_CWMIN, cw_min);
- NLA_PUT_U16(msg, NL80211_TXQ_ATTR_CWMAX, cw_max);
- NLA_PUT_U8(msg, NL80211_TXQ_ATTR_AIFS, aifs);
-
- nla_nest_end(msg, params);
-
- nla_nest_end(msg, txq);
-
- if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0)
- return 0;
- nla_put_failure:
- return -1;
-}
-
-
-static int i802_set_bss(void *priv, int cts, int preamble, int slot)
-{
- struct i802_bss *bss = priv;
- struct wpa_driver_nl80211_data *drv = bss->drv;
- struct nl_msg *msg;
-
- msg = nlmsg_alloc();
- if (!msg)
- return -ENOMEM;
-
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
- NL80211_CMD_SET_BSS, 0);
-
- if (cts >= 0)
- NLA_PUT_U8(msg, NL80211_ATTR_BSS_CTS_PROT, cts);
- if (preamble >= 0)
- NLA_PUT_U8(msg, NL80211_ATTR_BSS_SHORT_PREAMBLE, preamble);
- if (slot >= 0)
- NLA_PUT_U8(msg, NL80211_ATTR_BSS_SHORT_SLOT_TIME, slot);
-
- NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname));
-
- return send_and_recv_msgs(drv, msg, NULL, NULL);
- nla_put_failure:
- return -ENOBUFS;
-}
-
-
-static int i802_set_cts_protect(void *priv, int value)
-{
- return i802_set_bss(priv, value, -1, -1);
-}
-
+ NLA_PUT_U16(msg, NL80211_TXQ_ATTR_CWMAX, cw_max);
+ NLA_PUT_U8(msg, NL80211_TXQ_ATTR_AIFS, aifs);
-static int i802_set_preamble(void *priv, int value)
-{
- return i802_set_bss(priv, -1, value, -1);
-}
+ nla_nest_end(msg, params);
+ nla_nest_end(msg, txq);
-static int i802_set_short_slot_time(void *priv, int value)
-{
- return i802_set_bss(priv, -1, -1, value);
+ if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0)
+ return 0;
+ msg = NULL;
+ nla_put_failure:
+ nlmsg_free(msg);
+ return -1;
}
if (!msg)
return -ENOMEM;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
- 0, NL80211_CMD_SET_STATION, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_STATION);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX,
if_nametoindex(bss->ifname));
if_nametoindex(ifname));
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ msg = NULL;
if (ret < 0) {
wpa_printf(MSG_ERROR, "nl80211: NL80211_ATTR_STA_VLAN (addr="
MACSTR " ifname=%s vlan_id=%d) failed: %d (%s)",
strerror(-ret));
}
nla_put_failure:
+ nlmsg_free(msg);
return ret;
}
-static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val)
-{
- struct i802_bss *bss = priv;
- struct wpa_driver_nl80211_data *drv = bss->drv;
- char name[IFNAMSIZ + 1];
-
- os_snprintf(name, sizeof(name), "%s.sta%d", bss->ifname, aid);
- wpa_printf(MSG_DEBUG, "nl80211: Set WDS STA addr=" MACSTR
- " aid=%d val=%d name=%s", MAC2STR(addr), aid, val, name);
- if (val) {
- if (nl80211_create_iface(drv, name, NL80211_IFTYPE_AP_VLAN,
- NULL, 1) < 0)
- return -1;
- linux_set_iface_flags(drv->ioctl_sock, name, 1);
- return i802_set_sta_vlan(priv, addr, name, 0);
- } else {
- i802_set_sta_vlan(priv, addr, bss->ifname, 0);
- return wpa_driver_nl80211_if_remove(priv, WPA_IF_AP_VLAN,
- name);
- }
-}
-
-
-static void handle_eapol(int sock, void *eloop_ctx, void *sock_ctx)
-{
- struct wpa_driver_nl80211_data *drv = eloop_ctx;
- struct sockaddr_ll lladdr;
- unsigned char buf[3000];
- int len;
- socklen_t fromlen = sizeof(lladdr);
-
- len = recvfrom(sock, buf, sizeof(buf), 0,
- (struct sockaddr *)&lladdr, &fromlen);
- if (len < 0) {
- perror("recv");
- return;
- }
-
- if (have_ifidx(drv, lladdr.sll_ifindex))
- drv_event_eapol_rx(drv->ctx, lladdr.sll_addr, buf, len);
-}
-
-
static int i802_get_inact_sec(void *priv, const u8 *addr)
{
struct hostap_sta_driver_data data;
sizeof(mgmt.u.disassoc));
}
+#endif /* HOSTAPD || CONFIG_AP */
+
+#ifdef HOSTAPD
+
+static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
+{
+ int i;
+ int *old;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Add own interface ifindex %d",
+ ifidx);
+ for (i = 0; i < drv->num_if_indices; i++) {
+ if (drv->if_indices[i] == 0) {
+ drv->if_indices[i] = ifidx;
+ return;
+ }
+ }
+
+ if (drv->if_indices != drv->default_if_indices)
+ old = drv->if_indices;
+ else
+ old = NULL;
+
+ drv->if_indices = os_realloc(old,
+ sizeof(int) * (drv->num_if_indices + 1));
+ if (!drv->if_indices) {
+ if (!old)
+ drv->if_indices = drv->default_if_indices;
+ else
+ drv->if_indices = old;
+ wpa_printf(MSG_ERROR, "Failed to reallocate memory for "
+ "interfaces");
+ wpa_printf(MSG_ERROR, "Ignoring EAPOL on interface %d", ifidx);
+ return;
+ } else if (!old)
+ os_memcpy(drv->if_indices, drv->default_if_indices,
+ sizeof(drv->default_if_indices));
+ drv->if_indices[drv->num_if_indices] = ifidx;
+ drv->num_if_indices++;
+}
+
+
+static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
+{
+ int i;
+
+ for (i = 0; i < drv->num_if_indices; i++) {
+ if (drv->if_indices[i] == ifidx) {
+ drv->if_indices[i] = 0;
+ break;
+ }
+ }
+}
+
+
+static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
+{
+ int i;
+
+ for (i = 0; i < drv->num_if_indices; i++)
+ if (drv->if_indices[i] == ifidx)
+ return 1;
+
+ return 0;
+}
+
+
+static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val,
+ const char *bridge_ifname)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ char name[IFNAMSIZ + 1];
+
+ os_snprintf(name, sizeof(name), "%s.sta%d", bss->ifname, aid);
+ wpa_printf(MSG_DEBUG, "nl80211: Set WDS STA addr=" MACSTR
+ " aid=%d val=%d name=%s", MAC2STR(addr), aid, val, name);
+ if (val) {
+ if (!if_nametoindex(name)) {
+ if (nl80211_create_iface(drv, name,
+ NL80211_IFTYPE_AP_VLAN,
+ NULL, 1) < 0)
+ return -1;
+ if (bridge_ifname &&
+ linux_br_add_if(drv->global->ioctl_sock,
+ bridge_ifname, name) < 0)
+ return -1;
+ }
+ linux_set_iface_flags(drv->global->ioctl_sock, name, 1);
+ return i802_set_sta_vlan(priv, addr, name, 0);
+ } else {
+ i802_set_sta_vlan(priv, addr, bss->ifname, 0);
+ return wpa_driver_nl80211_if_remove(priv, WPA_IF_AP_VLAN,
+ name);
+ }
+}
+
+
+static void handle_eapol(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ struct wpa_driver_nl80211_data *drv = eloop_ctx;
+ struct sockaddr_ll lladdr;
+ unsigned char buf[3000];
+ int len;
+ socklen_t fromlen = sizeof(lladdr);
+
+ len = recvfrom(sock, buf, sizeof(buf), 0,
+ (struct sockaddr *)&lladdr, &fromlen);
+ if (len < 0) {
+ perror("recv");
+ return;
+ }
+
+ if (have_ifidx(drv, lladdr.sll_ifindex))
+ drv_event_eapol_rx(drv->ctx, lladdr.sll_addr, buf, len);
+}
+
static int i802_check_bridge(struct wpa_driver_nl80211_data *drv,
+ struct i802_bss *bss,
const char *brname, const char *ifname)
{
int ifindex;
char in_br[IFNAMSIZ];
- os_strlcpy(drv->brname, brname, IFNAMSIZ);
+ os_strlcpy(bss->brname, brname, IFNAMSIZ);
ifindex = if_nametoindex(brname);
if (ifindex == 0) {
/*
* Bridge was configured, but the bridge device does
* not exist. Try to add it now.
*/
- if (linux_br_add(drv->ioctl_sock, brname) < 0) {
+ if (linux_br_add(drv->global->ioctl_sock, brname) < 0) {
wpa_printf(MSG_ERROR, "nl80211: Failed to add the "
"bridge interface %s: %s",
brname, strerror(errno));
return -1;
}
- drv->added_bridge = 1;
+ bss->added_bridge = 1;
add_ifidx(drv, if_nametoindex(brname));
}
wpa_printf(MSG_DEBUG, "nl80211: Removing interface %s from "
"bridge %s", ifname, in_br);
- if (linux_br_del_if(drv->ioctl_sock, in_br, ifname) < 0) {
+ if (linux_br_del_if(drv->global->ioctl_sock, in_br, ifname) <
+ 0) {
wpa_printf(MSG_ERROR, "nl80211: Failed to "
"remove interface %s from bridge "
"%s: %s",
wpa_printf(MSG_DEBUG, "nl80211: Adding interface %s into bridge %s",
ifname, brname);
- if (linux_br_add_if(drv->ioctl_sock, brname, ifname) < 0) {
+ if (linux_br_add_if(drv->global->ioctl_sock, brname, ifname) < 0) {
wpa_printf(MSG_ERROR, "nl80211: Failed to add interface %s "
"into bridge %s: %s",
ifname, brname, strerror(errno));
return -1;
}
- drv->added_if_into_bridge = 1;
+ bss->added_if_into_bridge = 1;
return 0;
}
int ifindex, br_ifindex;
int br_added = 0;
- bss = wpa_driver_nl80211_init(hapd, params->ifname);
+ bss = wpa_driver_nl80211_init(hapd, params->ifname,
+ params->global_priv);
if (bss == NULL)
return NULL;
drv = bss->drv;
+ drv->nlmode = NL80211_IFTYPE_AP;
+ drv->eapol_sock = -1;
+
if (linux_br_get(brname, params->ifname) == 0) {
wpa_printf(MSG_DEBUG, "nl80211: Interface %s is in bridge %s",
params->ifname, brname);
/* start listening for EAPOL on the default AP interface */
add_ifidx(drv, drv->ifindex);
- if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 0))
+ if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0))
goto failed;
if (params->bssid) {
- if (linux_set_ifhwaddr(drv->ioctl_sock, bss->ifname,
+ if (linux_set_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
params->bssid))
goto failed;
}
- if (wpa_driver_nl80211_set_mode(bss, IEEE80211_MODE_AP)) {
+ if (wpa_driver_nl80211_set_mode(bss, drv->nlmode)) {
wpa_printf(MSG_ERROR, "nl80211: Failed to set interface %s "
"into AP mode", bss->ifname);
goto failed;
}
if (params->num_bridge && params->bridge[0] &&
- i802_check_bridge(drv, params->bridge[0], params->ifname) < 0)
+ i802_check_bridge(drv, bss, params->bridge[0], params->ifname) < 0)
goto failed;
- if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 1))
+ if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1))
goto failed;
drv->eapol_sock = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_PAE));
goto failed;
}
- if (linux_get_ifhwaddr(drv->ioctl_sock, bss->ifname, params->own_addr))
+ if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
+ params->own_addr))
goto failed;
return bss;
failed:
- nl80211_remove_monitor_interface(drv);
- if (drv->ioctl_sock >= 0)
- close(drv->ioctl_sock);
-
- genl_family_put(drv->nl80211);
- nl_cache_free(drv->nl_cache);
- nl_handle_destroy(drv->nl_handle);
- nl_cb_put(drv->nl_cb);
-
- os_free(drv);
+ wpa_driver_nl80211_deinit(bss);
return NULL;
}
switch (type) {
case WPA_IF_STATION:
return NL80211_IFTYPE_STATION;
+ case WPA_IF_P2P_CLIENT:
+ case WPA_IF_P2P_GROUP:
+ return NL80211_IFTYPE_P2P_CLIENT;
case WPA_IF_AP_VLAN:
return NL80211_IFTYPE_AP_VLAN;
case WPA_IF_AP_BSS:
return NL80211_IFTYPE_AP;
+ case WPA_IF_P2P_GO:
+ return NL80211_IFTYPE_P2P_GO;
}
return -1;
}
+#ifdef CONFIG_P2P
+
+static int nl80211_addr_in_use(struct nl80211_global *global, const u8 *addr)
+{
+ struct wpa_driver_nl80211_data *drv;
+ dl_list_for_each(drv, &global->interfaces,
+ struct wpa_driver_nl80211_data, list) {
+ if (os_memcmp(addr, drv->addr, ETH_ALEN) == 0)
+ return 1;
+ }
+ return 0;
+}
+
+
+static int nl80211_p2p_interface_addr(struct wpa_driver_nl80211_data *drv,
+ u8 *new_addr)
+{
+ unsigned int idx;
+
+ if (!drv->global)
+ return -1;
+
+ os_memcpy(new_addr, drv->addr, ETH_ALEN);
+ for (idx = 0; idx < 64; idx++) {
+ new_addr[0] = drv->addr[0] | 0x02;
+ new_addr[0] ^= idx << 2;
+ if (!nl80211_addr_in_use(drv->global, new_addr))
+ break;
+ }
+ if (idx == 64)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Assigned new P2P Interface Address "
+ MACSTR, MAC2STR(new_addr));
+
+ return 0;
+}
+
+#endif /* CONFIG_P2P */
+
+
static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type,
const char *ifname, const u8 *addr,
void *bss_ctx, void **drv_priv,
- char *force_ifname, u8 *if_addr)
+ char *force_ifname, u8 *if_addr,
+ const char *bridge)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
}
if (!addr &&
- linux_get_ifhwaddr(drv->ioctl_sock, bss->ifname, if_addr) < 0)
+ linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
+ if_addr) < 0) {
+ nl80211_remove_iface(drv, ifidx);
return -1;
+ }
+
+#ifdef CONFIG_P2P
+ if (!addr &&
+ (type == WPA_IF_P2P_CLIENT || type == WPA_IF_P2P_GROUP ||
+ type == WPA_IF_P2P_GO)) {
+ /* Enforce unique P2P Interface Address */
+ u8 new_addr[ETH_ALEN], own_addr[ETH_ALEN];
+
+ if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
+ own_addr) < 0 ||
+ linux_get_ifhwaddr(drv->global->ioctl_sock, ifname,
+ new_addr) < 0) {
+ nl80211_remove_iface(drv, ifidx);
+ return -1;
+ }
+ if (os_memcmp(own_addr, new_addr, ETH_ALEN) == 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: Allocate new address "
+ "for P2P group interface");
+ if (nl80211_p2p_interface_addr(drv, new_addr) < 0) {
+ nl80211_remove_iface(drv, ifidx);
+ return -1;
+ }
+ if (linux_set_ifhwaddr(drv->global->ioctl_sock, ifname,
+ new_addr) < 0) {
+ nl80211_remove_iface(drv, ifidx);
+ return -1;
+ }
+ }
+ os_memcpy(if_addr, new_addr, ETH_ALEN);
+ }
+#endif /* CONFIG_P2P */
#ifdef HOSTAPD
+ if (bridge &&
+ i802_check_bridge(drv, new_bss, bridge, ifname) < 0) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to add the new "
+ "interface %s to a bridge %s", ifname, bridge);
+ nl80211_remove_iface(drv, ifidx);
+ os_free(new_bss);
+ return -1;
+ }
+
if (type == WPA_IF_AP_BSS) {
- if (linux_set_iface_flags(drv->ioctl_sock, ifname, 1)) {
+ if (linux_set_iface_flags(drv->global->ioctl_sock, ifname, 1))
+ {
nl80211_remove_iface(drv, ifidx);
os_free(new_bss);
return -1;
}
#endif /* HOSTAPD */
+ if (drv->global)
+ drv->global->if_add_ifindex = ifidx;
+
return 0;
}
__func__, type, ifname, ifindex);
if (ifindex <= 0)
return -1;
+
+#ifdef HOSTAPD
+ if (bss->added_if_into_bridge) {
+ if (linux_br_del_if(drv->global->ioctl_sock, bss->brname,
+ bss->ifname) < 0)
+ wpa_printf(MSG_INFO, "nl80211: Failed to remove "
+ "interface %s from bridge %s: %s",
+ bss->ifname, bss->brname, strerror(errno));
+ }
+ if (bss->added_bridge) {
+ if (linux_br_del(drv->global->ioctl_sock, bss->brname) < 0)
+ wpa_printf(MSG_INFO, "nl80211: Failed to remove "
+ "bridge %s: %s",
+ bss->brname, strerror(errno));
+ }
+#endif /* HOSTAPD */
+
nl80211_remove_iface(drv, ifindex);
#ifdef HOSTAPD
return 0;
if (bss != &drv->first_bss) {
- struct i802_bss *tbss = &drv->first_bss;
-
- while (tbss) {
- if (tbss->next != bss)
- continue;
+ struct i802_bss *tbss;
- tbss->next = bss->next;
- os_free(bss);
- break;
+ for (tbss = &drv->first_bss; tbss; tbss = tbss->next) {
+ if (tbss->next == bss) {
+ tbss->next = bss->next;
+ os_free(bss);
+ bss = NULL;
+ break;
+ }
}
+ if (bss)
+ wpa_printf(MSG_INFO, "nl80211: %s - could not find "
+ "BSS %p in the list", __func__, bss);
}
#endif /* HOSTAPD */
}
+static int nl80211_send_frame_cmd(struct wpa_driver_nl80211_data *drv,
+ unsigned int freq, unsigned int wait,
+ const u8 *buf, size_t buf_len,
+ u64 *cookie_out, int no_cck)
+{
+ struct nl_msg *msg;
+ u64 cookie;
+ int ret = -1;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -1;
+
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_FRAME);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
+ if (wait)
+ NLA_PUT_U32(msg, NL80211_ATTR_DURATION, wait);
+ NLA_PUT_FLAG(msg, NL80211_ATTR_OFFCHANNEL_TX_OK);
+ if (no_cck)
+ NLA_PUT_FLAG(msg, NL80211_ATTR_TX_NO_CCK_RATE);
+
+ NLA_PUT(msg, NL80211_ATTR_FRAME, buf_len, buf);
+
+ cookie = 0;
+ ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie);
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: Frame command failed: ret=%d "
+ "(%s) (freq=%u wait=%u)", ret, strerror(-ret),
+ freq, wait);
+ goto nla_put_failure;
+ }
+ wpa_printf(MSG_DEBUG, "nl80211: Frame TX command accepted; "
+ "cookie 0x%llx", (long long unsigned int) cookie);
+
+ if (cookie_out)
+ *cookie_out = cookie;
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
static int wpa_driver_nl80211_send_action(void *priv, unsigned int freq,
+ unsigned int wait_time,
const u8 *dst, const u8 *src,
const u8 *bssid,
- const u8 *data, size_t data_len)
+ const u8 *data, size_t data_len,
+ int no_cck)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
int ret = -1;
- struct nl_msg *msg;
u8 *buf;
struct ieee80211_hdr *hdr;
- u64 cookie;
- wpa_printf(MSG_DEBUG, "nl80211: Send Action frame (ifindex=%d)",
- drv->ifindex);
+ wpa_printf(MSG_DEBUG, "nl80211: Send Action frame (ifindex=%d, "
+ "wait=%d ms no_cck=%d)", drv->ifindex, wait_time, no_cck);
buf = os_zalloc(24 + data_len);
if (buf == NULL)
os_memcpy(hdr->addr2, src, ETH_ALEN);
os_memcpy(hdr->addr3, bssid, ETH_ALEN);
- if (drv->nlmode == NL80211_IFTYPE_AP) {
- ret = wpa_driver_nl80211_send_mlme(priv, buf, 24 + data_len);
- os_free(buf);
- return ret;
- }
+ if (is_ap_interface(drv->nlmode))
+ ret = wpa_driver_nl80211_send_mlme(priv, buf, 24 + data_len);
+ else
+ ret = nl80211_send_frame_cmd(drv, freq, wait_time, buf,
+ 24 + data_len,
+ &drv->send_action_cookie,
+ no_cck);
+
+ os_free(buf);
+ return ret;
+}
+
+
+static void wpa_driver_nl80211_send_action_cancel_wait(void *priv)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int ret;
msg = nlmsg_alloc();
- if (!msg) {
- os_free(buf);
- return -1;
- }
+ if (!msg)
+ return;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
- NL80211_CMD_ACTION, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_FRAME_WAIT_CANCEL);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
- NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
- NLA_PUT(msg, NL80211_ATTR_FRAME, 24 + data_len, buf);
- os_free(buf);
- buf = NULL;
+ NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, drv->send_action_cookie);
- cookie = 0;
- ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
msg = NULL;
- if (ret) {
- wpa_printf(MSG_DEBUG, "nl80211: Action command failed: ret=%d "
+ if (ret)
+ wpa_printf(MSG_DEBUG, "nl80211: wait cancel failed: ret=%d "
"(%s)", ret, strerror(-ret));
- goto nla_put_failure;
- }
- wpa_printf(MSG_DEBUG, "nl80211: Action TX command accepted; "
- "cookie 0x%llx", (long long unsigned int) cookie);
- drv->send_action_cookie = cookie;
- ret = 0;
-nla_put_failure:
- os_free(buf);
+ nla_put_failure:
nlmsg_free(msg);
- return ret;
}
if (!msg)
return -1;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
- NL80211_CMD_REMAIN_ON_CHANNEL, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_REMAIN_ON_CHANNEL);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
cookie = 0;
ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie);
+ msg = NULL;
if (ret == 0) {
wpa_printf(MSG_DEBUG, "nl80211: Remain-on-channel cookie "
"0x%llx for freq=%u MHz duration=%u",
(long long unsigned int) cookie, freq, duration);
drv->remain_on_chan_cookie = cookie;
+ drv->pending_remain_on_chan = 1;
return 0;
}
wpa_printf(MSG_DEBUG, "nl80211: Failed to request remain-on-channel "
- "(freq=%d): %d (%s)", freq, ret, strerror(-ret));
+ "(freq=%d duration=%u): %d (%s)",
+ freq, duration, ret, strerror(-ret));
nla_put_failure:
+ nlmsg_free(msg);
return -1;
}
if (!msg)
return -1;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
- NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, drv->remain_on_chan_cookie);
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ msg = NULL;
if (ret == 0)
return 0;
wpa_printf(MSG_DEBUG, "nl80211: Failed to cancel remain-on-channel: "
"%d (%s)", ret, strerror(-ret));
nla_put_failure:
+ nlmsg_free(msg);
return -1;
}
-static void wpa_driver_nl80211_probe_req_report_timeout(void *eloop_ctx,
- void *timeout_ctx)
-{
- struct wpa_driver_nl80211_data *drv = eloop_ctx;
- if (drv->monitor_ifidx < 0)
- return; /* monitor interface already removed */
-
- if (drv->nlmode != NL80211_IFTYPE_STATION)
- return; /* not in station mode anymore */
-
- if (drv->probe_req_report)
- return; /* reporting enabled */
-
- wpa_printf(MSG_DEBUG, "nl80211: Remove monitor interface due to no "
- "Probe Request reporting needed anymore");
- nl80211_remove_monitor_interface(drv);
-}
-
-
static int wpa_driver_nl80211_probe_req_report(void *priv, int report)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
- if (drv->nlmode != NL80211_IFTYPE_STATION) {
- wpa_printf(MSG_DEBUG, "nl80211: probe_req_report control only "
- "allowed in station mode (iftype=%d)",
- drv->nlmode);
- return -1;
+ if (!report) {
+ if (drv->nl_preq.handle && drv->no_monitor_iface_capab &&
+ is_ap_interface(drv->nlmode)) {
+ /*
+ * Do not disable Probe Request reporting that was
+ * enabled in nl80211_setup_ap().
+ */
+ wpa_printf(MSG_DEBUG, "nl80211: Skip disabling of "
+ "Probe Request reporting nl_preq=%p while "
+ "in AP mode", drv->nl_preq.handle);
+ } else if (drv->nl_preq.handle) {
+ wpa_printf(MSG_DEBUG, "nl80211: Disable Probe Request "
+ "reporting nl_preq=%p", drv->nl_preq.handle);
+ eloop_unregister_read_sock(
+ nl_socket_get_fd(drv->nl_preq.handle));
+ nl_destroy_handles(&drv->nl_preq);
+ }
+ return 0;
}
- drv->probe_req_report = report;
- if (report) {
- eloop_cancel_timeout(
- wpa_driver_nl80211_probe_req_report_timeout,
- drv, NULL);
- if (drv->monitor_ifidx < 0 &&
- nl80211_create_monitor_interface(drv))
- return -1;
- } else {
- /*
- * It takes a while to remove the monitor interface, so try to
- * avoid doing this if it is needed again shortly. Instead,
- * schedule the interface to be removed later if no need for it
- * is seen.
- */
- wpa_printf(MSG_DEBUG, "nl80211: Scheduling monitor interface "
- "to be removed after 10 seconds of no use");
- eloop_register_timeout(
- 10, 0, wpa_driver_nl80211_probe_req_report_timeout,
- drv, NULL);
+ if (drv->nl_preq.handle) {
+ wpa_printf(MSG_DEBUG, "nl80211: Probe Request reporting "
+ "already on!");
+ return 0;
}
+ if (nl_create_handles(&drv->nl_preq, drv->global->nl_cb, "preq"))
+ return -1;
+
+ if (nl80211_register_frame(drv, drv->nl_preq.handle,
+ (WLAN_FC_TYPE_MGMT << 2) |
+ (WLAN_FC_STYPE_PROBE_REQ << 4),
+ NULL, 0) < 0)
+ goto out_err;
+
+ eloop_register_read_sock(nl_socket_get_fd(drv->nl_preq.handle),
+ wpa_driver_nl80211_event_receive, drv,
+ drv->nl_preq.handle);
+
return 0;
+
+ out_err:
+ nl_destroy_handles(&drv->nl_preq);
+ return -1;
}
if (!msg)
return -1;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
- NL80211_CMD_SET_TX_BITRATE_MASK, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_TX_BITRATE_MASK);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
bands = nla_nest_start(msg, NL80211_ATTR_TX_RATES);
band = nla_nest_start(msg, NL80211_BAND_2GHZ);
if (!band)
goto nla_put_failure;
- NLA_PUT(msg, NL80211_TXRATE_LEGACY, 8,
- "\x0c\x12\x18\x24\x30\x48\x60\x6c");
+ if (disabled) {
+ NLA_PUT(msg, NL80211_TXRATE_LEGACY, 8,
+ "\x0c\x12\x18\x24\x30\x48\x60\x6c");
+ }
nla_nest_end(msg, band);
nla_nest_end(msg, bands);
}
-static int wpa_driver_nl80211_disable_11b_rates(void *priv, int disabled)
+static int wpa_driver_nl80211_deinit_ap(void *priv)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
- drv->disable_11b_rates = disabled;
- return nl80211_disable_11b_rates(drv, drv->ifindex, disabled);
+ if (!is_ap_interface(drv->nlmode))
+ return -1;
+ wpa_driver_nl80211_del_beacon(drv);
+ return wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_STATION);
}
-static int wpa_driver_nl80211_deinit_ap(void *priv)
+static int wpa_driver_nl80211_deinit_p2p_cli(void *priv)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
- if (drv->nlmode != NL80211_IFTYPE_AP)
+ if (drv->nlmode != NL80211_IFTYPE_P2P_CLIENT)
return -1;
- wpa_driver_nl80211_del_beacon(drv);
- return wpa_driver_nl80211_set_mode(priv, IEEE80211_MODE_INFRA);
+ return wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_STATION);
}
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
- if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 1)) {
+ if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1)) {
wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface up on "
"resume event");
}
size_t data_len;
u8 own_addr[ETH_ALEN];
- if (linux_get_ifhwaddr(drv->ioctl_sock, bss->ifname, own_addr) < 0)
+ if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
+ own_addr) < 0)
return -1;
if (action != 1) {
pos += ETH_ALEN;
os_memcpy(pos, ies, ies_len);
- ret = wpa_driver_nl80211_send_action(bss, drv->assoc_freq, drv->bssid,
- own_addr, drv->bssid,
- data, data_len);
+ ret = wpa_driver_nl80211_send_action(bss, drv->assoc_freq, 0,
+ drv->bssid, own_addr, drv->bssid,
+ data, data_len, 0);
os_free(data);
return ret;
if (!msg)
return -1;
- genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
- 0, NL80211_CMD_SET_CQM, 0);
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_CQM);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex);
msg = NULL;
nla_put_failure:
- if (cqm)
- nlmsg_free(cqm);
+ nlmsg_free(cqm);
nlmsg_free(msg);
return -1;
}
+static int nl80211_signal_poll(void *priv, struct wpa_signal_info *si)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ int res;
+
+ os_memset(si, 0, sizeof(*si));
+ res = nl80211_get_link_signal(drv, si);
+ if (res != 0)
+ return res;
+
+ return nl80211_get_link_noise(drv, si);
+}
+
+
static int nl80211_send_frame(void *priv, const u8 *data, size_t data_len,
int encrypt)
{
}
+static int nl80211_set_param(void *priv, const char *param)
+{
+ wpa_printf(MSG_DEBUG, "nl80211: driver param='%s'", param);
+ if (param == NULL)
+ return 0;
+
+#ifdef CONFIG_P2P
+ if (os_strstr(param, "use_p2p_group_interface=1")) {
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Use separate P2P group "
+ "interface");
+ drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT;
+ drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P;
+ }
+#endif /* CONFIG_P2P */
+
+ return 0;
+}
+
+
+static void * nl80211_global_init(void)
+{
+ struct nl80211_global *global;
+ struct netlink_config *cfg;
+
+ global = os_zalloc(sizeof(*global));
+ if (global == NULL)
+ return NULL;
+ global->ioctl_sock = -1;
+ dl_list_init(&global->interfaces);
+ global->if_add_ifindex = -1;
+
+ cfg = os_zalloc(sizeof(*cfg));
+ if (cfg == NULL)
+ goto err;
+
+ cfg->ctx = global;
+ cfg->newlink_cb = wpa_driver_nl80211_event_rtm_newlink;
+ cfg->dellink_cb = wpa_driver_nl80211_event_rtm_dellink;
+ global->netlink = netlink_init(cfg);
+ if (global->netlink == NULL) {
+ os_free(cfg);
+ goto err;
+ }
+
+ if (wpa_driver_nl80211_init_nl_global(global) < 0)
+ goto err;
+
+ global->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
+ if (global->ioctl_sock < 0) {
+ perror("socket(PF_INET,SOCK_DGRAM)");
+ goto err;
+ }
+
+ return global;
+
+err:
+ nl80211_global_deinit(global);
+ return NULL;
+}
+
+
+static void nl80211_global_deinit(void *priv)
+{
+ struct nl80211_global *global = priv;
+ if (global == NULL)
+ return;
+ if (!dl_list_empty(&global->interfaces)) {
+ wpa_printf(MSG_ERROR, "nl80211: %u interface(s) remain at "
+ "nl80211_global_deinit",
+ dl_list_len(&global->interfaces));
+ }
+
+ if (global->netlink)
+ netlink_deinit(global->netlink);
+
+ if (global->nl80211)
+ genl_family_put(global->nl80211);
+ nl_destroy_handles(&global->nl);
+
+ if (global->nl_cb)
+ nl_cb_put(global->nl_cb);
+
+ if (global->ioctl_sock >= 0)
+ close(global->ioctl_sock);
+
+ os_free(global);
+}
+
+
+static const char * nl80211_get_radio_name(void *priv)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ return drv->phyname;
+}
+
+
+static int nl80211_pmkid(struct i802_bss *bss, int cmd, const u8 *bssid,
+ const u8 *pmkid)
+{
+ struct nl_msg *msg;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ nl80211_cmd(bss->drv, msg, 0, cmd);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname));
+ if (pmkid)
+ NLA_PUT(msg, NL80211_ATTR_PMKID, 16, pmkid);
+ if (bssid)
+ NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
+
+ return send_and_recv_msgs(bss->drv, msg, NULL, NULL);
+ nla_put_failure:
+ nlmsg_free(msg);
+ return -ENOBUFS;
+}
+
+
+static int nl80211_add_pmkid(void *priv, const u8 *bssid, const u8 *pmkid)
+{
+ struct i802_bss *bss = priv;
+ wpa_printf(MSG_DEBUG, "nl80211: Add PMKID for " MACSTR, MAC2STR(bssid));
+ return nl80211_pmkid(bss, NL80211_CMD_SET_PMKSA, bssid, pmkid);
+}
+
+
+static int nl80211_remove_pmkid(void *priv, const u8 *bssid, const u8 *pmkid)
+{
+ struct i802_bss *bss = priv;
+ wpa_printf(MSG_DEBUG, "nl80211: Delete PMKID for " MACSTR,
+ MAC2STR(bssid));
+ return nl80211_pmkid(bss, NL80211_CMD_DEL_PMKSA, bssid, pmkid);
+}
+
+
+static int nl80211_flush_pmkid(void *priv)
+{
+ struct i802_bss *bss = priv;
+ wpa_printf(MSG_DEBUG, "nl80211: Flush PMKIDs");
+ return nl80211_pmkid(bss, NL80211_CMD_FLUSH_PMKSA, NULL, NULL);
+}
+
+
+static void nl80211_set_rekey_info(void *priv, const u8 *kek, const u8 *kck,
+ const u8 *replay_ctr)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nlattr *replay_nested;
+ struct nl_msg *msg;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return;
+
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_REKEY_OFFLOAD);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex);
+
+ replay_nested = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA);
+ if (!replay_nested)
+ goto nla_put_failure;
+
+ NLA_PUT(msg, NL80211_REKEY_DATA_KEK, NL80211_KEK_LEN, kek);
+ NLA_PUT(msg, NL80211_REKEY_DATA_KCK, NL80211_KCK_LEN, kck);
+ NLA_PUT(msg, NL80211_REKEY_DATA_REPLAY_CTR, NL80211_REPLAY_CTR_LEN,
+ replay_ctr);
+
+ nla_nest_end(msg, replay_nested);
+
+ send_and_recv_msgs(drv, msg, NULL, NULL);
+ return;
+ nla_put_failure:
+ nlmsg_free(msg);
+}
+
+
+static void nl80211_poll_client(void *priv, const u8 *own_addr, const u8 *addr,
+ int qos)
+{
+ struct i802_bss *bss = priv;
+ struct {
+ struct ieee80211_hdr hdr;
+ u16 qos_ctl;
+ } STRUCT_PACKED nulldata;
+ size_t size;
+
+ /* Send data frame to poll STA and check whether this frame is ACKed */
+
+ os_memset(&nulldata, 0, sizeof(nulldata));
+
+ if (qos) {
+ nulldata.hdr.frame_control =
+ IEEE80211_FC(WLAN_FC_TYPE_DATA,
+ WLAN_FC_STYPE_QOS_NULL);
+ size = sizeof(nulldata);
+ } else {
+ nulldata.hdr.frame_control =
+ IEEE80211_FC(WLAN_FC_TYPE_DATA,
+ WLAN_FC_STYPE_NULLFUNC);
+ size = sizeof(struct ieee80211_hdr);
+ }
+
+ nulldata.hdr.frame_control |= host_to_le16(WLAN_FC_FROMDS);
+ os_memcpy(nulldata.hdr.IEEE80211_DA_FROMDS, addr, ETH_ALEN);
+ os_memcpy(nulldata.hdr.IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN);
+ os_memcpy(nulldata.hdr.IEEE80211_SA_FROMDS, own_addr, ETH_ALEN);
+
+ if (wpa_driver_nl80211_send_mlme(bss, (u8 *) &nulldata, size) < 0)
+ wpa_printf(MSG_DEBUG, "nl80211_send_null_frame: Failed to "
+ "send poll frame");
+}
+
+
+#ifdef CONFIG_TDLS
+
+static int nl80211_send_tdls_mgmt(void *priv, const u8 *dst, u8 action_code,
+ u8 dialog_token, u16 status_code,
+ const u8 *buf, size_t len)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+
+ if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT))
+ return -EOPNOTSUPP;
+
+ if (!dst)
+ return -EINVAL;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_TDLS_MGMT);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+ NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, dst);
+ NLA_PUT_U8(msg, NL80211_ATTR_TDLS_ACTION, action_code);
+ NLA_PUT_U8(msg, NL80211_ATTR_TDLS_DIALOG_TOKEN, dialog_token);
+ NLA_PUT_U16(msg, NL80211_ATTR_STATUS_CODE, status_code);
+ NLA_PUT(msg, NL80211_ATTR_IE, len, buf);
+
+ return send_and_recv_msgs(drv, msg, NULL, NULL);
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return -ENOBUFS;
+}
+
+
+static int nl80211_tdls_oper(void *priv, enum tdls_oper oper, const u8 *peer)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ enum nl80211_tdls_operation nl80211_oper;
+
+ if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT))
+ return -EOPNOTSUPP;
+
+ switch (oper) {
+ case TDLS_DISCOVERY_REQ:
+ nl80211_oper = NL80211_TDLS_DISCOVERY_REQ;
+ break;
+ case TDLS_SETUP:
+ nl80211_oper = NL80211_TDLS_SETUP;
+ break;
+ case TDLS_TEARDOWN:
+ nl80211_oper = NL80211_TDLS_TEARDOWN;
+ break;
+ case TDLS_ENABLE_LINK:
+ nl80211_oper = NL80211_TDLS_ENABLE_LINK;
+ break;
+ case TDLS_DISABLE_LINK:
+ nl80211_oper = NL80211_TDLS_DISABLE_LINK;
+ break;
+ case TDLS_ENABLE:
+ return 0;
+ case TDLS_DISABLE:
+ return 0;
+ default:
+ return -EINVAL;
+ }
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_TDLS_OPER);
+ NLA_PUT_U8(msg, NL80211_ATTR_TDLS_OPERATION, nl80211_oper);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+ NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, peer);
+
+ return send_and_recv_msgs(drv, msg, NULL, NULL);
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return -ENOBUFS;
+}
+
+#endif /* CONFIG TDLS */
+
+
const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.name = "nl80211",
.desc = "Linux nl80211/cfg80211",
.get_ssid = wpa_driver_nl80211_get_ssid,
.set_key = wpa_driver_nl80211_set_key,
.scan2 = wpa_driver_nl80211_scan,
+ .sched_scan = wpa_driver_nl80211_sched_scan,
+ .stop_sched_scan = wpa_driver_nl80211_stop_sched_scan,
.get_scan_results2 = wpa_driver_nl80211_get_scan_results,
.deauthenticate = wpa_driver_nl80211_deauthenticate,
.disassociate = wpa_driver_nl80211_disassociate,
.authenticate = wpa_driver_nl80211_authenticate,
.associate = wpa_driver_nl80211_associate,
- .init = wpa_driver_nl80211_init,
+ .global_init = nl80211_global_init,
+ .global_deinit = nl80211_global_deinit,
+ .init2 = wpa_driver_nl80211_init,
.deinit = wpa_driver_nl80211_deinit,
.get_capa = wpa_driver_nl80211_get_capa,
.set_operstate = wpa_driver_nl80211_set_operstate,
.set_supp_port = wpa_driver_nl80211_set_supp_port,
.set_country = wpa_driver_nl80211_set_country,
- .set_beacon = wpa_driver_nl80211_set_beacon,
+ .set_ap = wpa_driver_nl80211_set_ap,
.if_add = wpa_driver_nl80211_if_add,
.if_remove = wpa_driver_nl80211_if_remove,
.send_mlme = wpa_driver_nl80211_send_mlme,
#ifdef HOSTAPD
.hapd_init = i802_init,
.hapd_deinit = i802_deinit,
+ .set_wds_sta = i802_set_wds_sta,
+#endif /* HOSTAPD */
+#if defined(HOSTAPD) || defined(CONFIG_AP)
.get_seqnum = i802_get_seqnum,
.flush = i802_flush,
.read_sta_data = i802_read_sta_data,
- .sta_deauth = i802_sta_deauth,
- .sta_disassoc = i802_sta_disassoc,
.get_inact_sec = i802_get_inact_sec,
.sta_clear_stats = i802_sta_clear_stats,
.set_rts = i802_set_rts,
.set_frag = i802_set_frag,
- .set_rate_sets = i802_set_rate_sets,
- .set_cts_protect = i802_set_cts_protect,
- .set_preamble = i802_set_preamble,
- .set_short_slot_time = i802_set_short_slot_time,
.set_tx_queue_params = i802_set_tx_queue_params,
.set_sta_vlan = i802_set_sta_vlan,
- .set_wds_sta = i802_set_wds_sta,
-#endif /* HOSTAPD */
+ .set_rate_sets = i802_set_rate_sets,
+ .sta_deauth = i802_sta_deauth,
+ .sta_disassoc = i802_sta_disassoc,
+#endif /* HOSTAPD || CONFIG_AP */
.set_freq = i802_set_freq,
.send_action = wpa_driver_nl80211_send_action,
+ .send_action_cancel_wait = wpa_driver_nl80211_send_action_cancel_wait,
.remain_on_channel = wpa_driver_nl80211_remain_on_channel,
.cancel_remain_on_channel =
wpa_driver_nl80211_cancel_remain_on_channel,
.probe_req_report = wpa_driver_nl80211_probe_req_report,
- .disable_11b_rates = wpa_driver_nl80211_disable_11b_rates,
.deinit_ap = wpa_driver_nl80211_deinit_ap,
+ .deinit_p2p_cli = wpa_driver_nl80211_deinit_p2p_cli,
.resume = wpa_driver_nl80211_resume,
.send_ft_action = nl80211_send_ft_action,
.signal_monitor = nl80211_signal_monitor,
+ .signal_poll = nl80211_signal_poll,
.send_frame = nl80211_send_frame,
+ .set_param = nl80211_set_param,
+ .get_radio_name = nl80211_get_radio_name,
+ .add_pmkid = nl80211_add_pmkid,
+ .remove_pmkid = nl80211_remove_pmkid,
+ .flush_pmkid = nl80211_flush_pmkid,
+ .set_rekey_info = nl80211_set_rekey_info,
+ .poll_client = nl80211_poll_client,
+#ifdef CONFIG_TDLS
+ .send_tdls_mgmt = nl80211_send_tdls_mgmt,
+ .tdls_oper = nl80211_tdls_oper,
+#endif /* CONFIG_TDLS */
};
drv->bAddWepKey = FALSE;
- if (addr == NULL || os_memcmp(addr, "\xff\xff\xff\xff\xff\xff",
- ETH_ALEN) == 0) {
+ if (addr == NULL || is_broadcast_ether_addr(addr)) {
/* Group Key */
pairwise = 0;
wpa_driver_ralink_get_bssid(drv, bssid);
#include "includes.h"
#include <sys/ioctl.h>
-#include <linux/if.h>
#include <linux/sockios.h>
#include <linux/if_ether.h>
#include <linux/mii.h>
+#include <net/if.h>
#include "common.h"
#include "driver.h"
/* copy ifname and take a pointer to the second to last character */
sep = drv->ifname +
os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)) - 2;
- /* find the '.' seperating <interface> and <vlan> */
+ /* find the '.' separating <interface> and <vlan> */
while (sep > drv->ifname && *sep != '.') sep--;
if (sep <= drv->ifname) {
wpa_printf(MSG_INFO, "%s: No <interface>.<vlan> pair in "
#include "common/ieee802_11_defs.h"
#include "crypto/sha1.h"
#include "l2_packet/l2_packet.h"
+#include "p2p/p2p.h"
+#include "wps/wps.h"
#include "driver.h"
int use_associnfo;
u8 assoc_wpa_ie[80];
size_t assoc_wpa_ie_len;
- int use_mlme;
int associated;
u8 *probe_req_ie;
size_t probe_req_ie_len;
unsigned int remain_on_channel_duration;
int current_freq;
+
+ struct p2p_data *p2p;
+ unsigned int off_channel_freq;
+ struct wpabuf *pending_action_tx;
+ u8 pending_action_src[ETH_ALEN];
+ u8 pending_action_dst[ETH_ALEN];
+ u8 pending_action_bssid[ETH_ALEN];
+ unsigned int pending_action_freq;
+ unsigned int pending_action_no_cck;
+ unsigned int pending_listen_freq;
+ unsigned int pending_listen_duration;
+ int pending_p2p_scan;
+ struct sockaddr *probe_from;
+ socklen_t probe_from_len;
};
static void wpa_driver_test_close_test_socket(
struct wpa_driver_test_data *drv);
static void test_remain_on_channel_timeout(void *eloop_ctx, void *timeout_ctx);
+static int wpa_driver_test_init_p2p(struct wpa_driver_test_data *drv);
static void test_driver_free_bss(struct test_driver_bss *bss)
static int test_driver_send_eapol(void *priv, const u8 *addr, const u8 *data,
size_t data_len, int encrypt,
- const u8 *own_addr)
+ const u8 *own_addr, u32 flags)
{
struct test_driver_bss *dbss = priv;
struct wpa_driver_test_data *drv = dbss->drv;
event.tx_status.ack = ret >= 0;
wpa_supplicant_event(drv->ctx, EVENT_TX_STATUS, &event);
+#ifdef CONFIG_P2P
+ if (drv->p2p &&
+ WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
+ WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION) {
+ if (drv->pending_action_tx == NULL) {
+ wpa_printf(MSG_DEBUG, "P2P: Ignore Action TX status - "
+ "no pending operation");
+ return ret;
+ }
+
+ if (os_memcmp(hdr->addr1, drv->pending_action_dst, ETH_ALEN) !=
+ 0) {
+ wpa_printf(MSG_DEBUG, "P2P: Ignore Action TX status - "
+ "unknown destination address");
+ return ret;
+ }
+
+ wpabuf_free(drv->pending_action_tx);
+ drv->pending_action_tx = NULL;
+
+ p2p_send_action_cb(drv->p2p, drv->pending_action_freq,
+ drv->pending_action_dst,
+ drv->pending_action_src,
+ drv->pending_action_bssid,
+ ret >= 0);
+ }
+#endif /* CONFIG_P2P */
+
return ret;
}
event.rx_probe_req.ie = ie;
event.rx_probe_req.ie_len = ielen;
wpa_supplicant_event(drv->ctx, EVENT_RX_PROBE_REQ, &event);
+#ifdef CONFIG_P2P
+ if (drv->p2p)
+ p2p_probe_req_rx(drv->p2p, sa, NULL, NULL, ie, ielen);
+#endif /* CONFIG_P2P */
}
dl_list_for_each(bss, &drv->bss, struct test_driver_bss, list) {
sendto(drv->test_socket, cmd, strlen(cmd), 0,
(struct sockaddr *) from, fromlen);
- drv_event_assoc(bss->bss_ctx, cli->addr, ie, ielen);
+ drv_event_assoc(bss->bss_ctx, cli->addr, ie, ielen, 0);
}
static int test_driver_set_ap_wps_ie(void *priv, const struct wpabuf *beacon,
- const struct wpabuf *proberesp)
+ const struct wpabuf *proberesp,
+ const struct wpabuf *assocresp)
{
struct test_driver_bss *bss = priv;
static int test_driver_if_add(void *priv, enum wpa_driver_if_type type,
const char *ifname, const u8 *addr,
void *bss_ctx, void **drv_priv,
- char *force_ifname, u8 *if_addr)
+ char *force_ifname, u8 *if_addr,
+ const char *bridge)
{
struct test_driver_bss *dbss = priv;
struct wpa_driver_test_data *drv = dbss->drv;
sizeof(drv->alloc_iface_idx),
if_addr + 1, ETH_ALEN - 1);
}
- if (type == WPA_IF_AP_BSS)
+ if (type == WPA_IF_AP_BSS || type == WPA_IF_P2P_GO ||
+ type == WPA_IF_P2P_CLIENT || type == WPA_IF_P2P_GROUP)
return test_driver_bss_add(priv, ifname, if_addr, bss_ctx,
drv_priv);
return 0;
const char *ifname)
{
wpa_printf(MSG_DEBUG, "%s(type=%d ifname=%s)", __func__, type, ifname);
- if (type == WPA_IF_AP_BSS)
+ if (type == WPA_IF_AP_BSS || type == WPA_IF_P2P_GO ||
+ type == WPA_IF_P2P_CLIENT || type == WPA_IF_P2P_GROUP)
return test_driver_bss_remove(priv, ifname);
return 0;
}
-static int test_driver_valid_bss_mask(void *priv, const u8 *addr,
- const u8 *mask)
-{
- return 0;
-}
-
-
static int test_driver_set_ssid(void *priv, const u8 *buf, int len)
{
struct test_driver_bss *bss = priv;
wpa_printf(MSG_DEBUG, "%s(ifname=%s)", __func__, bss->ifname);
+ if (len < 0)
+ return -1;
wpa_hexdump_ascii(MSG_DEBUG, "test_driver_set_ssid: SSID", buf, len);
- if (len < 0 || (size_t) len > sizeof(bss->ssid))
+ if ((size_t) len > sizeof(bss->ssid))
return -1;
os_memcpy(bss->ssid, buf, len);
return NULL;
drv->ap = 1;
bss = dl_list_first(&drv->bss, struct test_driver_bss, list);
+ drv->global = params->global_priv;
bss->bss_ctx = hapd;
os_memcpy(bss->bssid, drv->own_addr, ETH_ALEN);
static void wpa_driver_test_scan_timeout(void *eloop_ctx, void *timeout_ctx)
{
+ struct wpa_driver_test_data *drv = eloop_ctx;
wpa_printf(MSG_DEBUG, "Scan timeout - try to get results");
+ if (drv->pending_p2p_scan && drv->p2p) {
+#ifdef CONFIG_P2P
+ size_t i;
+ for (i = 0; i < drv->num_scanres; i++) {
+ struct wpa_scan_res *bss = drv->scanres[i];
+ if (p2p_scan_res_handler(drv->p2p, bss->bssid,
+ bss->freq, bss->level,
+ (const u8 *) (bss + 1),
+ bss->ie_len) > 0)
+ return;
+ }
+ p2p_scan_res_handled(drv->p2p);
+#endif /* CONFIG_P2P */
+ return;
+ }
wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
}
__func__, priv, params->freq, params->pairwise_suite,
params->group_suite, params->key_mgmt_suite,
params->auth_alg, params->mode);
+ wpa_driver_update_mode(drv, params->mode == IEEE80211_MODE_AP);
if (params->bssid) {
wpa_printf(MSG_DEBUG, " bssid=" MACSTR,
MAC2STR(params->bssid));
{
int freq = 0, own_freq;
union wpa_event_data event;
+ const struct ieee80211_mgmt *mgmt;
+ u16 fc;
struct test_driver_bss *bss;
bss = dl_list_first(&drv->bss, struct test_driver_bss, list);
event.mlme_rx.freq = freq;
wpa_supplicant_event(drv->ctx, EVENT_MLME_RX, &event);
- if (drv->probe_req_report && data_len >= 24) {
- const struct ieee80211_mgmt *mgmt;
- u16 fc;
+ mgmt = (const struct ieee80211_mgmt *) data;
+ fc = le_to_host16(mgmt->frame_control);
- mgmt = (const struct ieee80211_mgmt *) data;
- fc = le_to_host16(mgmt->frame_control);
+ if (drv->probe_req_report && data_len >= 24) {
if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_REQ) {
os_memset(&event, 0, sizeof(event));
event.rx_probe_req.sa = mgmt->sa;
+ event.rx_probe_req.da = mgmt->da;
+ event.rx_probe_req.bssid = mgmt->bssid;
event.rx_probe_req.ie = mgmt->u.probe_req.variable;
event.rx_probe_req.ie_len =
data_len - (mgmt->u.probe_req.variable - data);
wpa_supplicant_event(drv->ctx, EVENT_RX_PROBE_REQ,
&event);
+#ifdef CONFIG_P2P
+ if (drv->p2p)
+ p2p_probe_req_rx(drv->p2p, mgmt->sa,
+ mgmt->da, mgmt->bssid,
+ event.rx_probe_req.ie,
+ event.rx_probe_req.ie_len);
+#endif /* CONFIG_P2P */
}
}
+
+#ifdef CONFIG_P2P
+ if (drv->p2p &&
+ WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
+ WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION) {
+ size_t hdr_len;
+ hdr_len = (const u8 *)
+ &mgmt->u.action.u.vs_public_action.action - data;
+ p2p_rx_action(drv->p2p, mgmt->da, mgmt->sa, mgmt->bssid,
+ mgmt->u.action.category,
+ &mgmt->u.action.u.vs_public_action.action,
+ data_len - hdr_len, freq);
+ }
+#endif /* CONFIG_P2P */
+
}
bss = dl_list_first(&drv->bss, struct test_driver_bss, list);
/* data: optional [ STA-addr | ' ' | IEs(hex) ] */
+#ifdef CONFIG_P2P
+ if (drv->probe_req_report && drv->p2p && data_len) {
+ const char *d = (const char *) data;
+ u8 sa[ETH_ALEN];
+ u8 ie[512];
+ size_t ielen;
+
+ if (hwaddr_aton(d, sa))
+ return;
+ d += 18;
+ while (*d == ' ')
+ d++;
+ ielen = os_strlen(d) / 2;
+ if (ielen > sizeof(ie))
+ ielen = sizeof(ie);
+ if (hexstr2bin(d, ie, ielen) < 0)
+ ielen = 0;
+ drv->probe_from = from;
+ drv->probe_from_len = fromlen;
+ p2p_probe_req_rx(drv->p2p, sa, NULL, NULL, ie, ielen);
+ drv->probe_from = NULL;
+ }
+#endif /* CONFIG_P2P */
if (!drv->ibss)
return;
struct test_client_socket *cli, *prev;
int i;
+#ifdef CONFIG_P2P
+ if (drv->p2p)
+ p2p_deinit(drv->p2p);
+ wpabuf_free(drv->pending_action_tx);
+#endif /* CONFIG_P2P */
+
cli = drv->cli;
while (cli) {
prev = cli;
drv->use_associnfo = 1;
}
-#ifdef CONFIG_CLIENT_MLME
- if (os_strstr(param, "use_mlme=1")) {
- wpa_printf(MSG_DEBUG, "test_driver: Use internal MLME");
- drv->use_mlme = 1;
+ if (os_strstr(param, "p2p_mgmt=1")) {
+ wpa_printf(MSG_DEBUG, "test_driver: Use internal P2P "
+ "management");
+ if (wpa_driver_test_init_p2p(drv) < 0)
+ return -1;
}
-#endif /* CONFIG_CLIENT_MLME */
return 0;
}
capa->auth = WPA_DRIVER_AUTH_OPEN |
WPA_DRIVER_AUTH_SHARED |
WPA_DRIVER_AUTH_LEAP;
- if (drv->use_mlme)
- capa->flags |= WPA_DRIVER_FLAGS_USER_SPACE_MLME;
+ if (drv->p2p)
+ capa->flags |= WPA_DRIVER_FLAGS_P2P_MGMT;
capa->flags |= WPA_DRIVER_FLAGS_AP;
+ capa->flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT;
+ capa->flags |= WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE;
+ capa->flags |= WPA_DRIVER_FLAGS_P2P_CAPABLE;
capa->max_scan_ssids = 2;
capa->max_remain_on_chan = 60000;
}
-static int wpa_driver_test_set_channel(void *priv,
- enum hostapd_hw_mode phymode,
- int chan, int freq)
-{
- struct test_driver_bss *dbss = priv;
- struct wpa_driver_test_data *drv = dbss->drv;
- wpa_printf(MSG_DEBUG, "%s: phymode=%d chan=%d freq=%d",
- __func__, phymode, chan, freq);
- drv->current_freq = freq;
- return 0;
-}
-
-
-static int wpa_driver_test_mlme_add_sta(void *priv, const u8 *addr,
- const u8 *supp_rates,
- size_t supp_rates_len)
-{
- wpa_printf(MSG_DEBUG, "%s: addr=" MACSTR, __func__, MAC2STR(addr));
- return 0;
-}
-
-
-static int wpa_driver_test_mlme_remove_sta(void *priv, const u8 *addr)
-{
- wpa_printf(MSG_DEBUG, "%s: addr=" MACSTR, __func__, MAC2STR(addr));
- return 0;
-}
-
-
-static int wpa_driver_test_set_ssid(void *priv, const u8 *ssid,
- size_t ssid_len)
-{
- wpa_printf(MSG_DEBUG, "%s", __func__);
- return 0;
-}
-
-
-static int wpa_driver_test_set_bssid(void *priv, const u8 *bssid)
-{
- wpa_printf(MSG_DEBUG, "%s: bssid=" MACSTR, __func__, MAC2STR(bssid));
- return 0;
-}
-
-
static void * wpa_driver_test_global_init(void)
{
struct wpa_driver_test_global *global;
static int wpa_driver_test_send_action(void *priv, unsigned int freq,
+ unsigned int wait,
const u8 *dst, const u8 *src,
const u8 *bssid,
- const u8 *data, size_t data_len)
+ const u8 *data, size_t data_len,
+ int no_cck)
{
struct test_driver_bss *dbss = priv;
struct wpa_driver_test_data *drv = dbss->drv;
}
+#ifdef CONFIG_P2P
+static void test_send_action_cb(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_driver_test_data *drv = eloop_ctx;
+
+ if (drv->pending_action_tx == NULL)
+ return;
+
+ if (drv->off_channel_freq != drv->pending_action_freq) {
+ wpa_printf(MSG_DEBUG, "P2P: Pending Action frame TX "
+ "waiting for another freq=%u",
+ drv->pending_action_freq);
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "P2P: Sending pending Action frame to "
+ MACSTR, MAC2STR(drv->pending_action_dst));
+ wpa_driver_test_send_action(drv, drv->pending_action_freq, 0,
+ drv->pending_action_dst,
+ drv->pending_action_src,
+ drv->pending_action_bssid,
+ wpabuf_head(drv->pending_action_tx),
+ wpabuf_len(drv->pending_action_tx),
+ drv->pending_action_no_cck);
+}
+#endif /* CONFIG_P2P */
+
+
static void test_remain_on_channel_timeout(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_driver_test_data *drv = eloop_ctx;
os_memset(&data, 0, sizeof(data));
data.remain_on_channel.freq = drv->remain_on_channel_freq;
data.remain_on_channel.duration = drv->remain_on_channel_duration;
- wpa_supplicant_event(drv->ctx, EVENT_CANCEL_REMAIN_ON_CHANNEL, &data);
+
+ if (drv->p2p)
+ drv->off_channel_freq = 0;
drv->remain_on_channel_freq = 0;
+
+ wpa_supplicant_event(drv->ctx, EVENT_CANCEL_REMAIN_ON_CHANNEL, &data);
}
data.remain_on_channel.duration = duration;
wpa_supplicant_event(drv->ctx, EVENT_REMAIN_ON_CHANNEL, &data);
+#ifdef CONFIG_P2P
+ if (drv->p2p) {
+ drv->off_channel_freq = drv->remain_on_channel_freq;
+ test_send_action_cb(drv, NULL);
+ if (drv->off_channel_freq == drv->pending_listen_freq) {
+ p2p_listen_cb(drv->p2p, drv->pending_listen_freq,
+ drv->pending_listen_duration);
+ drv->pending_listen_freq = 0;
+ }
+ }
+#endif /* CONFIG_P2P */
+
return 0;
}
}
+#ifdef CONFIG_P2P
+
+static int wpa_driver_test_p2p_find(void *priv, unsigned int timeout, int type)
+{
+ struct wpa_driver_test_data *drv = priv;
+ wpa_printf(MSG_DEBUG, "%s(timeout=%u)", __func__, timeout);
+ if (!drv->p2p)
+ return -1;
+ return p2p_find(drv->p2p, timeout, type, 0, NULL, NULL);
+}
+
+
+static int wpa_driver_test_p2p_stop_find(void *priv)
+{
+ struct wpa_driver_test_data *drv = priv;
+ wpa_printf(MSG_DEBUG, "%s", __func__);
+ if (!drv->p2p)
+ return -1;
+ p2p_stop_find(drv->p2p);
+ return 0;
+}
+
+
+static int wpa_driver_test_p2p_listen(void *priv, unsigned int timeout)
+{
+ struct wpa_driver_test_data *drv = priv;
+ wpa_printf(MSG_DEBUG, "%s(timeout=%u)", __func__, timeout);
+ if (!drv->p2p)
+ return -1;
+ return p2p_listen(drv->p2p, timeout);
+}
+
+
+static int wpa_driver_test_p2p_connect(void *priv, const u8 *peer_addr,
+ int wps_method, int go_intent,
+ const u8 *own_interface_addr,
+ unsigned int force_freq,
+ int persistent_group)
+{
+ struct wpa_driver_test_data *drv = priv;
+ wpa_printf(MSG_DEBUG, "%s(peer_addr=" MACSTR " wps_method=%d "
+ "go_intent=%d "
+ "own_interface_addr=" MACSTR " force_freq=%u "
+ "persistent_group=%d)",
+ __func__, MAC2STR(peer_addr), wps_method, go_intent,
+ MAC2STR(own_interface_addr), force_freq, persistent_group);
+ if (!drv->p2p)
+ return -1;
+ return p2p_connect(drv->p2p, peer_addr, wps_method, go_intent,
+ own_interface_addr, force_freq, persistent_group);
+}
+
+
+static int wpa_driver_test_wps_success_cb(void *priv, const u8 *peer_addr)
+{
+ struct wpa_driver_test_data *drv = priv;
+ wpa_printf(MSG_DEBUG, "%s(peer_addr=" MACSTR ")",
+ __func__, MAC2STR(peer_addr));
+ if (!drv->p2p)
+ return -1;
+ p2p_wps_success_cb(drv->p2p, peer_addr);
+ return 0;
+}
+
+
+static int wpa_driver_test_p2p_group_formation_failed(void *priv)
+{
+ struct wpa_driver_test_data *drv = priv;
+ wpa_printf(MSG_DEBUG, "%s", __func__);
+ if (!drv->p2p)
+ return -1;
+ p2p_group_formation_failed(drv->p2p);
+ return 0;
+}
+
+
+static int wpa_driver_test_p2p_set_params(void *priv,
+ const struct p2p_params *params)
+{
+ struct wpa_driver_test_data *drv = priv;
+ wpa_printf(MSG_DEBUG, "%s", __func__);
+ if (!drv->p2p)
+ return -1;
+ if (p2p_set_dev_name(drv->p2p, params->dev_name) < 0 ||
+ p2p_set_pri_dev_type(drv->p2p, params->pri_dev_type) < 0 ||
+ p2p_set_sec_dev_types(drv->p2p, params->sec_dev_type,
+ params->num_sec_dev_types) < 0)
+ return -1;
+ return 0;
+}
+
+
+static int test_p2p_scan(void *ctx, enum p2p_scan_type type, int freq,
+ unsigned int num_req_dev_types,
+ const u8 *req_dev_types, const u8 *dev_id)
+{
+ struct wpa_driver_test_data *drv = ctx;
+ struct wpa_driver_scan_params params;
+ int ret;
+ struct wpabuf *wps_ie, *ies;
+ int social_channels[] = { 2412, 2437, 2462, 0, 0 };
+ size_t ielen;
+
+ wpa_printf(MSG_DEBUG, "%s(type=%d freq=%d)",
+ __func__, type, freq);
+
+ os_memset(¶ms, 0, sizeof(params));
+
+ /* P2P Wildcard SSID */
+ params.num_ssids = 1;
+ params.ssids[0].ssid = (u8 *) P2P_WILDCARD_SSID;
+ params.ssids[0].ssid_len = P2P_WILDCARD_SSID_LEN;
+
+#if 0 /* TODO: WPS IE */
+ wpa_s->wps->dev.p2p = 1;
+ wps_ie = wps_build_probe_req_ie(0, &wpa_s->wps->dev, wpa_s->wps->uuid,
+ WPS_REQ_ENROLLEE);
+#else
+ wps_ie = wpabuf_alloc(1);
+#endif
+ if (wps_ie == NULL)
+ return -1;
+
+ ielen = p2p_scan_ie_buf_len(drv->p2p);
+ ies = wpabuf_alloc(wpabuf_len(wps_ie) + ielen);
+ if (ies == NULL) {
+ wpabuf_free(wps_ie);
+ return -1;
+ }
+ wpabuf_put_buf(ies, wps_ie);
+ wpabuf_free(wps_ie);
+
+ p2p_scan_ie(drv->p2p, ies, dev_id);
+
+ params.extra_ies = wpabuf_head(ies);
+ params.extra_ies_len = wpabuf_len(ies);
+
+ switch (type) {
+ case P2P_SCAN_SOCIAL:
+ params.freqs = social_channels;
+ break;
+ case P2P_SCAN_FULL:
+ break;
+ case P2P_SCAN_SPECIFIC:
+ social_channels[0] = freq;
+ social_channels[1] = 0;
+ params.freqs = social_channels;
+ break;
+ case P2P_SCAN_SOCIAL_PLUS_ONE:
+ social_channels[3] = freq;
+ params.freqs = social_channels;
+ break;
+ }
+
+ drv->pending_p2p_scan = 1;
+ ret = wpa_driver_test_scan(drv, ¶ms);
+
+ wpabuf_free(ies);
+
+ return ret;
+}
+
+
+static int test_send_action(void *ctx, unsigned int freq, const u8 *dst,
+ const u8 *src, const u8 *bssid, const u8 *buf,
+ size_t len, unsigned int wait_time)
+{
+ struct wpa_driver_test_data *drv = ctx;
+
+ wpa_printf(MSG_DEBUG, "%s(freq=%u dst=" MACSTR " src=" MACSTR
+ " bssid=" MACSTR " len=%d",
+ __func__, freq, MAC2STR(dst), MAC2STR(src), MAC2STR(bssid),
+ (int) len);
+ if (freq <= 0) {
+ wpa_printf(MSG_WARNING, "P2P: No frequency specified for "
+ "action frame TX");
+ return -1;
+ }
+
+ if (drv->pending_action_tx) {
+ wpa_printf(MSG_DEBUG, "P2P: Dropped pending Action frame TX "
+ "to " MACSTR, MAC2STR(drv->pending_action_dst));
+ wpabuf_free(drv->pending_action_tx);
+ }
+ drv->pending_action_tx = wpabuf_alloc(len);
+ if (drv->pending_action_tx == NULL)
+ return -1;
+ wpabuf_put_data(drv->pending_action_tx, buf, len);
+ os_memcpy(drv->pending_action_src, src, ETH_ALEN);
+ os_memcpy(drv->pending_action_dst, dst, ETH_ALEN);
+ os_memcpy(drv->pending_action_bssid, bssid, ETH_ALEN);
+ drv->pending_action_freq = freq;
+ drv->pending_action_no_cck = 1;
+
+ if (drv->off_channel_freq == freq) {
+ /* Already on requested channel; send immediately */
+ /* TODO: Would there ever be need to extend the current
+ * duration on the channel? */
+ eloop_cancel_timeout(test_send_action_cb, drv, NULL);
+ eloop_register_timeout(0, 0, test_send_action_cb, drv, NULL);
+ return 0;
+ }
+
+ wpa_printf(MSG_DEBUG, "P2P: Schedule Action frame to be transmitted "
+ "once the driver gets to the requested channel");
+ if (wpa_driver_test_remain_on_channel(drv, freq, wait_time) < 0) {
+ wpa_printf(MSG_DEBUG, "P2P: Failed to request driver "
+ "to remain on channel (%u MHz) for Action "
+ "Frame TX", freq);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void test_send_action_done(void *ctx)
+{
+ wpa_printf(MSG_DEBUG, "%s", __func__);
+ /* TODO */
+}
+
+
+static void test_go_neg_completed(void *ctx, struct p2p_go_neg_results *res)
+{
+ struct wpa_driver_test_data *drv = ctx;
+ union wpa_event_data event;
+ wpa_printf(MSG_DEBUG, "%s", __func__);
+ os_memset(&event, 0, sizeof(event));
+ event.p2p_go_neg_completed.res = res;
+ wpa_supplicant_event(drv->ctx, EVENT_P2P_GO_NEG_COMPLETED, &event);
+}
+
+
+static void test_go_neg_req_rx(void *ctx, const u8 *src, u16 dev_passwd_id)
+{
+ struct wpa_driver_test_data *drv = ctx;
+ union wpa_event_data event;
+ wpa_printf(MSG_DEBUG, "%s(src=" MACSTR ")", __func__, MAC2STR(src));
+ os_memset(&event, 0, sizeof(event));
+ event.p2p_go_neg_req_rx.src = src;
+ event.p2p_go_neg_req_rx.dev_passwd_id = dev_passwd_id;
+ wpa_supplicant_event(drv->ctx, EVENT_P2P_GO_NEG_REQ_RX, &event);
+}
+
+
+static void test_dev_found(void *ctx, const u8 *addr,
+ const struct p2p_peer_info *info, int new_device)
+{
+ struct wpa_driver_test_data *drv = ctx;
+ union wpa_event_data event;
+ char devtype[WPS_DEV_TYPE_BUFSIZE];
+ wpa_printf(MSG_DEBUG, "%s(" MACSTR " p2p_dev_addr=" MACSTR
+ " pri_dev_type=%s name='%s' config_methods=0x%x "
+ "dev_capab=0x%x group_capab=0x%x)",
+ __func__, MAC2STR(addr), MAC2STR(info->p2p_device_addr),
+ wps_dev_type_bin2str(info->pri_dev_type, devtype,
+ sizeof(devtype)),
+ info->device_name, info->config_methods, info->dev_capab,
+ info->group_capab);
+
+ os_memset(&event, 0, sizeof(event));
+ event.p2p_dev_found.addr = addr;
+ event.p2p_dev_found.dev_addr = info->p2p_device_addr;
+ event.p2p_dev_found.pri_dev_type = info->pri_dev_type;
+ event.p2p_dev_found.dev_name = info->device_name;
+ event.p2p_dev_found.config_methods = info->config_methods;
+ event.p2p_dev_found.dev_capab = info->dev_capab;
+ event.p2p_dev_found.group_capab = info->group_capab;
+ wpa_supplicant_event(drv->ctx, EVENT_P2P_DEV_FOUND, &event);
+}
+
+
+static int test_start_listen(void *ctx, unsigned int freq,
+ unsigned int duration,
+ const struct wpabuf *probe_resp_ie)
+{
+ struct wpa_driver_test_data *drv = ctx;
+
+ wpa_printf(MSG_DEBUG, "%s(freq=%u duration=%u)",
+ __func__, freq, duration);
+
+ if (wpa_driver_test_probe_req_report(drv, 1) < 0)
+ return -1;
+
+ drv->pending_listen_freq = freq;
+ drv->pending_listen_duration = duration;
+
+ if (wpa_driver_test_remain_on_channel(drv, freq, duration) < 0) {
+ drv->pending_listen_freq = 0;
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void test_stop_listen(void *ctx)
+{
+ wpa_printf(MSG_DEBUG, "%s", __func__);
+ /* TODO */
+}
+
+
+static int test_send_probe_resp(void *ctx, const struct wpabuf *buf)
+{
+ struct wpa_driver_test_data *drv = ctx;
+ char resp[512], *pos, *end;
+ int ret;
+ const struct ieee80211_mgmt *mgmt;
+ const u8 *ie, *ie_end;
+
+ wpa_printf(MSG_DEBUG, "%s", __func__);
+ wpa_hexdump_buf(MSG_MSGDUMP, "Probe Response", buf);
+ if (wpabuf_len(buf) < 24)
+ return -1;
+ if (!drv->probe_from) {
+ wpa_printf(MSG_DEBUG, "%s: probe_from not set", __func__);
+ return -1;
+ }
+
+ pos = resp;
+ end = resp + sizeof(resp);
+
+ mgmt = wpabuf_head(buf);
+
+ /* reply: SCANRESP BSSID SSID IEs */
+ ret = os_snprintf(pos, end - pos, "SCANRESP " MACSTR " ",
+ MAC2STR(mgmt->bssid));
+ if (ret < 0 || ret >= end - pos)
+ return -1;
+ pos += ret;
+
+ ie = mgmt->u.probe_resp.variable;
+ ie_end = wpabuf_head_u8(buf) + wpabuf_len(buf);
+ if (ie_end - ie < 2 || ie[0] != WLAN_EID_SSID ||
+ ie + 2 + ie[1] > ie_end)
+ return -1;
+ pos += wpa_snprintf_hex(pos, end - pos, ie + 2, ie[1]);
+
+ ret = os_snprintf(pos, end - pos, " ");
+ if (ret < 0 || ret >= end - pos)
+ return -1;
+ pos += ret;
+ pos += wpa_snprintf_hex(pos, end - pos, ie, ie_end - ie);
+
+ sendto(drv->test_socket, resp, pos - resp, 0,
+ drv->probe_from, drv->probe_from_len);
+
+ return 0;
+}
+
+
+static void test_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token,
+ u16 update_indic, const u8 *tlvs, size_t tlvs_len)
+{
+ wpa_printf(MSG_DEBUG, "%s", __func__);
+ /* TODO */
+}
+
+
+static void test_sd_response(void *ctx, const u8 *sa, u16 update_indic,
+ const u8 *tlvs, size_t tlvs_len)
+{
+ wpa_printf(MSG_DEBUG, "%s", __func__);
+ /* TODO */
+}
+
+
+static void test_prov_disc_req(void *ctx, const u8 *peer, u16 config_methods,
+ const u8 *dev_addr, const u8 *pri_dev_type,
+ const char *dev_name, u16 supp_config_methods,
+ u8 dev_capab, u8 group_capab,
+ const u8 *group_id, size_t group_id_len)
+{
+ wpa_printf(MSG_DEBUG, "%s(peer=" MACSTR " config_methods=0x%x)",
+ __func__, MAC2STR(peer), config_methods);
+ /* TODO */
+}
+
+
+static void test_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods)
+{
+ wpa_printf(MSG_DEBUG, "%s(peer=" MACSTR " config_methods=0x%x)",
+ __func__, MAC2STR(peer), config_methods);
+ /* TODO */
+}
+
+#endif /* CONFIG_P2P */
+
+
+static int wpa_driver_test_init_p2p(struct wpa_driver_test_data *drv)
+{
+#ifdef CONFIG_P2P
+ struct p2p_config p2p;
+ unsigned int r;
+ int i;
+
+ os_memset(&p2p, 0, sizeof(p2p));
+ p2p.msg_ctx = drv->ctx;
+ p2p.cb_ctx = drv;
+ p2p.p2p_scan = test_p2p_scan;
+ p2p.send_action = test_send_action;
+ p2p.send_action_done = test_send_action_done;
+ p2p.go_neg_completed = test_go_neg_completed;
+ p2p.go_neg_req_rx = test_go_neg_req_rx;
+ p2p.dev_found = test_dev_found;
+ p2p.start_listen = test_start_listen;
+ p2p.stop_listen = test_stop_listen;
+ p2p.send_probe_resp = test_send_probe_resp;
+ p2p.sd_request = test_sd_request;
+ p2p.sd_response = test_sd_response;
+ p2p.prov_disc_req = test_prov_disc_req;
+ p2p.prov_disc_resp = test_prov_disc_resp;
+
+ os_memcpy(p2p.dev_addr, drv->own_addr, ETH_ALEN);
+
+ p2p.reg_class = 12; /* TODO: change depending on location */
+ /*
+ * Pick one of the social channels randomly as the listen
+ * channel.
+ */
+ os_get_random((u8 *) &r, sizeof(r));
+ p2p.channel = 1 + (r % 3) * 5;
+
+ /* TODO: change depending on location */
+ p2p.op_reg_class = 12;
+ /*
+ * For initial tests, pick the operation channel randomly.
+ * TODO: Use scan results (etc.) to select the best channel.
+ */
+ p2p.op_channel = 1 + r % 11;
+
+ os_memcpy(p2p.country, "US ", 3);
+
+ /* FIX: fetch available channels from the driver */
+ p2p.channels.reg_classes = 1;
+ p2p.channels.reg_class[0].reg_class = 12; /* US/12 = 2.4 GHz band */
+ p2p.channels.reg_class[0].channels = 11;
+ for (i = 0; i < 11; i++)
+ p2p.channels.reg_class[0].channel[i] = i + 1;
+
+ p2p.max_peers = 100;
+
+ drv->p2p = p2p_init(&p2p);
+ if (drv->p2p == NULL)
+ return -1;
+ return 0;
+#else /* CONFIG_P2P */
+ wpa_printf(MSG_INFO, "driver_test: P2P support not included");
+ return -1;
+#endif /* CONFIG_P2P */
+}
+
+
const struct wpa_driver_ops wpa_driver_test_ops = {
"test",
"wpa_supplicant test driver",
.get_hw_feature_data = wpa_driver_test_get_hw_feature_data,
.if_add = test_driver_if_add,
.if_remove = test_driver_if_remove,
- .valid_bss_mask = test_driver_valid_bss_mask,
.hapd_set_ssid = test_driver_set_ssid,
.set_privacy = test_driver_set_privacy,
.set_sta_vlan = test_driver_set_sta_vlan,
.get_mac_addr = wpa_driver_test_get_mac_addr,
.send_eapol = wpa_driver_test_send_eapol,
.mlme_setprotection = wpa_driver_test_mlme_setprotection,
- .set_channel = wpa_driver_test_set_channel,
- .set_ssid = wpa_driver_test_set_ssid,
- .set_bssid = wpa_driver_test_set_bssid,
- .mlme_add_sta = wpa_driver_test_mlme_add_sta,
- .mlme_remove_sta = wpa_driver_test_mlme_remove_sta,
.get_scan_results2 = wpa_driver_test_get_scan_results2,
.global_init = wpa_driver_test_global_init,
.global_deinit = wpa_driver_test_global_deinit,
.remain_on_channel = wpa_driver_test_remain_on_channel,
.cancel_remain_on_channel = wpa_driver_test_cancel_remain_on_channel,
.probe_req_report = wpa_driver_test_probe_req_report,
+#ifdef CONFIG_P2P
+ .p2p_find = wpa_driver_test_p2p_find,
+ .p2p_stop_find = wpa_driver_test_p2p_stop_find,
+ .p2p_listen = wpa_driver_test_p2p_listen,
+ .p2p_connect = wpa_driver_test_p2p_connect,
+ .wps_success_cb = wpa_driver_test_wps_success_cb,
+ .p2p_group_formation_failed =
+ wpa_driver_test_p2p_group_formation_failed,
+ .p2p_set_params = wpa_driver_test_p2p_set_params,
+#endif /* CONFIG_P2P */
};
#include "includes.h"
#include <sys/ioctl.h>
+#include <sys/types.h>
#include <sys/stat.h>
+#include <fcntl.h>
#include <net/if_arp.h>
#include "wireless_copy.h"
#include "priv_netlink.h"
#include "netlink.h"
#include "linux_ioctl.h"
+#include "rfkill.h"
#include "driver.h"
#include "driver_wext.h"
del ? "removed" : "added");
if (os_strcmp(drv->ifname, event.interface_status.ifname) == 0) {
- if (del)
+ if (del) {
+ if (drv->if_removed) {
+ wpa_printf(MSG_DEBUG, "WEXT: if_removed "
+ "already set - ignore event");
+ return;
+ }
drv->if_removed = 1;
- else
+ } else {
+ if (if_nametoindex(drv->ifname) == 0) {
+ wpa_printf(MSG_DEBUG, "WEXT: Interface %s "
+ "does not exist - ignore "
+ "RTM_NEWLINK",
+ drv->ifname);
+ return;
+ }
+ if (!drv->if_removed) {
+ wpa_printf(MSG_DEBUG, "WEXT: if_removed "
+ "already cleared - ignore event");
+ return;
+ }
drv->if_removed = 0;
+ }
}
wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
struct wpa_driver_wext_data *drv = ctx;
int attrlen, rta_len;
struct rtattr *attr;
+ char namebuf[IFNAMSIZ];
if (!wpa_driver_wext_own_ifindex(drv, ifi->ifi_index, buf, len)) {
wpa_printf(MSG_DEBUG, "Ignore event for foreign ifindex %d",
(ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "",
(ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "",
(ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
+
+ if (!drv->if_disabled && !(ifi->ifi_flags & IFF_UP)) {
+ wpa_printf(MSG_DEBUG, "WEXT: Interface down");
+ drv->if_disabled = 1;
+ wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_DISABLED, NULL);
+ }
+
+ if (drv->if_disabled && (ifi->ifi_flags & IFF_UP)) {
+ if (if_indextoname(ifi->ifi_index, namebuf) &&
+ linux_iface_up(drv->ioctl_sock, drv->ifname) == 0) {
+ wpa_printf(MSG_DEBUG, "WEXT: Ignore interface up "
+ "event since interface %s is down",
+ namebuf);
+ } else if (if_nametoindex(drv->ifname) == 0) {
+ wpa_printf(MSG_DEBUG, "WEXT: Ignore interface up "
+ "event since interface %s does not exist",
+ drv->ifname);
+ } else if (drv->if_removed) {
+ wpa_printf(MSG_DEBUG, "WEXT: Ignore interface up "
+ "event since interface %s is marked "
+ "removed", drv->ifname);
+ } else {
+ wpa_printf(MSG_DEBUG, "WEXT: Interface up");
+ drv->if_disabled = 0;
+ wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED,
+ NULL);
+ }
+ }
+
/*
* Some drivers send the association event before the operup event--in
* this case, lifting operstate in wpa_driver_wext_set_operstate()
}
+static void wpa_driver_wext_rfkill_blocked(void *ctx)
+{
+ wpa_printf(MSG_DEBUG, "WEXT: RFKILL blocked");
+ /*
+ * This may be for any interface; use ifdown event to disable
+ * interface.
+ */
+}
+
+
+static void wpa_driver_wext_rfkill_unblocked(void *ctx)
+{
+ struct wpa_driver_wext_data *drv = ctx;
+ wpa_printf(MSG_DEBUG, "WEXT: RFKILL unblocked");
+ if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1)) {
+ wpa_printf(MSG_DEBUG, "WEXT: Could not set interface UP "
+ "after rfkill unblock");
+ return;
+ }
+ /* rtnetlink ifup handler will report interface as enabled */
+}
+
+
+static void wext_get_phy_name(struct wpa_driver_wext_data *drv)
+{
+ /* Find phy (radio) to which this interface belongs */
+ char buf[90], *pos;
+ int f, rv;
+
+ drv->phyname[0] = '\0';
+ snprintf(buf, sizeof(buf) - 1, "/sys/class/net/%s/phy80211/name",
+ drv->ifname);
+ f = open(buf, O_RDONLY);
+ if (f < 0) {
+ wpa_printf(MSG_DEBUG, "Could not open file %s: %s",
+ buf, strerror(errno));
+ return;
+ }
+
+ rv = read(f, drv->phyname, sizeof(drv->phyname) - 1);
+ close(f);
+ if (rv < 0) {
+ wpa_printf(MSG_DEBUG, "Could not read file %s: %s",
+ buf, strerror(errno));
+ return;
+ }
+
+ drv->phyname[rv] = '\0';
+ pos = os_strchr(drv->phyname, '\n');
+ if (pos)
+ *pos = '\0';
+ wpa_printf(MSG_DEBUG, "wext: interface %s phy: %s",
+ drv->ifname, drv->phyname);
+}
+
+
/**
* wpa_driver_wext_init - Initialize WE driver interface
* @ctx: context to be used when calling wpa_supplicant functions,
{
struct wpa_driver_wext_data *drv;
struct netlink_config *cfg;
+ struct rfkill_config *rcfg;
char path[128];
struct stat buf;
if (stat(path, &buf) == 0) {
wpa_printf(MSG_DEBUG, "WEXT: cfg80211-based driver detected");
drv->cfg80211 = 1;
+ wext_get_phy_name(drv);
}
drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
goto err2;
}
+ rcfg = os_zalloc(sizeof(*rcfg));
+ if (rcfg == NULL)
+ goto err3;
+ rcfg->ctx = drv;
+ os_strlcpy(rcfg->ifname, ifname, sizeof(rcfg->ifname));
+ rcfg->blocked_cb = wpa_driver_wext_rfkill_blocked;
+ rcfg->unblocked_cb = wpa_driver_wext_rfkill_unblocked;
+ drv->rfkill = rfkill_init(rcfg);
+ if (drv->rfkill == NULL) {
+ wpa_printf(MSG_DEBUG, "WEXT: RFKILL status not available");
+ os_free(rcfg);
+ }
+
drv->mlme_sock = -1;
if (wpa_driver_wext_finish_drv_init(drv) < 0)
return drv;
err3:
+ rfkill_deinit(drv->rfkill);
netlink_deinit(drv->netlink);
err2:
close(drv->ioctl_sock);
}
+static void wpa_driver_wext_send_rfkill(void *eloop_ctx, void *timeout_ctx)
+{
+ wpa_supplicant_event(timeout_ctx, EVENT_INTERFACE_DISABLED, NULL);
+}
+
+
static int wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv)
{
- if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1) < 0)
- return -1;
+ int send_rfkill_event = 0;
+
+ if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1) < 0) {
+ if (rfkill_is_blocked(drv->rfkill)) {
+ wpa_printf(MSG_DEBUG, "WEXT: Could not yet enable "
+ "interface '%s' due to rfkill",
+ drv->ifname);
+ drv->if_disabled = 1;
+ send_rfkill_event = 1;
+ } else {
+ wpa_printf(MSG_ERROR, "WEXT: Could not set "
+ "interface '%s' UP", drv->ifname);
+ return -1;
+ }
+ }
/*
* Make sure that the driver does not have any obsolete PMKID entries.
netlink_send_oper_ifla(drv->netlink, drv->ifindex,
1, IF_OPER_DORMANT);
+ if (send_rfkill_event) {
+ eloop_register_timeout(0, 0, wpa_driver_wext_send_rfkill,
+ drv, drv->ctx);
+ }
+
return 0;
}
netlink_send_oper_ifla(drv->netlink, drv->ifindex, 0, IF_OPER_UP);
netlink_deinit(drv->netlink);
+ rfkill_deinit(drv->rfkill);
if (drv->mlme_sock >= 0)
eloop_unregister_read_sock(drv->mlme_sock);
/* Not all drivers generate "scan completed" wireless event, so try to
* read results after a timeout. */
- timeout = 5;
+ timeout = 10;
if (drv->scan_complete_events) {
/*
* The driver seems to deliver SIOCGIWSCAN events to notify
}
-static void wext_get_scan_qual(struct iw_event *iwe,
+static void wext_get_scan_qual(struct wpa_driver_wext_data *drv,
+ struct iw_event *iwe,
struct wext_scan_data *res)
{
res->res.qual = iwe->u.qual.qual;
res->res.flags |= WPA_SCAN_NOISE_INVALID;
if (iwe->u.qual.updated & IW_QUAL_DBM)
res->res.flags |= WPA_SCAN_LEVEL_DBM;
+ if ((iwe->u.qual.updated & IW_QUAL_DBM) ||
+ ((iwe->u.qual.level != 0) &&
+ (iwe->u.qual.level > drv->max_level))) {
+ if (iwe->u.qual.level >= 64)
+ res->res.level -= 0x100;
+ if (iwe->u.qual.noise >= 64)
+ res->res.noise -= 0x100;
+ }
}
tmp[res->num++] = r;
res->res = tmp;
}
-
+
/**
* wpa_driver_wext_get_scan_results - Fetch the latest scan results
struct wpa_scan_results * wpa_driver_wext_get_scan_results(void *priv)
{
struct wpa_driver_wext_data *drv = priv;
- size_t ap_num = 0, len;
+ size_t len;
int first;
u8 *res_buf;
struct iw_event iwe_buf, *iwe = &iwe_buf;
if (res_buf == NULL)
return NULL;
- ap_num = 0;
first = 1;
res = os_zalloc(sizeof(*res));
wext_get_scan_freq(iwe, &data);
break;
case IWEVQUAL:
- wext_get_scan_qual(iwe, &data);
+ wext_get_scan_qual(drv, iwe, &data);
break;
case SIOCGIWENCODE:
wext_get_scan_encode(iwe, &data);
"assuming WPA is not supported");
}
+ drv->max_level = range->max_qual.level;
+
os_free(range);
return 0;
}
iwr.u.encoding.pointer = (caddr_t) ext;
iwr.u.encoding.length = sizeof(*ext) + key_len;
- if (addr == NULL ||
- os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) == 0)
+ if (addr == NULL || is_broadcast_ether_addr(addr))
ext->ext_flags |= IW_ENCODE_EXT_GROUP_KEY;
if (set_tx)
ext->ext_flags |= IW_ENCODE_EXT_SET_TX_KEY;
{
struct iwreq iwr;
const u8 null_bssid[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
+#ifndef ANDROID
u8 ssid[32];
int i;
+#endif /* ANDROID */
/*
* Only force-disconnect when the card is in infrastructure mode,
}
if (iwr.u.mode == IW_MODE_INFRA) {
+ /* Clear the BSSID selection */
+ if (wpa_driver_wext_set_bssid(drv, null_bssid) < 0) {
+ wpa_printf(MSG_DEBUG, "WEXT: Failed to clear BSSID "
+ "selection on disconnect");
+ }
+
+#ifndef ANDROID
if (drv->cfg80211) {
/*
* cfg80211 supports SIOCSIWMLME commands, so there is
* no need for the random SSID hack, but clear the
- * BSSID and SSID.
+ * SSID.
*/
- if (wpa_driver_wext_set_bssid(drv, null_bssid) < 0 ||
- wpa_driver_wext_set_ssid(drv, (u8 *) "", 0) < 0) {
+ if (wpa_driver_wext_set_ssid(drv, (u8 *) "", 0) < 0) {
wpa_printf(MSG_DEBUG, "WEXT: Failed to clear "
- "to disconnect");
+ "SSID on disconnect");
}
return;
}
+
/*
- * Clear the BSSID selection and set a random SSID to make sure
- * the driver will not be trying to associate with something
- * even if it does not understand SIOCSIWMLME commands (or
- * tries to associate automatically after deauth/disassoc).
+ * Set a random SSID to make sure the driver will not be trying
+ * to associate with something even if it does not understand
+ * SIOCSIWMLME commands (or tries to associate automatically
+ * after deauth/disassoc).
*/
for (i = 0; i < 32; i++)
ssid[i] = rand() & 0xFF;
- if (wpa_driver_wext_set_bssid(drv, null_bssid) < 0 ||
- wpa_driver_wext_set_ssid(drv, ssid, 32) < 0) {
+ if (wpa_driver_wext_set_ssid(drv, ssid, 32) < 0) {
wpa_printf(MSG_DEBUG, "WEXT: Failed to set bogus "
- "BSSID/SSID to disconnect");
+ "SSID to disconnect");
}
+#endif /* ANDROID */
}
}
}
+static const char * wext_get_radio_name(void *priv)
+{
+ struct wpa_driver_wext_data *drv = priv;
+ return drv->phyname;
+}
+
+
const struct wpa_driver_ops wpa_driver_wext_ops = {
.name = "wext",
.desc = "Linux wireless extensions (generic)",
.flush_pmkid = wpa_driver_wext_flush_pmkid,
.get_capa = wpa_driver_wext_get_capa,
.set_operstate = wpa_driver_wext_set_operstate,
+ .get_radio_name = wext_get_radio_name,
};
int ioctl_sock;
int mlme_sock;
char ifname[IFNAMSIZ + 1];
+ char phyname[32];
int ifindex;
int ifindex2;
int if_removed;
+ int if_disabled;
+ struct rfkill_data *rfkill;
u8 *assoc_req_ies;
size_t assoc_req_ies_len;
u8 *assoc_resp_ies;
int scan_complete_events;
int cfg80211; /* whether driver is using cfg80211 */
+
+ u8 max_level;
};
int wpa_driver_wext_get_bssid(void *priv, u8 *bssid);
#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
#include <net/if_dl.h>
#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) */
+#ifdef __sun__
+#include <sys/sockio.h>
+#endif /* __sun__ */
#include "common.h"
#include "eloop.h"
static int wired_send_eapol(void *priv, const u8 *addr,
const u8 *data, size_t data_len, int encrypt,
- const u8 *own_addr)
+ const u8 *own_addr, u32 flags)
{
struct wpa_driver_wired_data *drv = priv;
struct ieee8023_hdr *hdr;
struct ifreq ifr;
int s;
+#ifdef __sun__
+ return -1;
+#endif /* __sun__ */
+
s = socket(PF_INET, SOCK_DGRAM, 0);
if (s < 0) {
perror("socket");
#ifdef CONFIG_DRIVER_HOSTAP
extern struct wpa_driver_ops wpa_driver_hostap_ops; /* driver_hostap.c */
#endif /* CONFIG_DRIVER_HOSTAP */
-#ifdef CONFIG_DRIVER_HERMES
-extern struct wpa_driver_ops wpa_driver_hermes_ops; /* driver_hermes.c */
-#endif /* CONFIG_DRIVER_HERMES */
#ifdef CONFIG_DRIVER_MADWIFI
extern struct wpa_driver_ops wpa_driver_madwifi_ops; /* driver_madwifi.c */
#endif /* CONFIG_DRIVER_MADWIFI */
-#ifdef CONFIG_DRIVER_ATMEL
-extern struct wpa_driver_ops wpa_driver_atmel_ops; /* driver_atmel.c */
-#endif /* CONFIG_DRIVER_ATMEL */
-#ifdef CONFIG_DRIVER_NDISWRAPPER
-/* driver_ndiswrapper.c */
-extern struct wpa_driver_ops wpa_driver_ndiswrapper_ops;
-#endif /* CONFIG_DRIVER_NDISWRAPPER */
#ifdef CONFIG_DRIVER_BROADCOM
extern struct wpa_driver_ops wpa_driver_broadcom_ops; /* driver_broadcom.c */
#endif /* CONFIG_DRIVER_BROADCOM */
-#ifdef CONFIG_DRIVER_IPW
-extern struct wpa_driver_ops wpa_driver_ipw_ops; /* driver_ipw.c */
-#endif /* CONFIG_DRIVER_IPW */
#ifdef CONFIG_DRIVER_BSD
extern struct wpa_driver_ops wpa_driver_bsd_ops; /* driver_bsd.c */
#endif /* CONFIG_DRIVER_BSD */
#ifdef CONFIG_DRIVER_HOSTAP
&wpa_driver_hostap_ops,
#endif /* CONFIG_DRIVER_HOSTAP */
-#ifdef CONFIG_DRIVER_HERMES
- &wpa_driver_hermes_ops,
-#endif /* CONFIG_DRIVER_HERMES */
#ifdef CONFIG_DRIVER_MADWIFI
&wpa_driver_madwifi_ops,
#endif /* CONFIG_DRIVER_MADWIFI */
-#ifdef CONFIG_DRIVER_ATMEL
- &wpa_driver_atmel_ops,
-#endif /* CONFIG_DRIVER_ATMEL */
-#ifdef CONFIG_DRIVER_NDISWRAPPER
- &wpa_driver_ndiswrapper_ops,
-#endif /* CONFIG_DRIVER_NDISWRAPPER */
#ifdef CONFIG_DRIVER_BROADCOM
&wpa_driver_broadcom_ops,
#endif /* CONFIG_DRIVER_BROADCOM */
-#ifdef CONFIG_DRIVER_IPW
- &wpa_driver_ipw_ops,
-#endif /* CONFIG_DRIVER_IPW */
#ifdef CONFIG_DRIVER_BSD
&wpa_driver_bsd_ops,
#endif /* CONFIG_DRIVER_BSD */
+##### CLEAR VARS
+
+DRV_CFLAGS =
+DRV_WPA_CFLAGS =
+DRV_AP_CFLAGS =
+DRV_OBJS =
+DRV_WPA_OBJS =
+DRV_AP_OBJS =
+DRV_LIBS =
+DRV_WPA_LIBS =
+DRV_AP_LIBS =
+
##### COMMON DRIVERS
ifdef CONFIG_DRIVER_HOSTAP
NEED_AP_MLME=y
NEED_NETLINK=y
NEED_LINUX_IOCTL=y
-DRV_LIBS += -lnl
+NEED_RFKILL=y
+
+ifdef CONFIG_LIBNL32
+ DRV_LIBS += -lnl-3
+ DRV_LIBS += -lnl-genl-3
+ DRV_CFLAGS += -DCONFIG_LIBNL20
+else
+ ifdef CONFIG_LIBNL_TINY
+ DRV_LIBS += -lnl-tiny
+ else
+ DRV_LIBS += -lnl
+ endif
-ifdef CONFIG_LIBNL20
-DRV_LIBS += -lnl-genl
-DRV_CFLAGS += -DCONFIG_LIBNL20
+ ifdef CONFIG_LIBNL20
+ DRV_LIBS += -lnl-genl
+ DRV_CFLAGS += -DCONFIG_LIBNL20
+ endif
endif
endif
CONFIG_WIRELESS_EXTENSION=y
NEED_NETLINK=y
NEED_LINUX_IOCTL=y
-endif
-
-ifdef CONFIG_DRIVER_HERMES
-DRV_WPA_CFLAGS += -DCONFIG_DRIVER_HERMES
-DRV_WPA_OBJS += ../src/drivers/driver_hermes.o
-CONFIG_WIRELESS_EXTENSION=y
-endif
-
-ifdef CONFIG_DRIVER_ATMEL
-DRV_WPA_CFLAGS += -DCONFIG_DRIVER_ATMEL
-DRV_WPA_OBJS += ../src/drivers/driver_atmel.o
-CONFIG_WIRELESS_EXTENSION=y
-endif
-
-ifdef CONFIG_DRIVER_NDISWRAPPER
-DRV_WPA_CFLAGS += -DCONFIG_DRIVER_NDISWRAPPER
-DRV_WPA_OBJS += ../src/drivers/driver_ndiswrapper.o
-CONFIG_WIRELESS_EXTENSION=y
+NEED_RFKILL=y
endif
ifdef CONFIG_DRIVER_RALINK
DRV_WPA_OBJS += ../src/drivers/driver_broadcom.o
endif
-ifdef CONFIG_DRIVER_IPW
-DRV_WPA_CFLAGS += -DCONFIG_DRIVER_IPW
-DRV_WPA_OBJS += ../src/drivers/driver_ipw.o
-CONFIG_WIRELESS_EXTENSION=y
-endif
-
ifdef CONFIG_DRIVER_NDIS
DRV_WPA_CFLAGS += -DCONFIG_DRIVER_NDIS
DRV_WPA_OBJS += ../src/drivers/driver_ndis.o
ifdef CONFIG_WIRELESS_EXTENSION
DRV_WPA_CFLAGS += -DCONFIG_WIRELESS_EXTENSION
DRV_WPA_OBJS += ../src/drivers/driver_wext.o
+NEED_RFKILL=y
endif
ifdef NEED_NETLINK
DRV_OBJS += ../src/drivers/linux_ioctl.o
endif
+ifdef NEED_RFKILL
+DRV_OBJS += ../src/drivers/rfkill.o
+endif
+
##### COMMON VARS
DRV_BOTH_CFLAGS := $(DRV_CFLAGS) $(DRV_WPA_CFLAGS) $(DRV_AP_CFLAGS)
--- /dev/null
+##### CLEAR VARS
+
+DRV_CFLAGS =
+DRV_WPA_CFLAGS =
+DRV_AP_CFLAGS =
+DRV_OBJS =
+DRV_WPA_OBJS =
+DRV_AP_OBJS =
+DRV_LIBS =
+DRV_WPA_LIBS =
+DRV_AP_LIBS =
+
+##### COMMON DRIVERS
+
+ifdef CONFIG_DRIVER_HOSTAP
+DRV_CFLAGS += -DCONFIG_DRIVER_HOSTAP
+DRV_OBJS += src/drivers/driver_hostap.c
+CONFIG_WIRELESS_EXTENSION=y
+NEED_AP_MLME=y
+NEED_NETLINK=y
+NEED_LINUX_IOCTL=y
+endif
+
+ifdef CONFIG_DRIVER_WIRED
+DRV_CFLAGS += -DCONFIG_DRIVER_WIRED
+DRV_OBJS += src/drivers/driver_wired.c
+endif
+
+ifdef CONFIG_DRIVER_MADWIFI
+DRV_CFLAGS += -DCONFIG_DRIVER_MADWIFI
+DRV_OBJS += src/drivers/driver_madwifi.c
+CONFIG_WIRELESS_EXTENSION=y
+CONFIG_L2_PACKET=linux
+NEED_NETLINK=y
+NEED_LINUX_IOCTL=y
+endif
+
+ifdef CONFIG_DRIVER_NL80211
+DRV_CFLAGS += -DCONFIG_DRIVER_NL80211
+DRV_OBJS += src/drivers/driver_nl80211.c
+DRV_OBJS += src/utils/radiotap.c
+NEED_SME=y
+NEED_AP_MLME=y
+NEED_NETLINK=y
+NEED_LINUX_IOCTL=y
+NEED_RFKILL=y
+
+ifdef CONFIG_LIBNL32
+ DRV_LIBS += -lnl-3
+ DRV_LIBS += -lnl-genl-3
+ DRV_CFLAGS += -DCONFIG_LIBNL20
+else
+ ifdef CONFIG_LIBNL_TINY
+ DRV_LIBS += -lnl-tiny
+ else
+ DRV_LIBS += -lnl
+ endif
+
+ ifdef CONFIG_LIBNL20
+ DRV_LIBS += -lnl-genl
+ DRV_CFLAGS += -DCONFIG_LIBNL20
+ endif
+endif
+endif
+
+ifdef CONFIG_DRIVER_BSD
+ifndef CONFIG_L2_PACKET
+CONFIG_L2_PACKET=freebsd
+endif
+DRV_CFLAGS += -DCONFIG_DRIVER_BSD
+DRV_OBJS += src/drivers/driver_bsd.c
+CONFIG_L2_FREEBSD=y
+CONFIG_DNET_PCAP=y
+endif
+
+ifdef CONFIG_DRIVER_TEST
+DRV_CFLAGS += -DCONFIG_DRIVER_TEST
+DRV_OBJS += src/drivers/driver_test.c
+NEED_AP_MLME=y
+endif
+
+ifdef CONFIG_DRIVER_NONE
+DRV_CFLAGS += -DCONFIG_DRIVER_NONE
+DRV_OBJS += src/drivers/driver_none.c
+endif
+
+##### PURE AP DRIVERS
+
+ifdef CONFIG_DRIVER_ATHEROS
+DRV_AP_CFLAGS += -DCONFIG_DRIVER_ATHEROS
+DRV_AP_OBJS += src/drivers/driver_atheros.c
+CONFIG_L2_PACKET=linux
+NEED_NETLINK=y
+NEED_LINUX_IOCTL=y
+endif
+
+##### PURE CLIENT DRIVERS
+
+ifdef CONFIG_DRIVER_WEXT
+DRV_WPA_CFLAGS += -DCONFIG_DRIVER_WEXT
+CONFIG_WIRELESS_EXTENSION=y
+NEED_NETLINK=y
+NEED_LINUX_IOCTL=y
+NEED_RFKILL=y
+endif
+
+ifdef CONFIG_DRIVER_RALINK
+DRV_WPA_CFLAGS += -DCONFIG_DRIVER_RALINK
+DRV_WPA_OBJS += src/drivers/driver_ralink.c
+NEED_NETLINK=y
+NEED_LINUX_IOCTL=y
+endif
+
+ifdef CONFIG_DRIVER_BROADCOM
+DRV_WPA_CFLAGS += -DCONFIG_DRIVER_BROADCOM
+DRV_WPA_OBJS += src/drivers/driver_broadcom.c
+endif
+
+ifdef CONFIG_DRIVER_NDIS
+DRV_WPA_CFLAGS += -DCONFIG_DRIVER_NDIS
+DRV_WPA_OBJS += src/drivers/driver_ndis.c
+ifdef CONFIG_NDIS_EVENTS_INTEGRATED
+DRV_WPA_OBJS += src/drivers/driver_ndis_.c
+endif
+ifndef CONFIG_L2_PACKET
+CONFIG_L2_PACKET=pcap
+endif
+CONFIG_WINPCAP=y
+ifdef CONFIG_USE_NDISUIO
+DRV_WPA_CFLAGS += -DCONFIG_USE_NDISUIO
+endif
+endif
+
+ifdef CONFIG_DRIVER_OSX
+DRV_WPA_CFLAGS += -DCONFIG_DRIVER_OSX
+DRV_WPA_OBJS += src/drivers/driver_osx.c
+DRV_WPA_LDFLAGS += -framework CoreFoundation
+DRV_WPA_LDFLAGS += -F/System/Library/PrivateFrameworks -framework Apple80211
+endif
+
+ifdef CONFIG_DRIVER_IPHONE
+DRV_WPA_CFLAGS += -DCONFIG_DRIVER_IPHONE
+DRV_WPA_OBJS += src/drivers/driver_iphone.c
+DRV_WPA_OBJS += src/drivers/MobileApple80211.c
+DRV_WPA_LDFLAGS += -framework CoreFoundation
+endif
+
+ifdef CONFIG_DRIVER_ROBOSWITCH
+DRV_WPA_CFLAGS += -DCONFIG_DRIVER_ROBOSWITCH
+DRV_WPA_OBJS += src/drivers/driver_roboswitch.c
+endif
+
+ifdef CONFIG_WIRELESS_EXTENSION
+DRV_WPA_CFLAGS += -DCONFIG_WIRELESS_EXTENSION
+DRV_WPA_OBJS += src/drivers/driver_wext.c
+NEED_RFKILL=y
+endif
+
+ifdef NEED_NETLINK
+DRV_OBJS += src/drivers/netlink.c
+endif
+
+ifdef NEED_LINUX_IOCTL
+DRV_OBJS += src/drivers/linux_ioctl.c
+endif
+
+ifdef NEED_RFKILL
+DRV_OBJS += src/drivers/rfkill.c
+endif
+
+ifdef CONFIG_DRIVER_CUSTOM
+DRV_CFLAGS += -DCONFIG_DRIVER_CUSTOM
+endif
+
+##### COMMON VARS
+DRV_BOTH_CFLAGS := $(DRV_CFLAGS) $(DRV_WPA_CFLAGS) $(DRV_AP_CFLAGS)
+DRV_WPA_CFLAGS += $(DRV_CFLAGS)
+DRV_AP_CFLAGS += $(DRV_CFLAGS)
+
+DRV_BOTH_LIBS := $(DRV_LIBS) $(DRV_WPA_LIBS) $(DRV_AP_LIBS)
+DRV_WPA_LIBS += $(DRV_LIBS)
+DRV_AP_LIBS += $(DRV_LIBS)
+
+DRV_BOTH_OBJS := $(DRV_OBJS) $(DRV_WPA_OBJS) $(DRV_AP_OBJS)
+DRV_WPA_OBJS += $(DRV_OBJS)
+DRV_AP_OBJS += $(DRV_OBJS)
+
+DRV_BOTH_LDFLAGS := $(DRV_LDFLAGS) $(DRV_WPA_LDFLAGS) $(DRV_AP_LDFLAGS)
+DRV_WPA_LDFLAGS += $(DRV_LDFLAGS)
+DRV_AP_LDFLAGS += $(DRV_LDFLAGS)
int linux_set_iface_flags(int sock, const char *ifname, int dev_up)
{
struct ifreq ifr;
+ int ret;
if (sock < 0)
return -1;
os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
if (ioctl(sock, SIOCGIFFLAGS, &ifr) != 0) {
+ ret = errno ? -errno : -999;
wpa_printf(MSG_ERROR, "Could not read interface %s flags: %s",
ifname, strerror(errno));
- return -1;
+ return ret;
}
if (dev_up) {
}
if (ioctl(sock, SIOCSIFFLAGS, &ifr) != 0) {
+ ret = errno ? -errno : -999;
wpa_printf(MSG_ERROR, "Could not set interface %s flags: %s",
ifname, strerror(errno));
- return -1;
+ return ret;
}
return 0;
}
+int linux_iface_up(int sock, const char *ifname)
+{
+ struct ifreq ifr;
+ int ret;
+
+ if (sock < 0)
+ return -1;
+
+ os_memset(&ifr, 0, sizeof(ifr));
+ os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+
+ if (ioctl(sock, SIOCGIFFLAGS, &ifr) != 0) {
+ ret = errno ? -errno : -999;
+ wpa_printf(MSG_ERROR, "Could not read interface %s flags: %s",
+ ifname, strerror(errno));
+ return ret;
+ }
+
+ return !!(ifr.ifr_flags & IFF_UP);
+}
+
+
int linux_get_ifhwaddr(int sock, const char *ifname, u8 *addr)
{
struct ifreq ifr;
#define LINUX_IOCTL_H
int linux_set_iface_flags(int sock, const char *ifname, int dev_up);
+int linux_iface_up(int sock, const char *ifname);
int linux_get_ifhwaddr(int sock, const char *ifname, u8 *addr);
int linux_set_ifhwaddr(int sock, const char *ifname, const u8 *addr);
int linux_br_add(int sock, const char *brname);
if (cb == NULL || NLMSG_PAYLOAD(h, 0) < sizeof(struct ifinfomsg))
return;
cb(netlink->cfg->ctx, NLMSG_DATA(h),
- NLMSG_DATA(h) + NLMSG_ALIGN(sizeof(struct ifinfomsg)),
+ (u8 *) NLMSG_DATA(h) + NLMSG_ALIGN(sizeof(struct ifinfomsg)),
NLMSG_PAYLOAD(h, sizeof(struct ifinfomsg)));
}
#define NETLINK_H
struct netlink_data;
+struct ifinfomsg;
struct netlink_config {
void *ctx;
* Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2008 Michael Wu <flamingice@sourmilk.net>
* Copyright 2008 Luis Carlos Cobo <luisca@cozybit.com>
- * Copyright 2008 Michael Buesch <mb@bu3sch.de>
+ * Copyright 2008 Michael Buesch <m@bues.ch>
* Copyright 2008, 2009 Luis R. Rodriguez <lrodriguez@atheros.com>
* Copyright 2008 Jouni Malinen <jouni.malinen@atheros.com>
* Copyright 2008 Colin McCabe <colin@cozybit.com>
*/
/**
+ * DOC: Frame transmission/registration support
+ *
+ * Frame transmission and registration support exists to allow userspace
+ * management entities such as wpa_supplicant react to management frames
+ * that are not being handled by the kernel. This includes, for example,
+ * certain classes of action frames that cannot be handled in the kernel
+ * for various reasons.
+ *
+ * Frame registration is done on a per-interface basis and registrations
+ * cannot be removed other than by closing the socket. It is possible to
+ * specify a registration filter to register, for example, only for a
+ * certain type of action frame. In particular with action frames, those
+ * that userspace registers for will not be returned as unhandled by the
+ * driver, so that the registered application has to take responsibility
+ * for doing that.
+ *
+ * The type of frame that can be registered for is also dependent on the
+ * driver and interface type. The frame types are advertised in wiphy
+ * attributes so applications know what to expect.
+ *
+ * NOTE: When an interface changes type while registrations are active,
+ * these registrations are ignored until the interface type is
+ * changed again. This means that changing the interface type can
+ * lead to a situation that couldn't otherwise be produced, but
+ * any such registrations will be dormant in the sense that they
+ * will not be serviced, i.e. they will not receive any frames.
+ *
+ * Frame transmission allows userspace to send for example the required
+ * responses to action frames. It is subject to some sanity checking,
+ * but many frames can be transmitted. When a frame was transmitted, its
+ * status is indicated to the sending socket.
+ *
+ * For more technical details, see the corresponding command descriptions
+ * below.
+ */
+
+/**
+ * DOC: Virtual interface / concurrency capabilities
+ *
+ * Some devices are able to operate with virtual MACs, they can have
+ * more than one virtual interface. The capability handling for this
+ * is a bit complex though, as there may be a number of restrictions
+ * on the types of concurrency that are supported.
+ *
+ * To start with, each device supports the interface types listed in
+ * the %NL80211_ATTR_SUPPORTED_IFTYPES attribute, but by listing the
+ * types there no concurrency is implied.
+ *
+ * Once concurrency is desired, more attributes must be observed:
+ * To start with, since some interface types are purely managed in
+ * software, like the AP-VLAN type in mac80211 for example, there's
+ * an additional list of these, they can be added at any time and
+ * are only restricted by some semantic restrictions (e.g. AP-VLAN
+ * cannot be added without a corresponding AP interface). This list
+ * is exported in the %NL80211_ATTR_SOFTWARE_IFTYPES attribute.
+ *
+ * Further, the list of supported combinations is exported. This is
+ * in the %NL80211_ATTR_INTERFACE_COMBINATIONS attribute. Basically,
+ * it exports a list of "groups", and at any point in time the
+ * interfaces that are currently active must fall into any one of
+ * the advertised groups. Within each group, there are restrictions
+ * on the number of interfaces of different types that are supported
+ * and also the number of different channels, along with potentially
+ * some other restrictions. See &enum nl80211_if_combination_attrs.
+ *
+ * All together, these attributes define the concurrency of virtual
+ * interfaces that a given device supports.
+ */
+
+/**
* enum nl80211_commands - supported nl80211 commands
*
* @NL80211_CMD_UNSPEC: unspecified command to catch errors
* %NL80211_ATTR_WIPHY_CHANNEL_TYPE, %NL80211_ATTR_WIPHY_RETRY_SHORT,
* %NL80211_ATTR_WIPHY_RETRY_LONG, %NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
* and/or %NL80211_ATTR_WIPHY_RTS_THRESHOLD.
+ * However, for setting the channel, see %NL80211_CMD_SET_CHANNEL
+ * instead, the support here is for backward compatibility only.
* @NL80211_CMD_NEW_WIPHY: Newly created wiphy, response to get request
* or rename notification. Has attributes %NL80211_ATTR_WIPHY and
* %NL80211_ATTR_WIPHY_NAME.
* @NL80211_CMD_DEL_KEY: delete a key identified by %NL80211_ATTR_KEY_IDX
* or %NL80211_ATTR_MAC.
*
- * @NL80211_CMD_GET_BEACON: retrieve beacon information (returned in a
- * %NL80222_CMD_NEW_BEACON message)
- * @NL80211_CMD_SET_BEACON: set the beacon on an access point interface
- * using the %NL80211_ATTR_BEACON_INTERVAL, %NL80211_ATTR_DTIM_PERIOD,
- * %NL80211_ATTR_BEACON_HEAD and %NL80211_ATTR_BEACON_TAIL attributes.
- * @NL80211_CMD_NEW_BEACON: add a new beacon to an access point interface,
- * parameters are like for %NL80211_CMD_SET_BEACON.
- * @NL80211_CMD_DEL_BEACON: remove the beacon, stop sending it
+ * @NL80211_CMD_GET_BEACON: (not used)
+ * @NL80211_CMD_SET_BEACON: change the beacon on an access point interface
+ * using the %NL80211_ATTR_BEACON_HEAD and %NL80211_ATTR_BEACON_TAIL
+ * attributes. For drivers that generate the beacon and probe responses
+ * internally, the following attributes must be provided: %NL80211_ATTR_IE,
+ * %NL80211_ATTR_IE_PROBE_RESP and %NL80211_ATTR_IE_ASSOC_RESP.
+ * @NL80211_CMD_START_AP: Start AP operation on an AP interface, parameters
+ * are like for %NL80211_CMD_SET_BEACON, and additionally parameters that
+ * do not change are used, these include %NL80211_ATTR_BEACON_INTERVAL,
+ * %NL80211_ATTR_DTIM_PERIOD, %NL80211_ATTR_SSID,
+ * %NL80211_ATTR_HIDDEN_SSID, %NL80211_ATTR_CIPHERS_PAIRWISE,
+ * %NL80211_ATTR_CIPHER_GROUP, %NL80211_ATTR_WPA_VERSIONS,
+ * %NL80211_ATTR_AKM_SUITES, %NL80211_ATTR_PRIVACY,
+ * %NL80211_ATTR_AUTH_TYPE and %NL80211_ATTR_INACTIVITY_TIMEOUT.
+ * @NL80211_CMD_NEW_BEACON: old alias for %NL80211_CMD_START_AP
+ * @NL80211_CMD_STOP_AP: Stop AP operation on the given interface
+ * @NL80211_CMD_DEL_BEACON: old alias for %NL80211_CMD_STOP_AP
*
* @NL80211_CMD_GET_STATION: Get station attributes for station identified by
* %NL80211_ATTR_MAC on the interface identified by %NL80211_ATTR_IFINDEX.
* @NL80211_CMD_SET_MPATH: Set mesh path attributes for mesh path to
* destination %NL80211_ATTR_MAC on the interface identified by
* %NL80211_ATTR_IFINDEX.
+ * @NL80211_CMD_NEW_MPATH: Create a new mesh path for the destination given by
+ * %NL80211_ATTR_MAC via %NL80211_ATTR_MPATH_NEXT_HOP.
+ * @NL80211_CMD_DEL_MPATH: Delete a mesh path to the destination given by
+ * %NL80211_ATTR_MAC.
* @NL80211_CMD_NEW_PATH: Add a mesh path with given attributes to the
* the interface identified by %NL80211_ATTR_IFINDEX.
* @NL80211_CMD_DEL_PATH: Remove a mesh path identified by %NL80211_ATTR_MAC
* %NL80211_ATTR_REG_RULE_POWER_MAX_ANT_GAIN and
* %NL80211_ATTR_REG_RULE_POWER_MAX_EIRP.
* @NL80211_CMD_REQ_SET_REG: ask the wireless core to set the regulatory domain
- * to the the specified ISO/IEC 3166-1 alpha2 country code. The core will
+ * to the specified ISO/IEC 3166-1 alpha2 country code. The core will
* store this as a valid request and then query userspace for it.
*
- * @NL80211_CMD_GET_MESH_PARAMS: Get mesh networking properties for the
+ * @NL80211_CMD_GET_MESH_CONFIG: Get mesh networking properties for the
* interface identified by %NL80211_ATTR_IFINDEX
*
- * @NL80211_CMD_SET_MESH_PARAMS: Set mesh networking properties for the
+ * @NL80211_CMD_SET_MESH_CONFIG: Set mesh networking properties for the
* interface identified by %NL80211_ATTR_IFINDEX
*
* @NL80211_CMD_SET_MGMT_EXTRA_IE: Set extra IEs for management frames. The
*
* @NL80211_CMD_GET_SCAN: get scan results
* @NL80211_CMD_TRIGGER_SCAN: trigger a new scan with the given parameters
+ * %NL80211_ATTR_TX_NO_CCK_RATE is used to decide whether to send the
+ * probe requests at CCK rate or not.
* @NL80211_CMD_NEW_SCAN_RESULTS: scan notification (as a reply to
* NL80211_CMD_GET_SCAN and on the "scan" multicast group)
* @NL80211_CMD_SCAN_ABORTED: scan was aborted, for unspecified reasons,
* partial scan results may be available
*
+ * @NL80211_CMD_START_SCHED_SCAN: start a scheduled scan at certain
+ * intervals, as specified by %NL80211_ATTR_SCHED_SCAN_INTERVAL.
+ * Like with normal scans, if SSIDs (%NL80211_ATTR_SCAN_SSIDS)
+ * are passed, they are used in the probe requests. For
+ * broadcast, a broadcast SSID must be passed (ie. an empty
+ * string). If no SSID is passed, no probe requests are sent and
+ * a passive scan is performed. %NL80211_ATTR_SCAN_FREQUENCIES,
+ * if passed, define which channels should be scanned; if not
+ * passed, all channels allowed for the current regulatory domain
+ * are used. Extra IEs can also be passed from the userspace by
+ * using the %NL80211_ATTR_IE attribute.
+ * @NL80211_CMD_STOP_SCHED_SCAN: stop a scheduled scan. Returns -ENOENT
+ * if scheduled scan is not running.
+ * @NL80211_CMD_SCHED_SCAN_RESULTS: indicates that there are scheduled scan
+ * results available.
+ * @NL80211_CMD_SCHED_SCAN_STOPPED: indicates that the scheduled scan has
+ * stopped. The driver may issue this event at any time during a
+ * scheduled scan. One reason for stopping the scan is if the hardware
+ * does not support starting an association or a normal scan while running
+ * a scheduled scan. This event is also sent when the
+ * %NL80211_CMD_STOP_SCHED_SCAN command is received or when the interface
+ * is brought down while a scheduled scan was running.
+ *
* @NL80211_CMD_GET_SURVEY: get survey resuls, e.g. channel occupation
* or noise level
* @NL80211_CMD_NEW_SURVEY_RESULTS: survey data notification (as a reply to
* auth and assoc steps. For this, you need to specify the SSID in a
* %NL80211_ATTR_SSID attribute, and can optionally specify the association
* IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_MAC,
- * %NL80211_ATTR_WIPHY_FREQ and %NL80211_ATTR_CONTROL_PORT.
+ * %NL80211_ATTR_WIPHY_FREQ, %NL80211_ATTR_CONTROL_PORT,
+ * %NL80211_ATTR_CONTROL_PORT_ETHERTYPE and
+ * %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT.
+ * Background scan period can optionally be
+ * specified in %NL80211_ATTR_BG_SCAN_PERIOD,
+ * if not specified default background scan configuration
+ * in driver is used and if period value is 0, bg scan will be disabled.
+ * This attribute is ignored if driver does not support roam scan.
* It is also sent as an event, with the BSSID and response IEs when the
* connection is established or failed to be established. This can be
* determined by the STATUS_CODE attribute.
* channel for the specified amount of time. This can be used to do
* off-channel operations like transmit a Public Action frame and wait for
* a response while being associated to an AP on another channel.
- * %NL80211_ATTR_WIPHY or %NL80211_ATTR_IFINDEX is used to specify which
- * radio is used. %NL80211_ATTR_WIPHY_FREQ is used to specify the
+ * %NL80211_ATTR_IFINDEX is used to specify which interface (and thus
+ * radio) is used. %NL80211_ATTR_WIPHY_FREQ is used to specify the
* frequency for the operation and %NL80211_ATTR_WIPHY_CHANNEL_TYPE may be
* optionally used to specify additional channel parameters.
* %NL80211_ATTR_DURATION is used to specify the duration in milliseconds
* rate selection. %NL80211_ATTR_IFINDEX is used to specify the interface
* and @NL80211_ATTR_TX_RATES the set of allowed rates.
*
- * @NL80211_CMD_REGISTER_ACTION: Register for receiving certain action frames
- * (via @NL80211_CMD_ACTION) for processing in userspace. This command
- * requires an interface index and a match attribute containing the first
- * few bytes of the frame that should match, e.g. a single byte for only
- * a category match or four bytes for vendor frames including the OUI.
- * The registration cannot be dropped, but is removed automatically
- * when the netlink socket is closed. Multiple registrations can be made.
- * @NL80211_CMD_ACTION: Action frame TX request and RX notification. This
- * command is used both as a request to transmit an Action frame and as an
- * event indicating reception of an Action frame that was not processed in
+ * @NL80211_CMD_REGISTER_FRAME: Register for receiving certain mgmt frames
+ * (via @NL80211_CMD_FRAME) for processing in userspace. This command
+ * requires an interface index, a frame type attribute (optional for
+ * backward compatibility reasons, if not given assumes action frames)
+ * and a match attribute containing the first few bytes of the frame
+ * that should match, e.g. a single byte for only a category match or
+ * four bytes for vendor frames including the OUI. The registration
+ * cannot be dropped, but is removed automatically when the netlink
+ * socket is closed. Multiple registrations can be made.
+ * @NL80211_CMD_REGISTER_ACTION: Alias for @NL80211_CMD_REGISTER_FRAME for
+ * backward compatibility
+ * @NL80211_CMD_FRAME: Management frame TX request and RX notification. This
+ * command is used both as a request to transmit a management frame and
+ * as an event indicating reception of a frame that was not processed in
* kernel code, but is for us (i.e., which may need to be processed in a
* user space application). %NL80211_ATTR_FRAME is used to specify the
* frame contents (including header). %NL80211_ATTR_WIPHY_FREQ (and
* optionally %NL80211_ATTR_WIPHY_CHANNEL_TYPE) is used to indicate on
- * which channel the frame is to be transmitted or was received. This
- * channel has to be the current channel (remain-on-channel or the
- * operational channel). When called, this operation returns a cookie
- * (%NL80211_ATTR_COOKIE) that will be included with the TX status event
- * pertaining to the TX request.
- * @NL80211_CMD_ACTION_TX_STATUS: Report TX status of an Action frame
- * transmitted with %NL80211_CMD_ACTION. %NL80211_ATTR_COOKIE identifies
+ * which channel the frame is to be transmitted or was received. If this
+ * channel is not the current channel (remain-on-channel or the
+ * operational channel) the device will switch to the given channel and
+ * transmit the frame, optionally waiting for a response for the time
+ * specified using %NL80211_ATTR_DURATION. When called, this operation
+ * returns a cookie (%NL80211_ATTR_COOKIE) that will be included with the
+ * TX status event pertaining to the TX request.
+ * %NL80211_ATTR_TX_NO_CCK_RATE is used to decide whether to send the
+ * management frames at CCK rate or not in 2GHz band.
+ * @NL80211_CMD_FRAME_WAIT_CANCEL: When an off-channel TX was requested, this
+ * command may be used with the corresponding cookie to cancel the wait
+ * time if it is known that it is no longer necessary.
+ * @NL80211_CMD_ACTION: Alias for @NL80211_CMD_FRAME for backward compatibility.
+ * @NL80211_CMD_FRAME_TX_STATUS: Report TX status of a management frame
+ * transmitted with %NL80211_CMD_FRAME. %NL80211_ATTR_COOKIE identifies
* the TX command and %NL80211_ATTR_FRAME includes the contents of the
* frame. %NL80211_ATTR_ACK flag is included if the recipient acknowledged
* the frame.
+ * @NL80211_CMD_ACTION_TX_STATUS: Alias for @NL80211_CMD_FRAME_TX_STATUS for
+ * backward compatibility.
* @NL80211_CMD_SET_CQM: Connection quality monitor configuration. This command
* is used to configure connection quality monitoring notification trigger
* levels.
* @NL80211_CMD_NOTIFY_CQM: Connection quality monitor notification. This
* command is used as an event to indicate the that a trigger level was
* reached.
+ * @NL80211_CMD_SET_CHANNEL: Set the channel (using %NL80211_ATTR_WIPHY_FREQ
+ * and %NL80211_ATTR_WIPHY_CHANNEL_TYPE) the given interface (identifed
+ * by %NL80211_ATTR_IFINDEX) shall operate on.
+ * In case multiple channels are supported by the device, the mechanism
+ * with which it switches channels is implementation-defined.
+ * When a monitor interface is given, it can only switch channel while
+ * no other interfaces are operating to avoid disturbing the operation
+ * of any other interfaces, and other interfaces will again take
+ * precedence when they are used.
+ *
+ * @NL80211_CMD_SET_WDS_PEER: Set the MAC address of the peer on a WDS interface.
+ *
+ * @NL80211_CMD_JOIN_MESH: Join a mesh. The mesh ID must be given, and initial
+ * mesh config parameters may be given.
+ * @NL80211_CMD_LEAVE_MESH: Leave the mesh network -- no special arguments, the
+ * network is determined by the network interface.
+ *
+ * @NL80211_CMD_UNPROT_DEAUTHENTICATE: Unprotected deauthentication frame
+ * notification. This event is used to indicate that an unprotected
+ * deauthentication frame was dropped when MFP is in use.
+ * @NL80211_CMD_UNPROT_DISASSOCIATE: Unprotected disassociation frame
+ * notification. This event is used to indicate that an unprotected
+ * disassociation frame was dropped when MFP is in use.
+ *
+ * @NL80211_CMD_NEW_PEER_CANDIDATE: Notification on the reception of a
+ * beacon or probe response from a compatible mesh peer. This is only
+ * sent while no station information (sta_info) exists for the new peer
+ * candidate and when @NL80211_MESH_SETUP_USERSPACE_AUTH is set. On
+ * reception of this notification, userspace may decide to create a new
+ * station (@NL80211_CMD_NEW_STATION). To stop this notification from
+ * reoccurring, the userspace authentication daemon may want to create the
+ * new station with the AUTHENTICATED flag unset and maybe change it later
+ * depending on the authentication result.
+ *
+ * @NL80211_CMD_GET_WOWLAN: get Wake-on-Wireless-LAN (WoWLAN) settings.
+ * @NL80211_CMD_SET_WOWLAN: set Wake-on-Wireless-LAN (WoWLAN) settings.
+ * Since wireless is more complex than wired ethernet, it supports
+ * various triggers. These triggers can be configured through this
+ * command with the %NL80211_ATTR_WOWLAN_TRIGGERS attribute. For
+ * more background information, see
+ * http://wireless.kernel.org/en/users/Documentation/WoWLAN.
+ *
+ * @NL80211_CMD_SET_REKEY_OFFLOAD: This command is used give the driver
+ * the necessary information for supporting GTK rekey offload. This
+ * feature is typically used during WoWLAN. The configuration data
+ * is contained in %NL80211_ATTR_REKEY_DATA (which is nested and
+ * contains the data in sub-attributes). After rekeying happened,
+ * this command may also be sent by the driver as an MLME event to
+ * inform userspace of the new replay counter.
+ *
+ * @NL80211_CMD_PMKSA_CANDIDATE: This is used as an event to inform userspace
+ * of PMKSA caching dandidates.
+ *
+ * @NL80211_CMD_TDLS_OPER: Perform a high-level TDLS command (e.g. link setup).
+ * @NL80211_CMD_TDLS_MGMT: Send a TDLS management frame.
*
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
*/
enum nl80211_commands {
-/* don't change the order or add anything inbetween, this is ABI! */
+/* don't change the order or add anything between, this is ABI! */
NL80211_CMD_UNSPEC,
NL80211_CMD_GET_WIPHY, /* can dump */
NL80211_CMD_GET_BEACON,
NL80211_CMD_SET_BEACON,
- NL80211_CMD_NEW_BEACON,
- NL80211_CMD_DEL_BEACON,
+ NL80211_CMD_START_AP,
+ NL80211_CMD_NEW_BEACON = NL80211_CMD_START_AP,
+ NL80211_CMD_STOP_AP,
+ NL80211_CMD_DEL_BEACON = NL80211_CMD_STOP_AP,
NL80211_CMD_GET_STATION,
NL80211_CMD_SET_STATION,
NL80211_CMD_SET_REG,
NL80211_CMD_REQ_SET_REG,
- NL80211_CMD_GET_MESH_PARAMS,
- NL80211_CMD_SET_MESH_PARAMS,
+ NL80211_CMD_GET_MESH_CONFIG,
+ NL80211_CMD_SET_MESH_CONFIG,
NL80211_CMD_SET_MGMT_EXTRA_IE /* reserved; not used */,
NL80211_CMD_SET_TX_BITRATE_MASK,
- NL80211_CMD_REGISTER_ACTION,
- NL80211_CMD_ACTION,
- NL80211_CMD_ACTION_TX_STATUS,
+ NL80211_CMD_REGISTER_FRAME,
+ NL80211_CMD_REGISTER_ACTION = NL80211_CMD_REGISTER_FRAME,
+ NL80211_CMD_FRAME,
+ NL80211_CMD_ACTION = NL80211_CMD_FRAME,
+ NL80211_CMD_FRAME_TX_STATUS,
+ NL80211_CMD_ACTION_TX_STATUS = NL80211_CMD_FRAME_TX_STATUS,
NL80211_CMD_SET_POWER_SAVE,
NL80211_CMD_GET_POWER_SAVE,
NL80211_CMD_SET_CQM,
NL80211_CMD_NOTIFY_CQM,
+ NL80211_CMD_SET_CHANNEL,
+ NL80211_CMD_SET_WDS_PEER,
+
+ NL80211_CMD_FRAME_WAIT_CANCEL,
+
+ NL80211_CMD_JOIN_MESH,
+ NL80211_CMD_LEAVE_MESH,
+
+ NL80211_CMD_UNPROT_DEAUTHENTICATE,
+ NL80211_CMD_UNPROT_DISASSOCIATE,
+
+ NL80211_CMD_NEW_PEER_CANDIDATE,
+
+ NL80211_CMD_GET_WOWLAN,
+ NL80211_CMD_SET_WOWLAN,
+
+ NL80211_CMD_START_SCHED_SCAN,
+ NL80211_CMD_STOP_SCHED_SCAN,
+ NL80211_CMD_SCHED_SCAN_RESULTS,
+ NL80211_CMD_SCHED_SCAN_STOPPED,
+
+ NL80211_CMD_SET_REKEY_OFFLOAD,
+
+ NL80211_CMD_PMKSA_CANDIDATE,
+
+ NL80211_CMD_TDLS_OPER,
+ NL80211_CMD_TDLS_MGMT,
+
/* add new commands above here */
/* used to define NL80211_CMD_MAX below */
#define NL80211_CMD_DISASSOCIATE NL80211_CMD_DISASSOCIATE
#define NL80211_CMD_REG_BEACON_HINT NL80211_CMD_REG_BEACON_HINT
+/* source-level API compatibility */
+#define NL80211_CMD_GET_MESH_PARAMS NL80211_CMD_GET_MESH_CONFIG
+#define NL80211_CMD_SET_MESH_PARAMS NL80211_CMD_SET_MESH_CONFIG
+#define NL80211_MESH_SETUP_VENDOR_PATH_SEL_IE NL80211_MESH_SETUP_IE
+
/**
* enum nl80211_attrs - nl80211 netlink attributes
*
* consisting of a nested array.
*
* @NL80211_ATTR_MESH_ID: mesh id (1-32 bytes).
- * @NL80211_ATTR_PLINK_ACTION: action to perform on the mesh peer link.
+ * @NL80211_ATTR_STA_PLINK_ACTION: action to perform on the mesh peer link.
* @NL80211_ATTR_MPATH_NEXT_HOP: MAC address of the next hop for a mesh path.
* @NL80211_ATTR_MPATH_INFO: information about a mesh_path, part of mesh path
* info given for %NL80211_CMD_GET_MPATH, nested attribute described at
*
* @NL80211_ATTR_MAX_NUM_SCAN_SSIDS: number of SSIDs you can scan with
* a single scan request, a wiphy attribute.
+ * @NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS: number of SSIDs you can
+ * scan with a single scheduled scan request, a wiphy attribute.
* @NL80211_ATTR_MAX_SCAN_IE_LEN: maximum length of information elements
* that can be added to a scan request
+ * @NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN: maximum length of information
+ * elements that can be added to a scheduled scan request
+ * @NL80211_ATTR_MAX_MATCH_SETS: maximum number of sets that can be
+ * used with @NL80211_ATTR_SCHED_SCAN_MATCH, a wiphy attribute.
*
* @NL80211_ATTR_SCAN_FREQUENCIES: nested attribute with frequencies (in MHz)
* @NL80211_ATTR_SCAN_SSIDS: nested attribute with SSIDs, leave out for passive
* request, the driver will assume that the port is unauthorized until
* authorized by user space. Otherwise, port is marked authorized by
* default in station mode.
+ * @NL80211_ATTR_CONTROL_PORT_ETHERTYPE: A 16-bit value indicating the
+ * ethertype that will be used for key negotiation. It can be
+ * specified with the associate and connect commands. If it is not
+ * specified, the value defaults to 0x888E (PAE, 802.1X). This
+ * attribute is also used as a flag in the wiphy information to
+ * indicate that protocols other than PAE are supported.
+ * @NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT: When included along with
+ * %NL80211_ATTR_CONTROL_PORT_ETHERTYPE, indicates that the custom
+ * ethertype frames used for key negotiation must not be encrypted.
*
* @NL80211_ATTR_TESTDATA: Testmode data blob, passed through to the driver.
* We recommend using nested, driver-specific attributes within this.
* @NL80211_ATTR_STATUS_CODE: StatusCode for the %NL80211_CMD_CONNECT
* event (u16)
* @NL80211_ATTR_PRIVACY: Flag attribute, used with connect(), indicating
- * that protected APs should be used.
+ * that protected APs should be used. This is also used with NEW_BEACON to
+ * indicate that the BSS is to use protection.
*
- * @NL80211_ATTR_CIPHERS_PAIRWISE: Used with CONNECT and ASSOCIATE to
- * indicate which unicast key ciphers will be used with the connection
+ * @NL80211_ATTR_CIPHERS_PAIRWISE: Used with CONNECT, ASSOCIATE, and NEW_BEACON
+ * to indicate which unicast key ciphers will be used with the connection
* (an array of u32).
- * @NL80211_ATTR_CIPHER_GROUP: Used with CONNECT and ASSOCIATE to indicate
- * which group key cipher will be used with the connection (a u32).
- * @NL80211_ATTR_WPA_VERSIONS: Used with CONNECT and ASSOCIATE to indicate
- * which WPA version(s) the AP we want to associate with is using
+ * @NL80211_ATTR_CIPHER_GROUP: Used with CONNECT, ASSOCIATE, and NEW_BEACON to
+ * indicate which group key cipher will be used with the connection (a
+ * u32).
+ * @NL80211_ATTR_WPA_VERSIONS: Used with CONNECT, ASSOCIATE, and NEW_BEACON to
+ * indicate which WPA version(s) the AP we want to associate with is using
* (a u32 with flags from &enum nl80211_wpa_versions).
- * @NL80211_ATTR_AKM_SUITES: Used with CONNECT and ASSOCIATE to indicate
- * which key management algorithm(s) to use (an array of u32).
+ * @NL80211_ATTR_AKM_SUITES: Used with CONNECT, ASSOCIATE, and NEW_BEACON to
+ * indicate which key management algorithm(s) to use (an array of u32).
*
* @NL80211_ATTR_REQ_IE: (Re)association request information elements as
* sent out by the card, for ROAM and successful CONNECT events.
* cache, a wiphy attribute.
*
* @NL80211_ATTR_DURATION: Duration of an operation in milliseconds, u32.
+ * @NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION: Device attribute that
+ * specifies the maximum duration that can be requested with the
+ * remain-on-channel operation, in milliseconds, u32.
*
* @NL80211_ATTR_COOKIE: Generic 64-bit cookie to identify objects.
*
* is used with %NL80211_CMD_SET_TX_BITRATE_MASK.
*
* @NL80211_ATTR_FRAME_MATCH: A binary attribute which typically must contain
- * at least one byte, currently used with @NL80211_CMD_REGISTER_ACTION.
+ * at least one byte, currently used with @NL80211_CMD_REGISTER_FRAME.
+ * @NL80211_ATTR_FRAME_TYPE: A u16 indicating the frame type/subtype for the
+ * @NL80211_CMD_REGISTER_FRAME command.
+ * @NL80211_ATTR_TX_FRAME_TYPES: wiphy capability attribute, which is a
+ * nested attribute of %NL80211_ATTR_FRAME_TYPE attributes, containing
+ * information about which frame types can be transmitted with
+ * %NL80211_CMD_FRAME.
+ * @NL80211_ATTR_RX_FRAME_TYPES: wiphy capability attribute, which is a
+ * nested attribute of %NL80211_ATTR_FRAME_TYPE attributes, containing
+ * information about which frame types can be registered for RX.
*
* @NL80211_ATTR_ACK: Flag attribute indicating that the frame was
* acknowledged by the recipient.
* NL80211_CMD_AUTHENTICATE, NL80211_CMD_DEAUTHENTICATE,
* NL80211_CMD_DISASSOCIATE.
*
+ * @NL80211_ATTR_AP_ISOLATE: (AP mode) Do not forward traffic between stations
+ * connected to this BSS.
+ *
+ * @NL80211_ATTR_WIPHY_TX_POWER_SETTING: Transmit power setting type. See
+ * &enum nl80211_tx_power_setting for possible values.
+ * @NL80211_ATTR_WIPHY_TX_POWER_LEVEL: Transmit power level in signed mBm units.
+ * This is used in association with @NL80211_ATTR_WIPHY_TX_POWER_SETTING
+ * for non-automatic settings.
+ *
+ * @NL80211_ATTR_SUPPORT_IBSS_RSN: The device supports IBSS RSN, which mostly
+ * means support for per-station GTKs.
+ *
+ * @NL80211_ATTR_WIPHY_ANTENNA_TX: Bitmap of allowed antennas for transmitting.
+ * This can be used to mask out antennas which are not attached or should
+ * not be used for transmitting. If an antenna is not selected in this
+ * bitmap the hardware is not allowed to transmit on this antenna.
+ *
+ * Each bit represents one antenna, starting with antenna 1 at the first
+ * bit. Depending on which antennas are selected in the bitmap, 802.11n
+ * drivers can derive which chainmasks to use (if all antennas belonging to
+ * a particular chain are disabled this chain should be disabled) and if
+ * a chain has diversity antennas wether diversity should be used or not.
+ * HT capabilities (STBC, TX Beamforming, Antenna selection) can be
+ * derived from the available chains after applying the antenna mask.
+ * Non-802.11n drivers can derive wether to use diversity or not.
+ * Drivers may reject configurations or RX/TX mask combinations they cannot
+ * support by returning -EINVAL.
+ *
+ * @NL80211_ATTR_WIPHY_ANTENNA_RX: Bitmap of allowed antennas for receiving.
+ * This can be used to mask out antennas which are not attached or should
+ * not be used for receiving. If an antenna is not selected in this bitmap
+ * the hardware should not be configured to receive on this antenna.
+ * For a more detailed description see @NL80211_ATTR_WIPHY_ANTENNA_TX.
+ *
+ * @NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX: Bitmap of antennas which are available
+ * for configuration as TX antennas via the above parameters.
+ *
+ * @NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX: Bitmap of antennas which are available
+ * for configuration as RX antennas via the above parameters.
+ *
+ * @NL80211_ATTR_MCAST_RATE: Multicast tx rate (in 100 kbps) for IBSS
+ *
+ * @NL80211_ATTR_OFFCHANNEL_TX_OK: For management frame TX, the frame may be
+ * transmitted on another channel when the channel given doesn't match
+ * the current channel. If the current channel doesn't match and this
+ * flag isn't set, the frame will be rejected. This is also used as an
+ * nl80211 capability flag.
+ *
+ * @NL80211_ATTR_BSS_HTOPMODE: HT operation mode (u16)
+ *
+ * @NL80211_ATTR_KEY_DEFAULT_TYPES: A nested attribute containing flags
+ * attributes, specifying what a key should be set as default as.
+ * See &enum nl80211_key_default_types.
+ *
+ * @NL80211_ATTR_MESH_SETUP: Optional mesh setup parameters. These cannot be
+ * changed once the mesh is active.
+ * @NL80211_ATTR_MESH_CONFIG: Mesh configuration parameters, a nested attribute
+ * containing attributes from &enum nl80211_meshconf_params.
+ * @NL80211_ATTR_SUPPORT_MESH_AUTH: Currently, this means the underlying driver
+ * allows auth frames in a mesh to be passed to userspace for processing via
+ * the @NL80211_MESH_SETUP_USERSPACE_AUTH flag.
+ * @NL80211_ATTR_STA_PLINK_STATE: The state of a mesh peer link as
+ * defined in &enum nl80211_plink_state. Used when userspace is
+ * driving the peer link management state machine.
+ * @NL80211_MESH_SETUP_USERSPACE_AMPE must be enabled.
+ *
+ * @NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED: indicates, as part of the wiphy
+ * capabilities, the supported WoWLAN triggers
+ * @NL80211_ATTR_WOWLAN_TRIGGERS: used by %NL80211_CMD_SET_WOWLAN to
+ * indicate which WoW triggers should be enabled. This is also
+ * used by %NL80211_CMD_GET_WOWLAN to get the currently enabled WoWLAN
+ * triggers.
+
+ * @NL80211_ATTR_SCHED_SCAN_INTERVAL: Interval between scheduled scan
+ * cycles, in msecs.
+
+ * @NL80211_ATTR_SCHED_SCAN_MATCH: Nested attribute with one or more
+ * sets of attributes to match during scheduled scans. Only BSSs
+ * that match any of the sets will be reported. These are
+ * pass-thru filter rules.
+ * For a match to succeed, the BSS must match all attributes of a
+ * set. Since not every hardware supports matching all types of
+ * attributes, there is no guarantee that the reported BSSs are
+ * fully complying with the match sets and userspace needs to be
+ * able to ignore them by itself.
+ * Thus, the implementation is somewhat hardware-dependent, but
+ * this is only an optimization and the userspace application
+ * needs to handle all the non-filtered results anyway.
+ * If the match attributes don't make sense when combined with
+ * the values passed in @NL80211_ATTR_SCAN_SSIDS (eg. if an SSID
+ * is included in the probe request, but the match attributes
+ * will never let it go through), -EINVAL may be returned.
+ * If ommited, no filtering is done.
+ *
+ * @NL80211_ATTR_INTERFACE_COMBINATIONS: Nested attribute listing the supported
+ * interface combinations. In each nested item, it contains attributes
+ * defined in &enum nl80211_if_combination_attrs.
+ * @NL80211_ATTR_SOFTWARE_IFTYPES: Nested attribute (just like
+ * %NL80211_ATTR_SUPPORTED_IFTYPES) containing the interface types that
+ * are managed in software: interfaces of these types aren't subject to
+ * any restrictions in their number or combinations.
+ *
+ * @%NL80211_ATTR_REKEY_DATA: nested attribute containing the information
+ * necessary for GTK rekeying in the device, see &enum nl80211_rekey_data.
+ *
+ * @NL80211_ATTR_SCAN_SUPP_RATES: rates per to be advertised as supported in scan,
+ * nested array attribute containing an entry for each band, with the entry
+ * being a list of supported rates as defined by IEEE 802.11 7.3.2.2 but
+ * without the length restriction (at most %NL80211_MAX_SUPP_RATES).
+ *
+ * @NL80211_ATTR_HIDDEN_SSID: indicates whether SSID is to be hidden from Beacon
+ * and Probe Response (when response to wildcard Probe Request); see
+ * &enum nl80211_hidden_ssid, represented as a u32
+ *
+ * @NL80211_ATTR_IE_PROBE_RESP: Information element(s) for Probe Response frame.
+ * This is used with %NL80211_CMD_NEW_BEACON and %NL80211_CMD_SET_BEACON to
+ * provide extra IEs (e.g., WPS/P2P IE) into Probe Response frames when the
+ * driver (or firmware) replies to Probe Request frames.
+ * @NL80211_ATTR_IE_ASSOC_RESP: Information element(s) for (Re)Association
+ * Response frames. This is used with %NL80211_CMD_NEW_BEACON and
+ * %NL80211_CMD_SET_BEACON to provide extra IEs (e.g., WPS/P2P IE) into
+ * (Re)Association Response frames when the driver (or firmware) replies to
+ * (Re)Association Request frames.
+ *
+ * @NL80211_ATTR_STA_WME: Nested attribute containing the wme configuration
+ * of the station, see &enum nl80211_sta_wme_attr.
+ * @NL80211_ATTR_SUPPORT_AP_UAPSD: the device supports uapsd when working
+ * as AP.
+ *
+ * @NL80211_ATTR_ROAM_SUPPORT: Indicates whether the firmware is capable of
+ * roaming to another AP in the same ESS if the signal lever is low.
+ *
+ * @NL80211_ATTR_PMKSA_CANDIDATE: Nested attribute containing the PMKSA caching
+ * candidate information, see &enum nl80211_pmksa_candidate_attr.
+ *
+ * @NL80211_ATTR_TX_NO_CCK_RATE: Indicates whether to use CCK rate or not
+ * for management frames transmission. In order to avoid p2p probe/action
+ * frames are being transmitted at CCK rate in 2GHz band, the user space
+ * applications use this attribute.
+ * This attribute is used with %NL80211_CMD_TRIGGER_SCAN and
+ * %NL80211_CMD_FRAME commands.
+ *
+ * @NL80211_ATTR_TDLS_ACTION: Low level TDLS action code (e.g. link setup
+ * request, link setup confirm, link teardown, etc.). Values are
+ * described in the TDLS (802.11z) specification.
+ * @NL80211_ATTR_TDLS_DIALOG_TOKEN: Non-zero token for uniquely identifying a
+ * TDLS conversation between two devices.
+ * @NL80211_ATTR_TDLS_OPERATION: High level TDLS operation; see
+ * &enum nl80211_tdls_operation, represented as a u8.
+ * @NL80211_ATTR_TDLS_SUPPORT: A flag indicating the device can operate
+ * as a TDLS peer sta.
+ * @NL80211_ATTR_TDLS_EXTERNAL_SETUP: The TDLS discovery/setup and teardown
+ * procedures should be performed by sending TDLS packets via
+ * %NL80211_CMD_TDLS_MGMT. Otherwise %NL80211_CMD_TDLS_OPER should be
+ * used for asking the driver to perform a TDLS operation.
+ *
+ * @NL80211_ATTR_DEVICE_AP_SME: This u32 attribute may be listed for devices
+ * that have AP support to indicate that they have the AP SME integrated
+ * with support for the features listed in this attribute, see
+ * &enum nl80211_ap_sme_features.
+ *
+ * @NL80211_ATTR_DONT_WAIT_FOR_ACK: Used with %NL80211_CMD_FRAME, this tells
+ * the driver to not wait for an acknowledgement. Note that due to this,
+ * it will also not give a status callback nor return a cookie. This is
+ * mostly useful for probe responses to save airtime.
+ *
+ * @NL80211_ATTR_FEATURE_FLAGS: This u32 attribute contains flags from
+ * &enum nl80211_feature_flags and is advertised in wiphy information.
+ * @NL80211_ATTR_PROBE_RESP_OFFLOAD: Indicates that the HW responds to probe
+ *
+ * requests while operating in AP-mode.
+ * This attribute holds a bitmap of the supported protocols for
+ * offloading (see &enum nl80211_probe_resp_offload_support_attr).
+ *
+ * @NL80211_ATTR_PROBE_RESP: Probe Response template data. Contains the entire
+ * probe-response frame. The DA field in the 802.11 header is zero-ed out,
+ * to be filled by the FW.
+ * @NL80211_ATTR_DISABLE_HT: Force HT capable interfaces to disable
+ * this feature. Currently, only supported in mac80211 drivers.
+ * @NL80211_ATTR_HT_CAPABILITY_MASK: Specify which bits of the
+ * ATTR_HT_CAPABILITY to which attention should be paid.
+ * Currently, only mac80211 NICs support this feature.
+ * The values that may be configured are:
+ * MCS rates, MAX-AMSDU, HT-20-40 and HT_CAP_SGI_40
+ * AMPDU density and AMPDU factor.
+ * All values are treated as suggestions and may be ignored
+ * by the driver as required. The actual values may be seen in
+ * the station debugfs ht_caps file.
+ *
+ * @NL80211_ATTR_DFS_REGION: region for regulatory rules which this country
+ * abides to when initiating radiation on DFS channels. A country maps
+ * to one DFS region.
+ *
+ * @NL80211_ATTR_NOACK_MAP: This u16 bitmap contains the No Ack Policy of
+ * up to 16 TIDs.
+ *
+ * @NL80211_ATTR_INACTIVITY_TIMEOUT: timeout value in seconds, this can be
+ * used by the drivers which has MLME in firmware and does not have support
+ * to report per station tx/rx activity to free up the staion entry from
+ * the list. This needs to be used when the driver advertises the
+ * capability to timeout the stations.
+ *
+ * @NL80211_ATTR_RX_SIGNAL_DBM: signal strength in dBm (as a 32-bit int);
+ * this attribute is (depending on the driver capabilities) added to
+ * received frames indicated with %NL80211_CMD_FRAME.
+ *
+ * @NL80211_ATTR_BG_SCAN_PERIOD: Background scan period in seconds
+ * or 0 to disable background scan.
+ *
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
*/
enum nl80211_attrs {
-/* don't change the order or add anything inbetween, this is ABI! */
+/* don't change the order or add anything between, this is ABI! */
NL80211_ATTR_UNSPEC,
NL80211_ATTR_WIPHY,
NL80211_ATTR_REG_ALPHA2,
NL80211_ATTR_REG_RULES,
- NL80211_ATTR_MESH_PARAMS,
+ NL80211_ATTR_MESH_CONFIG,
NL80211_ATTR_BSS_BASIC_RATES,
NL80211_ATTR_LOCAL_STATE_CHANGE,
+ NL80211_ATTR_AP_ISOLATE,
+
+ NL80211_ATTR_WIPHY_TX_POWER_SETTING,
+ NL80211_ATTR_WIPHY_TX_POWER_LEVEL,
+
+ NL80211_ATTR_TX_FRAME_TYPES,
+ NL80211_ATTR_RX_FRAME_TYPES,
+ NL80211_ATTR_FRAME_TYPE,
+
+ NL80211_ATTR_CONTROL_PORT_ETHERTYPE,
+ NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT,
+
+ NL80211_ATTR_SUPPORT_IBSS_RSN,
+
+ NL80211_ATTR_WIPHY_ANTENNA_TX,
+ NL80211_ATTR_WIPHY_ANTENNA_RX,
+
+ NL80211_ATTR_MCAST_RATE,
+
+ NL80211_ATTR_OFFCHANNEL_TX_OK,
+
+ NL80211_ATTR_BSS_HT_OPMODE,
+
+ NL80211_ATTR_KEY_DEFAULT_TYPES,
+
+ NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION,
+
+ NL80211_ATTR_MESH_SETUP,
+
+ NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX,
+ NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX,
+
+ NL80211_ATTR_SUPPORT_MESH_AUTH,
+ NL80211_ATTR_STA_PLINK_STATE,
+
+ NL80211_ATTR_WOWLAN_TRIGGERS,
+ NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED,
+
+ NL80211_ATTR_SCHED_SCAN_INTERVAL,
+
+ NL80211_ATTR_INTERFACE_COMBINATIONS,
+ NL80211_ATTR_SOFTWARE_IFTYPES,
+
+ NL80211_ATTR_REKEY_DATA,
+
+ NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS,
+ NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN,
+
+ NL80211_ATTR_SCAN_SUPP_RATES,
+
+ NL80211_ATTR_HIDDEN_SSID,
+
+ NL80211_ATTR_IE_PROBE_RESP,
+ NL80211_ATTR_IE_ASSOC_RESP,
+
+ NL80211_ATTR_STA_WME,
+ NL80211_ATTR_SUPPORT_AP_UAPSD,
+
+ NL80211_ATTR_ROAM_SUPPORT,
+
+ NL80211_ATTR_SCHED_SCAN_MATCH,
+ NL80211_ATTR_MAX_MATCH_SETS,
+
+ NL80211_ATTR_PMKSA_CANDIDATE,
+
+ NL80211_ATTR_TX_NO_CCK_RATE,
+
+ NL80211_ATTR_TDLS_ACTION,
+ NL80211_ATTR_TDLS_DIALOG_TOKEN,
+ NL80211_ATTR_TDLS_OPERATION,
+ NL80211_ATTR_TDLS_SUPPORT,
+ NL80211_ATTR_TDLS_EXTERNAL_SETUP,
+
+ NL80211_ATTR_DEVICE_AP_SME,
+
+ NL80211_ATTR_DONT_WAIT_FOR_ACK,
+
+ NL80211_ATTR_FEATURE_FLAGS,
+
+ NL80211_ATTR_PROBE_RESP_OFFLOAD,
+
+ NL80211_ATTR_PROBE_RESP,
+
+ NL80211_ATTR_DFS_REGION,
+
+ NL80211_ATTR_DISABLE_HT,
+ NL80211_ATTR_HT_CAPABILITY_MASK,
+
+ NL80211_ATTR_NOACK_MAP,
+
+ NL80211_ATTR_INACTIVITY_TIMEOUT,
+
+ NL80211_ATTR_RX_SIGNAL_DBM,
+
+ NL80211_ATTR_BG_SCAN_PERIOD,
+
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
/* source-level API compatibility */
#define NL80211_ATTR_SCAN_GENERATION NL80211_ATTR_GENERATION
+#define NL80211_ATTR_MESH_PARAMS NL80211_ATTR_MESH_CONFIG
/*
* Allow user space programs to use #ifdef on new attributes by defining them
#define NL80211_ATTR_KEYS NL80211_ATTR_KEYS
#define NL80211_MAX_SUPP_RATES 32
+#define NL80211_MAX_SUPP_HT_RATES 77
#define NL80211_MAX_SUPP_REG_RULES 32
#define NL80211_TKIP_DATA_OFFSET_ENCR_KEY 0
#define NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY 16
* @NL80211_IFTYPE_ADHOC: independent BSS member
* @NL80211_IFTYPE_STATION: managed BSS member
* @NL80211_IFTYPE_AP: access point
- * @NL80211_IFTYPE_AP_VLAN: VLAN interface for access points
+ * @NL80211_IFTYPE_AP_VLAN: VLAN interface for access points; VLAN interfaces
+ * are a bit special in that they must always be tied to a pre-existing
+ * AP type interface.
* @NL80211_IFTYPE_WDS: wireless distribution interface
* @NL80211_IFTYPE_MONITOR: monitor interface receiving all frames
* @NL80211_IFTYPE_MESH_POINT: mesh point
+ * @NL80211_IFTYPE_P2P_CLIENT: P2P client
+ * @NL80211_IFTYPE_P2P_GO: P2P group owner
* @NL80211_IFTYPE_MAX: highest interface type number currently defined
- * @__NL80211_IFTYPE_AFTER_LAST: internal use
+ * @NUM_NL80211_IFTYPES: number of defined interface types
*
* These values are used with the %NL80211_ATTR_IFTYPE
* to set the type of an interface.
NL80211_IFTYPE_WDS,
NL80211_IFTYPE_MONITOR,
NL80211_IFTYPE_MESH_POINT,
+ NL80211_IFTYPE_P2P_CLIENT,
+ NL80211_IFTYPE_P2P_GO,
/* keep last */
- __NL80211_IFTYPE_AFTER_LAST,
- NL80211_IFTYPE_MAX = __NL80211_IFTYPE_AFTER_LAST - 1
+ NUM_NL80211_IFTYPES,
+ NL80211_IFTYPE_MAX = NUM_NL80211_IFTYPES - 1
};
/**
* Station flags. When a station is added to an AP interface, it is
* assumed to be already associated (and hence authenticated.)
*
+ * @__NL80211_STA_FLAG_INVALID: attribute number 0 is reserved
* @NL80211_STA_FLAG_AUTHORIZED: station is authorized (802.1X)
* @NL80211_STA_FLAG_SHORT_PREAMBLE: station is capable of receiving frames
* with short barker preamble
* @NL80211_STA_FLAG_WME: station is WME/QoS capable
* @NL80211_STA_FLAG_MFP: station uses management frame protection
+ * @NL80211_STA_FLAG_AUTHENTICATED: station is authenticated
+ * @NL80211_STA_FLAG_TDLS_PEER: station is a TDLS peer
+ * @NL80211_STA_FLAG_MAX: highest station flag number currently defined
+ * @__NL80211_STA_FLAG_AFTER_LAST: internal use
*/
enum nl80211_sta_flags {
__NL80211_STA_FLAG_INVALID,
NL80211_STA_FLAG_SHORT_PREAMBLE,
NL80211_STA_FLAG_WME,
NL80211_STA_FLAG_MFP,
+ NL80211_STA_FLAG_AUTHENTICATED,
+ NL80211_STA_FLAG_TDLS_PEER,
/* keep last */
__NL80211_STA_FLAG_AFTER_LAST,
};
/**
+ * enum nl80211_sta_bss_param - BSS information collected by STA
+ *
+ * These attribute types are used with %NL80211_STA_INFO_BSS_PARAM
+ * when getting information about the bitrate of a station.
+ *
+ * @__NL80211_STA_BSS_PARAM_INVALID: attribute number 0 is reserved
+ * @NL80211_STA_BSS_PARAM_CTS_PROT: whether CTS protection is enabled (flag)
+ * @NL80211_STA_BSS_PARAM_SHORT_PREAMBLE: whether short preamble is enabled
+ * (flag)
+ * @NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME: whether short slot time is enabled
+ * (flag)
+ * @NL80211_STA_BSS_PARAM_DTIM_PERIOD: DTIM period for beaconing (u8)
+ * @NL80211_STA_BSS_PARAM_BEACON_INTERVAL: Beacon interval (u16)
+ * @NL80211_STA_BSS_PARAM_MAX: highest sta_bss_param number currently defined
+ * @__NL80211_STA_BSS_PARAM_AFTER_LAST: internal use
+ */
+enum nl80211_sta_bss_param {
+ __NL80211_STA_BSS_PARAM_INVALID,
+ NL80211_STA_BSS_PARAM_CTS_PROT,
+ NL80211_STA_BSS_PARAM_SHORT_PREAMBLE,
+ NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME,
+ NL80211_STA_BSS_PARAM_DTIM_PERIOD,
+ NL80211_STA_BSS_PARAM_BEACON_INTERVAL,
+
+ /* keep last */
+ __NL80211_STA_BSS_PARAM_AFTER_LAST,
+ NL80211_STA_BSS_PARAM_MAX = __NL80211_STA_BSS_PARAM_AFTER_LAST - 1
+};
+
+/**
* enum nl80211_sta_info - station information
*
* These attribute types are used with %NL80211_ATTR_STA_INFO
* @NL80211_STA_INFO_INACTIVE_TIME: time since last activity (u32, msecs)
* @NL80211_STA_INFO_RX_BYTES: total received bytes (u32, from this station)
* @NL80211_STA_INFO_TX_BYTES: total transmitted bytes (u32, to this station)
- * @__NL80211_STA_INFO_AFTER_LAST: internal
- * @NL80211_STA_INFO_MAX: highest possible station info attribute
* @NL80211_STA_INFO_SIGNAL: signal strength of last received PPDU (u8, dBm)
* @NL80211_STA_INFO_TX_BITRATE: current unicast tx rate, nested attribute
- * containing info as possible, see &enum nl80211_sta_info_txrate.
+ * containing info as possible, see &enum nl80211_rate_info
* @NL80211_STA_INFO_RX_PACKETS: total received packet (u32, from this station)
* @NL80211_STA_INFO_TX_PACKETS: total transmitted packets (u32, to this
* station)
+ * @NL80211_STA_INFO_TX_RETRIES: total retries (u32, to this station)
+ * @NL80211_STA_INFO_TX_FAILED: total failed packets (u32, to this station)
+ * @NL80211_STA_INFO_SIGNAL_AVG: signal strength average (u8, dBm)
+ * @NL80211_STA_INFO_LLID: the station's mesh LLID
+ * @NL80211_STA_INFO_PLID: the station's mesh PLID
+ * @NL80211_STA_INFO_PLINK_STATE: peer link state for the station
+ * (see %enum nl80211_plink_state)
+ * @NL80211_STA_INFO_RX_BITRATE: last unicast data frame rx rate, nested
+ * attribute, like NL80211_STA_INFO_TX_BITRATE.
+ * @NL80211_STA_INFO_BSS_PARAM: current station's view of BSS, nested attribute
+ * containing info as possible, see &enum nl80211_sta_bss_param
+ * @NL80211_STA_INFO_CONNECTED_TIME: time since the station is last connected
+ * @__NL80211_STA_INFO_AFTER_LAST: internal
+ * @NL80211_STA_INFO_MAX: highest possible station info attribute
*/
enum nl80211_sta_info {
__NL80211_STA_INFO_INVALID,
NL80211_STA_INFO_TX_BITRATE,
NL80211_STA_INFO_RX_PACKETS,
NL80211_STA_INFO_TX_PACKETS,
+ NL80211_STA_INFO_TX_RETRIES,
+ NL80211_STA_INFO_TX_FAILED,
+ NL80211_STA_INFO_SIGNAL_AVG,
+ NL80211_STA_INFO_RX_BITRATE,
+ NL80211_STA_INFO_BSS_PARAM,
+ NL80211_STA_INFO_CONNECTED_TIME,
/* keep last */
__NL80211_STA_INFO_AFTER_LAST,
* information about a mesh path.
*
* @__NL80211_MPATH_INFO_INVALID: attribute number 0 is reserved
- * @NL80211_ATTR_MPATH_FRAME_QLEN: number of queued frames for this destination
- * @NL80211_ATTR_MPATH_SN: destination sequence number
- * @NL80211_ATTR_MPATH_METRIC: metric (cost) of this mesh path
- * @NL80211_ATTR_MPATH_EXPTIME: expiration time for the path, in msec from now
- * @NL80211_ATTR_MPATH_FLAGS: mesh path flags, enumerated in
+ * @NL80211_MPATH_INFO_FRAME_QLEN: number of queued frames for this destination
+ * @NL80211_MPATH_INFO_SN: destination sequence number
+ * @NL80211_MPATH_INFO_METRIC: metric (cost) of this mesh path
+ * @NL80211_MPATH_INFO_EXPTIME: expiration time for the path, in msec from now
+ * @NL80211_MPATH_INFO_FLAGS: mesh path flags, enumerated in
* &enum nl80211_mpath_flags;
- * @NL80211_ATTR_MPATH_DISCOVERY_TIMEOUT: total path discovery timeout, in msec
- * @NL80211_ATTR_MPATH_DISCOVERY_RETRIES: mesh path discovery retries
+ * @NL80211_MPATH_INFO_DISCOVERY_TIMEOUT: total path discovery timeout, in msec
+ * @NL80211_MPATH_INFO_DISCOVERY_RETRIES: mesh path discovery retries
+ * @NL80211_MPATH_INFO_MAX: highest mesh path information attribute number
+ * currently defind
+ * @__NL80211_MPATH_INFO_AFTER_LAST: internal use
*/
enum nl80211_mpath_info {
__NL80211_MPATH_INFO_INVALID,
* @NL80211_BAND_ATTR_HT_CAPA: HT capabilities, as in the HT information IE
* @NL80211_BAND_ATTR_HT_AMPDU_FACTOR: A-MPDU factor, as in 11n
* @NL80211_BAND_ATTR_HT_AMPDU_DENSITY: A-MPDU density, as in 11n
+ * @NL80211_BAND_ATTR_MAX: highest band attribute currently defined
+ * @__NL80211_BAND_ATTR_AFTER_LAST: internal use
*/
enum nl80211_band_attr {
__NL80211_BAND_ATTR_INVALID,
/**
* enum nl80211_frequency_attr - frequency attributes
+ * @__NL80211_FREQUENCY_ATTR_INVALID: attribute number 0 is reserved
* @NL80211_FREQUENCY_ATTR_FREQ: Frequency in MHz
* @NL80211_FREQUENCY_ATTR_DISABLED: Channel is disabled in current
* regulatory domain.
* on this channel in current regulatory domain.
* @NL80211_FREQUENCY_ATTR_MAX_TX_POWER: Maximum transmission power in mBm
* (100 * dBm).
+ * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number
+ * currently defined
+ * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use
*/
enum nl80211_frequency_attr {
__NL80211_FREQUENCY_ATTR_INVALID,
/**
* enum nl80211_bitrate_attr - bitrate attributes
+ * @__NL80211_BITRATE_ATTR_INVALID: attribute number 0 is reserved
* @NL80211_BITRATE_ATTR_RATE: Bitrate in units of 100 kbps
* @NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE: Short preamble supported
* in 2.4 GHz band.
+ * @NL80211_BITRATE_ATTR_MAX: highest bitrate attribute number
+ * currently defined
+ * @__NL80211_BITRATE_ATTR_AFTER_LAST: internal use
*/
enum nl80211_bitrate_attr {
__NL80211_BITRATE_ATTR_INVALID,
* wireless core it thinks its knows the regulatory domain we should be in.
* @NL80211_REGDOM_SET_BY_COUNTRY_IE: the wireless core has received an
* 802.11 country information element with regulatory information it
- * thinks we should consider.
+ * thinks we should consider. cfg80211 only processes the country
+ * code from the IE, and relies on the regulatory domain information
+ * structure passed by userspace (CRDA) from our wireless-regdb.
+ * If a channel is enabled but the country code indicates it should
+ * be disabled we disable the channel and re-enable it upon disassociation.
*/
enum nl80211_reg_initiator {
NL80211_REGDOM_SET_BY_CORE,
/**
* enum nl80211_reg_rule_attr - regulatory rule attributes
+ * @__NL80211_REG_RULE_ATTR_INVALID: attribute number 0 is reserved
* @NL80211_ATTR_REG_RULE_FLAGS: a set of flags which specify additional
* considerations for a given frequency range. These are the
* &enum nl80211_reg_rule_flags.
* If you don't have one then don't send this.
* @NL80211_ATTR_POWER_RULE_MAX_EIRP: the maximum allowed EIRP for
* a given frequency range. The value is in mBm (100 * dBm).
+ * @NL80211_REG_RULE_ATTR_MAX: highest regulatory rule attribute number
+ * currently defined
+ * @__NL80211_REG_RULE_ATTR_AFTER_LAST: internal use
*/
enum nl80211_reg_rule_attr {
__NL80211_REG_RULE_ATTR_INVALID,
};
/**
+ * enum nl80211_sched_scan_match_attr - scheduled scan match attributes
+ * @__NL80211_SCHED_SCAN_MATCH_ATTR_INVALID: attribute number 0 is reserved
+ * @NL80211_SCHED_SCAN_MATCH_ATTR_SSID: SSID to be used for matching,
+ * only report BSS with matching SSID.
+ * @NL80211_SCHED_SCAN_MATCH_ATTR_MAX: highest scheduled scan filter
+ * attribute number currently defined
+ * @__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST: internal use
+ */
+enum nl80211_sched_scan_match_attr {
+ __NL80211_SCHED_SCAN_MATCH_ATTR_INVALID,
+
+ NL80211_ATTR_SCHED_SCAN_MATCH_SSID,
+
+ /* keep last */
+ __NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST,
+ NL80211_SCHED_SCAN_MATCH_ATTR_MAX =
+ __NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST - 1
+};
+
+/**
* enum nl80211_reg_rule_flags - regulatory rule flags
*
* @NL80211_RRF_NO_OFDM: OFDM modulation not allowed
* @__NL80211_SURVEY_INFO_INVALID: attribute number 0 is reserved
* @NL80211_SURVEY_INFO_FREQUENCY: center frequency of channel
* @NL80211_SURVEY_INFO_NOISE: noise level of channel (u8, dBm)
+ * @NL80211_SURVEY_INFO_IN_USE: channel is currently being used
+ * @NL80211_SURVEY_INFO_CHANNEL_TIME: amount of time (in ms) that the radio
+ * spent on this channel
+ * @NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY: amount of the time the primary
+ * channel was sensed busy (either due to activity or energy detect)
+ * @NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY: amount of time the extension
+ * channel was sensed busy
+ * @NL80211_SURVEY_INFO_CHANNEL_TIME_RX: amount of time the radio spent
+ * receiving data
+ * @NL80211_SURVEY_INFO_CHANNEL_TIME_TX: amount of time the radio spent
+ * transmitting data
+ * @NL80211_SURVEY_INFO_MAX: highest survey info attribute number
+ * currently defined
+ * @__NL80211_SURVEY_INFO_AFTER_LAST: internal use
*/
enum nl80211_survey_info {
__NL80211_SURVEY_INFO_INVALID,
NL80211_SURVEY_INFO_FREQUENCY,
NL80211_SURVEY_INFO_NOISE,
+ NL80211_SURVEY_INFO_IN_USE,
+ NL80211_SURVEY_INFO_CHANNEL_TIME,
+ NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY,
+ NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY,
+ NL80211_SURVEY_INFO_CHANNEL_TIME_RX,
+ NL80211_SURVEY_INFO_CHANNEL_TIME_TX,
/* keep last */
__NL80211_SURVEY_INFO_AFTER_LAST,
/**
* enum nl80211_meshconf_params - mesh configuration parameters
*
- * Mesh configuration parameters
+ * Mesh configuration parameters. These can be changed while the mesh is
+ * active.
*
* @__NL80211_MESHCONF_INVALID: internal use
*
* @NL80211_MESHCONF_RETRY_TIMEOUT: specifies the initial retry timeout in
* millisecond units, used by the Peer Link Open message
*
- * @NL80211_MESHCONF_CONFIRM_TIMEOUT: specifies the inital confirm timeout, in
+ * @NL80211_MESHCONF_CONFIRM_TIMEOUT: specifies the initial confirm timeout, in
* millisecond units, used by the peer link management to close a peer link
*
* @NL80211_MESHCONF_HOLDING_TIMEOUT: specifies the holding timeout, in
* @NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME: The interval of time (in TUs)
* that it takes for an HWMP information element to propagate across the mesh
*
- * @NL80211_MESHCONF_ROOTMODE: whether root mode is enabled or not
+ * @NL80211_MESHCONF_HWMP_ROOTMODE: whether root mode is enabled or not
+ *
+ * @NL80211_MESHCONF_ELEMENT_TTL: specifies the value of TTL field set at a
+ * source mesh point for path selection elements.
+ *
+ * @NL80211_MESHCONF_HWMP_RANN_INTERVAL: The interval of time (in TUs) between
+ * root announcements are transmitted.
+ *
+ * @NL80211_MESHCONF_GATE_ANNOUNCEMENTS: Advertise that this mesh station has
+ * access to a broader network beyond the MBSS. This is done via Root
+ * Announcement frames.
+ *
+ * @NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL: The minimum interval of time (in
+ * TUs) during which a mesh STA can send only one Action frame containing a
+ * PERR element.
+ *
+ * @NL80211_MESHCONF_FORWARDING: set Mesh STA as forwarding or non-forwarding
+ * or forwarding entity (default is TRUE - forwarding entity)
+ *
+ * @NL80211_MESHCONF_RSSI_THRESHOLD: RSSI threshold in dBm. This specifies the
+ * threshold for average signal strength of candidate station to establish
+ * a peer link.
*
* @NL80211_MESHCONF_ATTR_MAX: highest possible mesh configuration attribute
*
NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
NL80211_MESHCONF_HWMP_ROOTMODE,
+ NL80211_MESHCONF_ELEMENT_TTL,
+ NL80211_MESHCONF_HWMP_RANN_INTERVAL,
+ NL80211_MESHCONF_GATE_ANNOUNCEMENTS,
+ NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL,
+ NL80211_MESHCONF_FORWARDING,
+ NL80211_MESHCONF_RSSI_THRESHOLD,
/* keep last */
__NL80211_MESHCONF_ATTR_AFTER_LAST,
};
/**
+ * enum nl80211_mesh_setup_params - mesh setup parameters
+ *
+ * Mesh setup parameters. These are used to start/join a mesh and cannot be
+ * changed while the mesh is active.
+ *
+ * @__NL80211_MESH_SETUP_INVALID: Internal use
+ *
+ * @NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL: Enable this option to use a
+ * vendor specific path selection algorithm or disable it to use the default
+ * HWMP.
+ *
+ * @NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC: Enable this option to use a
+ * vendor specific path metric or disable it to use the default Airtime
+ * metric.
+ *
+ * @NL80211_MESH_SETUP_IE: Information elements for this mesh, for instance, a
+ * robust security network ie, or a vendor specific information element that
+ * vendors will use to identify the path selection methods and metrics in use.
+ *
+ * @NL80211_MESH_SETUP_USERSPACE_AUTH: Enable this option if an authentication
+ * daemon will be authenticating mesh candidates.
+ *
+ * @NL80211_MESH_SETUP_USERSPACE_AMPE: Enable this option if an authentication
+ * daemon will be securing peer link frames. AMPE is a secured version of Mesh
+ * Peering Management (MPM) and is implemented with the assistance of a
+ * userspace daemon. When this flag is set, the kernel will send peer
+ * management frames to a userspace daemon that will implement AMPE
+ * functionality (security capabilities selection, key confirmation, and key
+ * management). When the flag is unset (default), the kernel can autonomously
+ * complete (unsecured) mesh peering without the need of a userspace daemon.
+ *
+ * @NL80211_MESH_SETUP_ATTR_MAX: highest possible mesh setup attribute number
+ * @__NL80211_MESH_SETUP_ATTR_AFTER_LAST: Internal use
+ */
+enum nl80211_mesh_setup_params {
+ __NL80211_MESH_SETUP_INVALID,
+ NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL,
+ NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC,
+ NL80211_MESH_SETUP_IE,
+ NL80211_MESH_SETUP_USERSPACE_AUTH,
+ NL80211_MESH_SETUP_USERSPACE_AMPE,
+
+ /* keep last */
+ __NL80211_MESH_SETUP_ATTR_AFTER_LAST,
+ NL80211_MESH_SETUP_ATTR_MAX = __NL80211_MESH_SETUP_ATTR_AFTER_LAST - 1
+};
+
+/**
* enum nl80211_txq_attr - TX queue parameter attributes
* @__NL80211_TXQ_ATTR_INVALID: Attribute number 0 is reserved
* @NL80211_TXQ_ATTR_QUEUE: TX queue identifier (NL80211_TXQ_Q_*)
* enum nl80211_bss - netlink attributes for a BSS
*
* @__NL80211_BSS_INVALID: invalid
+ * @NL80211_BSS_BSSID: BSSID of the BSS (6 octets)
* @NL80211_BSS_FREQUENCY: frequency in MHz (u32)
* @NL80211_BSS_TSF: TSF of the received probe response/beacon (u64)
* @NL80211_BSS_BEACON_INTERVAL: beacon interval of the (I)BSS (u16)
/**
* enum nl80211_bss_status - BSS "status"
+ * @NL80211_BSS_STATUS_AUTHENTICATED: Authenticated with this BSS.
+ * @NL80211_BSS_STATUS_ASSOCIATED: Associated with this BSS.
+ * @NL80211_BSS_STATUS_IBSS_JOINED: Joined to this IBSS.
+ *
+ * The BSS status is a BSS attribute in scan dumps, which
+ * indicates the status the interface has wrt. this BSS.
*/
enum nl80211_bss_status {
NL80211_BSS_STATUS_AUTHENTICATED,
* @NL80211_KEYTYPE_GROUP: Group (broadcast/multicast) key
* @NL80211_KEYTYPE_PAIRWISE: Pairwise (unicast/individual) key
* @NL80211_KEYTYPE_PEERKEY: PeerKey (DLS)
+ * @NUM_NL80211_KEYTYPES: number of defined key types
*/
enum nl80211_key_type {
NL80211_KEYTYPE_GROUP,
NL80211_KEYTYPE_PAIRWISE,
NL80211_KEYTYPE_PEERKEY,
+
+ NUM_NL80211_KEYTYPES
};
/**
};
/**
+ * enum nl80211_key_default_types - key default types
+ * @__NL80211_KEY_DEFAULT_TYPE_INVALID: invalid
+ * @NL80211_KEY_DEFAULT_TYPE_UNICAST: key should be used as default
+ * unicast key
+ * @NL80211_KEY_DEFAULT_TYPE_MULTICAST: key should be used as default
+ * multicast key
+ * @NUM_NL80211_KEY_DEFAULT_TYPES: number of default types
+ */
+enum nl80211_key_default_types {
+ __NL80211_KEY_DEFAULT_TYPE_INVALID,
+ NL80211_KEY_DEFAULT_TYPE_UNICAST,
+ NL80211_KEY_DEFAULT_TYPE_MULTICAST,
+
+ NUM_NL80211_KEY_DEFAULT_TYPES
+};
+
+/**
* enum nl80211_key_attributes - key attributes
* @__NL80211_KEY_INVALID: invalid
* @NL80211_KEY_DATA: (temporal) key data; for TKIP this consists of
* CCMP keys, each six bytes in little endian
* @NL80211_KEY_DEFAULT: flag indicating default key
* @NL80211_KEY_DEFAULT_MGMT: flag indicating default management key
+ * @NL80211_KEY_TYPE: the key type from enum nl80211_key_type, if not
+ * specified the default depends on whether a MAC address was
+ * given with the command using the key or not (u32)
+ * @NL80211_KEY_DEFAULT_TYPES: A nested attribute containing flags
+ * attributes, specifying what a key should be set as default as.
+ * See &enum nl80211_key_default_types.
* @__NL80211_KEY_AFTER_LAST: internal
* @NL80211_KEY_MAX: highest key attribute
*/
NL80211_KEY_SEQ,
NL80211_KEY_DEFAULT,
NL80211_KEY_DEFAULT_MGMT,
+ NL80211_KEY_TYPE,
+ NL80211_KEY_DEFAULT_TYPES,
/* keep last */
__NL80211_KEY_AFTER_LAST,
* in an array of rates as defined in IEEE 802.11 7.3.2.2 (u8 values with
* 1 = 500 kbps) but without the IE length restriction (at most
* %NL80211_MAX_SUPP_RATES in a single array).
+ * @NL80211_TXRATE_MCS: HT (MCS) rates allowed for TX rate selection
+ * in an array of MCS numbers.
* @__NL80211_TXRATE_AFTER_LAST: internal
* @NL80211_TXRATE_MAX: highest TX rate attribute
*/
enum nl80211_tx_rate_attributes {
__NL80211_TXRATE_INVALID,
NL80211_TXRATE_LEGACY,
+ NL80211_TXRATE_MCS,
/* keep last */
__NL80211_TXRATE_AFTER_LAST,
/**
* enum nl80211_band - Frequency band
- * @NL80211_BAND_2GHZ - 2.4 GHz ISM band
- * @NL80211_BAND_5GHZ - around 5 GHz band (4.9 - 5.7 GHz)
+ * @NL80211_BAND_2GHZ: 2.4 GHz ISM band
+ * @NL80211_BAND_5GHZ: around 5 GHz band (4.9 - 5.7 GHz)
*/
enum nl80211_band {
NL80211_BAND_2GHZ,
* the minimum amount the RSSI level must change after an event before a
* new event may be issued (to reduce effects of RSSI oscillation).
* @NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT: RSSI threshold event
+ * @NL80211_ATTR_CQM_PKT_LOSS_EVENT: a u32 value indicating that this many
+ * consecutive packets were not acknowledged by the peer
* @__NL80211_ATTR_CQM_AFTER_LAST: internal
* @NL80211_ATTR_CQM_MAX: highest key attribute
*/
NL80211_ATTR_CQM_RSSI_THOLD,
NL80211_ATTR_CQM_RSSI_HYST,
NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT,
+ NL80211_ATTR_CQM_PKT_LOSS_EVENT,
/* keep last */
__NL80211_ATTR_CQM_AFTER_LAST,
/**
* enum nl80211_cqm_rssi_threshold_event - RSSI threshold event
- * @NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW - The RSSI level is lower than the
+ * @NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW: The RSSI level is lower than the
* configured threshold
- * @NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH - The RSSI is higher than the
+ * @NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH: The RSSI is higher than the
* configured threshold
*/
enum nl80211_cqm_rssi_threshold_event {
NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
};
+
+/**
+ * enum nl80211_tx_power_setting - TX power adjustment
+ * @NL80211_TX_POWER_AUTOMATIC: automatically determine transmit power
+ * @NL80211_TX_POWER_LIMITED: limit TX power by the mBm parameter
+ * @NL80211_TX_POWER_FIXED: fix TX power to the mBm parameter
+ */
+enum nl80211_tx_power_setting {
+ NL80211_TX_POWER_AUTOMATIC,
+ NL80211_TX_POWER_LIMITED,
+ NL80211_TX_POWER_FIXED,
+};
+
+/**
+ * enum nl80211_wowlan_packet_pattern_attr - WoWLAN packet pattern attribute
+ * @__NL80211_WOWLAN_PKTPAT_INVALID: invalid number for nested attribute
+ * @NL80211_WOWLAN_PKTPAT_PATTERN: the pattern, values where the mask has
+ * a zero bit are ignored
+ * @NL80211_WOWLAN_PKTPAT_MASK: pattern mask, must be long enough to have
+ * a bit for each byte in the pattern. The lowest-order bit corresponds
+ * to the first byte of the pattern, but the bytes of the pattern are
+ * in a little-endian-like format, i.e. the 9th byte of the pattern
+ * corresponds to the lowest-order bit in the second byte of the mask.
+ * For example: The match 00:xx:00:00:xx:00:00:00:00:xx:xx:xx (where
+ * xx indicates "don't care") would be represented by a pattern of
+ * twelve zero bytes, and a mask of "0xed,0x07".
+ * Note that the pattern matching is done as though frames were not
+ * 802.11 frames but 802.3 frames, i.e. the frame is fully unpacked
+ * first (including SNAP header unpacking) and then matched.
+ * @NUM_NL80211_WOWLAN_PKTPAT: number of attributes
+ * @MAX_NL80211_WOWLAN_PKTPAT: max attribute number
+ */
+enum nl80211_wowlan_packet_pattern_attr {
+ __NL80211_WOWLAN_PKTPAT_INVALID,
+ NL80211_WOWLAN_PKTPAT_MASK,
+ NL80211_WOWLAN_PKTPAT_PATTERN,
+
+ NUM_NL80211_WOWLAN_PKTPAT,
+ MAX_NL80211_WOWLAN_PKTPAT = NUM_NL80211_WOWLAN_PKTPAT - 1,
+};
+
+/**
+ * struct nl80211_wowlan_pattern_support - pattern support information
+ * @max_patterns: maximum number of patterns supported
+ * @min_pattern_len: minimum length of each pattern
+ * @max_pattern_len: maximum length of each pattern
+ *
+ * This struct is carried in %NL80211_WOWLAN_TRIG_PKT_PATTERN when
+ * that is part of %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED in the
+ * capability information given by the kernel to userspace.
+ */
+struct nl80211_wowlan_pattern_support {
+ __u32 max_patterns;
+ __u32 min_pattern_len;
+ __u32 max_pattern_len;
+} __attribute__((packed));
+
+/**
+ * enum nl80211_wowlan_triggers - WoWLAN trigger definitions
+ * @__NL80211_WOWLAN_TRIG_INVALID: invalid number for nested attributes
+ * @NL80211_WOWLAN_TRIG_ANY: wake up on any activity, do not really put
+ * the chip into a special state -- works best with chips that have
+ * support for low-power operation already (flag)
+ * @NL80211_WOWLAN_TRIG_DISCONNECT: wake up on disconnect, the way disconnect
+ * is detected is implementation-specific (flag)
+ * @NL80211_WOWLAN_TRIG_MAGIC_PKT: wake up on magic packet (6x 0xff, followed
+ * by 16 repetitions of MAC addr, anywhere in payload) (flag)
+ * @NL80211_WOWLAN_TRIG_PKT_PATTERN: wake up on the specified packet patterns
+ * which are passed in an array of nested attributes, each nested attribute
+ * defining a with attributes from &struct nl80211_wowlan_trig_pkt_pattern.
+ * Each pattern defines a wakeup packet. The matching is done on the MSDU,
+ * i.e. as though the packet was an 802.3 packet, so the pattern matching
+ * is done after the packet is converted to the MSDU.
+ *
+ * In %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED, it is a binary attribute
+ * carrying a &struct nl80211_wowlan_pattern_support.
+ * @NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED: Not a real trigger, and cannot be
+ * used when setting, used only to indicate that GTK rekeying is supported
+ * by the device (flag)
+ * @NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE: wake up on GTK rekey failure (if
+ * done by the device) (flag)
+ * @NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST: wake up on EAP Identity Request
+ * packet (flag)
+ * @NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE: wake up on 4-way handshake (flag)
+ * @NL80211_WOWLAN_TRIG_RFKILL_RELEASE: wake up when rfkill is released
+ * (on devices that have rfkill in the device) (flag)
+ * @NUM_NL80211_WOWLAN_TRIG: number of wake on wireless triggers
+ * @MAX_NL80211_WOWLAN_TRIG: highest wowlan trigger attribute number
+ */
+enum nl80211_wowlan_triggers {
+ __NL80211_WOWLAN_TRIG_INVALID,
+ NL80211_WOWLAN_TRIG_ANY,
+ NL80211_WOWLAN_TRIG_DISCONNECT,
+ NL80211_WOWLAN_TRIG_MAGIC_PKT,
+ NL80211_WOWLAN_TRIG_PKT_PATTERN,
+ NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED,
+ NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE,
+ NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST,
+ NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE,
+ NL80211_WOWLAN_TRIG_RFKILL_RELEASE,
+
+ /* keep last */
+ NUM_NL80211_WOWLAN_TRIG,
+ MAX_NL80211_WOWLAN_TRIG = NUM_NL80211_WOWLAN_TRIG - 1
+};
+
+/**
+ * enum nl80211_iface_limit_attrs - limit attributes
+ * @NL80211_IFACE_LIMIT_UNSPEC: (reserved)
+ * @NL80211_IFACE_LIMIT_MAX: maximum number of interfaces that
+ * can be chosen from this set of interface types (u32)
+ * @NL80211_IFACE_LIMIT_TYPES: nested attribute containing a
+ * flag attribute for each interface type in this set
+ * @NUM_NL80211_IFACE_LIMIT: number of attributes
+ * @MAX_NL80211_IFACE_LIMIT: highest attribute number
+ */
+enum nl80211_iface_limit_attrs {
+ NL80211_IFACE_LIMIT_UNSPEC,
+ NL80211_IFACE_LIMIT_MAX,
+ NL80211_IFACE_LIMIT_TYPES,
+
+ /* keep last */
+ NUM_NL80211_IFACE_LIMIT,
+ MAX_NL80211_IFACE_LIMIT = NUM_NL80211_IFACE_LIMIT - 1
+};
+
+/**
+ * enum nl80211_if_combination_attrs -- interface combination attributes
+ *
+ * @NL80211_IFACE_COMB_UNSPEC: (reserved)
+ * @NL80211_IFACE_COMB_LIMITS: Nested attributes containing the limits
+ * for given interface types, see &enum nl80211_iface_limit_attrs.
+ * @NL80211_IFACE_COMB_MAXNUM: u32 attribute giving the total number of
+ * interfaces that can be created in this group. This number doesn't
+ * apply to interfaces purely managed in software, which are listed
+ * in a separate attribute %NL80211_ATTR_INTERFACES_SOFTWARE.
+ * @NL80211_IFACE_COMB_STA_AP_BI_MATCH: flag attribute specifying that
+ * beacon intervals within this group must be all the same even for
+ * infrastructure and AP/GO combinations, i.e. the GO(s) must adopt
+ * the infrastructure network's beacon interval.
+ * @NL80211_IFACE_COMB_NUM_CHANNELS: u32 attribute specifying how many
+ * different channels may be used within this group.
+ * @NUM_NL80211_IFACE_COMB: number of attributes
+ * @MAX_NL80211_IFACE_COMB: highest attribute number
+ *
+ * Examples:
+ * limits = [ #{STA} <= 1, #{AP} <= 1 ], matching BI, channels = 1, max = 2
+ * => allows an AP and a STA that must match BIs
+ *
+ * numbers = [ #{AP, P2P-GO} <= 8 ], channels = 1, max = 8
+ * => allows 8 of AP/GO
+ *
+ * numbers = [ #{STA} <= 2 ], channels = 2, max = 2
+ * => allows two STAs on different channels
+ *
+ * numbers = [ #{STA} <= 1, #{P2P-client,P2P-GO} <= 3 ], max = 4
+ * => allows a STA plus three P2P interfaces
+ *
+ * The list of these four possiblities could completely be contained
+ * within the %NL80211_ATTR_INTERFACE_COMBINATIONS attribute to indicate
+ * that any of these groups must match.
+ *
+ * "Combinations" of just a single interface will not be listed here,
+ * a single interface of any valid interface type is assumed to always
+ * be possible by itself. This means that implicitly, for each valid
+ * interface type, the following group always exists:
+ * numbers = [ #{<type>} <= 1 ], channels = 1, max = 1
+ */
+enum nl80211_if_combination_attrs {
+ NL80211_IFACE_COMB_UNSPEC,
+ NL80211_IFACE_COMB_LIMITS,
+ NL80211_IFACE_COMB_MAXNUM,
+ NL80211_IFACE_COMB_STA_AP_BI_MATCH,
+ NL80211_IFACE_COMB_NUM_CHANNELS,
+
+ /* keep last */
+ NUM_NL80211_IFACE_COMB,
+ MAX_NL80211_IFACE_COMB = NUM_NL80211_IFACE_COMB - 1
+};
+
+
+/**
+ * enum nl80211_plink_state - state of a mesh peer link finite state machine
+ *
+ * @NL80211_PLINK_LISTEN: initial state, considered the implicit
+ * state of non existant mesh peer links
+ * @NL80211_PLINK_OPN_SNT: mesh plink open frame has been sent to
+ * this mesh peer
+ * @NL80211_PLINK_OPN_RCVD: mesh plink open frame has been received
+ * from this mesh peer
+ * @NL80211_PLINK_CNF_RCVD: mesh plink confirm frame has been
+ * received from this mesh peer
+ * @NL80211_PLINK_ESTAB: mesh peer link is established
+ * @NL80211_PLINK_HOLDING: mesh peer link is being closed or cancelled
+ * @NL80211_PLINK_BLOCKED: all frames transmitted from this mesh
+ * plink are discarded
+ * @NUM_NL80211_PLINK_STATES: number of peer link states
+ * @MAX_NL80211_PLINK_STATES: highest numerical value of plink states
+ */
+enum nl80211_plink_state {
+ NL80211_PLINK_LISTEN,
+ NL80211_PLINK_OPN_SNT,
+ NL80211_PLINK_OPN_RCVD,
+ NL80211_PLINK_CNF_RCVD,
+ NL80211_PLINK_ESTAB,
+ NL80211_PLINK_HOLDING,
+ NL80211_PLINK_BLOCKED,
+
+ /* keep last */
+ NUM_NL80211_PLINK_STATES,
+ MAX_NL80211_PLINK_STATES = NUM_NL80211_PLINK_STATES - 1
+};
+
+#define NL80211_KCK_LEN 16
+#define NL80211_KEK_LEN 16
+#define NL80211_REPLAY_CTR_LEN 8
+
+/**
+ * enum nl80211_rekey_data - attributes for GTK rekey offload
+ * @__NL80211_REKEY_DATA_INVALID: invalid number for nested attributes
+ * @NL80211_REKEY_DATA_KEK: key encryption key (binary)
+ * @NL80211_REKEY_DATA_KCK: key confirmation key (binary)
+ * @NL80211_REKEY_DATA_REPLAY_CTR: replay counter (binary)
+ * @NUM_NL80211_REKEY_DATA: number of rekey attributes (internal)
+ * @MAX_NL80211_REKEY_DATA: highest rekey attribute (internal)
+ */
+enum nl80211_rekey_data {
+ __NL80211_REKEY_DATA_INVALID,
+ NL80211_REKEY_DATA_KEK,
+ NL80211_REKEY_DATA_KCK,
+ NL80211_REKEY_DATA_REPLAY_CTR,
+
+ /* keep last */
+ NUM_NL80211_REKEY_DATA,
+ MAX_NL80211_REKEY_DATA = NUM_NL80211_REKEY_DATA - 1
+};
+
+/**
+ * enum nl80211_hidden_ssid - values for %NL80211_ATTR_HIDDEN_SSID
+ * @NL80211_HIDDEN_SSID_NOT_IN_USE: do not hide SSID (i.e., broadcast it in
+ * Beacon frames)
+ * @NL80211_HIDDEN_SSID_ZERO_LEN: hide SSID by using zero-length SSID element
+ * in Beacon frames
+ * @NL80211_HIDDEN_SSID_ZERO_CONTENTS: hide SSID by using correct length of SSID
+ * element in Beacon frames but zero out each byte in the SSID
+ */
+enum nl80211_hidden_ssid {
+ NL80211_HIDDEN_SSID_NOT_IN_USE,
+ NL80211_HIDDEN_SSID_ZERO_LEN,
+ NL80211_HIDDEN_SSID_ZERO_CONTENTS
+};
+
+/**
+ * enum nl80211_sta_wme_attr - station WME attributes
+ * @__NL80211_STA_WME_INVALID: invalid number for nested attribute
+ * @NL80211_STA_WME_UAPSD_QUEUES: bitmap of uapsd queues. the format
+ * is the same as the AC bitmap in the QoS info field.
+ * @NL80211_STA_WME_MAX_SP: max service period. the format is the same
+ * as the MAX_SP field in the QoS info field (but already shifted down).
+ * @__NL80211_STA_WME_AFTER_LAST: internal
+ * @NL80211_STA_WME_MAX: highest station WME attribute
+ */
+enum nl80211_sta_wme_attr {
+ __NL80211_STA_WME_INVALID,
+ NL80211_STA_WME_UAPSD_QUEUES,
+ NL80211_STA_WME_MAX_SP,
+
+ /* keep last */
+ __NL80211_STA_WME_AFTER_LAST,
+ NL80211_STA_WME_MAX = __NL80211_STA_WME_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_pmksa_candidate_attr - attributes for PMKSA caching candidates
+ * @__NL80211_PMKSA_CANDIDATE_INVALID: invalid number for nested attributes
+ * @NL80211_PMKSA_CANDIDATE_INDEX: candidate index (u32; the smaller, the higher
+ * priority)
+ * @NL80211_PMKSA_CANDIDATE_BSSID: candidate BSSID (6 octets)
+ * @NL80211_PMKSA_CANDIDATE_PREAUTH: RSN pre-authentication supported (flag)
+ * @NUM_NL80211_PMKSA_CANDIDATE: number of PMKSA caching candidate attributes
+ * (internal)
+ * @MAX_NL80211_PMKSA_CANDIDATE: highest PMKSA caching candidate attribute
+ * (internal)
+ */
+enum nl80211_pmksa_candidate_attr {
+ __NL80211_PMKSA_CANDIDATE_INVALID,
+ NL80211_PMKSA_CANDIDATE_INDEX,
+ NL80211_PMKSA_CANDIDATE_BSSID,
+ NL80211_PMKSA_CANDIDATE_PREAUTH,
+
+ /* keep last */
+ NUM_NL80211_PMKSA_CANDIDATE,
+ MAX_NL80211_PMKSA_CANDIDATE = NUM_NL80211_PMKSA_CANDIDATE - 1
+};
+
+/**
+ * enum nl80211_tdls_operation - values for %NL80211_ATTR_TDLS_OPERATION
+ * @NL80211_TDLS_DISCOVERY_REQ: Send a TDLS discovery request
+ * @NL80211_TDLS_SETUP: Setup TDLS link
+ * @NL80211_TDLS_TEARDOWN: Teardown a TDLS link which is already established
+ * @NL80211_TDLS_ENABLE_LINK: Enable TDLS link
+ * @NL80211_TDLS_DISABLE_LINK: Disable TDLS link
+ */
+enum nl80211_tdls_operation {
+ NL80211_TDLS_DISCOVERY_REQ,
+ NL80211_TDLS_SETUP,
+ NL80211_TDLS_TEARDOWN,
+ NL80211_TDLS_ENABLE_LINK,
+ NL80211_TDLS_DISABLE_LINK,
+};
+
+/*
+ * enum nl80211_ap_sme_features - device-integrated AP features
+ * Reserved for future use, no bits are defined in
+ * NL80211_ATTR_DEVICE_AP_SME yet.
+enum nl80211_ap_sme_features {
+};
+ */
+
+/**
+ * enum nl80211_feature_flags - device/driver features
+ * @NL80211_FEATURE_SK_TX_STATUS: This driver supports reflecting back
+ * TX status to the socket error queue when requested with the
+ * socket option.
+ * @NL80211_FEATURE_HT_IBSS: This driver supports IBSS with HT datarates.
+ * @NL80211_FEATURE_INACTIVITY_TIMER: This driver takes care of freeing up
+ * the connected inactive stations in AP mode.
+ */
+enum nl80211_feature_flags {
+ NL80211_FEATURE_SK_TX_STATUS = 1 << 0,
+ NL80211_FEATURE_HT_IBSS = 1 << 1,
+ NL80211_FEATURE_INACTIVITY_TIMER = 1 << 2,
+};
+
+/**
+ * enum nl80211_probe_resp_offload_support_attr - optional supported
+ * protocols for probe-response offloading by the driver/FW.
+ * To be used with the %NL80211_ATTR_PROBE_RESP_OFFLOAD attribute.
+ * Each enum value represents a bit in the bitmap of supported
+ * protocols. Typically a subset of probe-requests belonging to a
+ * supported protocol will be excluded from offload and uploaded
+ * to the host.
+ *
+ * @NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS: Support for WPS ver. 1
+ * @NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2: Support for WPS ver. 2
+ * @NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P: Support for P2P
+ * @NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U: Support for 802.11u
+ */
+enum nl80211_probe_resp_offload_support_attr {
+ NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS = 1<<0,
+ NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 = 1<<1,
+ NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P = 1<<2,
+ NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U = 1<<3,
+};
+
#endif /* __LINUX_NL80211_H */
--- /dev/null
+/*
+ * Linux rfkill helper functions for driver wrappers
+ * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#include <fcntl.h>
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "rfkill.h"
+
+#define RFKILL_EVENT_SIZE_V1 8
+
+struct rfkill_event {
+ u32 idx;
+ u8 type;
+ u8 op;
+ u8 soft;
+ u8 hard;
+} STRUCT_PACKED;
+
+enum rfkill_operation {
+ RFKILL_OP_ADD = 0,
+ RFKILL_OP_DEL,
+ RFKILL_OP_CHANGE,
+ RFKILL_OP_CHANGE_ALL,
+};
+
+enum rfkill_type {
+ RFKILL_TYPE_ALL = 0,
+ RFKILL_TYPE_WLAN,
+ RFKILL_TYPE_BLUETOOTH,
+ RFKILL_TYPE_UWB,
+ RFKILL_TYPE_WIMAX,
+ RFKILL_TYPE_WWAN,
+ RFKILL_TYPE_GPS,
+ RFKILL_TYPE_FM,
+ NUM_RFKILL_TYPES,
+};
+
+
+struct rfkill_data {
+ struct rfkill_config *cfg;
+ int fd;
+ int blocked;
+};
+
+
+static void rfkill_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ struct rfkill_data *rfkill = eloop_ctx;
+ struct rfkill_event event;
+ ssize_t len;
+ int new_blocked;
+
+ len = read(rfkill->fd, &event, sizeof(event));
+ if (len < 0) {
+ wpa_printf(MSG_ERROR, "rfkill: Event read failed: %s",
+ strerror(errno));
+ return;
+ }
+ if (len != RFKILL_EVENT_SIZE_V1) {
+ wpa_printf(MSG_DEBUG, "rfkill: Unexpected event size "
+ "%d (expected %d)",
+ (int) len, RFKILL_EVENT_SIZE_V1);
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "rfkill: event: idx=%u type=%d "
+ "op=%u soft=%u hard=%u",
+ event.idx, event.type, event.op, event.soft,
+ event.hard);
+ if (event.op != RFKILL_OP_CHANGE || event.type != RFKILL_TYPE_WLAN)
+ return;
+
+ if (event.hard) {
+ wpa_printf(MSG_INFO, "rfkill: WLAN hard blocked");
+ new_blocked = 1;
+ } else if (event.soft) {
+ wpa_printf(MSG_INFO, "rfkill: WLAN soft blocked");
+ new_blocked = 1;
+ } else {
+ wpa_printf(MSG_INFO, "rfkill: WLAN unblocked");
+ new_blocked = 0;
+ }
+
+ if (new_blocked != rfkill->blocked) {
+ rfkill->blocked = new_blocked;
+ if (new_blocked)
+ rfkill->cfg->blocked_cb(rfkill->cfg->ctx);
+ else
+ rfkill->cfg->unblocked_cb(rfkill->cfg->ctx);
+ }
+}
+
+
+struct rfkill_data * rfkill_init(struct rfkill_config *cfg)
+{
+ struct rfkill_data *rfkill;
+ struct rfkill_event event;
+ ssize_t len;
+
+ rfkill = os_zalloc(sizeof(*rfkill));
+ if (rfkill == NULL)
+ return NULL;
+
+ rfkill->cfg = cfg;
+ rfkill->fd = open("/dev/rfkill", O_RDONLY);
+ if (rfkill->fd < 0) {
+ wpa_printf(MSG_INFO, "rfkill: Cannot open RFKILL control "
+ "device");
+ goto fail;
+ }
+
+ if (fcntl(rfkill->fd, F_SETFL, O_NONBLOCK) < 0) {
+ wpa_printf(MSG_ERROR, "rfkill: Cannot set non-blocking mode: "
+ "%s", strerror(errno));
+ goto fail2;
+ }
+
+ for (;;) {
+ len = read(rfkill->fd, &event, sizeof(event));
+ if (len < 0) {
+ if (errno == EAGAIN)
+ break; /* No more entries */
+ wpa_printf(MSG_ERROR, "rfkill: Event read failed: %s",
+ strerror(errno));
+ break;
+ }
+ if (len != RFKILL_EVENT_SIZE_V1) {
+ wpa_printf(MSG_DEBUG, "rfkill: Unexpected event size "
+ "%d (expected %d)",
+ (int) len, RFKILL_EVENT_SIZE_V1);
+ continue;
+ }
+ wpa_printf(MSG_DEBUG, "rfkill: initial event: idx=%u type=%d "
+ "op=%u soft=%u hard=%u",
+ event.idx, event.type, event.op, event.soft,
+ event.hard);
+ if (event.op != RFKILL_OP_ADD ||
+ event.type != RFKILL_TYPE_WLAN)
+ continue;
+ if (event.hard) {
+ wpa_printf(MSG_INFO, "rfkill: WLAN hard blocked");
+ rfkill->blocked = 1;
+ } else if (event.soft) {
+ wpa_printf(MSG_INFO, "rfkill: WLAN soft blocked");
+ rfkill->blocked = 1;
+ }
+ }
+
+ eloop_register_read_sock(rfkill->fd, rfkill_receive, rfkill, NULL);
+
+ return rfkill;
+
+fail2:
+ close(rfkill->fd);
+fail:
+ os_free(rfkill);
+ return NULL;
+}
+
+
+void rfkill_deinit(struct rfkill_data *rfkill)
+{
+ if (rfkill == NULL)
+ return;
+
+ if (rfkill->fd >= 0) {
+ eloop_unregister_read_sock(rfkill->fd);
+ close(rfkill->fd);
+ }
+
+ os_free(rfkill->cfg);
+ os_free(rfkill);
+}
+
+
+int rfkill_is_blocked(struct rfkill_data *rfkill)
+{
+ if (rfkill == NULL)
+ return 0;
+
+ return rfkill->blocked;
+}
--- /dev/null
+/*
+ * Linux rfkill helper functions for driver wrappers
+ * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef RFKILL_H
+#define RFKILL_H
+
+struct rfkill_data;
+
+struct rfkill_config {
+ void *ctx;
+ char ifname[IFNAMSIZ];
+ void (*blocked_cb)(void *ctx);
+ void (*unblocked_cb)(void *ctx);
+};
+
+struct rfkill_data * rfkill_init(struct rfkill_config *cfg);
+void rfkill_deinit(struct rfkill_data *rfkill);
+int rfkill_is_blocked(struct rfkill_data *rfkill);
+
+#endif /* RFKILL_H */
-/* This is based on Linux Wireless Extensions header file from WIRELESS_EXT 18.
+/* This is based on Linux Wireless Extensions header file from WIRELESS_EXT 22.
* I have just removed kernel related headers and added some typedefs etc. to
* make this easier to include into user space programs.
* Jouni Malinen, 2005-03-12.
/*
* This file define a set of standard wireless extensions
*
- * Version : 19 18.3.05
+ * Version : 22 16.3.07
*
* Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com>
- * Copyright (c) 1997-2005 Jean Tourrilhes, All Rights Reserved.
+ * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved.
*/
#ifndef _LINUX_WIRELESS_H
/***************************** INCLUDES *****************************/
/* jkm - replaced linux headers with C library headers, added typedefs */
-#if 0
-/* To minimise problems in user space, I might remove those headers
- * at some point. Jean II */
-#include <linux/types.h> /* for "caddr_t" et al */
+#ifdef ANDROID
+#include <linux/types.h> /* for __u* and __s* typedefs */
#include <linux/socket.h> /* for "struct sockaddr" et al */
#include <linux/if.h> /* for IFNAMSIZ and co... */
-#else
+#else /* ANDROID */
#include <sys/types.h>
#include <net/if.h>
typedef __uint32_t __u32;
#ifndef __user
#define __user
#endif /* __user */
-#endif
+#endif /* ANDROID */
/***************************** VERSION *****************************/
/*
* (there is some stuff that will be added in the future...)
* I just plan to increment with each new version.
*/
-#define WIRELESS_EXT 19
+#define WIRELESS_EXT 22
/*
* Changes :
* - Add IW_QUAL_ALL_UPDATED and IW_QUAL_ALL_INVALID macros
* - Add explicit flag to tell stats are in dBm : IW_QUAL_DBM
* - Add IW_IOCTL_IDX() and IW_EVENT_IDX() macros
+ *
+ * V19 to V20
+ * ----------
+ * - RtNetlink requests support (SET/GET)
+ *
+ * V20 to V21
+ * ----------
+ * - Remove (struct net_device *)->get_wireless_stats()
+ * - Change length in ESSID and NICK to strlen() instead of strlen()+1
+ * - Add IW_RETRY_SHORT/IW_RETRY_LONG retry modifiers
+ * - Power/Retry relative values no longer * 100000
+ * - Add explicit flag to tell stats are in 802.11k RCPI : IW_QUAL_RCPI
+ *
+ * V21 to V22
+ * ----------
+ * - Prevent leaking of kernel space in stream on 64 bits.
*/
/**************************** CONSTANTS ****************************/
* separate range because of collisions with other tools such as
* 'mii-tool'.
* We now have 32 commands, so a bit more space ;-).
- * Also, all 'odd' commands are only usable by root and don't return the
+ * Also, all 'even' commands are only usable by root and don't return the
* content of ifr/iwr to user (but you are not obliged to use the set/get
* convention, just use every other two command). More details in iwpriv.c.
* And I repeat : you are not forced to use them with iwpriv, but you
#define SIOCIWFIRST 0x8B00
#define SIOCIWLAST SIOCIWLASTPRIV /* 0x8BFF */
#define IW_IOCTL_IDX(cmd) ((cmd) - SIOCIWFIRST)
+#define IW_HANDLER(id, func) \
+ [IW_IOCTL_IDX(id)] = func
-/* Even : get (world access), odd : set (root access) */
+/* Odd : get (world access), even : set (root access) */
#define IW_IS_SET(cmd) (!((cmd) & 0x1))
#define IW_IS_GET(cmd) ((cmd) & 0x1)
#define IW_MODE_REPEAT 4 /* Wireless Repeater (forwarder) */
#define IW_MODE_SECOND 5 /* Secondary master/repeater (backup) */
#define IW_MODE_MONITOR 6 /* Passive monitor (listen only) */
+#define IW_MODE_MESH 7 /* Mesh (IEEE 802.11s) network */
/* Statistics flags (bitmask in updated) */
#define IW_QUAL_QUAL_UPDATED 0x01 /* Value was updated since last read */
#define IW_QUAL_QUAL_INVALID 0x10 /* Driver doesn't provide value */
#define IW_QUAL_LEVEL_INVALID 0x20
#define IW_QUAL_NOISE_INVALID 0x40
+#define IW_QUAL_RCPI 0x80 /* Level + Noise are 802.11k RCPI */
#define IW_QUAL_ALL_INVALID 0x70
/* Frequency flags */
#define IW_RETRY_TYPE 0xF000 /* Type of parameter */
#define IW_RETRY_LIMIT 0x1000 /* Maximum number of retries*/
#define IW_RETRY_LIFETIME 0x2000 /* Maximum duration of retries in us */
-#define IW_RETRY_MODIFIER 0x000F /* Modify a parameter */
+#define IW_RETRY_MODIFIER 0x00FF /* Modify a parameter */
#define IW_RETRY_MIN 0x0001 /* Value is a minimum */
#define IW_RETRY_MAX 0x0002 /* Value is a maximum */
#define IW_RETRY_RELATIVE 0x0004 /* Value is not in seconds/ms/us */
+#define IW_RETRY_SHORT 0x0010 /* Value is for short packets */
+#define IW_RETRY_LONG 0x0020 /* Value is for long packets */
/* Scanning request flags */
#define IW_SCAN_DEFAULT 0x0000 /* Default scan of the driver */
/* Maximum size of returned data */
#define IW_SCAN_MAX_DATA 4096 /* In bytes */
+/* Scan capability flags - in (struct iw_range *)->scan_capa */
+#define IW_SCAN_CAPA_NONE 0x00
+#define IW_SCAN_CAPA_ESSID 0x01
+#define IW_SCAN_CAPA_BSSID 0x02
+#define IW_SCAN_CAPA_CHANNEL 0x04
+#define IW_SCAN_CAPA_MODE 0x08
+#define IW_SCAN_CAPA_RATE 0x10
+#define IW_SCAN_CAPA_TYPE 0x20
+#define IW_SCAN_CAPA_TIME 0x40
+
/* Max number of char in custom event - use multiple of them if needed */
#define IW_CUSTOM_MAX 256 /* In bytes */
/* MLME requests (SIOCSIWMLME / struct iw_mlme) */
#define IW_MLME_DEAUTH 0
#define IW_MLME_DISASSOC 1
+#define IW_MLME_AUTH 2
+#define IW_MLME_ASSOC 3
/* SIOCSIWAUTH/SIOCGIWAUTH struct iw_param flags */
#define IW_AUTH_INDEX 0x0FFF
#define IW_AUTH_WPA_VERSION_WPA 0x00000002
#define IW_AUTH_WPA_VERSION_WPA2 0x00000004
-/* IW_AUTH_PAIRWISE_CIPHER and IW_AUTH_GROUP_CIPHER values (bit field) */
+/* IW_AUTH_PAIRWISE_CIPHER, IW_AUTH_GROUP_CIPHER, and IW_AUTH_CIPHER_GROUP_MGMT
+ * values (bit field) */
#define IW_AUTH_CIPHER_NONE 0x00000001
#define IW_AUTH_CIPHER_WEP40 0x00000002
#define IW_AUTH_CIPHER_TKIP 0x00000004
#define IW_AUTH_CIPHER_CCMP 0x00000008
#define IW_AUTH_CIPHER_WEP104 0x00000010
+#define IW_AUTH_CIPHER_AES_CMAC 0x00000020
/* IW_AUTH_KEY_MGMT values (bit field) */
#define IW_AUTH_KEY_MGMT_802_1X 1
* 32 bit bitmasks. Note : 32 bits = 0x20 = 2^5. */
#define IW_EVENT_CAPA_BASE(cmd) ((cmd >= SIOCIWFIRSTPRIV) ? \
(cmd - SIOCIWFIRSTPRIV + 0x60) : \
- (cmd - SIOCSIWCOMMIT))
+ (cmd - SIOCIWFIRST))
#define IW_EVENT_CAPA_INDEX(cmd) (IW_EVENT_CAPA_BASE(cmd) >> 5)
#define IW_EVENT_CAPA_MASK(cmd) (1 << (IW_EVENT_CAPA_BASE(cmd) & 0x1F))
/* Event capability constants - event autogenerated by the kernel
__u16 flags; /* Optional params */
};
+#ifdef __KERNEL__
+#ifdef CONFIG_COMPAT
+
+#include <linux/compat.h>
+
+struct compat_iw_point {
+ compat_caddr_t pointer;
+ __u16 length;
+ __u16 flags;
+};
+#endif
+#endif
+
/*
* A frequency
* For numbers lower than 10^9, we encode the number in 'm' and
__u16 old_num_channels;
__u8 old_num_frequency;
+ /* Scan capabilities */
+ __u8 scan_capa; /* IW_SCAN_CAPA_* bit field */
+
/* Wireless event capability bitmasks */
__u32 event_capa[6];
/* Note : this frequency list doesn't need to fit channel numbers,
* because each entry contain its channel index */
- __u32 enc_capa; /* IW_ENC_CAPA_* bit field */
+ __u32 enc_capa; /* IW_ENC_CAPA_* bit field */
};
/*
*/
struct iw_event
{
- __u16 len; /* Real lenght of this stuff */
+ __u16 len; /* Real length of this stuff */
__u16 cmd; /* Wireless IOCTL */
union iwreq_data u; /* IOCTL fixed payload */
};
#define IW_EV_POINT_LEN (IW_EV_LCP_LEN + sizeof(struct iw_point) - \
IW_EV_POINT_OFF)
+#ifdef __KERNEL__
+#ifdef CONFIG_COMPAT
+struct __compat_iw_event {
+ __u16 len; /* Real length of this stuff */
+ __u16 cmd; /* Wireless IOCTL */
+ compat_caddr_t pointer;
+};
+#define IW_EV_COMPAT_LCP_LEN offsetof(struct __compat_iw_event, pointer)
+#define IW_EV_COMPAT_POINT_OFF offsetof(struct compat_iw_point, length)
+
+/* Size of the various events for compat */
+#define IW_EV_COMPAT_CHAR_LEN (IW_EV_COMPAT_LCP_LEN + IFNAMSIZ)
+#define IW_EV_COMPAT_UINT_LEN (IW_EV_COMPAT_LCP_LEN + sizeof(__u32))
+#define IW_EV_COMPAT_FREQ_LEN (IW_EV_COMPAT_LCP_LEN + sizeof(struct iw_freq))
+#define IW_EV_COMPAT_PARAM_LEN (IW_EV_COMPAT_LCP_LEN + sizeof(struct iw_param))
+#define IW_EV_COMPAT_ADDR_LEN (IW_EV_COMPAT_LCP_LEN + sizeof(struct sockaddr))
+#define IW_EV_COMPAT_QUAL_LEN (IW_EV_COMPAT_LCP_LEN + sizeof(struct iw_quality))
+#define IW_EV_COMPAT_POINT_LEN \
+ (IW_EV_COMPAT_LCP_LEN + sizeof(struct compat_iw_point) - \
+ IW_EV_COMPAT_POINT_OFF)
+#endif
+#endif
+
+/* Size of the Event prefix when packed in stream */
+#define IW_EV_LCP_PK_LEN (4)
+/* Size of the various events when packed in stream */
+#define IW_EV_CHAR_PK_LEN (IW_EV_LCP_PK_LEN + IFNAMSIZ)
+#define IW_EV_UINT_PK_LEN (IW_EV_LCP_PK_LEN + sizeof(__u32))
+#define IW_EV_FREQ_PK_LEN (IW_EV_LCP_PK_LEN + sizeof(struct iw_freq))
+#define IW_EV_PARAM_PK_LEN (IW_EV_LCP_PK_LEN + sizeof(struct iw_param))
+#define IW_EV_ADDR_PK_LEN (IW_EV_LCP_PK_LEN + sizeof(struct sockaddr))
+#define IW_EV_QUAL_PK_LEN (IW_EV_LCP_PK_LEN + sizeof(struct iw_quality))
+#define IW_EV_POINT_PK_LEN (IW_EV_LCP_PK_LEN + 4)
+
#endif /* _LINUX_WIRELESS_H */
EAP_TYPE_IKEV2 = 49 /* RFC 5106 */,
EAP_TYPE_AKA_PRIME = 50 /* draft-arkko-eap-aka-kdf-10.txt */,
EAP_TYPE_GPSK = 51 /* RFC 5433 */,
+ EAP_TYPE_PWD = 52 /* RFC 5931 */,
EAP_TYPE_EXPANDED = 254 /* RFC 3748 */
} EapType;
/*
* EAP-PEAP common routines
- * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008-2011, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
#include "crypto/sha1.h"
#include "eap_peap_common.h"
-void peap_prfplus(int version, const u8 *key, size_t key_len,
- const char *label, const u8 *seed, size_t seed_len,
- u8 *buf, size_t buf_len)
+int peap_prfplus(int version, const u8 *key, size_t key_len,
+ const char *label, const u8 *seed, size_t seed_len,
+ u8 *buf, size_t buf_len)
{
unsigned char counter = 0;
size_t pos, plen;
while (pos < buf_len) {
counter++;
plen = buf_len - pos;
- hmac_sha1_vector(key, key_len, 5, addr, len, hash);
+ if (hmac_sha1_vector(key, key_len, 5, addr, len, hash) < 0)
+ return -1;
if (plen >= SHA1_MAC_LEN) {
os_memcpy(&buf[pos], hash, SHA1_MAC_LEN);
pos += SHA1_MAC_LEN;
}
len[0] = SHA1_MAC_LEN;
}
+
+ return 0;
}
/*
* EAP-PEAP common routines
- * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008-2011, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
#ifndef EAP_PEAP_COMMON_H
#define EAP_PEAP_COMMON_H
-void peap_prfplus(int version, const u8 *key, size_t key_len,
- const char *label, const u8 *seed, size_t seed_len,
- u8 *buf, size_t buf_len);
+int peap_prfplus(int version, const u8 *key, size_t key_len,
+ const char *label, const u8 *seed, size_t seed_len,
+ u8 *buf, size_t buf_len);
#endif /* EAP_PEAP_COMMON_H */
--- /dev/null
+/*
+ * EAP server/peer: EAP-pwd shared routines
+ * Copyright (c) 2010, Dan Harkins <dharkins@lounge.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the BSD license.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#include "common.h"
+#include "eap_defs.h"
+#include "eap_pwd_common.h"
+
+/* The random function H(x) = HMAC-SHA256(0^32, x) */
+void H_Init(HMAC_CTX *ctx)
+{
+ u8 allzero[SHA256_DIGEST_LENGTH];
+
+ os_memset(allzero, 0, SHA256_DIGEST_LENGTH);
+ HMAC_Init(ctx, allzero, SHA256_DIGEST_LENGTH, EVP_sha256());
+}
+
+
+void H_Update(HMAC_CTX *ctx, const u8 *data, int len)
+{
+ HMAC_Update(ctx, data, len);
+}
+
+
+void H_Final(HMAC_CTX *ctx, u8 *digest)
+{
+ unsigned int mdlen = SHA256_DIGEST_LENGTH;
+
+ HMAC_Final(ctx, digest, &mdlen);
+ HMAC_CTX_cleanup(ctx);
+}
+
+
+/* a counter-based KDF based on NIST SP800-108 */
+void eap_pwd_kdf(u8 *key, int keylen, u8 *label, int labellen,
+ u8 *result, int resultbitlen)
+{
+ HMAC_CTX hctx;
+ unsigned char digest[SHA256_DIGEST_LENGTH];
+ u16 i, ctr, L;
+ int resultbytelen, len = 0;
+ unsigned int mdlen = SHA256_DIGEST_LENGTH;
+ unsigned char mask = 0xff;
+
+ resultbytelen = (resultbitlen + 7)/8;
+ ctr = 0;
+ L = htons(resultbitlen);
+ while (len < resultbytelen) {
+ ctr++; i = htons(ctr);
+ HMAC_Init(&hctx, key, keylen, EVP_sha256());
+ if (ctr > 1)
+ HMAC_Update(&hctx, digest, mdlen);
+ HMAC_Update(&hctx, (u8 *) &i, sizeof(u16));
+ HMAC_Update(&hctx, label, labellen);
+ HMAC_Update(&hctx, (u8 *) &L, sizeof(u16));
+ HMAC_Final(&hctx, digest, &mdlen);
+ if ((len + (int) mdlen) > resultbytelen)
+ os_memcpy(result + len, digest, resultbytelen - len);
+ else
+ os_memcpy(result + len, digest, mdlen);
+ len += mdlen;
+ HMAC_CTX_cleanup(&hctx);
+ }
+
+ /* since we're expanding to a bit length, mask off the excess */
+ if (resultbitlen % 8) {
+ mask <<= (8 - (resultbitlen % 8));
+ result[resultbytelen - 1] &= mask;
+ }
+}
+
+
+/*
+ * compute a "random" secret point on an elliptic curve based
+ * on the password and identities.
+ */
+int compute_password_element(EAP_PWD_group *grp, u16 num,
+ u8 *password, int password_len,
+ u8 *id_server, int id_server_len,
+ u8 *id_peer, int id_peer_len, u8 *token)
+{
+ BIGNUM *x_candidate = NULL, *rnd = NULL, *cofactor = NULL;
+ HMAC_CTX ctx;
+ unsigned char pwe_digest[SHA256_DIGEST_LENGTH], *prfbuf = NULL, ctr;
+ int nid, is_odd, primebitlen, primebytelen, ret = 0;
+
+ switch (num) { /* from IANA registry for IKE D-H groups */
+ case 19:
+ nid = NID_X9_62_prime256v1;
+ break;
+ case 20:
+ nid = NID_secp384r1;
+ break;
+ case 21:
+ nid = NID_secp521r1;
+ break;
+ case 25:
+ nid = NID_X9_62_prime192v1;
+ break;
+ case 26:
+ nid = NID_secp224r1;
+ break;
+ default:
+ wpa_printf(MSG_INFO, "EAP-pwd: unsupported group %d", num);
+ return -1;
+ }
+
+ grp->pwe = NULL;
+ grp->order = NULL;
+ grp->prime = NULL;
+
+ if ((grp->group = EC_GROUP_new_by_curve_name(nid)) == NULL) {
+ wpa_printf(MSG_INFO, "EAP-pwd: unable to create EC_GROUP");
+ goto fail;
+ }
+
+ if (((rnd = BN_new()) == NULL) ||
+ ((cofactor = BN_new()) == NULL) ||
+ ((grp->pwe = EC_POINT_new(grp->group)) == NULL) ||
+ ((grp->order = BN_new()) == NULL) ||
+ ((grp->prime = BN_new()) == NULL) ||
+ ((x_candidate = BN_new()) == NULL)) {
+ wpa_printf(MSG_INFO, "EAP-pwd: unable to create bignums");
+ goto fail;
+ }
+
+ if (!EC_GROUP_get_curve_GFp(grp->group, grp->prime, NULL, NULL, NULL))
+ {
+ wpa_printf(MSG_INFO, "EAP-pwd: unable to get prime for GFp "
+ "curve");
+ goto fail;
+ }
+ if (!EC_GROUP_get_order(grp->group, grp->order, NULL)) {
+ wpa_printf(MSG_INFO, "EAP-pwd: unable to get order for curve");
+ goto fail;
+ }
+ if (!EC_GROUP_get_cofactor(grp->group, cofactor, NULL)) {
+ wpa_printf(MSG_INFO, "EAP-pwd: unable to get cofactor for "
+ "curve");
+ goto fail;
+ }
+ primebitlen = BN_num_bits(grp->prime);
+ primebytelen = BN_num_bytes(grp->prime);
+ if ((prfbuf = os_malloc(primebytelen)) == NULL) {
+ wpa_printf(MSG_INFO, "EAP-pwd: unable to malloc space for prf "
+ "buffer");
+ goto fail;
+ }
+ os_memset(prfbuf, 0, primebytelen);
+ ctr = 0;
+ while (1) {
+ if (ctr > 10) {
+ wpa_printf(MSG_INFO, "EAP-pwd: unable to find random "
+ "point on curve for group %d, something's "
+ "fishy", num);
+ goto fail;
+ }
+ ctr++;
+
+ /*
+ * compute counter-mode password value and stretch to prime
+ * pwd-seed = H(token | peer-id | server-id | password |
+ * counter)
+ */
+ H_Init(&ctx);
+ H_Update(&ctx, token, sizeof(u32));
+ H_Update(&ctx, id_peer, id_peer_len);
+ H_Update(&ctx, id_server, id_server_len);
+ H_Update(&ctx, password, password_len);
+ H_Update(&ctx, &ctr, sizeof(ctr));
+ H_Final(&ctx, pwe_digest);
+
+ BN_bin2bn(pwe_digest, SHA256_DIGEST_LENGTH, rnd);
+
+ eap_pwd_kdf(pwe_digest, SHA256_DIGEST_LENGTH,
+ (unsigned char *) "EAP-pwd Hunting And Pecking",
+ os_strlen("EAP-pwd Hunting And Pecking"),
+ prfbuf, primebitlen);
+
+ BN_bin2bn(prfbuf, primebytelen, x_candidate);
+
+ /*
+ * eap_pwd_kdf() returns a string of bits 0..primebitlen but
+ * BN_bin2bn will treat that string of bits as a big endian
+ * number. If the primebitlen is not an even multiple of 8
+ * then excessive bits-- those _after_ primebitlen-- so now
+ * we have to shift right the amount we masked off.
+ */
+ if (primebitlen % 8)
+ BN_rshift(x_candidate, x_candidate,
+ (8 - (primebitlen % 8)));
+
+ if (BN_ucmp(x_candidate, grp->prime) >= 0)
+ continue;
+
+ wpa_hexdump(MSG_DEBUG, "EAP-pwd: x_candidate",
+ prfbuf, primebytelen);
+
+ /*
+ * need to unambiguously identify the solution, if there is
+ * one...
+ */
+ if (BN_is_odd(rnd))
+ is_odd = 1;
+ else
+ is_odd = 0;
+
+ /*
+ * solve the quadratic equation, if it's not solvable then we
+ * don't have a point
+ */
+ if (!EC_POINT_set_compressed_coordinates_GFp(grp->group,
+ grp->pwe,
+ x_candidate,
+ is_odd, NULL))
+ continue;
+ /*
+ * If there's a solution to the equation then the point must be
+ * on the curve so why check again explicitly? OpenSSL code
+ * says this is required by X9.62. We're not X9.62 but it can't
+ * hurt just to be sure.
+ */
+ if (!EC_POINT_is_on_curve(grp->group, grp->pwe, NULL)) {
+ wpa_printf(MSG_INFO, "EAP-pwd: point is not on curve");
+ continue;
+ }
+
+ if (BN_cmp(cofactor, BN_value_one())) {
+ /* make sure the point is not in a small sub-group */
+ if (!EC_POINT_mul(grp->group, grp->pwe, NULL, grp->pwe,
+ cofactor, NULL)) {
+ wpa_printf(MSG_INFO, "EAP-pwd: cannot "
+ "multiply generator by order");
+ continue;
+ }
+ if (EC_POINT_is_at_infinity(grp->group, grp->pwe)) {
+ wpa_printf(MSG_INFO, "EAP-pwd: point is at "
+ "infinity");
+ continue;
+ }
+ }
+ /* if we got here then we have a new generator. */
+ break;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-pwd: found a PWE in %d tries", ctr);
+ grp->group_num = num;
+ if (0) {
+ fail:
+ EC_GROUP_free(grp->group);
+ EC_POINT_free(grp->pwe);
+ BN_free(grp->order);
+ BN_free(grp->prime);
+ os_free(grp);
+ grp = NULL;
+ ret = 1;
+ }
+ /* cleanliness and order.... */
+ BN_free(cofactor);
+ BN_free(x_candidate);
+ BN_free(rnd);
+ os_free(prfbuf);
+
+ return ret;
+}
+
+
+int compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, BIGNUM *k,
+ BIGNUM *peer_scalar, BIGNUM *server_scalar,
+ u8 *commit_peer, u8 *commit_server,
+ u32 *ciphersuite, u8 *msk, u8 *emsk)
+{
+ HMAC_CTX ctx;
+ u8 mk[SHA256_DIGEST_LENGTH], *cruft;
+ u8 session_id[SHA256_DIGEST_LENGTH + 1];
+ u8 msk_emsk[EAP_MSK_LEN + EAP_EMSK_LEN];
+ int offset;
+
+ if ((cruft = os_malloc(BN_num_bytes(grp->prime))) == NULL)
+ return -1;
+
+ /*
+ * first compute the session-id = TypeCode | H(ciphersuite | scal_p |
+ * scal_s)
+ */
+ session_id[0] = EAP_TYPE_PWD;
+ H_Init(&ctx);
+ H_Update(&ctx, (u8 *)ciphersuite, sizeof(u32));
+ offset = BN_num_bytes(grp->order) - BN_num_bytes(peer_scalar);
+ os_memset(cruft, 0, BN_num_bytes(grp->prime));
+ BN_bn2bin(peer_scalar, cruft + offset);
+ H_Update(&ctx, cruft, BN_num_bytes(grp->order));
+ offset = BN_num_bytes(grp->order) - BN_num_bytes(server_scalar);
+ os_memset(cruft, 0, BN_num_bytes(grp->prime));
+ BN_bn2bin(server_scalar, cruft + offset);
+ H_Update(&ctx, cruft, BN_num_bytes(grp->order));
+ H_Final(&ctx, &session_id[1]);
+
+ /* then compute MK = H(k | commit-peer | commit-server) */
+ H_Init(&ctx);
+ offset = BN_num_bytes(grp->prime) - BN_num_bytes(k);
+ os_memset(cruft, 0, BN_num_bytes(grp->prime));
+ BN_bn2bin(k, cruft + offset);
+ H_Update(&ctx, cruft, BN_num_bytes(grp->prime));
+ H_Update(&ctx, commit_peer, SHA256_DIGEST_LENGTH);
+ H_Update(&ctx, commit_server, SHA256_DIGEST_LENGTH);
+ H_Final(&ctx, mk);
+
+ /* stretch the mk with the session-id to get MSK | EMSK */
+ eap_pwd_kdf(mk, SHA256_DIGEST_LENGTH,
+ session_id, SHA256_DIGEST_LENGTH+1,
+ msk_emsk, (EAP_MSK_LEN + EAP_EMSK_LEN) * 8);
+
+ os_memcpy(msk, msk_emsk, EAP_MSK_LEN);
+ os_memcpy(emsk, msk_emsk + EAP_MSK_LEN, EAP_EMSK_LEN);
+
+ os_free(cruft);
+
+ return 1;
+}
--- /dev/null
+/*
+ * EAP server/peer: EAP-pwd shared definitions
+ * Copyright (c) 2009, Dan Harkins <dharkins@lounge.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the BSD license.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef EAP_PWD_COMMON_H
+#define EAP_PWD_COMMON_H
+
+#include <openssl/bn.h>
+#include <openssl/sha.h>
+#include <openssl/ec.h>
+#include <openssl/evp.h>
+#include <openssl/hmac.h>
+
+/*
+ * definition of a finite cyclic group
+ * TODO: support one based on a prime field
+ */
+typedef struct group_definition_ {
+ u16 group_num;
+ EC_GROUP *group;
+ EC_POINT *pwe;
+ BIGNUM *order;
+ BIGNUM *prime;
+} EAP_PWD_group;
+
+/*
+ * EAP-pwd header, included on all payloads
+ */
+struct eap_pwd_hdr {
+ u8 l_bit:1;
+ u8 m_bit:1;
+ u8 exch:6;
+ u8 total_length[0]; /* included when l_bit is set */
+} STRUCT_PACKED;
+
+#define EAP_PWD_OPCODE_ID_EXCH 1
+#define EAP_PWD_OPCODE_COMMIT_EXCH 2
+#define EAP_PWD_OPCODE_CONFIRM_EXCH 3
+#define EAP_PWD_GET_LENGTH_BIT(x) ((x)->lm_exch & 0x80)
+#define EAP_PWD_SET_LENGTH_BIT(x) ((x)->lm_exch |= 0x80)
+#define EAP_PWD_GET_MORE_BIT(x) ((x)->lm_exch & 0x40)
+#define EAP_PWD_SET_MORE_BIT(x) ((x)->lm_exch |= 0x40)
+#define EAP_PWD_GET_EXCHANGE(x) ((x)->lm_exch & 0x3f)
+#define EAP_PWD_SET_EXCHANGE(x,y) ((x)->lm_exch |= (y))
+
+/* EAP-pwd-ID payload */
+struct eap_pwd_id {
+ be16 group_num;
+ u8 random_function;
+#define EAP_PWD_DEFAULT_RAND_FUNC 1
+ u8 prf;
+#define EAP_PWD_DEFAULT_PRF 1
+ u8 token[4];
+ u8 prep;
+#define EAP_PWD_PREP_NONE 0
+#define EAP_PWD_PREP_MS 1
+ u8 identity[0]; /* length inferred from payload */
+} STRUCT_PACKED;
+
+/* common routines */
+int compute_password_element(EAP_PWD_group *, u16, u8 *, int, u8 *, int, u8 *,
+ int, u8 *);
+int compute_keys(EAP_PWD_group *, BN_CTX *, BIGNUM *, BIGNUM *, BIGNUM *,
+ u8 *, u8 *, u32 *, u8 *, u8 *);
+void H_Init(HMAC_CTX *);
+void H_Update(HMAC_CTX *, const u8 *, int);
+void H_Final(HMAC_CTX *, u8 *);
+
+#endif /* EAP_PWD_COMMON_H */
#include "crypto/crypto.h"
#include "crypto/sha1.h"
#include "crypto/sha256.h"
+#include "crypto/random.h"
#include "eap_common/eap_defs.h"
#include "eap_common/eap_sim_common.h"
if (pos == NULL)
return -1;
msg->iv = (pos - wpabuf_head_u8(msg->buf)) + 4;
- if (os_get_random(wpabuf_mhead_u8(msg->buf) + msg->iv,
- EAP_SIM_IV_LEN)) {
+ if (random_get_bytes(wpabuf_mhead_u8(msg->buf) + msg->iv,
+ EAP_SIM_IV_LEN)) {
msg->iv = 0;
return -1;
}
#include "crypto/crypto.h"
#include "crypto/md5.h"
#include "crypto/sha1.h"
+#include "crypto/random.h"
#include "ikev2_common.h"
phdr->flags = 0;
iv = wpabuf_put(msg, iv_len);
- if (os_get_random(iv, iv_len)) {
+ if (random_get_bytes(iv, iv_len)) {
wpa_printf(MSG_INFO, "IKEV2: Could not generate IV");
return -1;
}
IKEV2_TRANSFORM_ESN = 5
};
-/* IKEv2 Tranform Type 1 (Encryption Algorithm) */
+/* IKEv2 Transform Type 1 (Encryption Algorithm) */
enum {
ENCR_DES_IV64 = 1,
ENCR_DES = 2,
#define STATE_MACHINE_DEBUG_PREFIX "EAP"
#define EAP_MAX_AUTH_ROUNDS 50
+#define EAP_CLIENT_TIMEOUT_DEFAULT 60
static Boolean eap_sm_allowMethod(struct eap_sm *sm, int vendor,
sm->methodState = METHOD_NONE;
sm->allowNotifications = TRUE;
sm->decision = DECISION_FAIL;
+ sm->ClientTimeout = EAP_CLIENT_TIMEOUT_DEFAULT;
eapol_set_int(sm, EAPOL_idleWhile, sm->ClientTimeout);
eapol_set_bool(sm, EAPOL_eapSuccess, FALSE);
eapol_set_bool(sm, EAPOL_eapFail, FALSE);
goto nak;
}
+ sm->ClientTimeout = EAP_CLIENT_TIMEOUT_DEFAULT;
+
wpa_printf(MSG_DEBUG, "EAP: Initialize selected EAP method: "
"vendor %u method %u (%s)",
sm->reqVendor, method, sm->m->name);
wpa_hexdump_ascii(MSG_DEBUG, "IMSI", (u8 *) imsi, imsi_len);
+ if (imsi_len < 7) {
+ wpa_printf(MSG_WARNING, "Too short IMSI for SIM identity");
+ return -1;
+ }
+
for (i = 0; m && (m[i].vendor != EAP_VENDOR_IETF ||
m[i].method != EAP_TYPE_NONE); i++) {
if (m[i].vendor == EAP_VENDOR_IETF &&
{
struct eap_sm *sm = ctx;
char *hash_hex = NULL;
- char *cert_hex = NULL;
switch (ev) {
case TLS_CERT_CHAIN_FAILURE:
data->cert_fail.reason_txt);
break;
case TLS_PEER_CERTIFICATE:
+ if (!sm->eapol_cb->notify_cert)
+ break;
+
if (data->peer_cert.hash) {
size_t len = data->peer_cert.hash_len * 2 + 1;
hash_hex = os_malloc(len);
data->peer_cert.hash_len);
}
}
- wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PEER_CERT
- "depth=%d subject='%s'%s%s",
- data->peer_cert.depth, data->peer_cert.subject,
- hash_hex ? " hash=" : "", hash_hex ? hash_hex : "");
-
- if (data->peer_cert.cert) {
- size_t len = wpabuf_len(data->peer_cert.cert) * 2 + 1;
- cert_hex = os_malloc(len);
- if (cert_hex == NULL)
- break;
- wpa_snprintf_hex(cert_hex, len,
- wpabuf_head(data->peer_cert.cert),
- wpabuf_len(data->peer_cert.cert));
- wpa_msg_ctrl(sm->msg_ctx, MSG_INFO,
- WPA_EVENT_EAP_PEER_CERT
- "depth=%d subject='%s' cert=%s",
- data->peer_cert.depth,
- data->peer_cert.subject,
- cert_hex);
- }
+
+ sm->eapol_cb->notify_cert(sm->eapol_ctx,
+ data->peer_cert.depth,
+ data->peer_cert.subject,
+ hash_hex, data->peer_cert.cert);
break;
}
os_free(hash_hex);
- os_free(cert_hex);
}
sm->eapol_ctx = eapol_ctx;
sm->eapol_cb = eapol_cb;
sm->msg_ctx = msg_ctx;
- sm->ClientTimeout = 60;
+ sm->ClientTimeout = EAP_CLIENT_TIMEOUT_DEFAULT;
sm->wps = conf->wps;
os_memset(&tlsconf, 0, sizeof(tlsconf));
#endif /* CONFIG_FIPS */
tlsconf.event_cb = eap_peer_sm_tls_event;
tlsconf.cb_ctx = sm;
+ tlsconf.cert_in_cb = conf->cert_in_cb;
sm->ssl_ctx = tls_init(&tlsconf);
if (sm->ssl_ctx == NULL) {
wpa_printf(MSG_WARNING, "SSL: Failed to initialize TLS "
#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
-typedef enum {
- TYPE_IDENTITY, TYPE_PASSWORD, TYPE_OTP, TYPE_PIN, TYPE_NEW_PASSWORD,
- TYPE_PASSPHRASE
-} eap_ctrl_req_type;
-
-static void eap_sm_request(struct eap_sm *sm, eap_ctrl_req_type type,
+static void eap_sm_request(struct eap_sm *sm, enum wpa_ctrl_req_type field,
const char *msg, size_t msglen)
{
struct eap_peer_config *config;
- char *field, *txt, *tmp;
+ char *txt = NULL, *tmp;
if (sm == NULL)
return;
if (config == NULL)
return;
- switch (type) {
- case TYPE_IDENTITY:
- field = "IDENTITY";
- txt = "Identity";
+ switch (field) {
+ case WPA_CTRL_REQ_EAP_IDENTITY:
config->pending_req_identity++;
break;
- case TYPE_PASSWORD:
- field = "PASSWORD";
- txt = "Password";
+ case WPA_CTRL_REQ_EAP_PASSWORD:
config->pending_req_password++;
break;
- case TYPE_NEW_PASSWORD:
- field = "NEW_PASSWORD";
- txt = "New Password";
+ case WPA_CTRL_REQ_EAP_NEW_PASSWORD:
config->pending_req_new_password++;
break;
- case TYPE_PIN:
- field = "PIN";
- txt = "PIN";
+ case WPA_CTRL_REQ_EAP_PIN:
config->pending_req_pin++;
break;
- case TYPE_OTP:
- field = "OTP";
+ case WPA_CTRL_REQ_EAP_OTP:
if (msg) {
tmp = os_malloc(msglen + 3);
if (tmp == NULL)
txt = config->pending_req_otp;
}
break;
- case TYPE_PASSPHRASE:
- field = "PASSPHRASE";
- txt = "Private key passphrase";
+ case WPA_CTRL_REQ_EAP_PASSPHRASE:
config->pending_req_passphrase++;
break;
default:
#define eap_sm_request(sm, type, msg, msglen) do { } while (0)
#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+const char * eap_sm_get_method_name(struct eap_sm *sm)
+{
+ if (sm->m == NULL)
+ return "UNKNOWN";
+ return sm->m->name;
+}
+
/**
* eap_sm_request_identity - Request identity from user (ctrl_iface)
*/
void eap_sm_request_identity(struct eap_sm *sm)
{
- eap_sm_request(sm, TYPE_IDENTITY, NULL, 0);
+ eap_sm_request(sm, WPA_CTRL_REQ_EAP_IDENTITY, NULL, 0);
}
*/
void eap_sm_request_password(struct eap_sm *sm)
{
- eap_sm_request(sm, TYPE_PASSWORD, NULL, 0);
+ eap_sm_request(sm, WPA_CTRL_REQ_EAP_PASSWORD, NULL, 0);
}
*/
void eap_sm_request_new_password(struct eap_sm *sm)
{
- eap_sm_request(sm, TYPE_NEW_PASSWORD, NULL, 0);
+ eap_sm_request(sm, WPA_CTRL_REQ_EAP_NEW_PASSWORD, NULL, 0);
}
*/
void eap_sm_request_pin(struct eap_sm *sm)
{
- eap_sm_request(sm, TYPE_PIN, NULL, 0);
+ eap_sm_request(sm, WPA_CTRL_REQ_EAP_PIN, NULL, 0);
}
*/
void eap_sm_request_otp(struct eap_sm *sm, const char *msg, size_t msg_len)
{
- eap_sm_request(sm, TYPE_OTP, msg, msg_len);
+ eap_sm_request(sm, WPA_CTRL_REQ_EAP_OTP, msg, msg_len);
}
*/
void eap_sm_request_passphrase(struct eap_sm *sm)
{
- eap_sm_request(sm, TYPE_PASSPHRASE, NULL, 0);
+ eap_sm_request(sm, WPA_CTRL_REQ_EAP_PASSPHRASE, NULL, 0);
}
}
+int eap_get_config_fragment_size(struct eap_sm *sm)
+{
+ struct eap_peer_config *config = eap_get_config(sm);
+ if (config == NULL)
+ return -1;
+ return config->fragment_size;
+}
+
+
/**
* eap_key_available - Get key availability (eapKeyAvailable variable)
* @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
/**
* eap_param_needed - Notify that EAP parameter is needed
* @ctx: eapol_ctx from eap_peer_sm_init() call
- * @field: Field name (e.g., "IDENTITY")
+ * @field: Field indicator (e.g., WPA_CTRL_REQ_EAP_IDENTITY)
* @txt: User readable text describing the required parameter
*/
- void (*eap_param_needed)(void *ctx, const char *field,
+ void (*eap_param_needed)(void *ctx, enum wpa_ctrl_req_type field,
const char *txt);
+
+ /**
+ * notify_cert - Notification of a peer certificate
+ * @ctx: eapol_ctx from eap_peer_sm_init() call
+ * @depth: Depth in certificate chain (0 = server)
+ * @subject: Subject of the peer certificate
+ * @cert_hash: SHA-256 hash of the certificate
+ * @cert: Peer certificate
+ */
+ void (*notify_cert)(void *ctx, int depth, const char *subject,
+ const char *cert_hash, const struct wpabuf *cert);
};
/**
* This is only used by EAP-WSC and can be left %NULL if not available.
*/
struct wps_context *wps;
+
+ /**
+ * cert_in_cb - Include server certificates in callback
+ */
+ int cert_in_cb;
};
struct eap_sm * eap_peer_sm_init(void *eapol_ctx,
void eap_sm_abort(struct eap_sm *sm);
int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen,
int verbose);
+const char * eap_sm_get_method_name(struct eap_sm *sm);
struct wpabuf * eap_sm_buildIdentity(struct eap_sm *sm, int id, int encrypted);
void eap_sm_request_identity(struct eap_sm *sm);
void eap_sm_request_password(struct eap_sm *sm);
static void eap_aka_clear_identities(struct eap_aka_data *data, int id)
{
- wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old%s%s%s",
- id & CLEAR_PSEUDONYM ? " pseudonym" : "",
- id & CLEAR_REAUTH_ID ? " reauth_id" : "",
- id & CLEAR_EAP_ID ? " eap_id" : "");
- if (id & CLEAR_PSEUDONYM) {
+ if ((id & CLEAR_PSEUDONYM) && data->pseudonym) {
+ wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old pseudonym");
os_free(data->pseudonym);
data->pseudonym = NULL;
data->pseudonym_len = 0;
}
- if (id & CLEAR_REAUTH_ID) {
+ if ((id & CLEAR_REAUTH_ID) && data->reauth_id) {
+ wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old reauth_id");
os_free(data->reauth_id);
data->reauth_id = NULL;
data->reauth_id_len = 0;
}
- if (id & CLEAR_EAP_ID) {
+ if ((id & CLEAR_EAP_ID) && data->last_eap_identity) {
+ wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old eap_id");
os_free(data->last_eap_identity);
data->last_eap_identity = NULL;
data->last_eap_identity_len = 0;
}
-static int eap_aka_learn_ids(struct eap_aka_data *data,
+static int eap_aka_learn_ids(struct eap_sm *sm, struct eap_aka_data *data,
struct eap_sim_attrs *attr)
{
if (attr->next_pseudonym) {
+ const u8 *identity = NULL;
+ size_t identity_len = 0;
+ const u8 *realm = NULL;
+ size_t realm_len = 0;
+
+ wpa_hexdump_ascii(MSG_DEBUG,
+ "EAP-AKA: (encr) AT_NEXT_PSEUDONYM",
+ attr->next_pseudonym,
+ attr->next_pseudonym_len);
os_free(data->pseudonym);
- data->pseudonym = os_malloc(attr->next_pseudonym_len);
+ /* Look for the realm of the permanent identity */
+ identity = eap_get_config_identity(sm, &identity_len);
+ if (identity) {
+ for (realm = identity, realm_len = identity_len;
+ realm_len > 0; realm_len--, realm++) {
+ if (*realm == '@')
+ break;
+ }
+ }
+ data->pseudonym = os_malloc(attr->next_pseudonym_len +
+ realm_len);
if (data->pseudonym == NULL) {
wpa_printf(MSG_INFO, "EAP-AKA: (encr) No memory for "
"next pseudonym");
+ data->pseudonym_len = 0;
return -1;
}
os_memcpy(data->pseudonym, attr->next_pseudonym,
attr->next_pseudonym_len);
- data->pseudonym_len = attr->next_pseudonym_len;
- wpa_hexdump_ascii(MSG_DEBUG,
- "EAP-AKA: (encr) AT_NEXT_PSEUDONYM",
- data->pseudonym,
- data->pseudonym_len);
+ if (realm_len) {
+ os_memcpy(data->pseudonym + attr->next_pseudonym_len,
+ realm, realm_len);
+ }
+ data->pseudonym_len = attr->next_pseudonym_len + realm_len;
}
if (attr->next_reauth_id) {
if (data->reauth_id == NULL) {
wpa_printf(MSG_INFO, "EAP-AKA: (encr) No memory for "
"next reauth_id");
+ data->reauth_id_len = 0;
return -1;
}
os_memcpy(data->reauth_id, attr->next_reauth_id,
EAP_AKA_UNABLE_TO_PROCESS_PACKET);
}
- /* Old reauthentication and pseudonym identities must not be used
- * anymore. In other words, if no new identities are received, full
- * authentication will be used on next reauthentication. */
- eap_aka_clear_identities(data, CLEAR_PSEUDONYM | CLEAR_REAUTH_ID |
- CLEAR_EAP_ID);
+ /* Old reauthentication identity must not be used anymore. In
+ * other words, if no new identities are received, full
+ * authentication will be used on next reauthentication (using
+ * pseudonym identity or permanent identity). */
+ eap_aka_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
if (attr->encr_data) {
u8 *decrypted;
return eap_aka_client_error(
data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET);
}
- eap_aka_learn_ids(data, &eattr);
+ eap_aka_learn_ids(sm, data, &eattr);
os_free(decrypted);
}
data->msk, data->emsk);
}
eap_aka_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
- eap_aka_learn_ids(data, &eattr);
+ eap_aka_learn_ids(sm, data, &eattr);
if (data->result_ind && attr->result_ind)
data->use_result_ind = 1;
return 0;
}
- if (data->phase2_priv == NULL &&
- eap_fast_init_phase2_method(sm, data) < 0) {
+ if ((data->phase2_priv == NULL &&
+ eap_fast_init_phase2_method(sm, data) < 0) ||
+ data->phase2_method == NULL) {
wpa_printf(MSG_INFO, "EAP-FAST: Failed to initialize "
"Phase 2 EAP method %d", *pos);
ret->methodState = METHOD_DONE;
static struct wpabuf * eap_fast_process_eap_payload_tlv(
struct eap_sm *sm, struct eap_fast_data *data,
- struct eap_method_ret *ret, const struct eap_hdr *req,
+ struct eap_method_ret *ret,
u8 *eap_payload_tlv, size_t eap_payload_tlv_len)
{
struct eap_hdr *hdr;
} else {
/*
* This is PAC refreshing, i.e., normal authentication that is
- * expected to be completed with an EAP-Success.
+ * expected to be completed with an EAP-Success. However,
+ * RFC 5422, Section 3.5 allows EAP-Failure to be sent even
+ * after protected success exchange in case of EAP-Fast
+ * provisioning, so we better use DECISION_COND_SUCC here
+ * instead of DECISION_UNCOND_SUCC.
*/
wpa_printf(MSG_DEBUG, "EAP-FAST: Send PAC-Acknowledgement TLV "
"- PAC refreshing completed successfully");
- ret->decision = DECISION_UNCOND_SUCC;
+ ret->decision = DECISION_COND_SUCC;
}
ret->methodState = METHOD_DONE;
return eap_fast_tlv_pac_ack();
if (tlv.eap_payload_tlv) {
tmp = eap_fast_process_eap_payload_tlv(
- sm, data, ret, req, tlv.eap_payload_tlv,
+ sm, data, ret, tlv.eap_payload_tlv,
tlv.eap_payload_tlv_len);
resp = wpabuf_concat(resp, tmp);
}
*buf = NULL;
return;
}
+ *pos = nbuf + (*pos - *buf);
*buf = nbuf;
*buf_len += need;
}
#include "includes.h"
#include "common.h"
+#include "crypto/random.h"
#include "eap_peer/eap_i.h"
#include "eap_common/eap_gpsk_common.h"
wpabuf_put_be16(resp, data->id_server_len);
wpabuf_put_data(resp, data->id_server, data->id_server_len);
- if (os_get_random(data->rand_peer, EAP_GPSK_RAND_LEN)) {
+ if (random_get_bytes(data->rand_peer, EAP_GPSK_RAND_LEN)) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to get random data "
"for RAND_Peer");
eap_gpsk_state(data, FAILURE);
void eap_clear_config_otp(struct eap_sm *sm);
const char * eap_get_config_phase1(struct eap_sm *sm);
const char * eap_get_config_phase2(struct eap_sm *sm);
+int eap_get_config_fragment_size(struct eap_sm *sm);
struct eap_peer_config * eap_get_config(struct eap_sm *sm);
void eap_set_config_blob(struct eap_sm *sm, struct wpa_config_blob *blob);
const struct wpa_config_blob *
#include "common.h"
#include "crypto/ms_funcs.h"
#include "crypto/crypto.h"
+#include "crypto/random.h"
#include "eap_i.h"
#define LEAP_VERSION 1
wpabuf_put_u8(resp, 0); /* unused */
wpabuf_put_u8(resp, LEAP_CHALLENGE_LEN);
pos = wpabuf_put(resp, LEAP_CHALLENGE_LEN);
- if (os_get_random(pos, LEAP_CHALLENGE_LEN)) {
+ if (random_get_bytes(pos, LEAP_CHALLENGE_LEN)) {
wpa_printf(MSG_WARNING, "EAP-LEAP: Failed to read random data "
"for challenge");
wpabuf_free(resp);
const char * eap_get_name(int vendor, EapType type)
{
struct eap_method *m;
+ if (vendor == EAP_VENDOR_IETF && type == EAP_TYPE_EXPANDED)
+ return "expanded";
for (m = eap_methods; m; m = m->next) {
if (m->vendor == vendor && m->method == type)
return m->name;
int eap_peer_ikev2_register(void);
int eap_peer_vendor_test_register(void);
int eap_peer_tnc_register(void);
+int eap_peer_pwd_register(void);
#endif /* EAP_METHODS_H */
#include "common.h"
#include "crypto/ms_funcs.h"
+#include "crypto/random.h"
#include "common/wpa_ctrl.h"
#include "mschapv2.h"
#include "eap_i.h"
"in Phase 1");
peer_challenge = data->peer_challenge;
os_memset(r->peer_challenge, 0, MSCHAPV2_CHAL_LEN);
- } else if (os_get_random(peer_challenge, MSCHAPV2_CHAL_LEN)) {
+ } else if (random_get_bytes(peer_challenge, MSCHAPV2_CHAL_LEN)) {
wpabuf_free(resp);
return NULL;
}
}
/* Peer-Challenge */
- if (os_get_random(cp->peer_challenge, MSCHAPV2_CHAL_LEN))
+ if (random_get_bytes(cp->peer_challenge, MSCHAPV2_CHAL_LEN))
goto fail;
/* Reserved, must be zero */
#include "includes.h"
#include "common.h"
+#include "crypto/random.h"
#include "eap_common/eap_pax_common.h"
#include "eap_i.h"
pos, left);
}
- if (os_get_random(data->rand.r.y, EAP_PAX_RAND_LEN)) {
+ if (random_get_bytes(data->rand.r.y, EAP_PAX_RAND_LEN)) {
wpa_printf(MSG_ERROR, "EAP-PAX: Failed to get random data");
ret->ignore = TRUE;
return NULL;
* @nak_type: TLV type (EAP_TLV_*)
* Returns: Buffer to the allocated EAP-TLV NAK message or %NULL on failure
*
- * This funtion builds an EAP-TLV NAK message. The caller is responsible for
+ * This function builds an EAP-TLV NAK message. The caller is responsible for
* freeing the returned buffer.
*/
static struct wpabuf * eap_tlv_build_nak(int id, u16 nak_type)
* in the end of the label just before ISK; is that just a typo?)
*/
wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TempKey", tk, 40);
- peap_prfplus(data->peap_version, tk, 40, "Inner Methods Compound Keys",
- isk, sizeof(isk), imck, sizeof(imck));
+ if (peap_prfplus(data->peap_version, tk, 40,
+ "Inner Methods Compound Keys",
+ isk, sizeof(isk), imck, sizeof(imck)) < 0)
+ return -1;
wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IMCK (IPMKj)",
imck, sizeof(imck));
* @status: Status (EAP_TLV_RESULT_SUCCESS or EAP_TLV_RESULT_FAILURE)
* Returns: Buffer to the allocated EAP-TLV Result message or %NULL on failure
*
- * This funtion builds an EAP-TLV Result message. The caller is responsible for
- * freeing the returned buffer.
+ * This function builds an EAP-TLV Result message. The caller is responsible
+ * for freeing the returned buffer.
*/
static struct wpabuf * eap_tlv_build_result(struct eap_sm *sm,
struct eap_peap_data *data,
* termination for this label while the one used for deriving
* IPMK|CMK did not use null termination.
*/
- peap_prfplus(data->peap_version, data->ipmk, 40,
- "Session Key Generating Function",
- (u8 *) "\00", 1, csk, sizeof(csk));
+ if (peap_prfplus(data->peap_version, data->ipmk, 40,
+ "Session Key Generating Function",
+ (u8 *) "\00", 1, csk, sizeof(csk)) < 0) {
+ os_free(key);
+ return NULL;
+ }
wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CSK", csk, sizeof(csk));
os_memcpy(key, csk, EAP_TLS_KEY_LEN);
wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived key",
#include "common.h"
#include "crypto/aes_wrap.h"
+#include "crypto/random.h"
#include "eap_common/eap_psk_common.h"
#include "eap_i.h"
wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: ID_S",
data->id_s, data->id_s_len);
- if (os_get_random(data->rand_p, EAP_PSK_RAND_LEN)) {
+ if (random_get_bytes(data->rand_p, EAP_PSK_RAND_LEN)) {
wpa_printf(MSG_ERROR, "EAP-PSK: Failed to get random data");
ret->ignore = TRUE;
return NULL;
--- /dev/null
+/*
+ * EAP peer method: EAP-pwd (RFC 5931)
+ * Copyright (c) 2010, Dan Harkins <dharkins@lounge.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the BSD license.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_peer/eap_i.h"
+#include "eap_common/eap_pwd_common.h"
+
+
+struct eap_pwd_data {
+ enum {
+ PWD_ID_Req, PWD_Commit_Req, PWD_Confirm_Req, SUCCESS, FAILURE
+ } state;
+ u8 *id_peer;
+ size_t id_peer_len;
+ u8 *id_server;
+ size_t id_server_len;
+ u8 *password;
+ size_t password_len;
+ u16 group_num;
+ EAP_PWD_group *grp;
+
+ BIGNUM *k;
+ BIGNUM *private_value;
+ BIGNUM *server_scalar;
+ BIGNUM *my_scalar;
+ EC_POINT *my_element;
+ EC_POINT *server_element;
+
+ u8 msk[EAP_MSK_LEN];
+ u8 emsk[EAP_EMSK_LEN];
+
+ BN_CTX *bnctx;
+};
+
+
+#ifndef CONFIG_NO_STDOUT_DEBUG
+static const char * eap_pwd_state_txt(int state)
+{
+ switch (state) {
+ case PWD_ID_Req:
+ return "PWD-ID-Req";
+ case PWD_Commit_Req:
+ return "PWD-Commit-Req";
+ case PWD_Confirm_Req:
+ return "PWD-Confirm-Req";
+ case SUCCESS:
+ return "SUCCESS";
+ case FAILURE:
+ return "FAILURE";
+ default:
+ return "PWD-UNK";
+ }
+}
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+
+static void eap_pwd_state(struct eap_pwd_data *data, int state)
+{
+ wpa_printf(MSG_INFO, "EAP-PWD: %s -> %s",
+ eap_pwd_state_txt(data->state), eap_pwd_state_txt(state));
+ data->state = state;
+}
+
+
+static void * eap_pwd_init(struct eap_sm *sm)
+{
+ struct eap_pwd_data *data;
+ const u8 *identity, *password;
+ size_t identity_len, password_len;
+
+ password = eap_get_config_password(sm, &password_len);
+ if (password == NULL) {
+ wpa_printf(MSG_INFO, "EAP-PWD: No password configured!");
+ return NULL;
+ }
+
+ identity = eap_get_config_identity(sm, &identity_len);
+ if (identity == NULL) {
+ wpa_printf(MSG_INFO, "EAP-PWD: No identity configured!");
+ return NULL;
+ }
+
+ if ((data = os_zalloc(sizeof(*data))) == NULL) {
+ wpa_printf(MSG_INFO, "EAP-PWD: memory allocation data fail");
+ return NULL;
+ }
+
+ if ((data->bnctx = BN_CTX_new()) == NULL) {
+ wpa_printf(MSG_INFO, "EAP-PWD: bn context allocation fail");
+ os_free(data);
+ return NULL;
+ }
+
+ if ((data->id_peer = os_malloc(identity_len)) == NULL) {
+ wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail");
+ BN_CTX_free(data->bnctx);
+ os_free(data);
+ return NULL;
+ }
+
+ os_memcpy(data->id_peer, identity, identity_len);
+ data->id_peer_len = identity_len;
+
+ if ((data->password = os_malloc(password_len)) == NULL) {
+ wpa_printf(MSG_INFO, "EAP-PWD: memory allocation psk fail");
+ BN_CTX_free(data->bnctx);
+ os_free(data->id_peer);
+ os_free(data);
+ return NULL;
+ }
+ os_memcpy(data->password, password, password_len);
+ data->password_len = password_len;
+
+ data->state = PWD_ID_Req;
+
+ return data;
+}
+
+
+static void eap_pwd_deinit(struct eap_sm *sm, void *priv)
+{
+ struct eap_pwd_data *data = priv;
+
+ BN_free(data->private_value);
+ BN_free(data->server_scalar);
+ BN_free(data->my_scalar);
+ BN_free(data->k);
+ BN_CTX_free(data->bnctx);
+ EC_POINT_free(data->my_element);
+ EC_POINT_free(data->server_element);
+ os_free(data->id_peer);
+ os_free(data->id_server);
+ os_free(data->password);
+ if (data->grp) {
+ EC_GROUP_free(data->grp->group);
+ EC_POINT_free(data->grp->pwe);
+ BN_free(data->grp->order);
+ BN_free(data->grp->prime);
+ os_free(data->grp);
+ }
+ os_free(data);
+}
+
+
+static u8 * eap_pwd_getkey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_pwd_data *data = priv;
+ u8 *key;
+
+ if (data->state != SUCCESS)
+ return NULL;
+
+ key = os_malloc(EAP_MSK_LEN);
+ if (key == NULL)
+ return NULL;
+
+ os_memcpy(key, data->msk, EAP_MSK_LEN);
+ *len = EAP_MSK_LEN;
+
+ return key;
+}
+
+
+static struct wpabuf *
+eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData,
+ const u8 *payload, size_t payload_len)
+{
+ struct eap_pwd_id *id;
+ struct wpabuf *resp;
+
+ if (data->state != PWD_ID_Req) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ if (payload_len < sizeof(struct eap_pwd_id)) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ id = (struct eap_pwd_id *) payload;
+ data->group_num = be_to_host16(id->group_num);
+ if ((id->random_function != EAP_PWD_DEFAULT_RAND_FUNC) ||
+ (id->prf != EAP_PWD_DEFAULT_PRF)) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-PWD (peer): server said group %d",
+ data->group_num);
+
+ data->id_server = os_malloc(payload_len - sizeof(struct eap_pwd_id));
+ if (data->id_server == NULL) {
+ wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail");
+ return NULL;
+ }
+ data->id_server_len = payload_len - sizeof(struct eap_pwd_id);
+ os_memcpy(data->id_server, id->identity, data->id_server_len);
+ wpa_hexdump_ascii(MSG_INFO, "EAP-PWD (peer): server sent id of",
+ data->id_server, data->id_server_len);
+
+ if ((data->grp = (EAP_PWD_group *) os_malloc(sizeof(EAP_PWD_group))) ==
+ NULL) {
+ wpa_printf(MSG_INFO, "EAP-PWD: failed to allocate memory for "
+ "group");
+ return NULL;
+ }
+
+ /* compute PWE */
+ if (compute_password_element(data->grp, data->group_num,
+ data->password, data->password_len,
+ data->id_server, data->id_server_len,
+ data->id_peer, data->id_peer_len,
+ id->token)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to compute PWE");
+ return NULL;
+ }
+
+ wpa_printf(MSG_INFO, "EAP-PWD (peer): computed %d bit PWE...",
+ BN_num_bits(data->grp->prime));
+
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD,
+ 1 + sizeof(struct eap_pwd_id) + data->id_peer_len,
+ EAP_CODE_RESPONSE, eap_get_id(reqData));
+ if (resp == NULL)
+ return NULL;
+
+ wpabuf_put_u8(resp, EAP_PWD_OPCODE_ID_EXCH);
+ wpabuf_put_be16(resp, data->group_num);
+ wpabuf_put_u8(resp, EAP_PWD_DEFAULT_RAND_FUNC);
+ wpabuf_put_u8(resp, EAP_PWD_DEFAULT_PRF);
+ wpabuf_put_data(resp, id->token, sizeof(id->token));
+ wpabuf_put_u8(resp, EAP_PWD_PREP_NONE);
+ wpabuf_put_data(resp, data->id_peer, data->id_peer_len);
+
+ eap_pwd_state(data, PWD_Commit_Req);
+
+ return resp;
+}
+
+
+static struct wpabuf *
+eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData,
+ const u8 *payload, size_t payload_len)
+{
+ struct wpabuf *resp = NULL;
+ EC_POINT *K = NULL, *point = NULL;
+ BIGNUM *mask = NULL, *x = NULL, *y = NULL, *cofactor = NULL;
+ u16 offset;
+ u8 *ptr, *scalar = NULL, *element = NULL;
+
+ if (((data->private_value = BN_new()) == NULL) ||
+ ((data->my_element = EC_POINT_new(data->grp->group)) == NULL) ||
+ ((cofactor = BN_new()) == NULL) ||
+ ((data->my_scalar = BN_new()) == NULL) ||
+ ((mask = BN_new()) == NULL)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (peer): scalar allocation fail");
+ goto fin;
+ }
+
+ if (!EC_GROUP_get_cofactor(data->grp->group, cofactor, NULL)) {
+ wpa_printf(MSG_INFO, "EAP-pwd (peer): unable to get cofactor "
+ "for curve");
+ goto fin;
+ }
+
+ BN_rand_range(data->private_value, data->grp->order);
+ BN_rand_range(mask, data->grp->order);
+ BN_add(data->my_scalar, data->private_value, mask);
+ BN_mod(data->my_scalar, data->my_scalar, data->grp->order,
+ data->bnctx);
+
+ if (!EC_POINT_mul(data->grp->group, data->my_element, NULL,
+ data->grp->pwe, mask, data->bnctx)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (peer): element allocation "
+ "fail");
+ eap_pwd_state(data, FAILURE);
+ goto fin;
+ }
+
+ if (!EC_POINT_invert(data->grp->group, data->my_element, data->bnctx))
+ {
+ wpa_printf(MSG_INFO, "EAP-PWD (peer): element inversion fail");
+ goto fin;
+ }
+ BN_free(mask);
+
+ if (((x = BN_new()) == NULL) ||
+ ((y = BN_new()) == NULL)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (peer): point allocation fail");
+ goto fin;
+ }
+
+ /* process the request */
+ if (((data->server_scalar = BN_new()) == NULL) ||
+ ((data->k = BN_new()) == NULL) ||
+ ((K = EC_POINT_new(data->grp->group)) == NULL) ||
+ ((point = EC_POINT_new(data->grp->group)) == NULL) ||
+ ((data->server_element = EC_POINT_new(data->grp->group)) == NULL))
+ {
+ wpa_printf(MSG_INFO, "EAP-PWD (peer): peer data allocation "
+ "fail");
+ goto fin;
+ }
+
+ /* element, x then y, followed by scalar */
+ ptr = (u8 *) payload;
+ BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), x);
+ ptr += BN_num_bytes(data->grp->prime);
+ BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), y);
+ ptr += BN_num_bytes(data->grp->prime);
+ BN_bin2bn(ptr, BN_num_bytes(data->grp->order), data->server_scalar);
+ if (!EC_POINT_set_affine_coordinates_GFp(data->grp->group,
+ data->server_element, x, y,
+ data->bnctx)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (peer): setting peer element "
+ "fail");
+ goto fin;
+ }
+
+ /* check to ensure server's element is not in a small sub-group */
+ if (BN_cmp(cofactor, BN_value_one())) {
+ if (!EC_POINT_mul(data->grp->group, point, NULL,
+ data->server_element, cofactor, NULL)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (peer): cannot multiply "
+ "server element by order!\n");
+ goto fin;
+ }
+ if (EC_POINT_is_at_infinity(data->grp->group, point)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (peer): server element "
+ "is at infinity!\n");
+ goto fin;
+ }
+ }
+
+ /* compute the shared key, k */
+ if ((!EC_POINT_mul(data->grp->group, K, NULL, data->grp->pwe,
+ data->server_scalar, data->bnctx)) ||
+ (!EC_POINT_add(data->grp->group, K, K, data->server_element,
+ data->bnctx)) ||
+ (!EC_POINT_mul(data->grp->group, K, NULL, K, data->private_value,
+ data->bnctx))) {
+ wpa_printf(MSG_INFO, "EAP-PWD (peer): computing shared key "
+ "fail");
+ goto fin;
+ }
+
+ /* ensure that the shared key isn't in a small sub-group */
+ if (BN_cmp(cofactor, BN_value_one())) {
+ if (!EC_POINT_mul(data->grp->group, K, NULL, K, cofactor,
+ NULL)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (peer): cannot multiply "
+ "shared key point by order");
+ goto fin;
+ }
+ }
+
+ /*
+ * This check is strictly speaking just for the case above where
+ * co-factor > 1 but it was suggested that even though this is probably
+ * never going to happen it is a simple and safe check "just to be
+ * sure" so let's be safe.
+ */
+ if (EC_POINT_is_at_infinity(data->grp->group, K)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (peer): shared key point is at "
+ "infinity!\n");
+ goto fin;
+ }
+
+ if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, K, data->k,
+ NULL, data->bnctx)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to extract "
+ "shared secret from point");
+ goto fin;
+ }
+
+ /* now do the response */
+ if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
+ data->my_element, x, y,
+ data->bnctx)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (peer): point assignment fail");
+ goto fin;
+ }
+
+ if (((scalar = os_malloc(BN_num_bytes(data->grp->order))) == NULL) ||
+ ((element = os_malloc(BN_num_bytes(data->grp->prime) * 2)) ==
+ NULL)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (peer): data allocation fail");
+ goto fin;
+ }
+
+ /*
+ * bignums occupy as little memory as possible so one that is
+ * sufficiently smaller than the prime or order might need pre-pending
+ * with zeros.
+ */
+ os_memset(scalar, 0, BN_num_bytes(data->grp->order));
+ os_memset(element, 0, BN_num_bytes(data->grp->prime) * 2);
+ offset = BN_num_bytes(data->grp->order) -
+ BN_num_bytes(data->my_scalar);
+ BN_bn2bin(data->my_scalar, scalar + offset);
+
+ offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
+ BN_bn2bin(x, element + offset);
+ offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
+ BN_bn2bin(y, element + BN_num_bytes(data->grp->prime) + offset);
+
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD,
+ sizeof(struct eap_pwd_hdr) +
+ BN_num_bytes(data->grp->order) +
+ (2 * BN_num_bytes(data->grp->prime)),
+ EAP_CODE_RESPONSE, eap_get_id(reqData));
+ if (resp == NULL)
+ goto fin;
+
+ wpabuf_put_u8(resp, EAP_PWD_OPCODE_COMMIT_EXCH);
+
+ /* we send the element as (x,y) follwed by the scalar */
+ wpabuf_put_data(resp, element, (2 * BN_num_bytes(data->grp->prime)));
+ wpabuf_put_data(resp, scalar, BN_num_bytes(data->grp->order));
+
+fin:
+ os_free(scalar);
+ os_free(element);
+ BN_free(x);
+ BN_free(y);
+ BN_free(cofactor);
+ EC_POINT_free(K);
+ EC_POINT_free(point);
+ if (resp == NULL)
+ eap_pwd_state(data, FAILURE);
+ else
+ eap_pwd_state(data, PWD_Confirm_Req);
+
+ return resp;
+}
+
+
+static struct wpabuf *
+eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData,
+ const u8 *payload, size_t payload_len)
+{
+ struct wpabuf *resp = NULL;
+ BIGNUM *x = NULL, *y = NULL;
+ HMAC_CTX ctx;
+ u32 cs;
+ u16 grp;
+ u8 conf[SHA256_DIGEST_LENGTH], *cruft = NULL, *ptr;
+ int offset;
+
+ /*
+ * first build up the ciphersuite which is group | random_function |
+ * prf
+ */
+ grp = htons(data->group_num);
+ ptr = (u8 *) &cs;
+ os_memcpy(ptr, &grp, sizeof(u16));
+ ptr += sizeof(u16);
+ *ptr = EAP_PWD_DEFAULT_RAND_FUNC;
+ ptr += sizeof(u8);
+ *ptr = EAP_PWD_DEFAULT_PRF;
+
+ /* each component of the cruft will be at most as big as the prime */
+ if (((cruft = os_malloc(BN_num_bytes(data->grp->prime))) == NULL) ||
+ ((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): debug allocation "
+ "fail");
+ goto fin;
+ }
+
+ /*
+ * server's commit is H(k | server_element | server_scalar |
+ * peer_element | peer_scalar | ciphersuite)
+ */
+ H_Init(&ctx);
+
+ /*
+ * zero the memory each time because this is mod prime math and some
+ * value may start with a few zeros and the previous one did not.
+ */
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k);
+ BN_bn2bin(data->k, cruft + offset);
+ H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+
+ /* server element: x, y */
+ if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
+ data->server_element, x, y,
+ data->bnctx)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
+ "assignment fail");
+ goto fin;
+ }
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
+ BN_bn2bin(x, cruft + offset);
+ H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
+ BN_bn2bin(y, cruft + offset);
+ H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+
+ /* server scalar */
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->order) -
+ BN_num_bytes(data->server_scalar);
+ BN_bn2bin(data->server_scalar, cruft + offset);
+ H_Update(&ctx, cruft, BN_num_bytes(data->grp->order));
+
+ /* my element: x, y */
+ if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
+ data->my_element, x, y,
+ data->bnctx)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
+ "assignment fail");
+ goto fin;
+ }
+
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
+ BN_bn2bin(x, cruft + offset);
+ H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
+ BN_bn2bin(y, cruft + offset);
+ H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+
+ /* my scalar */
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->order) -
+ BN_num_bytes(data->my_scalar);
+ BN_bn2bin(data->my_scalar, cruft + offset);
+ H_Update(&ctx, cruft, BN_num_bytes(data->grp->order));
+
+ /* the ciphersuite */
+ H_Update(&ctx, (u8 *) &cs, sizeof(u32));
+
+ /* random function fin */
+ H_Final(&ctx, conf);
+
+ ptr = (u8 *) payload;
+ if (os_memcmp(conf, ptr, SHA256_DIGEST_LENGTH)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm did not verify");
+ goto fin;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-pwd (peer): confirm verified");
+
+ /*
+ * compute confirm:
+ * H(k | peer_element | peer_scalar | server_element | server_scalar |
+ * ciphersuite)
+ */
+ H_Init(&ctx);
+
+ /* k */
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k);
+ BN_bn2bin(data->k, cruft + offset);
+ H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+
+ /* my element */
+ if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
+ data->my_element, x, y,
+ data->bnctx)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm point "
+ "assignment fail");
+ goto fin;
+ }
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
+ BN_bn2bin(x, cruft + offset);
+ H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
+ BN_bn2bin(y, cruft + offset);
+ H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+
+ /* my scalar */
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->order) -
+ BN_num_bytes(data->my_scalar);
+ BN_bn2bin(data->my_scalar, cruft + offset);
+ H_Update(&ctx, cruft, BN_num_bytes(data->grp->order));
+
+ /* server element: x, y */
+ if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
+ data->server_element, x, y,
+ data->bnctx)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm point "
+ "assignment fail");
+ goto fin;
+ }
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
+ BN_bn2bin(x, cruft + offset);
+ H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
+ BN_bn2bin(y, cruft + offset);
+ H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+
+ /* server scalar */
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->order) -
+ BN_num_bytes(data->server_scalar);
+ BN_bn2bin(data->server_scalar, cruft + offset);
+ H_Update(&ctx, cruft, BN_num_bytes(data->grp->order));
+
+ /* the ciphersuite */
+ H_Update(&ctx, (u8 *) &cs, sizeof(u32));
+
+ /* all done */
+ H_Final(&ctx, conf);
+
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD,
+ sizeof(struct eap_pwd_hdr) + SHA256_DIGEST_LENGTH,
+ EAP_CODE_RESPONSE, eap_get_id(reqData));
+ if (resp == NULL)
+ goto fin;
+
+ wpabuf_put_u8(resp, EAP_PWD_OPCODE_CONFIRM_EXCH);
+ wpabuf_put_data(resp, conf, SHA256_DIGEST_LENGTH);
+
+ if (compute_keys(data->grp, data->bnctx, data->k,
+ data->my_scalar, data->server_scalar, conf, ptr,
+ &cs, data->msk, data->emsk) < 0) {
+ wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to compute MSK | "
+ "EMSK");
+ goto fin;
+ }
+
+fin:
+ os_free(cruft);
+ BN_free(x);
+ BN_free(y);
+ ret->methodState = METHOD_DONE;
+ if (resp == NULL) {
+ ret->decision = DECISION_FAIL;
+ eap_pwd_state(data, FAILURE);
+ } else {
+ ret->decision = DECISION_UNCOND_SUCC;
+ eap_pwd_state(data, SUCCESS);
+ }
+
+ return resp;
+}
+
+
+static struct wpabuf *
+eap_pwd_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ struct eap_pwd_data *data = priv;
+ struct wpabuf *resp = NULL;
+ const u8 *pos;
+ size_t len;
+ u8 exch;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PWD, reqData, &len);
+ if ((pos == NULL) || (len < 1)) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ wpa_printf(MSG_INFO, "EAP-pwd: Received frame: opcode %d", *pos);
+
+ ret->ignore = FALSE;
+ ret->methodState = METHOD_MAY_CONT;
+ ret->decision = DECISION_FAIL;
+ ret->allowNotifications = FALSE;
+
+ exch = *pos & 0x3f;
+ switch (exch) {
+ case EAP_PWD_OPCODE_ID_EXCH:
+ resp = eap_pwd_perform_id_exchange(sm, data, ret, reqData,
+ pos + 1, len - 1);
+ break;
+ case EAP_PWD_OPCODE_COMMIT_EXCH:
+ resp = eap_pwd_perform_commit_exchange(sm, data, ret, reqData,
+ pos + 1, len - 1);
+ break;
+ case EAP_PWD_OPCODE_CONFIRM_EXCH:
+ resp = eap_pwd_perform_confirm_exchange(sm, data, ret, reqData,
+ pos + 1, len - 1);
+ break;
+ default:
+ wpa_printf(MSG_INFO, "EAP-pwd: Ignoring message with unknown "
+ "opcode %d", exch);
+ break;
+ }
+
+ return resp;
+}
+
+
+static Boolean eap_pwd_key_available(struct eap_sm *sm, void *priv)
+{
+ struct eap_pwd_data *data = priv;
+ return data->state == SUCCESS;
+}
+
+
+static u8 * eap_pwd_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_pwd_data *data = priv;
+ u8 *key;
+
+ if (data->state != SUCCESS)
+ return NULL;
+
+ if ((key = os_malloc(EAP_EMSK_LEN)) == NULL)
+ return NULL;
+
+ os_memcpy(key, data->emsk, EAP_EMSK_LEN);
+ *len = EAP_EMSK_LEN;
+
+ return key;
+}
+
+
+int eap_peer_pwd_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ EVP_add_digest(EVP_sha256());
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_PWD, "PWD");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_pwd_init;
+ eap->deinit = eap_pwd_deinit;
+ eap->process = eap_pwd_process;
+ eap->isKeyAvailable = eap_pwd_key_available;
+ eap->getKey = eap_pwd_getkey;
+ eap->get_emsk = eap_pwd_get_emsk;
+
+ ret = eap_peer_method_register(eap);
+ if (ret)
+ eap_peer_method_free(eap);
+ return ret;
+}
#include "includes.h"
#include "common.h"
+#include "crypto/random.h"
#include "eap_peer/eap_i.h"
#include "eap_common/eap_sake_common.h"
wpa_hexdump(MSG_MSGDUMP, "EAP-SAKE: RAND_S (server rand)",
data->rand_s, EAP_SAKE_RAND_LEN);
- if (os_get_random(data->rand_p, EAP_SAKE_RAND_LEN)) {
+ if (random_get_bytes(data->rand_p, EAP_SAKE_RAND_LEN)) {
wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to get random data");
return NULL;
}
#include "common.h"
#include "pcsc_funcs.h"
#include "crypto/milenage.h"
+#include "crypto/random.h"
#include "eap_peer/eap_i.h"
#include "eap_config.h"
#include "eap_common/eap_sim_common.h"
if (data == NULL)
return NULL;
- if (os_get_random(data->nonce_mt, EAP_SIM_NONCE_MT_LEN)) {
+ if (random_get_bytes(data->nonce_mt, EAP_SIM_NONCE_MT_LEN)) {
wpa_printf(MSG_WARNING, "EAP-SIM: Failed to get random data "
"for NONCE_MT");
os_free(data);
static void eap_sim_clear_identities(struct eap_sim_data *data, int id)
{
- wpa_printf(MSG_DEBUG, "EAP-SIM: forgetting old%s%s%s",
- id & CLEAR_PSEUDONYM ? " pseudonym" : "",
- id & CLEAR_REAUTH_ID ? " reauth_id" : "",
- id & CLEAR_EAP_ID ? " eap_id" : "");
- if (id & CLEAR_PSEUDONYM) {
+ if ((id & CLEAR_PSEUDONYM) && data->pseudonym) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM: forgetting old pseudonym");
os_free(data->pseudonym);
data->pseudonym = NULL;
data->pseudonym_len = 0;
}
- if (id & CLEAR_REAUTH_ID) {
+ if ((id & CLEAR_REAUTH_ID) && data->reauth_id) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM: forgetting old reauth_id");
os_free(data->reauth_id);
data->reauth_id = NULL;
data->reauth_id_len = 0;
}
- if (id & CLEAR_EAP_ID) {
+ if ((id & CLEAR_EAP_ID) && data->last_eap_identity) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM: forgetting old eap_id");
os_free(data->last_eap_identity);
data->last_eap_identity = NULL;
data->last_eap_identity_len = 0;
}
-static int eap_sim_learn_ids(struct eap_sim_data *data,
+static int eap_sim_learn_ids(struct eap_sm *sm, struct eap_sim_data *data,
struct eap_sim_attrs *attr)
{
if (attr->next_pseudonym) {
+ const u8 *identity = NULL;
+ size_t identity_len = 0;
+ const u8 *realm = NULL;
+ size_t realm_len = 0;
+
+ wpa_hexdump_ascii(MSG_DEBUG,
+ "EAP-SIM: (encr) AT_NEXT_PSEUDONYM",
+ attr->next_pseudonym,
+ attr->next_pseudonym_len);
os_free(data->pseudonym);
- data->pseudonym = os_malloc(attr->next_pseudonym_len);
+ /* Look for the realm of the permanent identity */
+ identity = eap_get_config_identity(sm, &identity_len);
+ if (identity) {
+ for (realm = identity, realm_len = identity_len;
+ realm_len > 0; realm_len--, realm++) {
+ if (*realm == '@')
+ break;
+ }
+ }
+ data->pseudonym = os_malloc(attr->next_pseudonym_len +
+ realm_len);
if (data->pseudonym == NULL) {
wpa_printf(MSG_INFO, "EAP-SIM: (encr) No memory for "
"next pseudonym");
+ data->pseudonym_len = 0;
return -1;
}
os_memcpy(data->pseudonym, attr->next_pseudonym,
attr->next_pseudonym_len);
- data->pseudonym_len = attr->next_pseudonym_len;
- wpa_hexdump_ascii(MSG_DEBUG,
- "EAP-SIM: (encr) AT_NEXT_PSEUDONYM",
- data->pseudonym,
- data->pseudonym_len);
+ if (realm_len) {
+ os_memcpy(data->pseudonym + attr->next_pseudonym_len,
+ realm, realm_len);
+ }
+ data->pseudonym_len = attr->next_pseudonym_len + realm_len;
}
if (attr->next_reauth_id) {
if (data->reauth_id == NULL) {
wpa_printf(MSG_INFO, "EAP-SIM: (encr) No memory for "
"next reauth_id");
+ data->reauth_id_len = 0;
return -1;
}
os_memcpy(data->reauth_id, attr->next_reauth_id,
EAP_SIM_UNABLE_TO_PROCESS_PACKET);
}
- /* Old reauthentication and pseudonym identities must not be used
- * anymore. In other words, if no new identities are received, full
- * authentication will be used on next reauthentication. */
- eap_sim_clear_identities(data, CLEAR_PSEUDONYM | CLEAR_REAUTH_ID |
- CLEAR_EAP_ID);
+ /* Old reauthentication identity must not be used anymore. In
+ * other words, if no new reauth identity is received, full
+ * authentication will be used on next reauthentication (using
+ * pseudonym identity or permanent identity). */
+ eap_sim_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
if (attr->encr_data) {
u8 *decrypted;
return eap_sim_client_error(
data, id, EAP_SIM_UNABLE_TO_PROCESS_PACKET);
}
- eap_sim_learn_ids(data, &eattr);
+ eap_sim_learn_ids(sm, data, &eattr);
os_free(decrypted);
}
data->nonce_s, data->mk, data->msk,
data->emsk);
eap_sim_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
- eap_sim_learn_ids(data, &eattr);
+ eap_sim_learn_ids(sm, data, &eattr);
if (data->result_ind && attr->result_ind)
data->use_result_ind = 1;
static void * eap_sim_init_for_reauth(struct eap_sm *sm, void *priv)
{
struct eap_sim_data *data = priv;
- if (os_get_random(data->nonce_mt, EAP_SIM_NONCE_MT_LEN)) {
+ if (random_get_bytes(data->nonce_mt, EAP_SIM_NONCE_MT_LEN)) {
wpa_printf(MSG_WARNING, "EAP-SIM: Failed to get random data "
"for NONCE_MT");
os_free(data);
wpa_printf(MSG_DEBUG, "TLS: using phase1 config options");
eap_tls_params_from_conf1(params, config);
}
- params->tls_ia = data->tls_ia;
/*
* Use blob data, if available. Otherwise, leave reference to external
eap_peer_tls_reset_input(data);
return -1;
}
- wpabuf_put_buf(data->tls_in, in_data);
+ if (in_data)
+ wpabuf_put_buf(data->tls_in, in_data);
data->tls_in_left -= in_len;
if (data->tls_in_left > 0) {
int include_tls_length;
/**
- * tls_ia - Whether TLS/IA is enabled for this TLS connection
- */
- int tls_ia;
-
- /**
* eap - EAP state machine allocated with eap_peer_sm_init()
*/
struct eap_sm *eap;
/*
* EAP peer method: EAP-TTLS (RFC 5281)
- * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
#include "eap_config.h"
-/* Maximum supported TTLS version
- * 0 = RFC 5281
- * 1 = draft-funk-eap-ttls-v1-00.txt
- */
-#ifndef EAP_TTLS_VERSION
-#define EAP_TTLS_VERSION 0 /* TTLSv1 implementation is not yet complete */
-#endif /* EAP_TTLS_VERSION */
-
-
-#define MSCHAPV2_KEY_LEN 16
-#define MSCHAPV2_NT_RESPONSE_LEN 24
+#define EAP_TTLS_VERSION 0
static void eap_ttls_deinit(struct eap_sm *sm, void *priv);
struct eap_ttls_data {
struct eap_ssl_data ssl;
- int ssl_initialized;
- int ttls_version, force_ttls_version;
+ int ttls_version;
const struct eap_method *phase2_method;
void *phase2_priv;
if (data == NULL)
return NULL;
data->ttls_version = EAP_TTLS_VERSION;
- data->force_ttls_version = -1;
selected = "EAP";
data->phase2_type = EAP_TTLS_PHASE2_EAP;
-#if EAP_TTLS_VERSION > 0
- if (config && config->phase1) {
- const char *pos = os_strstr(config->phase1, "ttlsver=");
- if (pos) {
- data->force_ttls_version = atoi(pos + 8);
- data->ttls_version = data->force_ttls_version;
- wpa_printf(MSG_DEBUG, "EAP-TTLS: Forced TTLS version "
- "%d", data->force_ttls_version);
- }
- }
-#endif /* EAP_TTLS_VERSION */
-
if (config && config->phase2) {
if (os_strstr(config->phase2, "autheap=")) {
selected = "EAP";
data->phase2_eap_type.method = EAP_TYPE_NONE;
}
-#if EAP_TTLS_VERSION > 0
- if (!(tls_capabilities(sm->ssl_ctx) & TLS_CAPABILITY_IA) &&
- data->ttls_version > 0) {
- if (data->force_ttls_version > 0) {
- wpa_printf(MSG_INFO, "EAP-TTLS: Forced TTLSv%d and "
- "TLS library does not support TLS/IA.",
- data->force_ttls_version);
- eap_ttls_deinit(sm, data);
- return NULL;
- }
- data->ttls_version = 0;
+ if (eap_peer_tls_ssl_init(sm, &data->ssl, config)) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL.");
+ eap_ttls_deinit(sm, data);
+ return NULL;
}
-#endif /* EAP_TTLS_VERSION */
return data;
}
return;
eap_ttls_phase2_eap_deinit(sm, data);
os_free(data->phase2_eap_types);
- if (data->ssl_initialized)
- eap_peer_tls_ssl_deinit(sm, &data->ssl);
+ eap_peer_tls_ssl_deinit(sm, &data->ssl);
os_free(data->key_data);
wpabuf_free(data->pending_phase2_req);
os_free(data);
}
avp->avp_code = host_to_be32(avp_code);
- avp->avp_length = host_to_be32((flags << 24) | (hdrlen + len));
+ avp->avp_length = host_to_be32((flags << 24) | (u32) (hdrlen + len));
return avphdr + hdrlen;
}
}
-#if EAP_TTLS_VERSION > 0
-static int eap_ttls_ia_permute_inner_secret(struct eap_sm *sm,
- struct eap_ttls_data *data,
- const u8 *key, size_t key_len)
-{
- u8 *buf;
- size_t buf_len;
- int ret;
-
- if (key) {
- buf_len = 2 + key_len;
- buf = os_malloc(buf_len);
- if (buf == NULL)
- return -1;
- WPA_PUT_BE16(buf, key_len);
- os_memcpy(buf + 2, key, key_len);
- } else {
- buf = NULL;
- buf_len = 0;
- }
-
- wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Session keys for TLS/IA inner "
- "secret permutation", buf, buf_len);
- ret = tls_connection_ia_permute_inner_secret(sm->ssl_ctx,
- data->ssl.conn,
- buf, buf_len);
- os_free(buf);
-
- return ret;
-}
-#endif /* EAP_TTLS_VERSION */
-
-
static int eap_ttls_v0_derive_key(struct eap_sm *sm,
struct eap_ttls_data *data)
{
}
-#if EAP_TTLS_VERSION > 0
-static int eap_ttls_v1_derive_key(struct eap_sm *sm,
- struct eap_ttls_data *data)
-{
- struct tls_keys keys;
- u8 *rnd;
-
- os_free(data->key_data);
- data->key_data = NULL;
-
- os_memset(&keys, 0, sizeof(keys));
- if (tls_connection_get_keys(sm->ssl_ctx, data->ssl.conn, &keys) ||
- keys.client_random == NULL || keys.server_random == NULL ||
- keys.inner_secret == NULL) {
- wpa_printf(MSG_INFO, "EAP-TTLS: Could not get inner secret, "
- "client random, or server random to derive keying "
- "material");
- return -1;
- }
-
- rnd = os_malloc(keys.client_random_len + keys.server_random_len);
- data->key_data = os_malloc(EAP_TLS_KEY_LEN);
- if (rnd == NULL || data->key_data == NULL) {
- wpa_printf(MSG_INFO, "EAP-TTLS: No memory for key derivation");
- os_free(rnd);
- os_free(data->key_data);
- data->key_data = NULL;
- return -1;
- }
- os_memcpy(rnd, keys.client_random, keys.client_random_len);
- os_memcpy(rnd + keys.client_random_len, keys.server_random,
- keys.server_random_len);
-
- if (tls_prf(keys.inner_secret, keys.inner_secret_len,
- "ttls v1 keying material", rnd, keys.client_random_len +
- keys.server_random_len, data->key_data, EAP_TLS_KEY_LEN)) {
- wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive key");
- os_free(rnd);
- os_free(data->key_data);
- data->key_data = NULL;
- return -1;
- }
-
- wpa_hexdump(MSG_DEBUG, "EAP-TTLS: client/server random",
- rnd, keys.client_random_len + keys.server_random_len);
- wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: TLS/IA inner secret",
- keys.inner_secret, keys.inner_secret_len);
-
- os_free(rnd);
-
- wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived key",
- data->key_data, EAP_TLS_KEY_LEN);
-
- return 0;
-}
-#endif /* EAP_TTLS_VERSION */
-
-
static u8 * eap_ttls_implicit_challenge(struct eap_sm *sm,
struct eap_ttls_data *data, size_t len)
{
-#if EAP_TTLS_VERSION > 0
- struct tls_keys keys;
- u8 *challenge, *rnd;
-#endif /* EAP_TTLS_VERSION */
-
- if (data->ttls_version == 0) {
- return eap_peer_tls_derive_key(sm, &data->ssl,
- "ttls challenge", len);
- }
-
-#if EAP_TTLS_VERSION > 0
-
- os_memset(&keys, 0, sizeof(keys));
- if (tls_connection_get_keys(sm->ssl_ctx, data->ssl.conn, &keys) ||
- keys.client_random == NULL || keys.server_random == NULL ||
- keys.inner_secret == NULL) {
- wpa_printf(MSG_INFO, "EAP-TTLS: Could not get inner secret, "
- "client random, or server random to derive "
- "implicit challenge");
- return NULL;
- }
-
- rnd = os_malloc(keys.client_random_len + keys.server_random_len);
- challenge = os_malloc(len);
- if (rnd == NULL || challenge == NULL) {
- wpa_printf(MSG_INFO, "EAP-TTLS: No memory for implicit "
- "challenge derivation");
- os_free(rnd);
- os_free(challenge);
- return NULL;
- }
- os_memcpy(rnd, keys.server_random, keys.server_random_len);
- os_memcpy(rnd + keys.server_random_len, keys.client_random,
- keys.client_random_len);
-
- if (tls_prf(keys.inner_secret, keys.inner_secret_len,
- "inner application challenge", rnd,
- keys.client_random_len + keys.server_random_len,
- challenge, len)) {
- wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive implicit "
- "challenge");
- os_free(rnd);
- os_free(challenge);
- return NULL;
- }
-
- os_free(rnd);
-
- wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived implicit challenge",
- challenge, len);
-
- return challenge;
-
-#else /* EAP_TTLS_VERSION */
-
- return NULL;
-
-#endif /* EAP_TTLS_VERSION */
-}
-
-
-static void eap_ttlsv1_phase2_eap_finish(struct eap_sm *sm,
- struct eap_ttls_data *data,
- struct eap_method_ret *ret)
-{
-#if EAP_TTLS_VERSION > 0
- if (data->ttls_version > 0) {
- const struct eap_method *m = data->phase2_method;
- void *priv = data->phase2_priv;
-
- /* TTLSv1 requires TLS/IA FinalPhaseFinished */
- if (ret->decision == DECISION_UNCOND_SUCC)
- ret->decision = DECISION_COND_SUCC;
- ret->methodState = METHOD_CONT;
-
- if (ret->decision == DECISION_COND_SUCC &&
- m->isKeyAvailable && m->getKey &&
- m->isKeyAvailable(sm, priv)) {
- u8 *key;
- size_t key_len;
- key = m->getKey(sm, priv, &key_len);
- if (key) {
- eap_ttls_ia_permute_inner_secret(
- sm, data, key, key_len);
- os_free(key);
- }
- }
- }
-#endif /* EAP_TTLS_VERSION */
+ return eap_peer_tls_derive_key(sm, &data->ssl, "ttls challenge", len);
}
ret->methodState = iret.methodState;
ret->decision = iret.decision;
}
- eap_ttlsv1_phase2_eap_finish(sm, data, ret);
return 0;
}
}
-static void eap_ttlsv1_permute_inner(struct eap_sm *sm,
- struct eap_ttls_data *data)
-{
-#if EAP_TTLS_VERSION > 0
- u8 session_key[2 * MSCHAPV2_KEY_LEN];
-
- if (data->ttls_version == 0)
- return;
-
- get_asymetric_start_key(data->master_key, session_key,
- MSCHAPV2_KEY_LEN, 0, 0);
- get_asymetric_start_key(data->master_key,
- session_key + MSCHAPV2_KEY_LEN,
- MSCHAPV2_KEY_LEN, 1, 0);
- eap_ttls_ia_permute_inner_secret(sm, data, session_key,
- sizeof(session_key));
-#endif /* EAP_TTLS_VERSION */
-}
-
-
static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm,
struct eap_ttls_data *data,
struct eap_method_ret *ret,
"implicit challenge");
return -1;
}
- peer_challenge = challenge + 1 + EAP_TTLS_MSCHAPV2_CHALLENGE_LEN;
pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_MS_CHAP_CHALLENGE,
RADIUS_VENDOR_ID_MICROSOFT, 1,
data->ident = challenge[EAP_TTLS_MSCHAPV2_CHALLENGE_LEN];
*pos++ = data->ident;
*pos++ = 0; /* Flags */
- os_memcpy(pos, peer_challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN);
+ if (os_get_random(pos, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN) < 0) {
+ os_free(challenge);
+ wpabuf_free(msg);
+ wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to get "
+ "random data for peer challenge");
+ return -1;
+ }
+ peer_challenge = pos;
pos += EAP_TTLS_MSCHAPV2_CHALLENGE_LEN;
os_memset(pos, 0, 8); /* Reserved, must be zero */
pos += 8;
password_len, pwhash, challenge,
peer_challenge, pos, data->auth_response,
data->master_key)) {
+ os_free(challenge);
wpabuf_free(msg);
wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to derive "
"response");
}
data->auth_response_valid = 1;
- eap_ttlsv1_permute_inner(sm, data);
-
pos += 24;
os_free(challenge);
AVP_PAD(buf, pos);
wpabuf_put(msg, pos - buf);
*resp = msg;
- if (sm->workaround && data->ttls_version == 0) {
+ if (sm->workaround) {
/* At least FreeRADIUS seems to be terminating
* EAP-TTLS/MSHCAPV2 without the expected MS-CHAP-v2 Success
* packet. */
wpabuf_put(msg, pos - buf);
*resp = msg;
- if (data->ttls_version > 0) {
- /* EAP-TTLSv1 uses TLS/IA FinalPhaseFinished to report success,
- * so do not allow connection to be terminated yet. */
- ret->methodState = METHOD_CONT;
- ret->decision = DECISION_COND_SUCC;
- } else {
- /* EAP-TTLS/MSCHAP does not provide tunneled success
- * notification, so assume that Phase2 succeeds. */
- ret->methodState = METHOD_DONE;
- ret->decision = DECISION_COND_SUCC;
- }
+ /* EAP-TTLS/MSCHAP does not provide tunneled success
+ * notification, so assume that Phase2 succeeds. */
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_COND_SUCC;
return 0;
}
wpabuf_put(msg, pos - buf);
*resp = msg;
- if (data->ttls_version > 0) {
- /* EAP-TTLSv1 uses TLS/IA FinalPhaseFinished to report success,
- * so do not allow connection to be terminated yet. */
- ret->methodState = METHOD_CONT;
- ret->decision = DECISION_COND_SUCC;
- } else {
- /* EAP-TTLS/PAP does not provide tunneled success notification,
- * so assume that Phase2 succeeds. */
- ret->methodState = METHOD_DONE;
- ret->decision = DECISION_COND_SUCC;
- }
+ /* EAP-TTLS/PAP does not provide tunneled success notification,
+ * so assume that Phase2 succeeds. */
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_COND_SUCC;
return 0;
}
wpabuf_put(msg, pos - buf);
*resp = msg;
- if (data->ttls_version > 0) {
- /* EAP-TTLSv1 uses TLS/IA FinalPhaseFinished to report success,
- * so do not allow connection to be terminated yet. */
- ret->methodState = METHOD_CONT;
- ret->decision = DECISION_COND_SUCC;
- } else {
- /* EAP-TTLS/CHAP does not provide tunneled success
- * notification, so assume that Phase2 succeeds. */
- ret->methodState = METHOD_DONE;
- ret->decision = DECISION_COND_SUCC;
- }
+ /* EAP-TTLS/CHAP does not provide tunneled success
+ * notification, so assume that Phase2 succeeds. */
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_COND_SUCC;
return 0;
}
}
-#if EAP_TTLS_VERSION > 0
-static struct wpabuf * eap_ttls_build_phase_finished(
- struct eap_sm *sm, struct eap_ttls_data *data, int id, int final)
-{
- struct wpabuf *req, *buf;
-
- buf = tls_connection_ia_send_phase_finished(sm->ssl_ctx,
- data->ssl.conn,
- final);
- if (buf == NULL)
- return NULL;
-
- req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TTLS,
- 1 + wpabuf_len(buf),
- EAP_CODE_RESPONSE, id);
- if (req == NULL) {
- wpabuf_free(buf);
- return NULL;
- }
-
- wpabuf_put_u8(req, data->ttls_version);
- wpabuf_put_buf(req, buf);
- wpabuf_free(buf);
- eap_update_len(req);
-
- return req;
-}
-#endif /* EAP_TTLS_VERSION */
-
-
struct ttls_parse_avp {
u8 *mschapv2;
u8 *eapdata;
wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 MSCHAPV2 "
"authentication succeeded");
- if (data->ttls_version > 0) {
- /*
- * EAP-TTLSv1 uses TLS/IA FinalPhaseFinished to report
- * success, so do not allow connection to be terminated
- * yet.
- */
- ret->methodState = METHOD_CONT;
- ret->decision = DECISION_COND_SUCC;
- } else {
- ret->methodState = METHOD_DONE;
- ret->decision = DECISION_UNCOND_SUCC;
- data->phase2_success = 1;
- }
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_UNCOND_SUCC;
+ data->phase2_success = 1;
/*
* Reply with empty data; authentication server will reply
}
-#if EAP_TTLS_VERSION > 0
-static void eap_ttls_final_phase_finished(struct eap_sm *sm,
- struct eap_ttls_data *data,
- struct eap_method_ret *ret,
- u8 identifier,
- struct wpabuf **out_data)
-{
- wpa_printf(MSG_DEBUG, "EAP-TTLS: FinalPhaseFinished received");
- wpa_printf(MSG_INFO, "EAP-TTLS: TLS/IA authentication succeeded");
- ret->methodState = METHOD_DONE;
- ret->decision = DECISION_UNCOND_SUCC;
- data->phase2_success = 1;
- *out_data = eap_ttls_build_phase_finished(sm, data, identifier, 1);
- eap_ttls_v1_derive_key(sm, data);
-}
-#endif /* EAP_TTLS_VERSION */
-
-
static int eap_ttls_implicit_identity_request(struct eap_sm *sm,
struct eap_ttls_data *data,
struct eap_method_ret *ret,
"processing failed");
retval = -1;
} else {
+ struct eap_peer_config *config = eap_get_config(sm);
+ if (resp == NULL &&
+ (config->pending_req_identity ||
+ config->pending_req_password ||
+ config->pending_req_otp ||
+ config->pending_req_new_password)) {
+ /*
+ * Use empty buffer to force implicit request
+ * processing when EAP request is re-processed after
+ * user input.
+ */
+ wpabuf_free(data->pending_phase2_req);
+ data->pending_phase2_req = wpabuf_alloc(0);
+ }
+
retval = eap_ttls_encrypt_response(sm, data, resp, identifier,
out_data);
}
if (retval)
goto done;
-#if EAP_TTLS_VERSION > 0
- if (data->ttls_version > 0 &&
- (in_decrypted == NULL || wpabuf_len(in_decrypted) == 0) &&
- tls_connection_ia_final_phase_finished(sm->ssl_ctx,
- data->ssl.conn)) {
- eap_ttls_final_phase_finished(sm, data, ret, identifier,
- out_data);
- goto done;
- }
-#endif /* EAP_TTLS_VERSION */
-
continue_req:
data->phase2_start = 0;
}
-static int eap_ttls_process_start(struct eap_sm *sm,
- struct eap_ttls_data *data, u8 flags,
- struct eap_method_ret *ret)
-{
- struct eap_peer_config *config = eap_get_config(sm);
-
- wpa_printf(MSG_DEBUG, "EAP-TTLS: Start (server ver=%d, own ver=%d)",
- flags & EAP_TLS_VERSION_MASK, data->ttls_version);
-#if EAP_TTLS_VERSION > 0
- if ((flags & EAP_TLS_VERSION_MASK) < data->ttls_version)
- data->ttls_version = flags & EAP_TLS_VERSION_MASK;
- if (data->force_ttls_version >= 0 &&
- data->force_ttls_version != data->ttls_version) {
- wpa_printf(MSG_WARNING, "EAP-TTLS: Failed to select "
- "forced TTLS version %d",
- data->force_ttls_version);
- ret->methodState = METHOD_DONE;
- ret->decision = DECISION_FAIL;
- ret->allowNotifications = FALSE;
- return -1;
- }
- wpa_printf(MSG_DEBUG, "EAP-TTLS: Using TTLS version %d",
- data->ttls_version);
-
- if (data->ttls_version > 0)
- data->ssl.tls_ia = 1;
-#endif /* EAP_TTLS_VERSION */
- if (!data->ssl_initialized &&
- eap_peer_tls_ssl_init(sm, &data->ssl, config)) {
- wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL.");
- return -1;
- }
- data->ssl_initialized = 1;
-
- wpa_printf(MSG_DEBUG, "EAP-TTLS: Start");
-
- return 0;
-}
-
-
static int eap_ttls_process_handshake(struct eap_sm *sm,
struct eap_ttls_data *data,
struct eap_method_ret *ret,
ret->methodState = METHOD_MAY_CONT;
}
data->phase2_start = 1;
- if (data->ttls_version == 0)
- eap_ttls_v0_derive_key(sm, data);
+ eap_ttls_v0_derive_key(sm, data);
if (*out_data == NULL || wpabuf_len(*out_data) == 0) {
if (eap_ttls_decrypt(sm, data, ret, identifier,
struct eap_ttls_data *data,
struct eap_method_ret *ret)
{
- if (data->ttls_version == 0 && ret->methodState == METHOD_DONE) {
+ if (ret->methodState == METHOD_DONE) {
ret->allowNotifications = FALSE;
if (ret->decision == DECISION_UNCOND_SUCC ||
ret->decision == DECISION_COND_SUCC) {
}
#endif /* EAP_TNC */
}
- } else if (data->ttls_version == 0 &&
- ret->methodState == METHOD_MAY_CONT &&
+ } else if (ret->methodState == METHOD_MAY_CONT &&
(ret->decision == DECISION_UNCOND_SUCC ||
ret->decision == DECISION_COND_SUCC)) {
wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication "
id = eap_get_id(reqData);
if (flags & EAP_TLS_FLAGS_START) {
- if (eap_ttls_process_start(sm, data, flags, ret) < 0)
- return NULL;
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Start (server ver=%d, own "
+ "ver=%d)", flags & EAP_TLS_VERSION_MASK,
+ data->ttls_version);
/* RFC 5281, Ch. 9.2:
* "This packet MAY contain additional information in the form
* For now, ignore any potential extra data.
*/
left = 0;
- } else if (!data->ssl_initialized) {
- wpa_printf(MSG_DEBUG, "EAP-TTLS: First message did not "
- "include Start flag");
- ret->methodState = METHOD_DONE;
- ret->decision = DECISION_FAIL;
- ret->allowNotifications = FALSE;
- return NULL;
}
resp = NULL;
return NULL;
}
+ pos = os_strstr(phase1, "dev_pw_id=");
+ if (pos && cfg.pin)
+ cfg.dev_pw_id = atoi(pos + 10);
+
res = eap_wsc_new_ap_settings(&new_ap_settings, phase1);
if (res < 0) {
os_free(data);
os_free(data);
return NULL;
}
- data->fragment_size = WSC_FRAGMENT_SIZE;
+ res = eap_get_config_fragment_size(sm);
+ if (res > 0)
+ data->fragment_size = res;
+ else
+ data->fragment_size = WSC_FRAGMENT_SIZE;
+ wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment size limit %u",
+ (unsigned int) data->fragment_size);
if (registrar && cfg.pin) {
- wps_registrar_add_pin(data->wps_ctx->registrar, NULL,
+ wps_registrar_add_pin(data->wps_ctx->registrar, NULL, NULL,
cfg.pin, cfg.pin_len, 0);
}
#include "common.h"
#include "crypto/dh_groups.h"
+#include "crypto/random.h"
#include "ikev2.h"
}
/* RFC 4306, Section 3.4:
- * The length of DH public value MUST be equal to the lenght of the
+ * The length of DH public value MUST be equal to the length of the
* prime modulus.
*/
if (kei_len - 4 != data->dh->prime_len) {
data->r_spi, IKEV2_SPI_LEN);
data->r_nonce_len = IKEV2_NONCE_MIN_LEN;
- if (os_get_random(data->r_nonce, data->r_nonce_len))
+ if (random_get_bytes(data->r_nonce, data->r_nonce_len))
return NULL;
#ifdef CCNS_PL
/* Zeros are removed incorrectly from the beginning of the nonces in
imc = tnc_imc[imcID];
os_free(imc->supported_types);
imc->supported_types =
- os_malloc(typeCount * sizeof(TNC_MessageTypeList));
+ os_malloc(typeCount * sizeof(TNC_MessageType));
if (imc->supported_types == NULL)
return TNC_RESULT_FATAL;
os_memcpy(imc->supported_types, supportedTypes,
- typeCount * sizeof(TNC_MessageTypeList));
+ typeCount * sizeof(TNC_MessageType));
imc->num_supported_types = typeCount;
return TNC_RESULT_SUCCESS;
void *eap_sim_db_priv;
Boolean backend_auth;
int eap_server;
+ u16 pwd_group;
u8 *pac_opaque_encr_key;
u8 *eap_fast_a_id;
size_t eap_fast_a_id_len;
int tnc;
struct wps_context *wps;
const struct wpabuf *assoc_wps_ie;
+ const struct wpabuf *assoc_p2p_ie;
const u8 *peer_addr;
+ int fragment_size;
+
+ int pbc_in_m1;
};
int eap_sm_method_pending(struct eap_sm *sm);
const u8 * eap_get_identity(struct eap_sm *sm, size_t *len);
struct eap_eapol_interface * eap_get_interface(struct eap_sm *sm);
+void eap_server_clear_identity(struct eap_sm *sm);
#endif /* EAP_H */
/* Full authenticator state machine local variables */
- /* Long-term (maintained betwen packets) */
+ /* Long-term (maintained between packets) */
EapType currentMethod;
int currentId;
enum {
int pac_key_refresh_time;
int eap_sim_aka_result_ind;
int tnc;
+ u16 pwd_group;
struct wps_context *wps;
struct wpabuf *assoc_wps_ie;
+ struct wpabuf *assoc_p2p_ie;
Boolean start_reauth;
u8 peer_addr[ETH_ALEN];
+
+ /* Fragmentation size for EAP method init() handler */
+ int fragment_size;
+
+ int pbc_in_m1;
};
int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len,
int eap_server_wsc_register(void);
int eap_server_ikev2_register(void);
int eap_server_tnc_register(void);
+int eap_server_pwd_register(void);
#endif /* EAP_SERVER_METHODS_H */
{
SM_ENTRY(EAP, INITIALIZE);
+ if (sm->eap_if.eapRestart && !sm->eap_server && sm->identity) {
+ /*
+ * Need to allow internal Identity method to be used instead
+ * of passthrough at the beginning of reauthentication.
+ */
+ eap_server_clear_identity(sm);
+ }
+
sm->currentId = -1;
sm->eap_if.eapSuccess = FALSE;
sm->eap_if.eapFail = FALSE;
not_found:
/* not found - remove from the list */
- os_memmove(&sm->user->methods[i], &sm->user->methods[i + 1],
- (EAP_MAX_METHODS - i - 1) *
- sizeof(sm->user->methods[0]));
+ if (i + 1 < EAP_MAX_METHODS) {
+ os_memmove(&sm->user->methods[i],
+ &sm->user->methods[i + 1],
+ (EAP_MAX_METHODS - i - 1) *
+ sizeof(sm->user->methods[0]));
+ }
sm->user->methods[EAP_MAX_METHODS - 1].vendor =
EAP_VENDOR_IETF;
sm->user->methods[EAP_MAX_METHODS - 1].method = EAP_TYPE_NONE;
sm->wps = conf->wps;
if (conf->assoc_wps_ie)
sm->assoc_wps_ie = wpabuf_dup(conf->assoc_wps_ie);
+ if (conf->assoc_p2p_ie)
+ sm->assoc_p2p_ie = wpabuf_dup(conf->assoc_p2p_ie);
if (conf->peer_addr)
os_memcpy(sm->peer_addr, conf->peer_addr, ETH_ALEN);
+ sm->fragment_size = conf->fragment_size;
+ sm->pwd_group = conf->pwd_group;
+ sm->pbc_in_m1 = conf->pbc_in_m1;
wpa_printf(MSG_DEBUG, "EAP: Server state machine created");
os_free(sm->eap_if.aaaEapKeyData);
eap_user_free(sm->user);
wpabuf_free(sm->assoc_wps_ie);
+ wpabuf_free(sm->assoc_p2p_ie);
os_free(sm);
}
{
return &sm->eap_if;
}
+
+
+/**
+ * eap_server_clear_identity - Clear EAP identity information
+ * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
+ *
+ * This function can be used to clear the EAP identity information in the EAP
+ * server context. This allows the EAP/Identity method to be used again after
+ * EAPOL-Start or EAPOL-Logoff.
+ */
+void eap_server_clear_identity(struct eap_sm *sm)
+{
+ os_free(sm->identity);
+ sm->identity = NULL;
+}
#include "common.h"
#include "crypto/sha256.h"
#include "crypto/crypto.h"
+#include "crypto/random.h"
#include "eap_common/eap_sim_common.h"
#include "eap_server/eap_i.h"
#include "eap_server/eap_sim_db.h"
return NULL;
data->eap_method = EAP_TYPE_AKA_PRIME;
- data->network_name = os_malloc(os_strlen(network_name));
+ data->network_name = (u8 *) os_strdup(network_name);
if (data->network_name == NULL) {
os_free(data);
return NULL;
}
data->network_name_len = os_strlen(network_name);
- os_memcpy(data->network_name, network_name, data->network_name_len);
data->state = IDENTITY;
eap_aka_determine_identity(sm, data, 1, 0);
const u8 *nonce_s)
{
os_free(data->next_pseudonym);
- data->next_pseudonym =
- eap_sim_db_get_next_pseudonym(sm->eap_sim_db_priv, 1);
+ if (nonce_s == NULL) {
+ data->next_pseudonym =
+ eap_sim_db_get_next_pseudonym(sm->eap_sim_db_priv, 1);
+ } else {
+ /* Do not update pseudonym during re-authentication */
+ data->next_pseudonym = NULL;
+ }
os_free(data->next_reauth_id);
if (data->counter <= EAP_AKA_MAX_FAST_REAUTHS) {
data->next_reauth_id =
wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Re-authentication");
- if (os_get_random(data->nonce_s, EAP_SIM_NONCE_S_LEN))
+ if (random_get_bytes(data->nonce_s, EAP_SIM_NONCE_S_LEN))
return NULL;
wpa_hexdump_key(MSG_MSGDUMP, "EAP-AKA: NONCE_S",
data->nonce_s, EAP_SIM_NONCE_S_LEN);
identity_len = id2_len;
}
- if (data->next_pseudonym) {
- eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, identity,
- identity_len, data->next_pseudonym);
- data->next_pseudonym = NULL;
- }
if (data->next_reauth_id) {
if (data->eap_method == EAP_TYPE_AKA_PRIME) {
#ifdef EAP_SERVER_AKA_PRIME
#include "crypto/aes_wrap.h"
#include "crypto/sha1.h"
#include "crypto/tls.h"
+#include "crypto/random.h"
#include "eap_common/eap_tlv_common.h"
#include "eap_common/eap_fast_common.h"
#include "eap_i.h"
binding->version = EAP_FAST_VERSION;
binding->received_version = data->peer_version;
binding->subtype = EAP_TLV_CRYPTO_BINDING_SUBTYPE_REQUEST;
- if (os_get_random(binding->nonce, sizeof(binding->nonce)) < 0) {
+ if (random_get_bytes(binding->nonce, sizeof(binding->nonce)) < 0) {
wpabuf_free(buf);
return NULL;
}
struct eap_tlv_result_tlv *result;
struct os_time now;
- if (os_get_random(pac_key, EAP_FAST_PAC_KEY_LEN) < 0 ||
+ if (random_get_bytes(pac_key, EAP_FAST_PAC_KEY_LEN) < 0 ||
os_get_time(&now) < 0)
return NULL;
wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Generated PAC-Key",
#include "includes.h"
#include "common.h"
+#include "crypto/random.h"
#include "eap_server/eap_i.h"
#include "eap_common/eap_gpsk_common.h"
wpa_printf(MSG_DEBUG, "EAP-GPSK: Request/GPSK-1");
- if (os_get_random(data->rand_server, EAP_GPSK_RAND_LEN)) {
+ if (random_get_bytes(data->rand_server, EAP_GPSK_RAND_LEN)) {
wpa_printf(MSG_ERROR, "EAP-GPSK: Failed to get random data");
eap_gpsk_state(data, FAILURE);
return NULL;
if (data == NULL)
return NULL;
data->state = MSG;
- data->fragment_size = IKEV2_FRAGMENT_SIZE;
+ data->fragment_size = sm->fragment_size > 0 ? sm->fragment_size :
+ IKEV2_FRAGMENT_SIZE;
data->ikev2.state = SA_INIT;
data->ikev2.peer_auth = PEER_AUTH_SECRET;
data->ikev2.key_pad = (u8 *) os_strdup("Key Pad for EAP-IKEv2");
#include "includes.h"
#include "common.h"
+#include "crypto/random.h"
#include "eap_i.h"
#include "eap_common/chap.h"
struct eap_md5_data *data = priv;
struct wpabuf *req;
- if (os_get_random(data->challenge, CHALLENGE_LEN)) {
+ if (random_get_bytes(data->challenge, CHALLENGE_LEN)) {
wpa_printf(MSG_ERROR, "EAP-MD5: Failed to get random data");
data->state = FAILURE;
return NULL;
const char * eap_server_get_name(int vendor, EapType type)
{
struct eap_method *m;
+ if (vendor == EAP_VENDOR_IETF && type == EAP_TYPE_EXPANDED)
+ return "expanded";
for (m = eap_methods; m; m = m->next) {
if (m->vendor == vendor && m->method == type)
return m->name;
#include "common.h"
#include "crypto/ms_funcs.h"
+#include "crypto/random.h"
#include "eap_i.h"
size_t ms_len;
if (!data->auth_challenge_from_tls &&
- os_get_random(data->auth_challenge, CHALLENGE_LEN)) {
+ random_get_bytes(data->auth_challenge, CHALLENGE_LEN)) {
wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to get random "
"data");
data->state = FAILURE;
#include "includes.h"
#include "common.h"
+#include "crypto/random.h"
#include "eap_server/eap_i.h"
#include "eap_common/eap_pax_common.h"
wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-1 (sending)");
- if (os_get_random(data->rand.r.x, EAP_PAX_RAND_LEN)) {
+ if (random_get_bytes(data->rand.r.x, EAP_PAX_RAND_LEN)) {
wpa_printf(MSG_ERROR, "EAP-PAX: Failed to get random data");
data->state = FAILURE;
return NULL;
#include "common.h"
#include "crypto/sha1.h"
#include "crypto/tls.h"
+#include "crypto/random.h"
#include "eap_i.h"
#include "eap_tls_common.h"
#include "eap_common/eap_tlv_common.h"
* in the end of the label just before ISK; is that just a typo?)
*/
wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TempKey", tk, 40);
- peap_prfplus(data->peap_version, tk, 40, "Inner Methods Compound Keys",
- isk, sizeof(isk), imck, sizeof(imck));
+ if (peap_prfplus(data->peap_version, tk, 40,
+ "Inner Methods Compound Keys",
+ isk, sizeof(isk), imck, sizeof(imck)) < 0) {
+ os_free(tk);
+ return -1;
+ }
wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IMCK (IPMKj)",
imck, sizeof(imck));
#endif /* EAP_SERVER_TNC */
if (eap_peap_derive_cmk(sm, data) < 0 ||
- os_get_random(data->binding_nonce, 32)) {
+ random_get_bytes(data->binding_nonce, 32)) {
wpabuf_free(buf);
return NULL;
}
* termination for this label while the one used for deriving
* IPMK|CMK did not use null termination.
*/
- peap_prfplus(data->peap_version, data->ipmk, 40,
- "Session Key Generating Function",
- (u8 *) "\00", 1, csk, sizeof(csk));
+ if (peap_prfplus(data->peap_version, data->ipmk, 40,
+ "Session Key Generating Function",
+ (u8 *) "\00", 1, csk, sizeof(csk)) < 0)
+ return NULL;
wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CSK", csk, sizeof(csk));
eapKeyData = os_malloc(EAP_TLS_KEY_LEN);
if (eapKeyData) {
#include "common.h"
#include "crypto/aes_wrap.h"
+#include "crypto/random.h"
#include "eap_common/eap_psk_common.h"
#include "eap_server/eap_i.h"
wpa_printf(MSG_DEBUG, "EAP-PSK: PSK-1 (sending)");
- if (os_get_random(data->rand_s, EAP_PSK_RAND_LEN)) {
+ if (random_get_bytes(data->rand_s, EAP_PSK_RAND_LEN)) {
wpa_printf(MSG_ERROR, "EAP-PSK: Failed to get random data");
data->state = FAILURE;
return NULL;
os_memcpy(buf, data->id_s, data->id_s_len);
os_memcpy(buf + data->id_s_len, data->rand_p, EAP_PSK_RAND_LEN);
- if (omac1_aes_128(data->ak, buf, buflen, psk->mac_s))
+ if (omac1_aes_128(data->ak, buf, buflen, psk->mac_s)) {
+ os_free(buf);
goto fail;
+ }
os_free(buf);
if (eap_psk_derive_keys(data->kdk, data->rand_p, data->tek, data->msk,
--- /dev/null
+/*
+ * hostapd / EAP-pwd (RFC 5931) server
+ * Copyright (c) 2010, Dan Harkins <dharkins@lounge.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the BSD license.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_server/eap_i.h"
+#include "eap_common/eap_pwd_common.h"
+
+
+struct eap_pwd_data {
+ enum {
+ PWD_ID_Req, PWD_Commit_Req, PWD_Confirm_Req, SUCCESS, FAILURE
+ } state;
+ u8 *id_peer;
+ size_t id_peer_len;
+ u8 *id_server;
+ size_t id_server_len;
+ u8 *password;
+ size_t password_len;
+ u32 token;
+ u16 group_num;
+ EAP_PWD_group *grp;
+
+ BIGNUM *k;
+ BIGNUM *private_value;
+ BIGNUM *peer_scalar;
+ BIGNUM *my_scalar;
+ EC_POINT *my_element;
+ EC_POINT *peer_element;
+
+ u8 my_confirm[SHA256_DIGEST_LENGTH];
+
+ u8 msk[EAP_MSK_LEN];
+ u8 emsk[EAP_EMSK_LEN];
+
+ BN_CTX *bnctx;
+};
+
+
+static const char * eap_pwd_state_txt(int state)
+{
+ switch (state) {
+ case PWD_ID_Req:
+ return "PWD-ID-Req";
+ case PWD_Commit_Req:
+ return "PWD-Commit-Req";
+ case PWD_Confirm_Req:
+ return "PWD-Confirm-Req";
+ case SUCCESS:
+ return "SUCCESS";
+ case FAILURE:
+ return "FAILURE";
+ default:
+ return "PWD-Unk";
+ }
+}
+
+
+static void eap_pwd_state(struct eap_pwd_data *data, int state)
+{
+ wpa_printf(MSG_DEBUG, "EAP-pwd: %s -> %s",
+ eap_pwd_state_txt(data->state), eap_pwd_state_txt(state));
+ data->state = state;
+}
+
+
+static void * eap_pwd_init(struct eap_sm *sm)
+{
+ struct eap_pwd_data *data;
+
+ if (sm->user == NULL || sm->user->password == NULL ||
+ sm->user->password_len == 0) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): Password is not "
+ "configured");
+ return NULL;
+ }
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+
+ data->group_num = sm->pwd_group;
+ wpa_printf(MSG_DEBUG, "EAP-pwd: Selected group number %d",
+ data->group_num);
+ data->state = PWD_ID_Req;
+
+ data->id_server = (u8 *) os_strdup("server");
+ if (data->id_server)
+ data->id_server_len = os_strlen((char *) data->id_server);
+
+ data->password = os_malloc(sm->user->password_len);
+ if (data->password == NULL) {
+ wpa_printf(MSG_INFO, "EAP-PWD: Memory allocation password "
+ "fail");
+ os_free(data->id_server);
+ os_free(data);
+ return NULL;
+ }
+ data->password_len = sm->user->password_len;
+ os_memcpy(data->password, sm->user->password, data->password_len);
+
+ data->bnctx = BN_CTX_new();
+ if (data->bnctx == NULL) {
+ wpa_printf(MSG_INFO, "EAP-PWD: bn context allocation fail");
+ os_free(data->password);
+ os_free(data->id_server);
+ os_free(data);
+ return NULL;
+ }
+
+ return data;
+}
+
+
+static void eap_pwd_reset(struct eap_sm *sm, void *priv)
+{
+ struct eap_pwd_data *data = priv;
+
+ BN_free(data->private_value);
+ BN_free(data->peer_scalar);
+ BN_free(data->my_scalar);
+ BN_free(data->k);
+ BN_CTX_free(data->bnctx);
+ EC_POINT_free(data->my_element);
+ EC_POINT_free(data->peer_element);
+ os_free(data->id_peer);
+ os_free(data->id_server);
+ os_free(data->password);
+ if (data->grp) {
+ EC_GROUP_free(data->grp->group);
+ EC_POINT_free(data->grp->pwe);
+ BN_free(data->grp->order);
+ BN_free(data->grp->prime);
+ os_free(data->grp);
+ }
+ os_free(data);
+}
+
+
+static struct wpabuf *
+eap_pwd_build_id_req(struct eap_sm *sm, struct eap_pwd_data *data, u8 id)
+{
+ struct wpabuf *req;
+
+ wpa_printf(MSG_DEBUG, "EAP-pwd: ID/Request");
+ req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD,
+ sizeof(struct eap_pwd_hdr) +
+ sizeof(struct eap_pwd_id) + data->id_server_len,
+ EAP_CODE_REQUEST, id);
+ if (req == NULL) {
+ eap_pwd_state(data, FAILURE);
+ return NULL;
+ }
+
+ /* an lfsr is good enough to generate unpredictable tokens */
+ data->token = os_random();
+ wpabuf_put_u8(req, EAP_PWD_OPCODE_ID_EXCH);
+ wpabuf_put_be16(req, data->group_num);
+ wpabuf_put_u8(req, EAP_PWD_DEFAULT_RAND_FUNC);
+ wpabuf_put_u8(req, EAP_PWD_DEFAULT_PRF);
+ wpabuf_put_data(req, &data->token, sizeof(data->token));
+ wpabuf_put_u8(req, EAP_PWD_PREP_NONE);
+ wpabuf_put_data(req, data->id_server, data->id_server_len);
+
+ return req;
+}
+
+
+static struct wpabuf *
+eap_pwd_build_commit_req(struct eap_sm *sm, struct eap_pwd_data *data, u8 id)
+{
+ struct wpabuf *req = NULL;
+ BIGNUM *mask = NULL, *x = NULL, *y = NULL;
+ u8 *scalar = NULL, *element = NULL;
+ u16 offset;
+
+ wpa_printf(MSG_DEBUG, "EAP-pwd: Commit/Request");
+
+ if (((data->private_value = BN_new()) == NULL) ||
+ ((data->my_element = EC_POINT_new(data->grp->group)) == NULL) ||
+ ((data->my_scalar = BN_new()) == NULL) ||
+ ((mask = BN_new()) == NULL)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): scalar allocation "
+ "fail");
+ goto fin;
+ }
+
+ BN_rand_range(data->private_value, data->grp->order);
+ BN_rand_range(mask, data->grp->order);
+ BN_add(data->my_scalar, data->private_value, mask);
+ BN_mod(data->my_scalar, data->my_scalar, data->grp->order,
+ data->bnctx);
+
+ if (!EC_POINT_mul(data->grp->group, data->my_element, NULL,
+ data->grp->pwe, mask, data->bnctx)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): element allocation "
+ "fail");
+ eap_pwd_state(data, FAILURE);
+ goto fin;
+ }
+
+ if (!EC_POINT_invert(data->grp->group, data->my_element, data->bnctx))
+ {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): element inversion "
+ "fail");
+ goto fin;
+ }
+ BN_free(mask);
+
+ if (((x = BN_new()) == NULL) ||
+ ((y = BN_new()) == NULL)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): point allocation "
+ "fail");
+ goto fin;
+ }
+ if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
+ data->my_element, x, y,
+ data->bnctx)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): point assignment "
+ "fail");
+ goto fin;
+ }
+
+ if (((scalar = os_malloc(BN_num_bytes(data->grp->order))) == NULL) ||
+ ((element = os_malloc(BN_num_bytes(data->grp->prime) * 2)) ==
+ NULL)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): data allocation fail");
+ goto fin;
+ }
+
+ /*
+ * bignums occupy as little memory as possible so one that is
+ * sufficiently smaller than the prime or order might need pre-pending
+ * with zeros.
+ */
+ os_memset(scalar, 0, BN_num_bytes(data->grp->order));
+ os_memset(element, 0, BN_num_bytes(data->grp->prime) * 2);
+ offset = BN_num_bytes(data->grp->order) -
+ BN_num_bytes(data->my_scalar);
+ BN_bn2bin(data->my_scalar, scalar + offset);
+
+ offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
+ BN_bn2bin(x, element + offset);
+ offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
+ BN_bn2bin(y, element + BN_num_bytes(data->grp->prime) + offset);
+
+ req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD,
+ sizeof(struct eap_pwd_hdr) +
+ (2 * BN_num_bytes(data->grp->prime)) +
+ BN_num_bytes(data->grp->order),
+ EAP_CODE_REQUEST, id);
+ if (req == NULL)
+ goto fin;
+ wpabuf_put_u8(req, EAP_PWD_OPCODE_COMMIT_EXCH);
+
+ /* We send the element as (x,y) followed by the scalar */
+ wpabuf_put_data(req, element, (2 * BN_num_bytes(data->grp->prime)));
+ wpabuf_put_data(req, scalar, BN_num_bytes(data->grp->order));
+
+fin:
+ os_free(scalar);
+ os_free(element);
+ BN_free(x);
+ BN_free(y);
+ if (req == NULL)
+ eap_pwd_state(data, FAILURE);
+
+ return req;
+}
+
+
+static struct wpabuf *
+eap_pwd_build_confirm_req(struct eap_sm *sm, struct eap_pwd_data *data, u8 id)
+{
+ struct wpabuf *req = NULL;
+ BIGNUM *x = NULL, *y = NULL;
+ HMAC_CTX ctx;
+ u8 conf[SHA256_DIGEST_LENGTH], *cruft = NULL, *ptr;
+ u16 grp;
+ int offset;
+
+ wpa_printf(MSG_DEBUG, "EAP-pwd: Confirm/Request");
+
+ /* Each component of the cruft will be at most as big as the prime */
+ if (((cruft = os_malloc(BN_num_bytes(data->grp->prime))) == NULL) ||
+ ((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): debug allocation "
+ "fail");
+ goto fin;
+ }
+
+ /*
+ * commit is H(k | server_element | server_scalar | peer_element |
+ * peer_scalar | ciphersuite)
+ */
+ H_Init(&ctx);
+
+ /*
+ * Zero the memory each time because this is mod prime math and some
+ * value may start with a few zeros and the previous one did not.
+ *
+ * First is k
+ */
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k);
+ BN_bn2bin(data->k, cruft + offset);
+ H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+
+ /* server element: x, y */
+ if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
+ data->my_element, x, y,
+ data->bnctx)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
+ "assignment fail");
+ goto fin;
+ }
+
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
+ BN_bn2bin(x, cruft + offset);
+ H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
+ BN_bn2bin(y, cruft + offset);
+ H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+
+ /* server scalar */
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->order) -
+ BN_num_bytes(data->my_scalar);
+ BN_bn2bin(data->my_scalar, cruft + offset);
+ H_Update(&ctx, cruft, BN_num_bytes(data->grp->order));
+
+ /* peer element: x, y */
+ if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
+ data->peer_element, x, y,
+ data->bnctx)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
+ "assignment fail");
+ goto fin;
+ }
+
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
+ BN_bn2bin(x, cruft + offset);
+ H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
+ BN_bn2bin(y, cruft + offset);
+ H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+
+ /* peer scalar */
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->order) -
+ BN_num_bytes(data->peer_scalar);
+ BN_bn2bin(data->peer_scalar, cruft + offset);
+ H_Update(&ctx, cruft, BN_num_bytes(data->grp->order));
+
+ /* ciphersuite */
+ grp = htons(data->group_num);
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ ptr = cruft;
+ os_memcpy(ptr, &grp, sizeof(u16));
+ ptr += sizeof(u16);
+ *ptr = EAP_PWD_DEFAULT_RAND_FUNC;
+ ptr += sizeof(u8);
+ *ptr = EAP_PWD_DEFAULT_PRF;
+ ptr += sizeof(u8);
+ H_Update(&ctx, cruft, ptr-cruft);
+
+ /* all done with the random function */
+ H_Final(&ctx, conf);
+ os_memcpy(data->my_confirm, conf, SHA256_DIGEST_LENGTH);
+
+ req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD,
+ sizeof(struct eap_pwd_hdr) + SHA256_DIGEST_LENGTH,
+ EAP_CODE_REQUEST, id);
+ if (req == NULL)
+ goto fin;
+
+ wpabuf_put_u8(req, EAP_PWD_OPCODE_CONFIRM_EXCH);
+ wpabuf_put_data(req, conf, SHA256_DIGEST_LENGTH);
+
+fin:
+ os_free(cruft);
+ BN_free(x);
+ BN_free(y);
+ if (req == NULL)
+ eap_pwd_state(data, FAILURE);
+
+ return req;
+}
+
+
+static struct wpabuf *
+eap_pwd_build_req(struct eap_sm *sm, void *priv, u8 id)
+{
+ struct eap_pwd_data *data = priv;
+
+ switch (data->state) {
+ case PWD_ID_Req:
+ return eap_pwd_build_id_req(sm, data, id);
+ case PWD_Commit_Req:
+ return eap_pwd_build_commit_req(sm, data, id);
+ case PWD_Confirm_Req:
+ return eap_pwd_build_confirm_req(sm, data, id);
+ default:
+ wpa_printf(MSG_INFO, "EAP-pwd: Unknown state %d in build_req",
+ data->state);
+ break;
+ }
+
+ return NULL;
+}
+
+
+static Boolean eap_pwd_check(struct eap_sm *sm, void *priv,
+ struct wpabuf *respData)
+{
+ struct eap_pwd_data *data = priv;
+ const u8 *pos;
+ size_t len;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PWD, respData, &len);
+ if (pos == NULL || len < 1) {
+ wpa_printf(MSG_INFO, "EAP-pwd: Invalid frame");
+ return TRUE;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-pwd: Received frame: opcode=%d", *pos);
+
+ if (data->state == PWD_ID_Req && *pos == EAP_PWD_OPCODE_ID_EXCH)
+ return FALSE;
+
+ if (data->state == PWD_Commit_Req &&
+ *pos == EAP_PWD_OPCODE_COMMIT_EXCH)
+ return FALSE;
+
+ if (data->state == PWD_Confirm_Req &&
+ *pos == EAP_PWD_OPCODE_CONFIRM_EXCH)
+ return FALSE;
+
+ wpa_printf(MSG_INFO, "EAP-pwd: Unexpected opcode=%d in state=%d",
+ *pos, data->state);
+
+ return TRUE;
+}
+
+
+static void eap_pwd_process_id_resp(struct eap_sm *sm,
+ struct eap_pwd_data *data,
+ const u8 *payload, size_t payload_len)
+{
+ struct eap_pwd_id *id;
+
+ if (payload_len < sizeof(struct eap_pwd_id)) {
+ wpa_printf(MSG_INFO, "EAP-pwd: Invalid ID response");
+ return;
+ }
+
+ id = (struct eap_pwd_id *) payload;
+ if ((data->group_num != be_to_host16(id->group_num)) ||
+ (id->random_function != EAP_PWD_DEFAULT_RAND_FUNC) ||
+ (os_memcmp(id->token, (u8 *)&data->token, sizeof(data->token))) ||
+ (id->prf != EAP_PWD_DEFAULT_PRF)) {
+ wpa_printf(MSG_INFO, "EAP-pwd: peer changed parameters");
+ eap_pwd_state(data, FAILURE);
+ return;
+ }
+ data->id_peer = os_malloc(payload_len - sizeof(struct eap_pwd_id));
+ if (data->id_peer == NULL) {
+ wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail");
+ return;
+ }
+ data->id_peer_len = payload_len - sizeof(struct eap_pwd_id);
+ os_memcpy(data->id_peer, id->identity, data->id_peer_len);
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-PWD (server): peer sent id of",
+ data->id_peer, data->id_peer_len);
+
+ if ((data->grp = os_malloc(sizeof(EAP_PWD_group))) == NULL) {
+ wpa_printf(MSG_INFO, "EAP-PWD: failed to allocate memory for "
+ "group");
+ return;
+ }
+ if (compute_password_element(data->grp, data->group_num,
+ data->password, data->password_len,
+ data->id_server, data->id_server_len,
+ data->id_peer, data->id_peer_len,
+ (u8 *) &data->token)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): unable to compute "
+ "PWE");
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-PWD (server): computed %d bit PWE...",
+ BN_num_bits(data->grp->prime));
+
+ eap_pwd_state(data, PWD_Commit_Req);
+}
+
+
+static void
+eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data,
+ const u8 *payload, size_t payload_len)
+{
+ u8 *ptr;
+ BIGNUM *x = NULL, *y = NULL, *cofactor = NULL;
+ EC_POINT *K = NULL, *point = NULL;
+ int res = 0;
+
+ wpa_printf(MSG_DEBUG, "EAP-pwd: Received commit response");
+
+ if (((data->peer_scalar = BN_new()) == NULL) ||
+ ((data->k = BN_new()) == NULL) ||
+ ((cofactor = BN_new()) == NULL) ||
+ ((x = BN_new()) == NULL) ||
+ ((y = BN_new()) == NULL) ||
+ ((point = EC_POINT_new(data->grp->group)) == NULL) ||
+ ((K = EC_POINT_new(data->grp->group)) == NULL) ||
+ ((data->peer_element = EC_POINT_new(data->grp->group)) == NULL)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): peer data allocation "
+ "fail");
+ goto fin;
+ }
+
+ if (!EC_GROUP_get_cofactor(data->grp->group, cofactor, NULL)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): unable to get "
+ "cofactor for curve");
+ goto fin;
+ }
+
+ /* element, x then y, followed by scalar */
+ ptr = (u8 *) payload;
+ BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), x);
+ ptr += BN_num_bytes(data->grp->prime);
+ BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), y);
+ ptr += BN_num_bytes(data->grp->prime);
+ BN_bin2bn(ptr, BN_num_bytes(data->grp->order), data->peer_scalar);
+ if (!EC_POINT_set_affine_coordinates_GFp(data->grp->group,
+ data->peer_element, x, y,
+ data->bnctx)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): setting peer element "
+ "fail");
+ goto fin;
+ }
+
+ /* check to ensure peer's element is not in a small sub-group */
+ if (BN_cmp(cofactor, BN_value_one())) {
+ if (!EC_POINT_mul(data->grp->group, point, NULL,
+ data->peer_element, cofactor, NULL)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): cannot "
+ "multiply peer element by order");
+ goto fin;
+ }
+ if (EC_POINT_is_at_infinity(data->grp->group, point)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): peer element "
+ "is at infinity!\n");
+ goto fin;
+ }
+ }
+
+ /* compute the shared key, k */
+ if ((!EC_POINT_mul(data->grp->group, K, NULL, data->grp->pwe,
+ data->peer_scalar, data->bnctx)) ||
+ (!EC_POINT_add(data->grp->group, K, K, data->peer_element,
+ data->bnctx)) ||
+ (!EC_POINT_mul(data->grp->group, K, NULL, K, data->private_value,
+ data->bnctx))) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): computing shared key "
+ "fail");
+ goto fin;
+ }
+
+ /* ensure that the shared key isn't in a small sub-group */
+ if (BN_cmp(cofactor, BN_value_one())) {
+ if (!EC_POINT_mul(data->grp->group, K, NULL, K, cofactor,
+ NULL)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): cannot "
+ "multiply shared key point by order!\n");
+ goto fin;
+ }
+ }
+
+ /*
+ * This check is strictly speaking just for the case above where
+ * co-factor > 1 but it was suggested that even though this is probably
+ * never going to happen it is a simple and safe check "just to be
+ * sure" so let's be safe.
+ */
+ if (EC_POINT_is_at_infinity(data->grp->group, K)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): shared key point is "
+ "at infinity");
+ goto fin;
+ }
+ if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, K, data->k,
+ NULL, data->bnctx)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): unable to extract "
+ "shared secret from secret point");
+ goto fin;
+ }
+ res = 1;
+
+fin:
+ EC_POINT_free(K);
+ EC_POINT_free(point);
+ BN_free(cofactor);
+ BN_free(x);
+ BN_free(y);
+
+ if (res)
+ eap_pwd_state(data, PWD_Confirm_Req);
+ else
+ eap_pwd_state(data, FAILURE);
+}
+
+
+static void
+eap_pwd_process_confirm_resp(struct eap_sm *sm, struct eap_pwd_data *data,
+ const u8 *payload, size_t payload_len)
+{
+ BIGNUM *x = NULL, *y = NULL;
+ HMAC_CTX ctx;
+ u32 cs;
+ u16 grp;
+ u8 conf[SHA256_DIGEST_LENGTH], *cruft = NULL, *ptr;
+ int offset;
+
+ /* build up the ciphersuite: group | random_function | prf */
+ grp = htons(data->group_num);
+ ptr = (u8 *) &cs;
+ os_memcpy(ptr, &grp, sizeof(u16));
+ ptr += sizeof(u16);
+ *ptr = EAP_PWD_DEFAULT_RAND_FUNC;
+ ptr += sizeof(u8);
+ *ptr = EAP_PWD_DEFAULT_PRF;
+
+ /* each component of the cruft will be at most as big as the prime */
+ if (((cruft = os_malloc(BN_num_bytes(data->grp->prime))) == NULL) ||
+ ((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (peer): allocation fail");
+ goto fin;
+ }
+
+ /*
+ * commit is H(k | peer_element | peer_scalar | server_element |
+ * server_scalar | ciphersuite)
+ */
+ H_Init(&ctx);
+
+ /* k */
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k);
+ BN_bn2bin(data->k, cruft + offset);
+ H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+
+ /* peer element: x, y */
+ if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
+ data->peer_element, x, y,
+ data->bnctx)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
+ "assignment fail");
+ goto fin;
+ }
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
+ BN_bn2bin(x, cruft + offset);
+ H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
+ BN_bn2bin(y, cruft + offset);
+ H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+
+ /* peer scalar */
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->order) -
+ BN_num_bytes(data->peer_scalar);
+ BN_bn2bin(data->peer_scalar, cruft + offset);
+ H_Update(&ctx, cruft, BN_num_bytes(data->grp->order));
+
+ /* server element: x, y */
+ if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
+ data->my_element, x, y,
+ data->bnctx)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
+ "assignment fail");
+ goto fin;
+ }
+
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
+ BN_bn2bin(x, cruft + offset);
+ H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
+ BN_bn2bin(y, cruft + offset);
+ H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+
+ /* server scalar */
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->order) -
+ BN_num_bytes(data->my_scalar);
+ BN_bn2bin(data->my_scalar, cruft + offset);
+ H_Update(&ctx, cruft, BN_num_bytes(data->grp->order));
+
+ /* ciphersuite */
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ H_Update(&ctx, (u8 *)&cs, sizeof(u32));
+
+ /* all done */
+ H_Final(&ctx, conf);
+
+ ptr = (u8 *) payload;
+ if (os_memcmp(conf, ptr, SHA256_DIGEST_LENGTH)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): confirm did not "
+ "verify");
+ goto fin;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-pwd (server): confirm verified");
+ if (compute_keys(data->grp, data->bnctx, data->k,
+ data->peer_scalar, data->my_scalar, conf,
+ data->my_confirm, &cs, data->msk, data->emsk) < 0)
+ eap_pwd_state(data, FAILURE);
+ else
+ eap_pwd_state(data, SUCCESS);
+
+fin:
+ os_free(cruft);
+ BN_free(x);
+ BN_free(y);
+}
+
+
+static void eap_pwd_process(struct eap_sm *sm, void *priv,
+ struct wpabuf *respData)
+{
+ struct eap_pwd_data *data = priv;
+ const u8 *pos;
+ size_t len;
+ u8 exch;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PWD, respData, &len);
+ if ((pos == NULL) || (len < 1)) {
+ wpa_printf(MSG_INFO, "Bad EAP header! pos %s and len = %d",
+ (pos == NULL) ? "is NULL" : "is not NULL",
+ (int) len);
+ return;
+ }
+
+ exch = *pos & 0x3f;
+ switch (exch) {
+ case EAP_PWD_OPCODE_ID_EXCH:
+ eap_pwd_process_id_resp(sm, data, pos + 1, len - 1);
+ break;
+ case EAP_PWD_OPCODE_COMMIT_EXCH:
+ eap_pwd_process_commit_resp(sm, data, pos + 1, len - 1);
+ break;
+ case EAP_PWD_OPCODE_CONFIRM_EXCH:
+ eap_pwd_process_confirm_resp(sm, data, pos + 1, len - 1);
+ break;
+ }
+}
+
+
+static u8 * eap_pwd_getkey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_pwd_data *data = priv;
+ u8 *key;
+
+ if (data->state != SUCCESS)
+ return NULL;
+
+ key = os_malloc(EAP_MSK_LEN);
+ if (key == NULL)
+ return NULL;
+
+ os_memcpy(key, data->msk, EAP_MSK_LEN);
+ *len = EAP_MSK_LEN;
+
+ return key;
+}
+
+
+static u8 * eap_pwd_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_pwd_data *data = priv;
+ u8 *key;
+
+ if (data->state != SUCCESS)
+ return NULL;
+
+ key = os_malloc(EAP_EMSK_LEN);
+ if (key == NULL)
+ return NULL;
+
+ os_memcpy(key, data->emsk, EAP_EMSK_LEN);
+ *len = EAP_EMSK_LEN;
+
+ return key;
+}
+
+
+static Boolean eap_pwd_is_success(struct eap_sm *sm, void *priv)
+{
+ struct eap_pwd_data *data = priv;
+ return data->state == SUCCESS;
+}
+
+
+static Boolean eap_pwd_is_done(struct eap_sm *sm, void *priv)
+{
+ struct eap_pwd_data *data = priv;
+ return (data->state == SUCCESS) || (data->state == FAILURE);
+}
+
+
+int eap_server_pwd_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+ struct timeval tp;
+ struct timezone tz;
+ u32 sr;
+
+ EVP_add_digest(EVP_sha256());
+
+ sr = 0xdeaddada;
+ (void) gettimeofday(&tp, &tz);
+ sr ^= (tp.tv_sec ^ tp.tv_usec);
+ srandom(sr);
+
+ eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_PWD,
+ "PWD");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_pwd_init;
+ eap->reset = eap_pwd_reset;
+ eap->buildReq = eap_pwd_build_req;
+ eap->check = eap_pwd_check;
+ eap->process = eap_pwd_process;
+ eap->isDone = eap_pwd_is_done;
+ eap->getKey = eap_pwd_getkey;
+ eap->get_emsk = eap_pwd_get_emsk;
+ eap->isSuccess = eap_pwd_is_success;
+
+ ret = eap_server_method_register(eap);
+ if (ret)
+ eap_server_method_free(eap);
+ return ret;
+}
+
#include "includes.h"
#include "common.h"
+#include "crypto/random.h"
#include "eap_server/eap_i.h"
#include "eap_common/eap_sake_common.h"
wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Challenge");
- if (os_get_random(data->rand_s, EAP_SAKE_RAND_LEN)) {
+ if (random_get_bytes(data->rand_s, EAP_SAKE_RAND_LEN)) {
wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to get random data");
data->state = FAILURE;
return NULL;
#include "includes.h"
#include "common.h"
+#include "crypto/random.h"
#include "eap_server/eap_i.h"
#include "eap_common/eap_sim_common.h"
#include "eap_server/eap_sim_db.h"
const u8 *nonce_s)
{
os_free(data->next_pseudonym);
- data->next_pseudonym =
- eap_sim_db_get_next_pseudonym(sm->eap_sim_db_priv, 0);
+ if (nonce_s == NULL) {
+ data->next_pseudonym =
+ eap_sim_db_get_next_pseudonym(sm->eap_sim_db_priv, 0);
+ } else {
+ /* Do not update pseudonym during re-authentication */
+ data->next_pseudonym = NULL;
+ }
os_free(data->next_reauth_id);
if (data->counter <= EAP_SIM_MAX_FAST_REAUTHS) {
data->next_reauth_id =
wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Re-authentication");
- if (os_get_random(data->nonce_s, EAP_SIM_NONCE_S_LEN))
+ if (random_get_bytes(data->nonce_s, EAP_SIM_NONCE_S_LEN))
return NULL;
wpa_hexdump_key(MSG_MSGDUMP, "EAP-SIM: NONCE_S",
data->nonce_s, EAP_SIM_NONCE_S_LEN);
identity_len = id2_len;
}
- if (data->next_pseudonym) {
- eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, identity,
- identity_len, data->next_pseudonym);
- data->next_pseudonym = NULL;
- }
if (data->next_reauth_id) {
eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity,
identity_len, data->next_reauth_id,
return -1;
}
- /* TODO: make this configurable */
- data->tls_out_limit = 1398;
+ data->tls_out_limit = sm->fragment_size > 0 ? sm->fragment_size : 1398;
if (data->phase2) {
/* Limit the fragment size in the inner TLS authentication
* since the outer authentication with EAP-PEAP does not yet
return NULL;
}
- data->fragment_size = 1300;
+ data->fragment_size = sm->fragment_size > 100 ?
+ sm->fragment_size - 98 : 1300;
return data;
}
/*
* hostapd / EAP-TTLS (RFC 5281)
- * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
#include "eap_common/eap_ttls.h"
-/* Maximum supported TTLS version
- * 0 = RFC 5281
- * 1 = draft-funk-eap-ttls-v1-00.txt
- */
-#ifndef EAP_TTLS_VERSION
-#define EAP_TTLS_VERSION 0 /* TTLSv1 implementation is not yet complete */
-#endif /* EAP_TTLS_VERSION */
-
-
-#define MSCHAPV2_KEY_LEN 16
+#define EAP_TTLS_VERSION 0
static void eap_ttls_reset(struct eap_sm *sm, void *priv);
struct eap_ssl_data ssl;
enum {
START, PHASE1, PHASE2_START, PHASE2_METHOD,
- PHASE2_MSCHAPV2_RESP, PHASE_FINISHED, SUCCESS, FAILURE
+ PHASE2_MSCHAPV2_RESP, SUCCESS, FAILURE
} state;
int ttls_version;
- int force_version;
const struct eap_method *phase2_method;
void *phase2_priv;
int mschapv2_resp_ok;
u8 mschapv2_auth_response[20];
u8 mschapv2_ident;
- int tls_ia_configured;
struct wpabuf *pending_phase2_eap_resp;
int tnc_started;
};
return "PHASE2_METHOD";
case PHASE2_MSCHAPV2_RESP:
return "PHASE2_MSCHAPV2_RESP";
- case PHASE_FINISHED:
- return "PHASE_FINISHED";
case SUCCESS:
return "SUCCESS";
case FAILURE:
}
avp->avp_code = host_to_be32(avp_code);
- avp->avp_length = host_to_be32((flags << 24) | (hdrlen + len));
+ avp->avp_length = host_to_be32(((u32) flags << 24) |
+ ((u32) (hdrlen + len)));
return avphdr + hdrlen;
}
static u8 * eap_ttls_implicit_challenge(struct eap_sm *sm,
struct eap_ttls_data *data, size_t len)
{
- struct tls_keys keys;
- u8 *challenge, *rnd;
-
- if (data->ttls_version == 0) {
- return eap_server_tls_derive_key(sm, &data->ssl,
- "ttls challenge", len);
- }
-
- os_memset(&keys, 0, sizeof(keys));
- if (tls_connection_get_keys(sm->ssl_ctx, data->ssl.conn, &keys) ||
- keys.client_random == NULL || keys.server_random == NULL ||
- keys.inner_secret == NULL) {
- wpa_printf(MSG_INFO, "EAP-TTLS: Could not get inner secret, "
- "client random, or server random to derive "
- "implicit challenge");
- return NULL;
- }
-
- rnd = os_malloc(keys.client_random_len + keys.server_random_len);
- challenge = os_malloc(len);
- if (rnd == NULL || challenge == NULL) {
- wpa_printf(MSG_INFO, "EAP-TTLS: No memory for implicit "
- "challenge derivation");
- os_free(rnd);
- os_free(challenge);
- return NULL;
- }
- os_memcpy(rnd, keys.server_random, keys.server_random_len);
- os_memcpy(rnd + keys.server_random_len, keys.client_random,
- keys.client_random_len);
-
- if (tls_prf(keys.inner_secret, keys.inner_secret_len,
- "inner application challenge", rnd,
- keys.client_random_len + keys.server_random_len,
- challenge, len)) {
- wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive implicit "
- "challenge");
- os_free(rnd);
- os_free(challenge);
- return NULL;
- }
-
- os_free(rnd);
-
- wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived implicit challenge",
- challenge, len);
-
- return challenge;
+ return eap_server_tls_derive_key(sm, &data->ssl, "ttls challenge",
+ len);
}
if (data == NULL)
return NULL;
data->ttls_version = EAP_TTLS_VERSION;
- data->force_version = -1;
- if (sm->user && sm->user->force_version >= 0) {
- data->force_version = sm->user->force_version;
- wpa_printf(MSG_DEBUG, "EAP-TTLS: forcing version %d",
- data->force_version);
- data->ttls_version = data->force_version;
- }
data->state = START;
- if (!(tls_capabilities(sm->ssl_ctx) & TLS_CAPABILITY_IA) &&
- data->ttls_version > 0) {
- if (data->force_version > 0) {
- wpa_printf(MSG_INFO, "EAP-TTLS: Forced TTLSv%d and "
- "TLS library does not support TLS/IA.",
- data->force_version);
- eap_ttls_reset(sm, data);
- return NULL;
- }
- data->ttls_version = 0;
- }
-
if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) {
wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL.");
eap_ttls_reset(sm, data);
}
-static struct wpabuf * eap_ttls_build_phase_finished(
- struct eap_sm *sm, struct eap_ttls_data *data, int final)
-{
- return tls_connection_ia_send_phase_finished(sm->ssl_ctx,
- data->ssl.conn, final);
-}
-
-
static struct wpabuf * eap_ttls_buildReq(struct eap_sm *sm, void *priv, u8 id)
{
struct eap_ttls_data *data = priv;
data->ssl.tls_out_pos = 0;
data->ssl.tls_out = eap_ttls_build_phase2_mschapv2(sm, data);
break;
- case PHASE_FINISHED:
- wpabuf_free(data->ssl.tls_out);
- data->ssl.tls_out_pos = 0;
- data->ssl.tls_out = eap_ttls_build_phase_finished(sm, data, 1);
- break;
default:
wpa_printf(MSG_DEBUG, "EAP-TTLS: %s - unexpected state %d",
__func__, data->state);
}
-static int eap_ttls_ia_permute_inner_secret(struct eap_sm *sm,
- struct eap_ttls_data *data,
- const u8 *key, size_t key_len)
-{
- u8 *buf;
- size_t buf_len;
- int ret;
-
- if (key) {
- buf_len = 2 + key_len;
- buf = os_malloc(buf_len);
- if (buf == NULL)
- return -1;
- WPA_PUT_BE16(buf, key_len);
- os_memcpy(buf + 2, key, key_len);
- } else {
- buf = NULL;
- buf_len = 0;
- }
-
- wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Session keys for TLS/IA inner "
- "secret permutation", buf, buf_len);
- ret = tls_connection_ia_permute_inner_secret(sm->ssl_ctx,
- data->ssl.conn,
- buf, buf_len);
- os_free(buf);
-
- return ret;
-}
-
-
static void eap_ttls_process_phase2_pap(struct eap_sm *sm,
struct eap_ttls_data *data,
const u8 *user_password,
}
wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP: Correct user password");
- eap_ttls_state(data, data->ttls_version > 0 ? PHASE_FINISHED :
- SUCCESS);
+ eap_ttls_state(data, SUCCESS);
}
if (os_memcmp(hash, password + 1, EAP_TTLS_CHAP_PASSWORD_LEN) == 0) {
wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Correct user password");
- eap_ttls_state(data, data->ttls_version > 0 ? PHASE_FINISHED :
- SUCCESS);
+ eap_ttls_state(data, SUCCESS);
} else {
wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Invalid user password");
eap_ttls_state(data, FAILURE);
if (os_memcmp(nt_response, response + 2 + 24, 24) == 0) {
wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Correct response");
- eap_ttls_state(data, data->ttls_version > 0 ? PHASE_FINISHED :
- SUCCESS);
+ eap_ttls_state(data, SUCCESS);
} else {
wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Invalid NT-Response");
wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAP: Received",
wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Correct "
"NT-Response");
data->mschapv2_resp_ok = 1;
- if (data->ttls_version > 0) {
- const u8 *pw_hash;
- u8 pw_hash_buf[16], pw_hash_hash[16], master_key[16];
- u8 session_key[2 * MSCHAPV2_KEY_LEN];
-
- if (sm->user->password_hash)
- pw_hash = sm->user->password;
- else {
- nt_password_hash(sm->user->password,
- sm->user->password_len,
- pw_hash_buf);
- pw_hash = pw_hash_buf;
- }
- hash_nt_password_hash(pw_hash, pw_hash_hash);
- get_master_key(pw_hash_hash, nt_response, master_key);
- get_asymetric_start_key(master_key, session_key,
- MSCHAPV2_KEY_LEN, 0, 0);
- get_asymetric_start_key(master_key,
- session_key + MSCHAPV2_KEY_LEN,
- MSCHAPV2_KEY_LEN, 1, 0);
- eap_ttls_ia_permute_inner_secret(sm, data,
- session_key,
- sizeof(session_key));
- }
if (sm->user->password_hash) {
generate_authenticator_response_pwhash(
}
break;
case PHASE2_METHOD:
- if (data->ttls_version > 0) {
- if (m->getKey) {
- u8 *key;
- size_t key_len;
- key = m->getKey(sm, priv, &key_len);
- eap_ttls_ia_permute_inner_secret(sm, data,
- key, key_len);
- }
- eap_ttls_state(data, PHASE_FINISHED);
- } else
- eap_ttls_state(data, SUCCESS);
+ eap_ttls_state(data, SUCCESS);
break;
case FAILURE:
break;
return;
}
- if (data->state == PHASE_FINISHED) {
- if (wpabuf_len(in_decrypted) == 0 &&
- tls_connection_ia_final_phase_finished(sm->ssl_ctx,
- data->ssl.conn)) {
- wpa_printf(MSG_DEBUG, "EAP-TTLS: FinalPhaseFinished "
- "received");
- eap_ttls_state(data, SUCCESS);
- } else {
- wpa_printf(MSG_INFO, "EAP-TTLS: Did not receive valid "
- "FinalPhaseFinished");
- eap_ttls_state(data, FAILURE);
- }
-
- wpabuf_free(in_decrypted);
- return;
- }
-
wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TTLS: Decrypted Phase 2 EAP",
in_decrypted);
data->ttls_version = peer_version;
}
- if (data->ttls_version > 0 && !data->tls_ia_configured) {
- if (tls_connection_set_ia(sm->ssl_ctx, data->ssl.conn, 1)) {
- wpa_printf(MSG_INFO, "EAP-TTLS: Failed to enable "
- "TLS/IA");
- return -1;
- }
- data->tls_ia_configured = 1;
- }
-
return 0;
}
break;
case PHASE2_START:
case PHASE2_METHOD:
- case PHASE_FINISHED:
eap_ttls_process_phase2(sm, data, data->ssl.tls_in);
eap_ttls_start_tnc(sm, data);
break;
0) {
wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Peer "
"acknowledged response");
- eap_ttls_state(data, data->ttls_version > 0 ?
- PHASE_FINISHED : SUCCESS);
+ eap_ttls_state(data, SUCCESS);
} else if (!data->mschapv2_resp_ok) {
wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Peer "
"acknowledged error");
}
-static u8 * eap_ttls_v1_derive_key(struct eap_sm *sm,
- struct eap_ttls_data *data)
-{
- struct tls_keys keys;
- u8 *rnd, *key;
-
- os_memset(&keys, 0, sizeof(keys));
- if (tls_connection_get_keys(sm->ssl_ctx, data->ssl.conn, &keys) ||
- keys.client_random == NULL || keys.server_random == NULL ||
- keys.inner_secret == NULL) {
- wpa_printf(MSG_INFO, "EAP-TTLS: Could not get inner secret, "
- "client random, or server random to derive keying "
- "material");
- return NULL;
- }
-
- rnd = os_malloc(keys.client_random_len + keys.server_random_len);
- key = os_malloc(EAP_TLS_KEY_LEN);
- if (rnd == NULL || key == NULL) {
- wpa_printf(MSG_INFO, "EAP-TTLS: No memory for key derivation");
- os_free(rnd);
- os_free(key);
- return NULL;
- }
- os_memcpy(rnd, keys.client_random, keys.client_random_len);
- os_memcpy(rnd + keys.client_random_len, keys.server_random,
- keys.server_random_len);
-
- if (tls_prf(keys.inner_secret, keys.inner_secret_len,
- "ttls v1 keying material", rnd, keys.client_random_len +
- keys.server_random_len, key, EAP_TLS_KEY_LEN)) {
- wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive key");
- os_free(rnd);
- os_free(key);
- return NULL;
- }
-
- wpa_hexdump(MSG_DEBUG, "EAP-TTLS: client/server random",
- rnd, keys.client_random_len + keys.server_random_len);
- wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: TLS/IA inner secret",
- keys.inner_secret, keys.inner_secret_len);
-
- os_free(rnd);
-
- return key;
-}
-
-
static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_ttls_data *data = priv;
if (data->state != SUCCESS)
return NULL;
- if (data->ttls_version == 0) {
- eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
- "ttls keying material",
- EAP_TLS_KEY_LEN);
- } else {
- eapKeyData = eap_ttls_v1_derive_key(sm, data);
- }
-
+ eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
+ "ttls keying material",
+ EAP_TLS_KEY_LEN);
if (eapKeyData) {
*len = EAP_TLS_KEY_LEN;
wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived key",
#include "eloop.h"
#include "eap_i.h"
#include "eap_common/eap_wsc_common.h"
+#include "p2p/p2p.h"
#include "wps/wps.h"
}
cfg.assoc_wps_ie = sm->assoc_wps_ie;
cfg.peer_addr = sm->peer_addr;
- if (0 /* TODO: could provide option for forcing PSK format */)
- cfg.use_psk_key = 1;
+#ifdef CONFIG_P2P
+ if (sm->assoc_p2p_ie) {
+ wpa_printf(MSG_DEBUG, "EAP-WSC: Prefer PSK format for P2P "
+ "client");
+ cfg.use_psk_key = 1;
+ cfg.p2p_dev_addr = p2p_get_go_dev_addr(sm->assoc_p2p_ie);
+ }
+#endif /* CONFIG_P2P */
+ cfg.pbc_in_m1 = sm->pbc_in_m1;
data->wps = wps_init(&cfg);
if (data->wps == NULL) {
os_free(data);
return NULL;
}
- data->fragment_size = WSC_FRAGMENT_SIZE;
+ data->fragment_size = sm->fragment_size > 0 ? sm->fragment_size :
+ WSC_FRAGMENT_SIZE;
return data;
}
#include <sys/un.h>
#include "common.h"
+#include "crypto/random.h"
#include "eap_common/eap_sim_common.h"
#include "eap_server/eap_sim_db.h"
#include "eloop.h"
char *id, *pos, *end;
u8 buf[10];
- if (os_get_random(buf, sizeof(buf)))
+ if (random_get_bytes(buf, sizeof(buf)))
return NULL;
id = os_malloc(sizeof(buf) * 2 + 2);
if (id == NULL)
#include "common.h"
#include "crypto/dh_groups.h"
+#include "crypto/random.h"
#include "ikev2.h"
}
/* RFC 4306, Section 3.4:
- * The length of DH public value MUST be equal to the lenght of the
+ * The length of DH public value MUST be equal to the length of the
* prime modulus.
*/
if (ker_len - 4 != data->dh->prime_len) {
data->i_spi, IKEV2_SPI_LEN);
data->i_nonce_len = IKEV2_NONCE_MIN_LEN;
- if (os_get_random(data->i_nonce, data->i_nonce_len))
+ if (random_get_bytes(data->i_nonce, data->i_nonce_len))
return NULL;
wpa_hexdump(MSG_DEBUG, "IKEV2: Ni", data->i_nonce, data->i_nonce_len);
if (data->shared_secret == NULL)
return NULL;
data->shared_secret_len = 16;
- if (os_get_random(data->shared_secret, 16))
+ if (random_get_bytes(data->shared_secret, 16))
return NULL;
} else {
os_free(data->shared_secret);
return TNC_RESULT_INVALID_PARAMETER;
os_free(imv->supported_types);
imv->supported_types =
- os_malloc(typeCount * sizeof(TNC_MessageTypeList));
+ os_malloc(typeCount * sizeof(TNC_MessageType));
if (imv->supported_types == NULL)
return TNC_RESULT_FATAL;
os_memcpy(imv->supported_types, supportedTypes,
- typeCount * sizeof(TNC_MessageTypeList));
+ typeCount * sizeof(TNC_MessageType));
imv->num_supported_types = typeCount;
return TNC_RESULT_SUCCESS;
struct eapol_state_machine *
eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr,
- int flags, const struct wpabuf *assoc_wps_ie, void *sta_ctx)
+ int flags, const struct wpabuf *assoc_wps_ie,
+ const struct wpabuf *assoc_p2p_ie, void *sta_ctx)
{
struct eapol_state_machine *sm;
struct eap_config eap_conf;
eap_conf.tnc = eapol->conf.tnc;
eap_conf.wps = eapol->conf.wps;
eap_conf.assoc_wps_ie = assoc_wps_ie;
+ eap_conf.assoc_p2p_ie = assoc_p2p_ie;
eap_conf.peer_addr = addr;
+ eap_conf.fragment_size = eapol->conf.fragment_size;
+ eap_conf.pwd_group = eapol->conf.pwd_group;
+ eap_conf.pbc_in_m1 = eapol->conf.pbc_in_m1;
sm->eap = eap_server_sm_init(sm, &eapol_cb, &eap_conf);
if (sm->eap == NULL) {
eapol_auth_free(sm);
int eapol_auth_eap_pending_cb(struct eapol_state_machine *sm, void *ctx)
{
- if (sm == NULL || ctx != sm->eap)
+ if (sm == NULL || ctx == NULL || ctx != sm->eap)
return -1;
eap_sm_pending_cb(sm->eap);
dst->msg_ctx = src->msg_ctx;
dst->eap_sim_db_priv = src->eap_sim_db_priv;
os_free(dst->eap_req_id_text);
+ dst->pwd_group = src->pwd_group;
+ dst->pbc_in_m1 = src->pbc_in_m1;
if (src->eap_req_id_text) {
dst->eap_req_id_text = os_malloc(src->eap_req_id_text_len);
if (dst->eap_req_id_text == NULL)
dst->eap_sim_aka_result_ind = src->eap_sim_aka_result_ind;
dst->tnc = src->tnc;
dst->wps = src->wps;
+ dst->fragment_size = src->fragment_size;
return 0;
}
int eap_sim_aka_result_ind;
int tnc;
struct wps_context *wps;
+ int fragment_size;
+ u16 pwd_group;
+ int pbc_in_m1;
/* Opaque context pointer to owner data for callback functions */
void *ctx;
void eapol_auth_deinit(struct eapol_authenticator *eapol);
struct eapol_state_machine *
eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr,
- int flags, const struct wpabuf *assoc_wps_ie, void *sta_ctx);
+ int flags, const struct wpabuf *assoc_wps_ie,
+ const struct wpabuf *assoc_p2p_ie, void *sta_ctx);
void eapol_auth_free(struct eapol_state_machine *sm);
void eapol_auth_step(struct eapol_state_machine *sm);
void eapol_auth_dump_state(FILE *f, const char *prefix,
* IEEE Std 802.1X-2004 has transitions from REQUEST to FAIL
* and SUCCESS based on eapFail and eapSuccess, respectively.
* However, IEEE Std 802.1X-2004 is also specifying that
- * eapNoResp should be set in conjuction with eapSuccess and
+ * eapNoResp should be set in conjunction with eapSuccess and
* eapFail which would mean that more than one of the
* transitions here would be activated at the same time.
* Skipping RESPONSE and/or RECEIVE states in these cases can
}
+/**
+ * eapol_sm_get_method_name - Get EAPOL method name
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * Returns: Static string containing name of current eap method or NULL
+ */
+const char * eapol_sm_get_method_name(struct eapol_sm *sm)
+{
+ if (sm->SUPP_PAE_state != SUPP_PAE_AUTHENTICATED ||
+ sm->suppPortStatus != Authorized)
+ return NULL;
+
+ return eap_sm_get_method_name(sm->eap);
+}
+
+
#ifdef CONFIG_CTRL_IFACE
/**
* eapol_sm_get_status - Get EAPOL state machine status
#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
-static void eapol_sm_eap_param_needed(void *ctx, const char *field,
+static void eapol_sm_eap_param_needed(void *ctx, enum wpa_ctrl_req_type field,
const char *txt)
{
struct eapol_sm *sm = ctx;
#define eapol_sm_eap_param_needed NULL
#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+static void eapol_sm_notify_cert(void *ctx, int depth, const char *subject,
+ const char *cert_hash,
+ const struct wpabuf *cert)
+{
+ struct eapol_sm *sm = ctx;
+ if (sm->ctx->cert_cb)
+ sm->ctx->cert_cb(sm->ctx->ctx, depth, subject,
+ cert_hash, cert);
+}
static struct eapol_callbacks eapol_cb =
{
eapol_sm_set_config_blob,
eapol_sm_get_config_blob,
eapol_sm_notify_pending,
- eapol_sm_eap_param_needed
+ eapol_sm_eap_param_needed,
+ eapol_sm_notify_cert
};
conf.pkcs11_engine_path = ctx->pkcs11_engine_path;
conf.pkcs11_module_path = ctx->pkcs11_module_path;
conf.wps = ctx->wps;
+ conf.cert_in_cb = ctx->cert_in_cb;
sm->eap = eap_peer_sm_init(sm, &eapol_cb, sm->ctx->msg_ctx, &conf);
if (sm->eap == NULL) {
/**
* eap_param_needed - Notify that EAP parameter is needed
* @ctx: Callback context (ctx)
- * @field: Field name (e.g., "IDENTITY")
+ * @field: Field indicator (e.g., WPA_CTRL_REQ_EAP_IDENTITY)
* @txt: User readable text describing the required parameter
*/
- void (*eap_param_needed)(void *ctx, const char *field,
+ void (*eap_param_needed)(void *ctx, enum wpa_ctrl_req_type field,
const char *txt);
/**
* @authorized: Whether the supplicant port is now in authorized state
*/
void (*port_cb)(void *ctx, int authorized);
+
+ /**
+ * cert_cb - Notification of a peer certificate
+ * @ctx: Callback context (ctx)
+ * @depth: Depth in certificate chain (0 = server)
+ * @subject: Subject of the peer certificate
+ * @cert_hash: SHA-256 hash of the certificate
+ * @cert: Peer certificate
+ */
+ void (*cert_cb)(void *ctx, int depth, const char *subject,
+ const char *cert_hash, const struct wpabuf *cert);
+
+ /**
+ * cert_in_cb - Include server certificates in callback
+ */
+ int cert_in_cb;
};
void eapol_sm_request_reauth(struct eapol_sm *sm);
void eapol_sm_notify_lower_layer_success(struct eapol_sm *sm, int in_eapol_sm);
void eapol_sm_invalidate_cached_session(struct eapol_sm *sm);
+const char * eapol_sm_get_method_name(struct eapol_sm *sm);
#else /* IEEE8021X_EAPOL */
static inline struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx)
{
static inline void eapol_sm_invalidate_cached_session(struct eapol_sm *sm)
{
}
+static inline const char * eapol_sm_get_method_name(struct eapol_sm *sm)
+{
+ return NULL;
+}
#endif /* IEEE8021X_EAPOL */
#endif /* EAPOL_SUPP_SM_H */
#include <pcap.h>
#include <sys/ioctl.h>
+#ifdef __sun__
+#include <libdlpi.h>
+#else /* __sun__ */
#include <sys/sysctl.h>
+#endif /* __sun__ */
#include <net/if.h>
#include <net/if_dl.h>
}
pcap_freecode(&pcap_fp);
+#ifndef __sun__
/*
* When libpcap uses BPF we must enable "immediate mode" to
* receive frames right away; otherwise the system may
/* XXX should we fail? */
}
}
+#endif /* __sun__ */
eloop_register_read_sock(pcap_get_selectable_fd(l2->pcap),
l2_packet_receive, l2, l2->pcap);
static int eth_get(const char *device, u8 ea[ETH_ALEN])
{
+#ifdef __sun__
+ dlpi_handle_t dh;
+ u32 physaddrlen = DLPI_PHYSADDR_MAX;
+ u8 physaddr[DLPI_PHYSADDR_MAX];
+ int retval;
+
+ retval = dlpi_open(device, &dh, 0);
+ if (retval != DLPI_SUCCESS) {
+ wpa_printf(MSG_ERROR, "dlpi_open error: %s",
+ dlpi_strerror(retval));
+ return -1;
+ }
+
+ retval = dlpi_get_physaddr(dh, DL_CURR_PHYS_ADDR, physaddr,
+ &physaddrlen);
+ if (retval != DLPI_SUCCESS) {
+ wpa_printf(MSG_ERROR, "dlpi_get_physaddr error: %s",
+ dlpi_strerror(retval));
+ dlpi_close(dh);
+ return -1;
+ }
+ os_memcpy(ea, physaddr, ETH_ALEN);
+ dlpi_close(dh);
+#else /* __sun__ */
struct if_msghdr *ifm;
struct sockaddr_dl *sdl;
u_char *p, *buf;
errno = ESRCH;
return -1;
}
+#endif /* __sun__ */
return 0;
}
if (l2->l2_hdr) {
ret = send(l2->fd, buf, len, 0);
if (ret < 0)
- perror("l2_packet_send - send");
+ wpa_printf(MSG_ERROR, "l2_packet_send - send: %s",
+ strerror(errno));
} else {
struct sockaddr_ll ll;
os_memset(&ll, 0, sizeof(ll));
os_memcpy(ll.sll_addr, dst_addr, ETH_ALEN);
ret = sendto(l2->fd, buf, len, 0, (struct sockaddr *) &ll,
sizeof(ll));
- if (ret < 0)
- perror("l2_packet_send - sendto");
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR, "l2_packet_send - sendto: %s",
+ strerror(errno));
+ }
}
return ret;
}
res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &ll,
&fromlen);
if (res < 0) {
- perror("l2_packet_receive - recvfrom");
+ wpa_printf(MSG_DEBUG, "l2_packet_receive - recvfrom: %s",
+ strerror(errno));
return;
}
l2->fd = socket(PF_PACKET, l2_hdr ? SOCK_RAW : SOCK_DGRAM,
htons(protocol));
if (l2->fd < 0) {
- perror("socket(PF_PACKET)");
+ wpa_printf(MSG_ERROR, "%s: socket(PF_PACKET): %s",
+ __func__, strerror(errno));
os_free(l2);
return NULL;
}
os_memset(&ifr, 0, sizeof(ifr));
os_strlcpy(ifr.ifr_name, l2->ifname, sizeof(ifr.ifr_name));
if (ioctl(l2->fd, SIOCGIFINDEX, &ifr) < 0) {
- perror("ioctl[SIOCGIFINDEX]");
+ wpa_printf(MSG_ERROR, "%s: ioctl[SIOCGIFINDEX]: %s",
+ __func__, strerror(errno));
close(l2->fd);
os_free(l2);
return NULL;
ll.sll_ifindex = ifr.ifr_ifindex;
ll.sll_protocol = htons(protocol);
if (bind(l2->fd, (struct sockaddr *) &ll, sizeof(ll)) < 0) {
- perror("bind[PF_PACKET]");
+ wpa_printf(MSG_ERROR, "%s: bind[PF_PACKET]: %s",
+ __func__, strerror(errno));
close(l2->fd);
os_free(l2);
return NULL;
}
if (ioctl(l2->fd, SIOCGIFHWADDR, &ifr) < 0) {
- perror("ioctl[SIOCGIFHWADDR]");
+ wpa_printf(MSG_ERROR, "%s: ioctl[SIOCGIFHWADDR]: %s",
+ __func__, strerror(errno));
close(l2->fd);
os_free(l2);
return NULL;
s = socket(PF_INET, SOCK_DGRAM, 0);
if (s < 0) {
- perror("socket");
+ wpa_printf(MSG_ERROR, "%s: socket: %s",
+ __func__, strerror(errno));
return -1;
}
os_memset(&ifr, 0, sizeof(ifr));
os_strlcpy(ifr.ifr_name, l2->ifname, sizeof(ifr.ifr_name));
if (ioctl(s, SIOCGIFADDR, &ifr) < 0) {
if (errno != EADDRNOTAVAIL)
- perror("ioctl[SIOCGIFADDR]");
+ wpa_printf(MSG_ERROR, "%s: ioctl[SIOCGIFADDR]: %s",
+ __func__, strerror(errno));
close(s);
return -1;
}
--- /dev/null
+all:
+ @echo Nothing to be made.
+
+clean:
+ for d in $(SUBDIRS); do make -C $$d clean; done
+ rm -f *~ *.o *.d
+
+install:
+ @echo Nothing to be made.
--- /dev/null
+/*
+ * Wi-Fi Direct - P2P module
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "common/wpa_ctrl.h"
+#include "wps/wps_i.h"
+#include "p2p_i.h"
+#include "p2p.h"
+
+
+static void p2p_state_timeout(void *eloop_ctx, void *timeout_ctx);
+static void p2p_device_free(struct p2p_data *p2p, struct p2p_device *dev);
+static void p2p_process_presence_req(struct p2p_data *p2p, const u8 *da,
+ const u8 *sa, const u8 *data, size_t len,
+ int rx_freq);
+static void p2p_process_presence_resp(struct p2p_data *p2p, const u8 *da,
+ const u8 *sa, const u8 *data,
+ size_t len);
+static void p2p_ext_listen_timeout(void *eloop_ctx, void *timeout_ctx);
+static void p2p_scan_timeout(void *eloop_ctx, void *timeout_ctx);
+
+
+/*
+ * p2p_scan recovery timeout
+ *
+ * Many drivers are using 30 second timeout on scan results. Allow a bit larger
+ * timeout for this to avoid hitting P2P timeout unnecessarily.
+ */
+#define P2P_SCAN_TIMEOUT 35
+
+/**
+ * P2P_PEER_EXPIRATION_AGE - Number of seconds after which inactive peer
+ * entries will be removed
+ */
+#define P2P_PEER_EXPIRATION_AGE 300
+
+#define P2P_PEER_EXPIRATION_INTERVAL (P2P_PEER_EXPIRATION_AGE / 2)
+
+static void p2p_expire_peers(struct p2p_data *p2p)
+{
+ struct p2p_device *dev, *n;
+ struct os_time now;
+ size_t i;
+
+ os_get_time(&now);
+ dl_list_for_each_safe(dev, n, &p2p->devices, struct p2p_device, list) {
+ if (dev->last_seen.sec + P2P_PEER_EXPIRATION_AGE >= now.sec)
+ continue;
+
+ if (p2p->cfg->go_connected &&
+ p2p->cfg->go_connected(p2p->cfg->cb_ctx,
+ dev->info.p2p_device_addr)) {
+ /*
+ * We are connected as a client to a group in which the
+ * peer is the GO, so do not expire the peer entry.
+ */
+ os_get_time(&dev->last_seen);
+ continue;
+ }
+
+ for (i = 0; i < p2p->num_groups; i++) {
+ if (p2p_group_is_client_connected(
+ p2p->groups[i], dev->info.p2p_device_addr))
+ break;
+ }
+ if (i < p2p->num_groups) {
+ /*
+ * The peer is connected as a client in a group where
+ * we are the GO, so do not expire the peer entry.
+ */
+ os_get_time(&dev->last_seen);
+ continue;
+ }
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Expiring old peer "
+ "entry " MACSTR, MAC2STR(dev->info.p2p_device_addr));
+ dl_list_del(&dev->list);
+ p2p_device_free(p2p, dev);
+ }
+}
+
+
+static void p2p_expiration_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct p2p_data *p2p = eloop_ctx;
+ p2p_expire_peers(p2p);
+ eloop_register_timeout(P2P_PEER_EXPIRATION_INTERVAL, 0,
+ p2p_expiration_timeout, p2p, NULL);
+}
+
+
+static const char * p2p_state_txt(int state)
+{
+ switch (state) {
+ case P2P_IDLE:
+ return "IDLE";
+ case P2P_SEARCH:
+ return "SEARCH";
+ case P2P_CONNECT:
+ return "CONNECT";
+ case P2P_CONNECT_LISTEN:
+ return "CONNECT_LISTEN";
+ case P2P_GO_NEG:
+ return "GO_NEG";
+ case P2P_LISTEN_ONLY:
+ return "LISTEN_ONLY";
+ case P2P_WAIT_PEER_CONNECT:
+ return "WAIT_PEER_CONNECT";
+ case P2P_WAIT_PEER_IDLE:
+ return "WAIT_PEER_IDLE";
+ case P2P_SD_DURING_FIND:
+ return "SD_DURING_FIND";
+ case P2P_PROVISIONING:
+ return "PROVISIONING";
+ case P2P_PD_DURING_FIND:
+ return "PD_DURING_FIND";
+ case P2P_INVITE:
+ return "INVITE";
+ case P2P_INVITE_LISTEN:
+ return "INVITE_LISTEN";
+ case P2P_SEARCH_WHEN_READY:
+ return "SEARCH_WHEN_READY";
+ default:
+ return "?";
+ }
+}
+
+
+u16 p2p_get_provisioning_info(struct p2p_data *p2p, const u8 *addr)
+{
+ struct p2p_device *dev = NULL;
+
+ if (!addr || !p2p)
+ return 0;
+
+ dev = p2p_get_device(p2p, addr);
+ if (dev)
+ return dev->wps_prov_info;
+ else
+ return 0;
+}
+
+
+void p2p_clear_provisioning_info(struct p2p_data *p2p, const u8 *addr)
+{
+ struct p2p_device *dev = NULL;
+
+ if (!addr || !p2p)
+ return;
+
+ dev = p2p_get_device(p2p, addr);
+ if (dev)
+ dev->wps_prov_info = 0;
+}
+
+
+void p2p_set_state(struct p2p_data *p2p, int new_state)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: State %s -> %s",
+ p2p_state_txt(p2p->state), p2p_state_txt(new_state));
+ p2p->state = new_state;
+}
+
+
+void p2p_set_timeout(struct p2p_data *p2p, unsigned int sec, unsigned int usec)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Set timeout (state=%s): %u.%06u sec",
+ p2p_state_txt(p2p->state), sec, usec);
+ eloop_cancel_timeout(p2p_state_timeout, p2p, NULL);
+ eloop_register_timeout(sec, usec, p2p_state_timeout, p2p, NULL);
+}
+
+
+void p2p_clear_timeout(struct p2p_data *p2p)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Clear timeout (state=%s)",
+ p2p_state_txt(p2p->state));
+ eloop_cancel_timeout(p2p_state_timeout, p2p, NULL);
+}
+
+
+void p2p_go_neg_failed(struct p2p_data *p2p, struct p2p_device *peer,
+ int status)
+{
+ struct p2p_go_neg_results res;
+ p2p_clear_timeout(p2p);
+ p2p_set_state(p2p, P2P_IDLE);
+ if (p2p->go_neg_peer)
+ p2p->go_neg_peer->wps_method = WPS_NOT_READY;
+ p2p->go_neg_peer = NULL;
+
+ os_memset(&res, 0, sizeof(res));
+ res.status = status;
+ if (peer) {
+ os_memcpy(res.peer_device_addr, peer->info.p2p_device_addr,
+ ETH_ALEN);
+ os_memcpy(res.peer_interface_addr, peer->intended_addr,
+ ETH_ALEN);
+ }
+ p2p->cfg->go_neg_completed(p2p->cfg->cb_ctx, &res);
+}
+
+
+static void p2p_listen_in_find(struct p2p_data *p2p)
+{
+ unsigned int r, tu;
+ int freq;
+ struct wpabuf *ies;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Starting short listen state (state=%s)",
+ p2p_state_txt(p2p->state));
+
+ freq = p2p_channel_to_freq(p2p->cfg->country, p2p->cfg->reg_class,
+ p2p->cfg->channel);
+ if (freq < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unknown regulatory class/channel");
+ return;
+ }
+
+ os_get_random((u8 *) &r, sizeof(r));
+ tu = (r % ((p2p->max_disc_int - p2p->min_disc_int) + 1) +
+ p2p->min_disc_int) * 100;
+
+ p2p->pending_listen_freq = freq;
+ p2p->pending_listen_sec = 0;
+ p2p->pending_listen_usec = 1024 * tu;
+
+ ies = p2p_build_probe_resp_ies(p2p);
+ if (ies == NULL)
+ return;
+
+ if (p2p->cfg->start_listen(p2p->cfg->cb_ctx, freq, 1024 * tu / 1000,
+ ies) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to start listen mode");
+ p2p->pending_listen_freq = 0;
+ }
+ wpabuf_free(ies);
+}
+
+
+int p2p_listen(struct p2p_data *p2p, unsigned int timeout)
+{
+ int freq;
+ struct wpabuf *ies;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Going to listen(only) state");
+
+ freq = p2p_channel_to_freq(p2p->cfg->country, p2p->cfg->reg_class,
+ p2p->cfg->channel);
+ if (freq < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unknown regulatory class/channel");
+ return -1;
+ }
+
+ p2p->pending_listen_freq = freq;
+ p2p->pending_listen_sec = timeout / 1000;
+ p2p->pending_listen_usec = (timeout % 1000) * 1000;
+
+ if (p2p->p2p_scan_running) {
+ if (p2p->start_after_scan == P2P_AFTER_SCAN_NOTHING) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: p2p_scan running - connect is already "
+ "pending - skip listen");
+ return 0;
+ }
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: p2p_scan running - delay start of listen state");
+ p2p->start_after_scan = P2P_AFTER_SCAN_LISTEN;
+ return 0;
+ }
+
+ ies = p2p_build_probe_resp_ies(p2p);
+ if (ies == NULL)
+ return -1;
+
+ if (p2p->cfg->start_listen(p2p->cfg->cb_ctx, freq, timeout, ies) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to start listen mode");
+ p2p->pending_listen_freq = 0;
+ wpabuf_free(ies);
+ return -1;
+ }
+ wpabuf_free(ies);
+
+ p2p_set_state(p2p, P2P_LISTEN_ONLY);
+
+ return 0;
+}
+
+
+static void p2p_device_clear_reported(struct p2p_data *p2p)
+{
+ struct p2p_device *dev;
+ dl_list_for_each(dev, &p2p->devices, struct p2p_device, list)
+ dev->flags &= ~P2P_DEV_REPORTED;
+}
+
+
+/**
+ * p2p_get_device - Fetch a peer entry
+ * @p2p: P2P module context from p2p_init()
+ * @addr: P2P Device Address of the peer
+ * Returns: Pointer to the device entry or %NULL if not found
+ */
+struct p2p_device * p2p_get_device(struct p2p_data *p2p, const u8 *addr)
+{
+ struct p2p_device *dev;
+ dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
+ if (os_memcmp(dev->info.p2p_device_addr, addr, ETH_ALEN) == 0)
+ return dev;
+ }
+ return NULL;
+}
+
+
+/**
+ * p2p_get_device_interface - Fetch a peer entry based on P2P Interface Address
+ * @p2p: P2P module context from p2p_init()
+ * @addr: P2P Interface Address of the peer
+ * Returns: Pointer to the device entry or %NULL if not found
+ */
+struct p2p_device * p2p_get_device_interface(struct p2p_data *p2p,
+ const u8 *addr)
+{
+ struct p2p_device *dev;
+ dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
+ if (os_memcmp(dev->interface_addr, addr, ETH_ALEN) == 0)
+ return dev;
+ }
+ return NULL;
+}
+
+
+/**
+ * p2p_create_device - Create a peer entry
+ * @p2p: P2P module context from p2p_init()
+ * @addr: P2P Device Address of the peer
+ * Returns: Pointer to the device entry or %NULL on failure
+ *
+ * If there is already an entry for the peer, it will be returned instead of
+ * creating a new one.
+ */
+static struct p2p_device * p2p_create_device(struct p2p_data *p2p,
+ const u8 *addr)
+{
+ struct p2p_device *dev, *oldest = NULL;
+ size_t count = 0;
+
+ dev = p2p_get_device(p2p, addr);
+ if (dev)
+ return dev;
+
+ dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
+ count++;
+ if (oldest == NULL ||
+ os_time_before(&dev->last_seen, &oldest->last_seen))
+ oldest = dev;
+ }
+ if (count + 1 > p2p->cfg->max_peers && oldest) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Remove oldest peer entry to make room for a new "
+ "peer");
+ dl_list_del(&oldest->list);
+ p2p_device_free(p2p, oldest);
+ }
+
+ dev = os_zalloc(sizeof(*dev));
+ if (dev == NULL)
+ return NULL;
+ dl_list_add(&p2p->devices, &dev->list);
+ os_memcpy(dev->info.p2p_device_addr, addr, ETH_ALEN);
+
+ return dev;
+}
+
+
+static void p2p_copy_client_info(struct p2p_device *dev,
+ struct p2p_client_info *cli)
+{
+ os_memcpy(dev->info.device_name, cli->dev_name, cli->dev_name_len);
+ dev->info.device_name[cli->dev_name_len] = '\0';
+ dev->info.dev_capab = cli->dev_capab;
+ dev->info.config_methods = cli->config_methods;
+ os_memcpy(dev->info.pri_dev_type, cli->pri_dev_type, 8);
+ dev->info.wps_sec_dev_type_list_len = 8 * cli->num_sec_dev_types;
+ os_memcpy(dev->info.wps_sec_dev_type_list, cli->sec_dev_types,
+ dev->info.wps_sec_dev_type_list_len);
+}
+
+
+static int p2p_add_group_clients(struct p2p_data *p2p, const u8 *go_dev_addr,
+ const u8 *go_interface_addr, int freq,
+ const u8 *gi, size_t gi_len)
+{
+ struct p2p_group_info info;
+ size_t c;
+ struct p2p_device *dev;
+
+ if (gi == NULL)
+ return 0;
+
+ if (p2p_group_info_parse(gi, gi_len, &info) < 0)
+ return -1;
+
+ /*
+ * Clear old data for this group; if the devices are still in the
+ * group, the information will be restored in the loop following this.
+ */
+ dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
+ if (os_memcmp(dev->member_in_go_iface, go_interface_addr,
+ ETH_ALEN) == 0) {
+ os_memset(dev->member_in_go_iface, 0, ETH_ALEN);
+ os_memset(dev->member_in_go_dev, 0, ETH_ALEN);
+ }
+ }
+
+ for (c = 0; c < info.num_clients; c++) {
+ struct p2p_client_info *cli = &info.client[c];
+ if (os_memcmp(cli->p2p_device_addr, p2p->cfg->dev_addr,
+ ETH_ALEN) == 0)
+ continue; /* ignore our own entry */
+ dev = p2p_get_device(p2p, cli->p2p_device_addr);
+ if (dev) {
+ /*
+ * Update information only if we have not received this
+ * directly from the client.
+ */
+ if (dev->flags & (P2P_DEV_GROUP_CLIENT_ONLY |
+ P2P_DEV_PROBE_REQ_ONLY))
+ p2p_copy_client_info(dev, cli);
+ if (dev->flags & P2P_DEV_PROBE_REQ_ONLY) {
+ dev->flags &= ~P2P_DEV_PROBE_REQ_ONLY;
+ }
+ } else {
+ dev = p2p_create_device(p2p, cli->p2p_device_addr);
+ if (dev == NULL)
+ continue;
+ dev->flags |= P2P_DEV_GROUP_CLIENT_ONLY;
+ p2p_copy_client_info(dev, cli);
+ dev->oper_freq = freq;
+ p2p->cfg->dev_found(p2p->cfg->cb_ctx,
+ dev->info.p2p_device_addr,
+ &dev->info, 1);
+ dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE;
+ }
+
+ os_memcpy(dev->interface_addr, cli->p2p_interface_addr,
+ ETH_ALEN);
+ os_get_time(&dev->last_seen);
+ os_memcpy(dev->member_in_go_dev, go_dev_addr, ETH_ALEN);
+ os_memcpy(dev->member_in_go_iface, go_interface_addr,
+ ETH_ALEN);
+ }
+
+ return 0;
+}
+
+
+static void p2p_copy_wps_info(struct p2p_device *dev, int probe_req,
+ const struct p2p_message *msg)
+{
+ os_memcpy(dev->info.device_name, msg->device_name,
+ sizeof(dev->info.device_name));
+
+ if (msg->manufacturer &&
+ msg->manufacturer_len < sizeof(dev->info.manufacturer)) {
+ os_memset(dev->info.manufacturer, 0,
+ sizeof(dev->info.manufacturer));
+ os_memcpy(dev->info.manufacturer, msg->manufacturer,
+ msg->manufacturer_len);
+ }
+
+ if (msg->model_name &&
+ msg->model_name_len < sizeof(dev->info.model_name)) {
+ os_memset(dev->info.model_name, 0,
+ sizeof(dev->info.model_name));
+ os_memcpy(dev->info.model_name, msg->model_name,
+ msg->model_name_len);
+ }
+
+ if (msg->model_number &&
+ msg->model_number_len < sizeof(dev->info.model_number)) {
+ os_memset(dev->info.model_number, 0,
+ sizeof(dev->info.model_number));
+ os_memcpy(dev->info.model_number, msg->model_number,
+ msg->model_number_len);
+ }
+
+ if (msg->serial_number &&
+ msg->serial_number_len < sizeof(dev->info.serial_number)) {
+ os_memset(dev->info.serial_number, 0,
+ sizeof(dev->info.serial_number));
+ os_memcpy(dev->info.serial_number, msg->serial_number,
+ msg->serial_number_len);
+ }
+
+ if (msg->pri_dev_type)
+ os_memcpy(dev->info.pri_dev_type, msg->pri_dev_type,
+ sizeof(dev->info.pri_dev_type));
+ else if (msg->wps_pri_dev_type)
+ os_memcpy(dev->info.pri_dev_type, msg->wps_pri_dev_type,
+ sizeof(dev->info.pri_dev_type));
+
+ if (msg->wps_sec_dev_type_list) {
+ os_memcpy(dev->info.wps_sec_dev_type_list,
+ msg->wps_sec_dev_type_list,
+ msg->wps_sec_dev_type_list_len);
+ dev->info.wps_sec_dev_type_list_len =
+ msg->wps_sec_dev_type_list_len;
+ }
+
+ if (msg->capability) {
+ dev->info.dev_capab = msg->capability[0];
+ dev->info.group_capab = msg->capability[1];
+ }
+
+ if (msg->ext_listen_timing) {
+ dev->ext_listen_period = WPA_GET_LE16(msg->ext_listen_timing);
+ dev->ext_listen_interval =
+ WPA_GET_LE16(msg->ext_listen_timing + 2);
+ }
+
+ if (!probe_req) {
+ dev->info.config_methods = msg->config_methods ?
+ msg->config_methods : msg->wps_config_methods;
+ }
+}
+
+
+/**
+ * p2p_add_device - Add peer entries based on scan results
+ * @p2p: P2P module context from p2p_init()
+ * @addr: Source address of Beacon or Probe Response frame (may be either
+ * P2P Device Address or P2P Interface Address)
+ * @level: Signal level (signal strength of the received frame from the peer)
+ * @freq: Frequency on which the Beacon or Probe Response frame was received
+ * @ies: IEs from the Beacon or Probe Response frame
+ * @ies_len: Length of ies buffer in octets
+ * Returns: 0 on success, -1 on failure
+ *
+ * If the scan result is for a GO, the clients in the group will also be added
+ * to the peer table. This function can also be used with some other frames
+ * like Provision Discovery Request that contains P2P Capability and P2P Device
+ * Info attributes.
+ */
+int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, int level,
+ const u8 *ies, size_t ies_len)
+{
+ struct p2p_device *dev;
+ struct p2p_message msg;
+ const u8 *p2p_dev_addr;
+ int i;
+
+ os_memset(&msg, 0, sizeof(msg));
+ if (p2p_parse_ies(ies, ies_len, &msg)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to parse P2P IE for a device entry");
+ p2p_parse_free(&msg);
+ return -1;
+ }
+
+ if (msg.p2p_device_addr)
+ p2p_dev_addr = msg.p2p_device_addr;
+ else if (msg.device_id)
+ p2p_dev_addr = msg.device_id;
+ else {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Ignore scan data without P2P Device Info or "
+ "P2P Device Id");
+ p2p_parse_free(&msg);
+ return -1;
+ }
+
+ if (!is_zero_ether_addr(p2p->peer_filter) &&
+ os_memcmp(p2p_dev_addr, p2p->peer_filter, ETH_ALEN) != 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Do not add peer "
+ "filter for " MACSTR " due to peer filter",
+ MAC2STR(p2p_dev_addr));
+ return 0;
+ }
+
+ dev = p2p_create_device(p2p, p2p_dev_addr);
+ if (dev == NULL) {
+ p2p_parse_free(&msg);
+ return -1;
+ }
+ os_get_time(&dev->last_seen);
+ dev->flags &= ~(P2P_DEV_PROBE_REQ_ONLY | P2P_DEV_GROUP_CLIENT_ONLY);
+
+ if (os_memcmp(addr, p2p_dev_addr, ETH_ALEN) != 0)
+ os_memcpy(dev->interface_addr, addr, ETH_ALEN);
+ if (msg.ssid &&
+ (msg.ssid[1] != P2P_WILDCARD_SSID_LEN ||
+ os_memcmp(msg.ssid + 2, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN)
+ != 0)) {
+ os_memcpy(dev->oper_ssid, msg.ssid + 2, msg.ssid[1]);
+ dev->oper_ssid_len = msg.ssid[1];
+ }
+
+ if (freq >= 2412 && freq <= 2484 && msg.ds_params &&
+ *msg.ds_params >= 1 && *msg.ds_params <= 14) {
+ int ds_freq;
+ if (*msg.ds_params == 14)
+ ds_freq = 2484;
+ else
+ ds_freq = 2407 + *msg.ds_params * 5;
+ if (freq != ds_freq) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Update Listen frequency based on DS "
+ "Parameter Set IE: %d -> %d MHz",
+ freq, ds_freq);
+ freq = ds_freq;
+ }
+ }
+
+ if (dev->listen_freq && dev->listen_freq != freq) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Update Listen frequency based on scan "
+ "results (" MACSTR " %d -> %d MHz (DS param %d)",
+ MAC2STR(dev->info.p2p_device_addr), dev->listen_freq,
+ freq, msg.ds_params ? *msg.ds_params : -1);
+ }
+ dev->listen_freq = freq;
+ if (msg.group_info)
+ dev->oper_freq = freq;
+ dev->info.level = level;
+
+ p2p_copy_wps_info(dev, 0, &msg);
+
+ for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
+ wpabuf_free(dev->info.wps_vendor_ext[i]);
+ dev->info.wps_vendor_ext[i] = NULL;
+ }
+
+ for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
+ if (msg.wps_vendor_ext[i] == NULL)
+ break;
+ dev->info.wps_vendor_ext[i] = wpabuf_alloc_copy(
+ msg.wps_vendor_ext[i], msg.wps_vendor_ext_len[i]);
+ if (dev->info.wps_vendor_ext[i] == NULL)
+ break;
+ }
+
+ p2p_add_group_clients(p2p, p2p_dev_addr, addr, freq, msg.group_info,
+ msg.group_info_len);
+
+ p2p_parse_free(&msg);
+
+ if (p2p_pending_sd_req(p2p, dev))
+ dev->flags |= P2P_DEV_SD_SCHEDULE;
+
+ if (dev->flags & P2P_DEV_REPORTED)
+ return 0;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Peer found with Listen frequency %d MHz", freq);
+ if (dev->flags & P2P_DEV_USER_REJECTED) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Do not report rejected device");
+ return 0;
+ }
+
+ p2p->cfg->dev_found(p2p->cfg->cb_ctx, addr, &dev->info,
+ !(dev->flags & P2P_DEV_REPORTED_ONCE));
+ dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE;
+
+ return 0;
+}
+
+
+static void p2p_device_free(struct p2p_data *p2p, struct p2p_device *dev)
+{
+ int i;
+
+ if (p2p->go_neg_peer == dev) {
+ /*
+ * If GO Negotiation is in progress, report that it has failed.
+ */
+ p2p_go_neg_failed(p2p, dev, -1);
+ p2p->go_neg_peer = NULL;
+ }
+ if (p2p->invite_peer == dev)
+ p2p->invite_peer = NULL;
+ if (p2p->sd_peer == dev)
+ p2p->sd_peer = NULL;
+ if (p2p->pending_client_disc_go == dev)
+ p2p->pending_client_disc_go = NULL;
+
+ /* dev_lost() device, but only if it was previously dev_found() */
+ if (dev->flags & P2P_DEV_REPORTED_ONCE)
+ p2p->cfg->dev_lost(p2p->cfg->cb_ctx,
+ dev->info.p2p_device_addr);
+
+ for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
+ wpabuf_free(dev->info.wps_vendor_ext[i]);
+ dev->info.wps_vendor_ext[i] = NULL;
+ }
+
+ os_free(dev);
+}
+
+
+static int p2p_get_next_prog_freq(struct p2p_data *p2p)
+{
+ struct p2p_channels *c;
+ struct p2p_reg_class *cla;
+ size_t cl, ch;
+ int found = 0;
+ u8 reg_class;
+ u8 channel;
+ int freq;
+
+ c = &p2p->cfg->channels;
+ for (cl = 0; cl < c->reg_classes; cl++) {
+ cla = &c->reg_class[cl];
+ if (cla->reg_class != p2p->last_prog_scan_class)
+ continue;
+ for (ch = 0; ch < cla->channels; ch++) {
+ if (cla->channel[ch] == p2p->last_prog_scan_chan) {
+ found = 1;
+ break;
+ }
+ }
+ if (found)
+ break;
+ }
+
+ if (!found) {
+ /* Start from beginning */
+ reg_class = c->reg_class[0].reg_class;
+ channel = c->reg_class[0].channel[0];
+ } else {
+ /* Pick the next channel */
+ ch++;
+ if (ch == cla->channels) {
+ cl++;
+ if (cl == c->reg_classes)
+ cl = 0;
+ ch = 0;
+ }
+ reg_class = c->reg_class[cl].reg_class;
+ channel = c->reg_class[cl].channel[ch];
+ }
+
+ freq = p2p_channel_to_freq(p2p->cfg->country, reg_class, channel);
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Next progressive search "
+ "channel: reg_class %u channel %u -> %d MHz",
+ reg_class, channel, freq);
+ p2p->last_prog_scan_class = reg_class;
+ p2p->last_prog_scan_chan = channel;
+
+ if (freq == 2412 || freq == 2437 || freq == 2462)
+ return 0; /* No need to add social channels */
+ return freq;
+}
+
+
+static void p2p_search(struct p2p_data *p2p)
+{
+ int freq = 0;
+ enum p2p_scan_type type;
+
+ if (p2p->drv_in_listen) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Driver is still "
+ "in Listen state - wait for it to end before "
+ "continuing");
+ return;
+ }
+ p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
+
+ if (p2p->go_neg_peer) {
+ /*
+ * Only scan the known listen frequency of the peer
+ * during GO Negotiation start.
+ */
+ freq = p2p->go_neg_peer->listen_freq;
+ if (freq <= 0)
+ freq = p2p->go_neg_peer->oper_freq;
+ type = P2P_SCAN_SPECIFIC;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting search "
+ "for freq %u (GO Neg)", freq);
+ } else if (p2p->invite_peer) {
+ /*
+ * Only scan the known listen frequency of the peer
+ * during Invite start.
+ */
+ freq = p2p->invite_peer->listen_freq;
+ if (freq <= 0)
+ freq = p2p->invite_peer->oper_freq;
+ type = P2P_SCAN_SPECIFIC;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting search "
+ "for freq %u (Invite)", freq);
+ } else if (p2p->find_type == P2P_FIND_PROGRESSIVE &&
+ (freq = p2p_get_next_prog_freq(p2p)) > 0) {
+ type = P2P_SCAN_SOCIAL_PLUS_ONE;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting search "
+ "(+ freq %u)", freq);
+ } else {
+ type = P2P_SCAN_SOCIAL;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting search");
+ }
+
+ if (p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, type, freq,
+ p2p->num_req_dev_types, p2p->req_dev_types,
+ p2p->find_dev_id)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Scan request failed");
+ p2p_continue_find(p2p);
+ } else {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Running p2p_scan");
+ p2p->p2p_scan_running = 1;
+ eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL);
+ eloop_register_timeout(P2P_SCAN_TIMEOUT, 0, p2p_scan_timeout,
+ p2p, NULL);
+ }
+}
+
+
+static void p2p_find_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct p2p_data *p2p = eloop_ctx;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Find timeout -> stop");
+ p2p_stop_find(p2p);
+}
+
+
+static int p2p_run_after_scan(struct p2p_data *p2p)
+{
+ struct p2p_device *dev;
+ enum p2p_after_scan op;
+
+ if (p2p->after_scan_tx) {
+ /* TODO: schedule p2p_run_after_scan to be called from TX
+ * status callback(?) */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Send pending "
+ "Action frame at p2p_scan completion");
+ p2p->cfg->send_action(p2p->cfg->cb_ctx,
+ p2p->after_scan_tx->freq,
+ p2p->after_scan_tx->dst,
+ p2p->after_scan_tx->src,
+ p2p->after_scan_tx->bssid,
+ (u8 *) (p2p->after_scan_tx + 1),
+ p2p->after_scan_tx->len,
+ p2p->after_scan_tx->wait_time);
+ os_free(p2p->after_scan_tx);
+ p2p->after_scan_tx = NULL;
+ return 1;
+ }
+
+ op = p2p->start_after_scan;
+ p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING;
+ switch (op) {
+ case P2P_AFTER_SCAN_NOTHING:
+ break;
+ case P2P_AFTER_SCAN_LISTEN:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Start previously "
+ "requested Listen state");
+ p2p_listen(p2p, p2p->pending_listen_sec * 1000 +
+ p2p->pending_listen_usec / 1000);
+ return 1;
+ case P2P_AFTER_SCAN_CONNECT:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Start previously "
+ "requested connect with " MACSTR,
+ MAC2STR(p2p->after_scan_peer));
+ dev = p2p_get_device(p2p, p2p->after_scan_peer);
+ if (dev == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer not "
+ "known anymore");
+ break;
+ }
+ p2p_connect_send(p2p, dev);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static void p2p_scan_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct p2p_data *p2p = eloop_ctx;
+ int running;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: p2p_scan timeout "
+ "(running=%d)", p2p->p2p_scan_running);
+ running = p2p->p2p_scan_running;
+ /* Make sure we recover from missed scan results callback */
+ p2p->p2p_scan_running = 0;
+
+ if (running)
+ p2p_run_after_scan(p2p);
+}
+
+
+static void p2p_free_req_dev_types(struct p2p_data *p2p)
+{
+ p2p->num_req_dev_types = 0;
+ os_free(p2p->req_dev_types);
+ p2p->req_dev_types = NULL;
+}
+
+
+int p2p_find(struct p2p_data *p2p, unsigned int timeout,
+ enum p2p_discovery_type type,
+ unsigned int num_req_dev_types, const u8 *req_dev_types,
+ const u8 *dev_id)
+{
+ int res;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting find (type=%d)",
+ type);
+ if (p2p->p2p_scan_running) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: p2p_scan is "
+ "already running");
+ }
+
+ p2p_free_req_dev_types(p2p);
+ if (req_dev_types && num_req_dev_types) {
+ p2p->req_dev_types = os_malloc(num_req_dev_types *
+ WPS_DEV_TYPE_LEN);
+ if (p2p->req_dev_types == NULL)
+ return -1;
+ os_memcpy(p2p->req_dev_types, req_dev_types,
+ num_req_dev_types * WPS_DEV_TYPE_LEN);
+ p2p->num_req_dev_types = num_req_dev_types;
+ }
+
+ if (dev_id) {
+ os_memcpy(p2p->find_dev_id_buf, dev_id, ETH_ALEN);
+ p2p->find_dev_id = p2p->find_dev_id_buf;
+ } else
+ p2p->find_dev_id = NULL;
+
+ p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING;
+ p2p_clear_timeout(p2p);
+ p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
+ p2p->find_type = type;
+ p2p_device_clear_reported(p2p);
+ p2p_set_state(p2p, P2P_SEARCH);
+ eloop_cancel_timeout(p2p_find_timeout, p2p, NULL);
+ p2p->last_p2p_find_timeout = timeout;
+ if (timeout)
+ eloop_register_timeout(timeout, 0, p2p_find_timeout,
+ p2p, NULL);
+ switch (type) {
+ case P2P_FIND_START_WITH_FULL:
+ case P2P_FIND_PROGRESSIVE:
+ res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, P2P_SCAN_FULL, 0,
+ p2p->num_req_dev_types,
+ p2p->req_dev_types, dev_id);
+ break;
+ case P2P_FIND_ONLY_SOCIAL:
+ res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, P2P_SCAN_SOCIAL, 0,
+ p2p->num_req_dev_types,
+ p2p->req_dev_types, dev_id);
+ break;
+ default:
+ return -1;
+ }
+
+ if (res == 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Running p2p_scan");
+ p2p->p2p_scan_running = 1;
+ eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL);
+ eloop_register_timeout(P2P_SCAN_TIMEOUT, 0, p2p_scan_timeout,
+ p2p, NULL);
+ } else if (res == 1) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Could not start "
+ "p2p_scan at this point - will try again after "
+ "previous scan completes");
+ res = 0;
+ p2p_set_state(p2p, P2P_SEARCH_WHEN_READY);
+ eloop_cancel_timeout(p2p_find_timeout, p2p, NULL);
+ } else {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Failed to start "
+ "p2p_scan");
+ p2p_set_state(p2p, P2P_IDLE);
+ eloop_cancel_timeout(p2p_find_timeout, p2p, NULL);
+ }
+
+ return res;
+}
+
+
+int p2p_other_scan_completed(struct p2p_data *p2p)
+{
+ if (p2p->state != P2P_SEARCH_WHEN_READY)
+ return 0;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting pending P2P find "
+ "now that previous scan was completed");
+ if (p2p_find(p2p, p2p->last_p2p_find_timeout, p2p->find_type,
+ p2p->num_req_dev_types, p2p->req_dev_types,
+ p2p->find_dev_id) < 0)
+ return 0;
+ return 1;
+}
+
+
+void p2p_stop_find_for_freq(struct p2p_data *p2p, int freq)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Stopping find");
+ eloop_cancel_timeout(p2p_find_timeout, p2p, NULL);
+ p2p_clear_timeout(p2p);
+ if (p2p->state == P2P_SEARCH)
+ wpa_msg(p2p->cfg->msg_ctx, MSG_INFO, P2P_EVENT_FIND_STOPPED);
+ p2p_set_state(p2p, P2P_IDLE);
+ p2p_free_req_dev_types(p2p);
+ p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING;
+ p2p->go_neg_peer = NULL;
+ p2p->sd_peer = NULL;
+ p2p->invite_peer = NULL;
+ p2p_stop_listen_for_freq(p2p, freq);
+}
+
+
+void p2p_stop_listen_for_freq(struct p2p_data *p2p, int freq)
+{
+ if (freq > 0 && p2p->drv_in_listen == freq && p2p->in_listen) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Skip stop_listen "
+ "since we are on correct channel for response");
+ return;
+ }
+ if (p2p->in_listen) {
+ p2p->in_listen = 0;
+ p2p_clear_timeout(p2p);
+ }
+ if (p2p->drv_in_listen) {
+ /*
+ * The driver may not deliver callback to p2p_listen_end()
+ * when the operation gets canceled, so clear the internal
+ * variable that is tracking driver state.
+ */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Clear "
+ "drv_in_listen (%d)", p2p->drv_in_listen);
+ p2p->drv_in_listen = 0;
+ }
+ p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
+}
+
+
+void p2p_stop_find(struct p2p_data *p2p)
+{
+ p2p_stop_find_for_freq(p2p, 0);
+}
+
+
+static int p2p_prepare_channel(struct p2p_data *p2p, unsigned int force_freq)
+{
+ if (force_freq) {
+ u8 op_reg_class, op_channel;
+ if (p2p_freq_to_channel(p2p->cfg->country, force_freq,
+ &op_reg_class, &op_channel) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported frequency %u MHz",
+ force_freq);
+ return -1;
+ }
+ if (!p2p_channels_includes(&p2p->cfg->channels, op_reg_class,
+ op_channel)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Frequency %u MHz (oper_class %u "
+ "channel %u) not allowed for P2P",
+ force_freq, op_reg_class, op_channel);
+ return -1;
+ }
+ p2p->op_reg_class = op_reg_class;
+ p2p->op_channel = op_channel;
+ p2p->channels.reg_classes = 1;
+ p2p->channels.reg_class[0].channels = 1;
+ p2p->channels.reg_class[0].reg_class = p2p->op_reg_class;
+ p2p->channels.reg_class[0].channel[0] = p2p->op_channel;
+ } else {
+ u8 op_reg_class, op_channel;
+
+ if (!p2p->cfg->cfg_op_channel && p2p->best_freq_overall > 0 &&
+ p2p_supported_freq(p2p, p2p->best_freq_overall) &&
+ p2p_freq_to_channel(p2p->cfg->country,
+ p2p->best_freq_overall,
+ &op_reg_class, &op_channel) == 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Select best overall channel as "
+ "operating channel preference");
+ p2p->op_reg_class = op_reg_class;
+ p2p->op_channel = op_channel;
+ } else if (!p2p->cfg->cfg_op_channel && p2p->best_freq_5 > 0 &&
+ p2p_supported_freq(p2p, p2p->best_freq_5) &&
+ p2p_freq_to_channel(p2p->cfg->country,
+ p2p->best_freq_5,
+ &op_reg_class, &op_channel) ==
+ 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Select best 5 GHz channel as "
+ "operating channel preference");
+ p2p->op_reg_class = op_reg_class;
+ p2p->op_channel = op_channel;
+ } else if (!p2p->cfg->cfg_op_channel &&
+ p2p->best_freq_24 > 0 &&
+ p2p_supported_freq(p2p, p2p->best_freq_24) &&
+ p2p_freq_to_channel(p2p->cfg->country,
+ p2p->best_freq_24,
+ &op_reg_class, &op_channel) ==
+ 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Select best 2.4 GHz channel as "
+ "operating channel preference");
+ p2p->op_reg_class = op_reg_class;
+ p2p->op_channel = op_channel;
+ } else {
+ p2p->op_reg_class = p2p->cfg->op_reg_class;
+ p2p->op_channel = p2p->cfg->op_channel;
+ }
+
+ os_memcpy(&p2p->channels, &p2p->cfg->channels,
+ sizeof(struct p2p_channels));
+ }
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Own preference for operation channel: "
+ "Operating Class %u Channel %u%s",
+ p2p->op_reg_class, p2p->op_channel,
+ force_freq ? " (forced)" : "");
+
+ return 0;
+}
+
+
+static void p2p_set_dev_persistent(struct p2p_device *dev,
+ int persistent_group)
+{
+ switch (persistent_group) {
+ case 0:
+ dev->flags &= ~(P2P_DEV_PREFER_PERSISTENT_GROUP |
+ P2P_DEV_PREFER_PERSISTENT_RECONN);
+ break;
+ case 1:
+ dev->flags |= P2P_DEV_PREFER_PERSISTENT_GROUP;
+ dev->flags &= ~P2P_DEV_PREFER_PERSISTENT_RECONN;
+ break;
+ case 2:
+ dev->flags |= P2P_DEV_PREFER_PERSISTENT_GROUP |
+ P2P_DEV_PREFER_PERSISTENT_RECONN;
+ break;
+ }
+}
+
+
+int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr,
+ enum p2p_wps_method wps_method,
+ int go_intent, const u8 *own_interface_addr,
+ unsigned int force_freq, int persistent_group)
+{
+ struct p2p_device *dev;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Request to start group negotiation - peer=" MACSTR
+ " GO Intent=%d Intended Interface Address=" MACSTR
+ " wps_method=%d persistent_group=%d",
+ MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr),
+ wps_method, persistent_group);
+
+ if (p2p_prepare_channel(p2p, force_freq) < 0)
+ return -1;
+
+ p2p->ssid_set = 0;
+ dev = p2p_get_device(p2p, peer_addr);
+ if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Cannot connect to unknown P2P Device " MACSTR,
+ MAC2STR(peer_addr));
+ return -1;
+ }
+
+ if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) {
+ if (!(dev->info.dev_capab &
+ P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Cannot connect to P2P Device " MACSTR
+ " that is in a group and is not discoverable",
+ MAC2STR(peer_addr));
+ return -1;
+ }
+ if (dev->oper_freq <= 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Cannot connect to P2P Device " MACSTR
+ " with incomplete information",
+ MAC2STR(peer_addr));
+ return -1;
+ }
+
+ /*
+ * First, try to connect directly. If the peer does not
+ * acknowledge frames, assume it is sleeping and use device
+ * discoverability via the GO at that point.
+ */
+ }
+
+ dev->flags &= ~P2P_DEV_NOT_YET_READY;
+ dev->flags &= ~P2P_DEV_USER_REJECTED;
+ dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE;
+ dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM;
+ dev->connect_reqs = 0;
+ dev->go_neg_req_sent = 0;
+ dev->go_state = UNKNOWN_GO;
+ p2p_set_dev_persistent(dev, persistent_group);
+ p2p->go_intent = go_intent;
+ os_memcpy(p2p->intended_addr, own_interface_addr, ETH_ALEN);
+
+ if (p2p->state != P2P_IDLE)
+ p2p_stop_find(p2p);
+
+ if (p2p->after_scan_tx) {
+ /*
+ * We need to drop the pending frame to avoid issues with the
+ * new GO Negotiation, e.g., when the pending frame was from a
+ * previous attempt at starting a GO Negotiation.
+ */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Dropped "
+ "previous pending Action frame TX that was waiting "
+ "for p2p_scan completion");
+ os_free(p2p->after_scan_tx);
+ p2p->after_scan_tx = NULL;
+ }
+
+ dev->wps_method = wps_method;
+ dev->status = P2P_SC_SUCCESS;
+
+ if (force_freq)
+ dev->flags |= P2P_DEV_FORCE_FREQ;
+ else
+ dev->flags &= ~P2P_DEV_FORCE_FREQ;
+
+ if (p2p->p2p_scan_running) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: p2p_scan running - delay connect send");
+ p2p->start_after_scan = P2P_AFTER_SCAN_CONNECT;
+ os_memcpy(p2p->after_scan_peer, peer_addr, ETH_ALEN);
+ return 0;
+ }
+ p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING;
+
+ return p2p_connect_send(p2p, dev);
+}
+
+
+int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr,
+ enum p2p_wps_method wps_method,
+ int go_intent, const u8 *own_interface_addr,
+ unsigned int force_freq, int persistent_group)
+{
+ struct p2p_device *dev;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Request to authorize group negotiation - peer=" MACSTR
+ " GO Intent=%d Intended Interface Address=" MACSTR
+ " wps_method=%d persistent_group=%d",
+ MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr),
+ wps_method, persistent_group);
+
+ if (p2p_prepare_channel(p2p, force_freq) < 0)
+ return -1;
+
+ dev = p2p_get_device(p2p, peer_addr);
+ if (dev == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Cannot authorize unknown P2P Device " MACSTR,
+ MAC2STR(peer_addr));
+ return -1;
+ }
+
+ dev->flags &= ~P2P_DEV_NOT_YET_READY;
+ dev->flags &= ~P2P_DEV_USER_REJECTED;
+ dev->go_neg_req_sent = 0;
+ dev->go_state = UNKNOWN_GO;
+ p2p_set_dev_persistent(dev, persistent_group);
+ p2p->go_intent = go_intent;
+ os_memcpy(p2p->intended_addr, own_interface_addr, ETH_ALEN);
+
+ dev->wps_method = wps_method;
+ dev->status = P2P_SC_SUCCESS;
+
+ if (force_freq)
+ dev->flags |= P2P_DEV_FORCE_FREQ;
+ else
+ dev->flags &= ~P2P_DEV_FORCE_FREQ;
+
+ return 0;
+}
+
+
+void p2p_add_dev_info(struct p2p_data *p2p, const u8 *addr,
+ struct p2p_device *dev, struct p2p_message *msg)
+{
+ os_get_time(&dev->last_seen);
+
+ p2p_copy_wps_info(dev, 0, msg);
+
+ if (msg->listen_channel) {
+ int freq;
+ freq = p2p_channel_to_freq((char *) msg->listen_channel,
+ msg->listen_channel[3],
+ msg->listen_channel[4]);
+ if (freq < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unknown peer Listen channel: "
+ "country=%c%c(0x%02x) reg_class=%u channel=%u",
+ msg->listen_channel[0],
+ msg->listen_channel[1],
+ msg->listen_channel[2],
+ msg->listen_channel[3],
+ msg->listen_channel[4]);
+ } else {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Update "
+ "peer " MACSTR " Listen channel: %u -> %u MHz",
+ MAC2STR(dev->info.p2p_device_addr),
+ dev->listen_freq, freq);
+ dev->listen_freq = freq;
+ }
+ }
+
+ if (dev->flags & P2P_DEV_PROBE_REQ_ONLY) {
+ dev->flags &= ~P2P_DEV_PROBE_REQ_ONLY;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Completed device entry based on data from "
+ "GO Negotiation Request");
+ } else {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Created device entry based on GO Neg Req: "
+ MACSTR " dev_capab=0x%x group_capab=0x%x name='%s' "
+ "listen_freq=%d",
+ MAC2STR(dev->info.p2p_device_addr),
+ dev->info.dev_capab, dev->info.group_capab,
+ dev->info.device_name, dev->listen_freq);
+ }
+
+ dev->flags &= ~P2P_DEV_GROUP_CLIENT_ONLY;
+
+ if (dev->flags & P2P_DEV_USER_REJECTED) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Do not report rejected device");
+ return;
+ }
+
+ p2p->cfg->dev_found(p2p->cfg->cb_ctx, addr, &dev->info,
+ !(dev->flags & P2P_DEV_REPORTED_ONCE));
+ dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE;
+}
+
+
+void p2p_build_ssid(struct p2p_data *p2p, u8 *ssid, size_t *ssid_len)
+{
+ os_memcpy(ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN);
+ p2p_random((char *) &ssid[P2P_WILDCARD_SSID_LEN], 2);
+ os_memcpy(&ssid[P2P_WILDCARD_SSID_LEN + 2],
+ p2p->cfg->ssid_postfix, p2p->cfg->ssid_postfix_len);
+ *ssid_len = P2P_WILDCARD_SSID_LEN + 2 + p2p->cfg->ssid_postfix_len;
+}
+
+
+int p2p_go_params(struct p2p_data *p2p, struct p2p_go_neg_results *params)
+{
+ p2p_build_ssid(p2p, params->ssid, ¶ms->ssid_len);
+ p2p_random(params->passphrase, 8);
+ return 0;
+}
+
+
+void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer)
+{
+ struct p2p_go_neg_results res;
+ int go = peer->go_state == LOCAL_GO;
+ struct p2p_channels intersection;
+ int freqs;
+ size_t i, j;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: GO Negotiation with " MACSTR " completed (%s will be "
+ "GO)", MAC2STR(peer->info.p2p_device_addr),
+ go ? "local end" : "peer");
+
+ os_memset(&res, 0, sizeof(res));
+ res.role_go = go;
+ os_memcpy(res.peer_device_addr, peer->info.p2p_device_addr, ETH_ALEN);
+ os_memcpy(res.peer_interface_addr, peer->intended_addr, ETH_ALEN);
+ res.wps_method = peer->wps_method;
+ if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP) {
+ if (peer->flags & P2P_DEV_PREFER_PERSISTENT_RECONN)
+ res.persistent_group = 2;
+ else
+ res.persistent_group = 1;
+ }
+
+ if (go) {
+ /* Setup AP mode for WPS provisioning */
+ res.freq = p2p_channel_to_freq(p2p->cfg->country,
+ p2p->op_reg_class,
+ p2p->op_channel);
+ os_memcpy(res.ssid, p2p->ssid, p2p->ssid_len);
+ res.ssid_len = p2p->ssid_len;
+ p2p_random(res.passphrase, 8);
+ } else {
+ res.freq = peer->oper_freq;
+ if (p2p->ssid_len) {
+ os_memcpy(res.ssid, p2p->ssid, p2p->ssid_len);
+ res.ssid_len = p2p->ssid_len;
+ }
+ }
+
+ p2p_channels_intersect(&p2p->channels, &peer->channels,
+ &intersection);
+ freqs = 0;
+ for (i = 0; i < intersection.reg_classes; i++) {
+ struct p2p_reg_class *c = &intersection.reg_class[i];
+ if (freqs + 1 == P2P_MAX_CHANNELS)
+ break;
+ for (j = 0; j < c->channels; j++) {
+ int freq;
+ if (freqs + 1 == P2P_MAX_CHANNELS)
+ break;
+ freq = p2p_channel_to_freq(peer->country, c->reg_class,
+ c->channel[j]);
+ if (freq < 0)
+ continue;
+ res.freq_list[freqs++] = freq;
+ }
+ }
+
+ res.peer_config_timeout = go ? peer->client_timeout : peer->go_timeout;
+
+ p2p_clear_timeout(p2p);
+ p2p->ssid_set = 0;
+ peer->go_neg_req_sent = 0;
+ peer->wps_method = WPS_NOT_READY;
+
+ p2p_set_state(p2p, P2P_PROVISIONING);
+ p2p->cfg->go_neg_completed(p2p->cfg->cb_ctx, &res);
+}
+
+
+static void p2p_rx_p2p_action(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len, int rx_freq)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: RX P2P Public Action from " MACSTR, MAC2STR(sa));
+ wpa_hexdump(MSG_MSGDUMP, "P2P: P2P Public Action contents", data, len);
+
+ if (len < 1)
+ return;
+
+ switch (data[0]) {
+ case P2P_GO_NEG_REQ:
+ p2p_process_go_neg_req(p2p, sa, data + 1, len - 1, rx_freq);
+ break;
+ case P2P_GO_NEG_RESP:
+ p2p_process_go_neg_resp(p2p, sa, data + 1, len - 1, rx_freq);
+ break;
+ case P2P_GO_NEG_CONF:
+ p2p_process_go_neg_conf(p2p, sa, data + 1, len - 1);
+ break;
+ case P2P_INVITATION_REQ:
+ p2p_process_invitation_req(p2p, sa, data + 1, len - 1,
+ rx_freq);
+ break;
+ case P2P_INVITATION_RESP:
+ p2p_process_invitation_resp(p2p, sa, data + 1, len - 1);
+ break;
+ case P2P_PROV_DISC_REQ:
+ p2p_process_prov_disc_req(p2p, sa, data + 1, len - 1, rx_freq);
+ break;
+ case P2P_PROV_DISC_RESP:
+ p2p_process_prov_disc_resp(p2p, sa, data + 1, len - 1);
+ break;
+ case P2P_DEV_DISC_REQ:
+ p2p_process_dev_disc_req(p2p, sa, data + 1, len - 1, rx_freq);
+ break;
+ case P2P_DEV_DISC_RESP:
+ p2p_process_dev_disc_resp(p2p, sa, data + 1, len - 1);
+ break;
+ default:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported P2P Public Action frame type %d",
+ data[0]);
+ break;
+ }
+}
+
+
+void p2p_rx_action_public(struct p2p_data *p2p, const u8 *da, const u8 *sa,
+ const u8 *bssid, const u8 *data, size_t len,
+ int freq)
+{
+ if (len < 1)
+ return;
+
+ switch (data[0]) {
+ case WLAN_PA_VENDOR_SPECIFIC:
+ data++;
+ len--;
+ if (len < 3)
+ return;
+ if (WPA_GET_BE24(data) != OUI_WFA)
+ return;
+
+ data += 3;
+ len -= 3;
+ if (len < 1)
+ return;
+
+ if (*data != P2P_OUI_TYPE)
+ return;
+
+ p2p_rx_p2p_action(p2p, sa, data + 1, len - 1, freq);
+ break;
+ case WLAN_PA_GAS_INITIAL_REQ:
+ p2p_rx_gas_initial_req(p2p, sa, data + 1, len - 1, freq);
+ break;
+ case WLAN_PA_GAS_INITIAL_RESP:
+ p2p_rx_gas_initial_resp(p2p, sa, data + 1, len - 1, freq);
+ break;
+ case WLAN_PA_GAS_COMEBACK_REQ:
+ p2p_rx_gas_comeback_req(p2p, sa, data + 1, len - 1, freq);
+ break;
+ case WLAN_PA_GAS_COMEBACK_RESP:
+ p2p_rx_gas_comeback_resp(p2p, sa, data + 1, len - 1, freq);
+ break;
+ }
+}
+
+
+void p2p_rx_action(struct p2p_data *p2p, const u8 *da, const u8 *sa,
+ const u8 *bssid, u8 category,
+ const u8 *data, size_t len, int freq)
+{
+ if (category == WLAN_ACTION_PUBLIC) {
+ p2p_rx_action_public(p2p, da, sa, bssid, data, len, freq);
+ return;
+ }
+
+ if (category != WLAN_ACTION_VENDOR_SPECIFIC)
+ return;
+
+ if (len < 4)
+ return;
+
+ if (WPA_GET_BE24(data) != OUI_WFA)
+ return;
+ data += 3;
+ len -= 3;
+
+ if (*data != P2P_OUI_TYPE)
+ return;
+ data++;
+ len--;
+
+ /* P2P action frame */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: RX P2P Action from " MACSTR, MAC2STR(sa));
+ wpa_hexdump(MSG_MSGDUMP, "P2P: P2P Action contents", data, len);
+
+ if (len < 1)
+ return;
+ switch (data[0]) {
+ case P2P_NOA:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Received P2P Action - Notice of Absence");
+ /* TODO */
+ break;
+ case P2P_PRESENCE_REQ:
+ p2p_process_presence_req(p2p, da, sa, data + 1, len - 1, freq);
+ break;
+ case P2P_PRESENCE_RESP:
+ p2p_process_presence_resp(p2p, da, sa, data + 1, len - 1);
+ break;
+ case P2P_GO_DISC_REQ:
+ p2p_process_go_disc_req(p2p, da, sa, data + 1, len - 1, freq);
+ break;
+ default:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Received P2P Action - unknown type %u", data[0]);
+ break;
+ }
+}
+
+
+static void p2p_go_neg_start(void *eloop_ctx, void *timeout_ctx)
+{
+ struct p2p_data *p2p = eloop_ctx;
+ if (p2p->go_neg_peer == NULL)
+ return;
+ p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
+ p2p->go_neg_peer->status = P2P_SC_SUCCESS;
+ p2p_connect_send(p2p, p2p->go_neg_peer);
+}
+
+
+static void p2p_invite_start(void *eloop_ctx, void *timeout_ctx)
+{
+ struct p2p_data *p2p = eloop_ctx;
+ if (p2p->invite_peer == NULL)
+ return;
+ p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
+ p2p_invite_send(p2p, p2p->invite_peer, p2p->invite_go_dev_addr);
+}
+
+
+static void p2p_add_dev_from_probe_req(struct p2p_data *p2p, const u8 *addr,
+ const u8 *ie, size_t ie_len)
+{
+ struct p2p_message msg;
+ struct p2p_device *dev;
+
+ os_memset(&msg, 0, sizeof(msg));
+ if (p2p_parse_ies(ie, ie_len, &msg) < 0 || msg.p2p_attributes == NULL)
+ {
+ p2p_parse_free(&msg);
+ return; /* not a P2P probe */
+ }
+
+ if (msg.ssid == NULL || msg.ssid[1] != P2P_WILDCARD_SSID_LEN ||
+ os_memcmp(msg.ssid + 2, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN)
+ != 0) {
+ /* The Probe Request is not part of P2P Device Discovery. It is
+ * not known whether the source address of the frame is the P2P
+ * Device Address or P2P Interface Address. Do not add a new
+ * peer entry based on this frames.
+ */
+ p2p_parse_free(&msg);
+ return;
+ }
+
+ dev = p2p_get_device(p2p, addr);
+ if (dev) {
+ if (dev->country[0] == 0 && msg.listen_channel)
+ os_memcpy(dev->country, msg.listen_channel, 3);
+ os_get_time(&dev->last_seen);
+ p2p_parse_free(&msg);
+ return; /* already known */
+ }
+
+ dev = p2p_create_device(p2p, addr);
+ if (dev == NULL) {
+ p2p_parse_free(&msg);
+ return;
+ }
+
+ os_get_time(&dev->last_seen);
+ dev->flags |= P2P_DEV_PROBE_REQ_ONLY;
+
+ if (msg.listen_channel) {
+ os_memcpy(dev->country, msg.listen_channel, 3);
+ dev->listen_freq = p2p_channel_to_freq(dev->country,
+ msg.listen_channel[3],
+ msg.listen_channel[4]);
+ }
+
+ p2p_copy_wps_info(dev, 1, &msg);
+
+ p2p_parse_free(&msg);
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Created device entry based on Probe Req: " MACSTR
+ " dev_capab=0x%x group_capab=0x%x name='%s' listen_freq=%d",
+ MAC2STR(dev->info.p2p_device_addr), dev->info.dev_capab,
+ dev->info.group_capab, dev->info.device_name,
+ dev->listen_freq);
+}
+
+
+struct p2p_device * p2p_add_dev_from_go_neg_req(struct p2p_data *p2p,
+ const u8 *addr,
+ struct p2p_message *msg)
+{
+ struct p2p_device *dev;
+
+ dev = p2p_get_device(p2p, addr);
+ if (dev) {
+ os_get_time(&dev->last_seen);
+ return dev; /* already known */
+ }
+
+ dev = p2p_create_device(p2p, addr);
+ if (dev == NULL)
+ return NULL;
+
+ p2p_add_dev_info(p2p, addr, dev, msg);
+
+ return dev;
+}
+
+
+static int dev_type_match(const u8 *dev_type, const u8 *req_dev_type)
+{
+ if (os_memcmp(dev_type, req_dev_type, WPS_DEV_TYPE_LEN) == 0)
+ return 1;
+ if (os_memcmp(dev_type, req_dev_type, 2) == 0 &&
+ WPA_GET_BE32(&req_dev_type[2]) == 0 &&
+ WPA_GET_BE16(&req_dev_type[6]) == 0)
+ return 1; /* Category match with wildcard OUI/sub-category */
+ return 0;
+}
+
+
+int dev_type_list_match(const u8 *dev_type, const u8 *req_dev_type[],
+ size_t num_req_dev_type)
+{
+ size_t i;
+ for (i = 0; i < num_req_dev_type; i++) {
+ if (dev_type_match(dev_type, req_dev_type[i]))
+ return 1;
+ }
+ return 0;
+}
+
+
+/**
+ * p2p_match_dev_type - Match local device type with requested type
+ * @p2p: P2P module context from p2p_init()
+ * @wps: WPS TLVs from Probe Request frame (concatenated WPS IEs)
+ * Returns: 1 on match, 0 on mismatch
+ *
+ * This function can be used to match the Requested Device Type attribute in
+ * WPS IE with the local device types for deciding whether to reply to a Probe
+ * Request frame.
+ */
+int p2p_match_dev_type(struct p2p_data *p2p, struct wpabuf *wps)
+{
+ struct wps_parse_attr attr;
+ size_t i;
+
+ if (wps_parse_msg(wps, &attr))
+ return 1; /* assume no Requested Device Type attributes */
+
+ if (attr.num_req_dev_type == 0)
+ return 1; /* no Requested Device Type attributes -> match */
+
+ if (dev_type_list_match(p2p->cfg->pri_dev_type, attr.req_dev_type,
+ attr.num_req_dev_type))
+ return 1; /* Own Primary Device Type matches */
+
+ for (i = 0; i < p2p->cfg->num_sec_dev_types; i++)
+ if (dev_type_list_match(p2p->cfg->sec_dev_type[i],
+ attr.req_dev_type,
+ attr.num_req_dev_type))
+ return 1; /* Own Secondary Device Type matches */
+
+ /* No matching device type found */
+ return 0;
+}
+
+
+struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p)
+{
+ struct wpabuf *buf;
+ u8 *len;
+
+ buf = wpabuf_alloc(1000);
+ if (buf == NULL)
+ return NULL;
+
+ p2p_build_wps_ie(p2p, buf, DEV_PW_DEFAULT, 1);
+
+ /* P2P IE */
+ len = p2p_buf_add_ie_hdr(buf);
+ p2p_buf_add_capability(buf, p2p->dev_capab, 0);
+ if (p2p->ext_listen_interval)
+ p2p_buf_add_ext_listen_timing(buf, p2p->ext_listen_period,
+ p2p->ext_listen_interval);
+ p2p_buf_add_device_info(buf, p2p, NULL);
+ p2p_buf_update_ie_hdr(buf, len);
+
+ return buf;
+}
+
+
+static int is_11b(u8 rate)
+{
+ return rate == 0x02 || rate == 0x04 || rate == 0x0b || rate == 0x16;
+}
+
+
+static int supp_rates_11b_only(struct ieee802_11_elems *elems)
+{
+ int num_11b = 0, num_others = 0;
+ int i;
+
+ if (elems->supp_rates == NULL && elems->ext_supp_rates == NULL)
+ return 0;
+
+ for (i = 0; elems->supp_rates && i < elems->supp_rates_len; i++) {
+ if (is_11b(elems->supp_rates[i]))
+ num_11b++;
+ else
+ num_others++;
+ }
+
+ for (i = 0; elems->ext_supp_rates && i < elems->ext_supp_rates_len;
+ i++) {
+ if (is_11b(elems->ext_supp_rates[i]))
+ num_11b++;
+ else
+ num_others++;
+ }
+
+ return num_11b > 0 && num_others == 0;
+}
+
+
+static void p2p_reply_probe(struct p2p_data *p2p, const u8 *addr,
+ const u8 *dst, const u8 *bssid, const u8 *ie,
+ size_t ie_len)
+{
+ struct ieee802_11_elems elems;
+ struct wpabuf *buf;
+ struct ieee80211_mgmt *resp;
+ struct p2p_message msg;
+ struct wpabuf *ies;
+
+ if (!p2p->in_listen || !p2p->drv_in_listen) {
+ /* not in Listen state - ignore Probe Request */
+ return;
+ }
+
+ if (ieee802_11_parse_elems((u8 *) ie, ie_len, &elems, 0) ==
+ ParseFailed) {
+ /* Ignore invalid Probe Request frames */
+ return;
+ }
+
+ if (elems.p2p == NULL) {
+ /* not a P2P probe - ignore it */
+ return;
+ }
+
+ if (dst && !is_broadcast_ether_addr(dst) &&
+ os_memcmp(dst, p2p->cfg->dev_addr, ETH_ALEN) != 0) {
+ /* Not sent to the broadcast address or our P2P Device Address
+ */
+ return;
+ }
+
+ if (bssid && !is_broadcast_ether_addr(bssid)) {
+ /* Not sent to the Wildcard BSSID */
+ return;
+ }
+
+ if (elems.ssid == NULL || elems.ssid_len != P2P_WILDCARD_SSID_LEN ||
+ os_memcmp(elems.ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) !=
+ 0) {
+ /* not using P2P Wildcard SSID - ignore */
+ return;
+ }
+
+ if (supp_rates_11b_only(&elems)) {
+ /* Indicates support for 11b rates only */
+ return;
+ }
+
+ os_memset(&msg, 0, sizeof(msg));
+ if (p2p_parse_ies(ie, ie_len, &msg) < 0) {
+ /* Could not parse P2P attributes */
+ return;
+ }
+
+ if (msg.device_id &&
+ os_memcmp(msg.device_id, p2p->cfg->dev_addr, ETH_ALEN != 0)) {
+ /* Device ID did not match */
+ p2p_parse_free(&msg);
+ return;
+ }
+
+ /* Check Requested Device Type match */
+ if (msg.wps_attributes &&
+ !p2p_match_dev_type(p2p, msg.wps_attributes)) {
+ /* No match with Requested Device Type */
+ p2p_parse_free(&msg);
+ return;
+ }
+ p2p_parse_free(&msg);
+
+ if (!p2p->cfg->send_probe_resp)
+ return; /* Response generated elsewhere */
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Reply to P2P Probe Request in Listen state");
+
+ /*
+ * We do not really have a specific BSS that this frame is advertising,
+ * so build a frame that has some information in valid format. This is
+ * really only used for discovery purposes, not to learn exact BSS
+ * parameters.
+ */
+ ies = p2p_build_probe_resp_ies(p2p);
+ if (ies == NULL)
+ return;
+
+ buf = wpabuf_alloc(200 + wpabuf_len(ies));
+ if (buf == NULL) {
+ wpabuf_free(ies);
+ return;
+ }
+
+ resp = NULL;
+ resp = wpabuf_put(buf, resp->u.probe_resp.variable - (u8 *) resp);
+
+ resp->frame_control = host_to_le16((WLAN_FC_TYPE_MGMT << 2) |
+ (WLAN_FC_STYPE_PROBE_RESP << 4));
+ os_memcpy(resp->da, addr, ETH_ALEN);
+ os_memcpy(resp->sa, p2p->cfg->dev_addr, ETH_ALEN);
+ os_memcpy(resp->bssid, p2p->cfg->dev_addr, ETH_ALEN);
+ resp->u.probe_resp.beacon_int = host_to_le16(100);
+ /* hardware or low-level driver will setup seq_ctrl and timestamp */
+ resp->u.probe_resp.capab_info =
+ host_to_le16(WLAN_CAPABILITY_SHORT_PREAMBLE |
+ WLAN_CAPABILITY_PRIVACY |
+ WLAN_CAPABILITY_SHORT_SLOT_TIME);
+
+ wpabuf_put_u8(buf, WLAN_EID_SSID);
+ wpabuf_put_u8(buf, P2P_WILDCARD_SSID_LEN);
+ wpabuf_put_data(buf, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN);
+
+ wpabuf_put_u8(buf, WLAN_EID_SUPP_RATES);
+ wpabuf_put_u8(buf, 8);
+ wpabuf_put_u8(buf, (60 / 5) | 0x80);
+ wpabuf_put_u8(buf, 90 / 5);
+ wpabuf_put_u8(buf, (120 / 5) | 0x80);
+ wpabuf_put_u8(buf, 180 / 5);
+ wpabuf_put_u8(buf, (240 / 5) | 0x80);
+ wpabuf_put_u8(buf, 360 / 5);
+ wpabuf_put_u8(buf, 480 / 5);
+ wpabuf_put_u8(buf, 540 / 5);
+
+ wpabuf_put_u8(buf, WLAN_EID_DS_PARAMS);
+ wpabuf_put_u8(buf, 1);
+ wpabuf_put_u8(buf, p2p->cfg->channel);
+
+ wpabuf_put_buf(buf, ies);
+ wpabuf_free(ies);
+
+ p2p->cfg->send_probe_resp(p2p->cfg->cb_ctx, buf);
+
+ wpabuf_free(buf);
+}
+
+
+int p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
+ const u8 *bssid, const u8 *ie, size_t ie_len)
+{
+ p2p_add_dev_from_probe_req(p2p, addr, ie, ie_len);
+
+ p2p_reply_probe(p2p, addr, dst, bssid, ie, ie_len);
+
+ if ((p2p->state == P2P_CONNECT || p2p->state == P2P_CONNECT_LISTEN) &&
+ p2p->go_neg_peer &&
+ os_memcmp(addr, p2p->go_neg_peer->info.p2p_device_addr, ETH_ALEN)
+ == 0) {
+ /* Received a Probe Request from GO Negotiation peer */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Found GO Negotiation peer - try to start GO "
+ "negotiation from timeout");
+ eloop_register_timeout(0, 0, p2p_go_neg_start, p2p, NULL);
+ return 1;
+ }
+
+ if ((p2p->state == P2P_INVITE || p2p->state == P2P_INVITE_LISTEN) &&
+ p2p->invite_peer &&
+ os_memcmp(addr, p2p->invite_peer->info.p2p_device_addr, ETH_ALEN)
+ == 0) {
+ /* Received a Probe Request from Invite peer */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Found Invite peer - try to start Invite from "
+ "timeout");
+ eloop_register_timeout(0, 0, p2p_invite_start, p2p, NULL);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static int p2p_assoc_req_ie_wlan_ap(struct p2p_data *p2p, const u8 *bssid,
+ u8 *buf, size_t len, struct wpabuf *p2p_ie)
+{
+ struct wpabuf *tmp;
+ u8 *lpos;
+ size_t tmplen;
+ int res;
+ u8 group_capab;
+
+ if (p2p_ie == NULL)
+ return 0; /* WLAN AP is not a P2P manager */
+
+ /*
+ * (Re)Association Request - P2P IE
+ * P2P Capability attribute (shall be present)
+ * P2P Interface attribute (present if concurrent device and
+ * P2P Management is enabled)
+ */
+ tmp = wpabuf_alloc(200);
+ if (tmp == NULL)
+ return -1;
+
+ lpos = p2p_buf_add_ie_hdr(tmp);
+ group_capab = 0;
+ if (p2p->num_groups > 0) {
+ group_capab |= P2P_GROUP_CAPAB_GROUP_OWNER;
+ if ((p2p->dev_capab & P2P_DEV_CAPAB_CONCURRENT_OPER) &&
+ (p2p->dev_capab & P2P_DEV_CAPAB_INFRA_MANAGED) &&
+ p2p->cross_connect)
+ group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
+ }
+ p2p_buf_add_capability(tmp, p2p->dev_capab, group_capab);
+ if ((p2p->dev_capab & P2P_DEV_CAPAB_CONCURRENT_OPER) &&
+ (p2p->dev_capab & P2P_DEV_CAPAB_INFRA_MANAGED))
+ p2p_buf_add_p2p_interface(tmp, p2p);
+ p2p_buf_update_ie_hdr(tmp, lpos);
+
+ tmplen = wpabuf_len(tmp);
+ if (tmplen > len)
+ res = -1;
+ else {
+ os_memcpy(buf, wpabuf_head(tmp), tmplen);
+ res = tmplen;
+ }
+ wpabuf_free(tmp);
+
+ return res;
+}
+
+
+int p2p_assoc_req_ie(struct p2p_data *p2p, const u8 *bssid, u8 *buf,
+ size_t len, int p2p_group, struct wpabuf *p2p_ie)
+{
+ struct wpabuf *tmp;
+ u8 *lpos;
+ struct p2p_device *peer;
+ size_t tmplen;
+ int res;
+
+ if (!p2p_group)
+ return p2p_assoc_req_ie_wlan_ap(p2p, bssid, buf, len, p2p_ie);
+
+ /*
+ * (Re)Association Request - P2P IE
+ * P2P Capability attribute (shall be present)
+ * Extended Listen Timing (may be present)
+ * P2P Device Info attribute (shall be present)
+ */
+ tmp = wpabuf_alloc(200);
+ if (tmp == NULL)
+ return -1;
+
+ peer = bssid ? p2p_get_device(p2p, bssid) : NULL;
+
+ lpos = p2p_buf_add_ie_hdr(tmp);
+ p2p_buf_add_capability(tmp, p2p->dev_capab, 0);
+ if (p2p->ext_listen_interval)
+ p2p_buf_add_ext_listen_timing(tmp, p2p->ext_listen_period,
+ p2p->ext_listen_interval);
+ p2p_buf_add_device_info(tmp, p2p, peer);
+ p2p_buf_update_ie_hdr(tmp, lpos);
+
+ tmplen = wpabuf_len(tmp);
+ if (tmplen > len)
+ res = -1;
+ else {
+ os_memcpy(buf, wpabuf_head(tmp), tmplen);
+ res = tmplen;
+ }
+ wpabuf_free(tmp);
+
+ return res;
+}
+
+
+int p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf, char *end)
+{
+ struct wpabuf *p2p_ie;
+ int ret;
+
+ p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len, P2P_IE_VENDOR_TYPE);
+ if (p2p_ie == NULL)
+ return 0;
+
+ ret = p2p_attr_text(p2p_ie, buf, end);
+ wpabuf_free(p2p_ie);
+ return ret;
+}
+
+
+int p2p_parse_dev_addr(const u8 *ies, size_t ies_len, u8 *dev_addr)
+{
+ struct wpabuf *p2p_ie;
+ struct p2p_message msg;
+ int ret = -1;
+
+ p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len,
+ P2P_IE_VENDOR_TYPE);
+ if (p2p_ie == NULL)
+ return -1;
+ os_memset(&msg, 0, sizeof(msg));
+ if (p2p_parse_p2p_ie(p2p_ie, &msg)) {
+ wpabuf_free(p2p_ie);
+ return -1;
+ }
+
+ if (msg.p2p_device_addr) {
+ os_memcpy(dev_addr, msg.p2p_device_addr, ETH_ALEN);
+ ret = 0;
+ } else if (msg.device_id) {
+ os_memcpy(dev_addr, msg.device_id, ETH_ALEN);
+ ret = 0;
+ }
+
+ wpabuf_free(p2p_ie);
+ return ret;
+}
+
+
+static void p2p_clear_go_neg(struct p2p_data *p2p)
+{
+ p2p->go_neg_peer = NULL;
+ p2p_clear_timeout(p2p);
+ p2p_set_state(p2p, P2P_IDLE);
+}
+
+
+void p2p_wps_success_cb(struct p2p_data *p2p, const u8 *mac_addr)
+{
+ if (p2p->go_neg_peer == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No pending Group Formation - "
+ "ignore WPS registration success notification");
+ return; /* No pending Group Formation */
+ }
+
+ if (os_memcmp(mac_addr, p2p->go_neg_peer->intended_addr, ETH_ALEN) !=
+ 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Ignore WPS registration success notification "
+ "for " MACSTR " (GO Negotiation peer " MACSTR ")",
+ MAC2STR(mac_addr),
+ MAC2STR(p2p->go_neg_peer->intended_addr));
+ return; /* Ignore unexpected peer address */
+ }
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Group Formation completed successfully with " MACSTR,
+ MAC2STR(mac_addr));
+
+ p2p_clear_go_neg(p2p);
+}
+
+
+void p2p_group_formation_failed(struct p2p_data *p2p)
+{
+ if (p2p->go_neg_peer == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No pending Group Formation - "
+ "ignore group formation failure notification");
+ return; /* No pending Group Formation */
+ }
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Group Formation failed with " MACSTR,
+ MAC2STR(p2p->go_neg_peer->intended_addr));
+
+ p2p_clear_go_neg(p2p);
+}
+
+
+struct p2p_data * p2p_init(const struct p2p_config *cfg)
+{
+ struct p2p_data *p2p;
+
+ if (cfg->max_peers < 1)
+ return NULL;
+
+ p2p = os_zalloc(sizeof(*p2p) + sizeof(*cfg));
+ if (p2p == NULL)
+ return NULL;
+ p2p->cfg = (struct p2p_config *) (p2p + 1);
+ os_memcpy(p2p->cfg, cfg, sizeof(*cfg));
+ if (cfg->dev_name)
+ p2p->cfg->dev_name = os_strdup(cfg->dev_name);
+ if (cfg->manufacturer)
+ p2p->cfg->manufacturer = os_strdup(cfg->manufacturer);
+ if (cfg->model_name)
+ p2p->cfg->model_name = os_strdup(cfg->model_name);
+ if (cfg->model_number)
+ p2p->cfg->model_number = os_strdup(cfg->model_number);
+ if (cfg->serial_number)
+ p2p->cfg->serial_number = os_strdup(cfg->serial_number);
+ if (cfg->pref_chan) {
+ p2p->cfg->pref_chan = os_malloc(cfg->num_pref_chan *
+ sizeof(struct p2p_channel));
+ if (p2p->cfg->pref_chan) {
+ os_memcpy(p2p->cfg->pref_chan, cfg->pref_chan,
+ cfg->num_pref_chan *
+ sizeof(struct p2p_channel));
+ } else
+ p2p->cfg->num_pref_chan = 0;
+ }
+
+ p2p->min_disc_int = 1;
+ p2p->max_disc_int = 3;
+
+ os_get_random(&p2p->next_tie_breaker, 1);
+ p2p->next_tie_breaker &= 0x01;
+ if (cfg->sd_request)
+ p2p->dev_capab |= P2P_DEV_CAPAB_SERVICE_DISCOVERY;
+ p2p->dev_capab |= P2P_DEV_CAPAB_INVITATION_PROCEDURE;
+ if (cfg->concurrent_operations)
+ p2p->dev_capab |= P2P_DEV_CAPAB_CONCURRENT_OPER;
+ p2p->dev_capab |= P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
+
+ dl_list_init(&p2p->devices);
+
+ eloop_register_timeout(P2P_PEER_EXPIRATION_INTERVAL, 0,
+ p2p_expiration_timeout, p2p, NULL);
+
+ return p2p;
+}
+
+
+void p2p_deinit(struct p2p_data *p2p)
+{
+ eloop_cancel_timeout(p2p_expiration_timeout, p2p, NULL);
+ eloop_cancel_timeout(p2p_ext_listen_timeout, p2p, NULL);
+ eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL);
+ p2p_flush(p2p);
+ p2p_free_req_dev_types(p2p);
+ os_free(p2p->cfg->dev_name);
+ os_free(p2p->cfg->manufacturer);
+ os_free(p2p->cfg->model_name);
+ os_free(p2p->cfg->model_number);
+ os_free(p2p->cfg->serial_number);
+ os_free(p2p->cfg->pref_chan);
+ os_free(p2p->groups);
+ wpabuf_free(p2p->sd_resp);
+ os_free(p2p->after_scan_tx);
+ p2p_remove_wps_vendor_extensions(p2p);
+ os_free(p2p);
+}
+
+
+void p2p_flush(struct p2p_data *p2p)
+{
+ struct p2p_device *dev, *prev;
+ p2p_stop_find(p2p);
+ dl_list_for_each_safe(dev, prev, &p2p->devices, struct p2p_device,
+ list) {
+ dl_list_del(&dev->list);
+ p2p_device_free(p2p, dev);
+ }
+ p2p_free_sd_queries(p2p);
+ os_free(p2p->after_scan_tx);
+ p2p->after_scan_tx = NULL;
+}
+
+
+int p2p_unauthorize(struct p2p_data *p2p, const u8 *addr)
+{
+ struct p2p_device *dev;
+
+ dev = p2p_get_device(p2p, addr);
+ if (dev == NULL)
+ return -1;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Unauthorizing " MACSTR,
+ MAC2STR(addr));
+
+ if (p2p->go_neg_peer == dev)
+ p2p->go_neg_peer = NULL;
+
+ dev->wps_method = WPS_NOT_READY;
+ dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE;
+ dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM;
+
+ /* Check if after_scan_tx is for this peer. If so free it */
+ if (p2p->after_scan_tx &&
+ os_memcmp(addr, p2p->after_scan_tx->dst, ETH_ALEN) == 0) {
+ os_free(p2p->after_scan_tx);
+ p2p->after_scan_tx = NULL;
+ }
+
+ return 0;
+}
+
+
+int p2p_set_dev_name(struct p2p_data *p2p, const char *dev_name)
+{
+ os_free(p2p->cfg->dev_name);
+ if (dev_name) {
+ p2p->cfg->dev_name = os_strdup(dev_name);
+ if (p2p->cfg->dev_name == NULL)
+ return -1;
+ } else
+ p2p->cfg->dev_name = NULL;
+ return 0;
+}
+
+
+int p2p_set_manufacturer(struct p2p_data *p2p, const char *manufacturer)
+{
+ os_free(p2p->cfg->manufacturer);
+ p2p->cfg->manufacturer = NULL;
+ if (manufacturer) {
+ p2p->cfg->manufacturer = os_strdup(manufacturer);
+ if (p2p->cfg->manufacturer == NULL)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int p2p_set_model_name(struct p2p_data *p2p, const char *model_name)
+{
+ os_free(p2p->cfg->model_name);
+ p2p->cfg->model_name = NULL;
+ if (model_name) {
+ p2p->cfg->model_name = os_strdup(model_name);
+ if (p2p->cfg->model_name == NULL)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int p2p_set_model_number(struct p2p_data *p2p, const char *model_number)
+{
+ os_free(p2p->cfg->model_number);
+ p2p->cfg->model_number = NULL;
+ if (model_number) {
+ p2p->cfg->model_number = os_strdup(model_number);
+ if (p2p->cfg->model_number == NULL)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int p2p_set_serial_number(struct p2p_data *p2p, const char *serial_number)
+{
+ os_free(p2p->cfg->serial_number);
+ p2p->cfg->serial_number = NULL;
+ if (serial_number) {
+ p2p->cfg->serial_number = os_strdup(serial_number);
+ if (p2p->cfg->serial_number == NULL)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+void p2p_set_config_methods(struct p2p_data *p2p, u16 config_methods)
+{
+ p2p->cfg->config_methods = config_methods;
+}
+
+
+void p2p_set_uuid(struct p2p_data *p2p, const u8 *uuid)
+{
+ os_memcpy(p2p->cfg->uuid, uuid, 16);
+}
+
+
+int p2p_set_pri_dev_type(struct p2p_data *p2p, const u8 *pri_dev_type)
+{
+ os_memcpy(p2p->cfg->pri_dev_type, pri_dev_type, 8);
+ return 0;
+}
+
+
+int p2p_set_sec_dev_types(struct p2p_data *p2p, const u8 dev_types[][8],
+ size_t num_dev_types)
+{
+ if (num_dev_types > P2P_SEC_DEVICE_TYPES)
+ num_dev_types = P2P_SEC_DEVICE_TYPES;
+ p2p->cfg->num_sec_dev_types = num_dev_types;
+ os_memcpy(p2p->cfg->sec_dev_type, dev_types, num_dev_types * 8);
+ return 0;
+}
+
+
+void p2p_remove_wps_vendor_extensions(struct p2p_data *p2p)
+{
+ int i;
+
+ for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
+ wpabuf_free(p2p->wps_vendor_ext[i]);
+ p2p->wps_vendor_ext[i] = NULL;
+ }
+}
+
+
+int p2p_add_wps_vendor_extension(struct p2p_data *p2p,
+ const struct wpabuf *vendor_ext)
+{
+ int i;
+
+ if (vendor_ext == NULL)
+ return -1;
+
+ for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
+ if (p2p->wps_vendor_ext[i] == NULL)
+ break;
+ }
+ if (i >= P2P_MAX_WPS_VENDOR_EXT)
+ return -1;
+
+ p2p->wps_vendor_ext[i] = wpabuf_dup(vendor_ext);
+ if (p2p->wps_vendor_ext[i] == NULL)
+ return -1;
+
+ return 0;
+}
+
+
+int p2p_set_country(struct p2p_data *p2p, const char *country)
+{
+ os_memcpy(p2p->cfg->country, country, 3);
+ return 0;
+}
+
+
+void p2p_continue_find(struct p2p_data *p2p)
+{
+ struct p2p_device *dev;
+ p2p_set_state(p2p, P2P_SEARCH);
+ dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
+ if (dev->flags & P2P_DEV_SD_SCHEDULE) {
+ if (p2p_start_sd(p2p, dev) == 0)
+ return;
+ else
+ break;
+ } else if (dev->req_config_methods &&
+ !(dev->flags & P2P_DEV_PD_FOR_JOIN)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Send "
+ "pending Provisioning Discovery Request to "
+ MACSTR " (config methods 0x%x)",
+ MAC2STR(dev->info.p2p_device_addr),
+ dev->req_config_methods);
+ if (p2p_send_prov_disc_req(p2p, dev, 0, 0) == 0)
+ return;
+ }
+ }
+
+ p2p_listen_in_find(p2p);
+}
+
+
+static void p2p_sd_cb(struct p2p_data *p2p, int success)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Service Discovery Query TX callback: success=%d",
+ success);
+ p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+
+ if (!success) {
+ if (p2p->sd_peer) {
+ p2p->sd_peer->flags &= ~P2P_DEV_SD_SCHEDULE;
+ p2p->sd_peer = NULL;
+ }
+ p2p_continue_find(p2p);
+ return;
+ }
+
+ if (p2p->sd_peer == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No SD peer entry known");
+ p2p_continue_find(p2p);
+ return;
+ }
+
+ /* Wait for response from the peer */
+ p2p_set_state(p2p, P2P_SD_DURING_FIND);
+ p2p_set_timeout(p2p, 0, 200000);
+}
+
+
+/**
+ * p2p_retry_pd - Retry any pending provision disc requests in IDLE state
+ * @p2p: P2P module context from p2p_init()
+ */
+void p2p_retry_pd(struct p2p_data *p2p)
+{
+ struct p2p_device *dev;
+
+ if (p2p->state != P2P_IDLE)
+ return;
+
+ /*
+ * Retry the prov disc req attempt only for the peer that the user had
+ * requested for and provided a join has not been initiated on it
+ * in the meantime.
+ */
+
+ dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
+ if (os_memcmp(p2p->pending_pd_devaddr,
+ dev->info.p2p_device_addr, ETH_ALEN) != 0)
+ continue;
+ if (!dev->req_config_methods)
+ continue;
+ if (dev->flags & P2P_DEV_PD_FOR_JOIN)
+ continue;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Send "
+ "pending Provisioning Discovery Request to "
+ MACSTR " (config methods 0x%x)",
+ MAC2STR(dev->info.p2p_device_addr),
+ dev->req_config_methods);
+ p2p_send_prov_disc_req(p2p, dev, 0, 0);
+ return;
+ }
+}
+
+
+static void p2p_prov_disc_cb(struct p2p_data *p2p, int success)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Provision Discovery Request TX callback: success=%d",
+ success);
+
+ /*
+ * Postpone resetting the pending action state till after we actually
+ * time out. This allows us to take some action like notifying any
+ * interested parties about no response to the request.
+ *
+ * When the timer (below) goes off we check in IDLE, SEARCH, or
+ * LISTEN_ONLY state, which are the only allowed states to issue a PD
+ * requests in, if this was still pending and then raise notification.
+ */
+
+ if (!success) {
+ p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+
+ if (p2p->state != P2P_IDLE)
+ p2p_continue_find(p2p);
+ else if (p2p->user_initiated_pd) {
+ p2p->pending_action_state = P2P_PENDING_PD;
+ p2p_set_timeout(p2p, 0, 300000);
+ }
+ return;
+ }
+
+ /*
+ * This postponing, of resetting pending_action_state, needs to be
+ * done only for user initiated PD requests and not internal ones.
+ */
+ if (p2p->user_initiated_pd)
+ p2p->pending_action_state = P2P_PENDING_PD;
+ else
+ p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+
+ /* Wait for response from the peer */
+ if (p2p->state == P2P_SEARCH)
+ p2p_set_state(p2p, P2P_PD_DURING_FIND);
+ p2p_set_timeout(p2p, 0, 200000);
+}
+
+
+int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq,
+ int level, const u8 *ies, size_t ies_len)
+{
+ p2p_add_device(p2p, bssid, freq, level, ies, ies_len);
+
+ if (p2p->go_neg_peer && p2p->state == P2P_SEARCH &&
+ os_memcmp(p2p->go_neg_peer->info.p2p_device_addr, bssid, ETH_ALEN)
+ == 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Found GO Negotiation peer - try to start GO "
+ "negotiation");
+ p2p_connect_send(p2p, p2p->go_neg_peer);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+void p2p_scan_res_handled(struct p2p_data *p2p)
+{
+ if (!p2p->p2p_scan_running) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: p2p_scan was not "
+ "running, but scan results received");
+ }
+ p2p->p2p_scan_running = 0;
+ eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL);
+
+ if (p2p_run_after_scan(p2p))
+ return;
+ if (p2p->state == P2P_SEARCH)
+ p2p_continue_find(p2p);
+}
+
+
+void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id)
+{
+ u8 *len = p2p_buf_add_ie_hdr(ies);
+ p2p_buf_add_capability(ies, p2p->dev_capab, 0);
+ if (dev_id)
+ p2p_buf_add_device_id(ies, dev_id);
+ if (p2p->cfg->reg_class && p2p->cfg->channel)
+ p2p_buf_add_listen_channel(ies, p2p->cfg->country,
+ p2p->cfg->reg_class,
+ p2p->cfg->channel);
+ if (p2p->ext_listen_interval)
+ p2p_buf_add_ext_listen_timing(ies, p2p->ext_listen_period,
+ p2p->ext_listen_interval);
+ /* TODO: p2p_buf_add_operating_channel() if GO */
+ p2p_buf_update_ie_hdr(ies, len);
+}
+
+
+size_t p2p_scan_ie_buf_len(struct p2p_data *p2p)
+{
+ return 100;
+}
+
+
+int p2p_ie_text(struct wpabuf *p2p_ie, char *buf, char *end)
+{
+ return p2p_attr_text(p2p_ie, buf, end);
+}
+
+
+static void p2p_go_neg_req_cb(struct p2p_data *p2p, int success)
+{
+ struct p2p_device *dev = p2p->go_neg_peer;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: GO Negotiation Request TX callback: success=%d",
+ success);
+
+ if (dev == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No pending GO Negotiation");
+ return;
+ }
+
+ if (success) {
+ dev->go_neg_req_sent++;
+ if (dev->flags & P2P_DEV_USER_REJECTED) {
+ p2p_set_state(p2p, P2P_IDLE);
+ return;
+ }
+ }
+
+ if (!success &&
+ (dev->info.dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY) &&
+ !is_zero_ether_addr(dev->member_in_go_dev)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Peer " MACSTR " did not acknowledge request - "
+ "try to use device discoverability through its GO",
+ MAC2STR(dev->info.p2p_device_addr));
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+ p2p_send_dev_disc_req(p2p, dev);
+ return;
+ }
+
+ /*
+ * Use P2P find, if needed, to find the other device from its listen
+ * channel.
+ */
+ p2p_set_state(p2p, P2P_CONNECT);
+ p2p_set_timeout(p2p, 0, 100000);
+}
+
+
+static void p2p_go_neg_resp_cb(struct p2p_data *p2p, int success)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: GO Negotiation Response TX callback: success=%d",
+ success);
+ if (!p2p->go_neg_peer && p2p->state == P2P_PROVISIONING) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Ignore TX callback event - GO Negotiation is "
+ "not running anymore");
+ return;
+ }
+ p2p_set_state(p2p, P2P_CONNECT);
+ p2p_set_timeout(p2p, 0, 100000);
+}
+
+
+static void p2p_go_neg_resp_failure_cb(struct p2p_data *p2p, int success)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: GO Negotiation Response (failure) TX callback: "
+ "success=%d", success);
+ if (p2p->go_neg_peer && p2p->go_neg_peer->status != P2P_SC_SUCCESS) {
+ p2p_go_neg_failed(p2p, p2p->go_neg_peer,
+ p2p->go_neg_peer->status);
+ }
+}
+
+
+static void p2p_go_neg_conf_cb(struct p2p_data *p2p,
+ enum p2p_send_action_result result)
+{
+ struct p2p_device *dev;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: GO Negotiation Confirm TX callback: result=%d",
+ result);
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+ if (result == P2P_SEND_ACTION_FAILED) {
+ p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1);
+ return;
+ }
+ if (result == P2P_SEND_ACTION_NO_ACK) {
+ /*
+ * It looks like the TX status for GO Negotiation Confirm is
+ * often showing failure even when the peer has actually
+ * received the frame. Since the peer may change channels
+ * immediately after having received the frame, we may not see
+ * an Ack for retries, so just dropping a single frame may
+ * trigger this. To allow the group formation to succeed if the
+ * peer did indeed receive the frame, continue regardless of
+ * the TX status.
+ */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Assume GO Negotiation Confirm TX was actually "
+ "received by the peer even though Ack was not "
+ "reported");
+ }
+
+ dev = p2p->go_neg_peer;
+ if (dev == NULL)
+ return;
+
+ p2p_go_complete(p2p, dev);
+}
+
+
+void p2p_send_action_cb(struct p2p_data *p2p, unsigned int freq, const u8 *dst,
+ const u8 *src, const u8 *bssid,
+ enum p2p_send_action_result result)
+{
+ enum p2p_pending_action_state state;
+ int success;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Action frame TX callback (state=%d freq=%u dst=" MACSTR
+ " src=" MACSTR " bssid=" MACSTR " result=%d",
+ p2p->pending_action_state, freq, MAC2STR(dst), MAC2STR(src),
+ MAC2STR(bssid), result);
+ success = result == P2P_SEND_ACTION_SUCCESS;
+ state = p2p->pending_action_state;
+ p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+ switch (state) {
+ case P2P_NO_PENDING_ACTION:
+ break;
+ case P2P_PENDING_GO_NEG_REQUEST:
+ p2p_go_neg_req_cb(p2p, success);
+ break;
+ case P2P_PENDING_GO_NEG_RESPONSE:
+ p2p_go_neg_resp_cb(p2p, success);
+ break;
+ case P2P_PENDING_GO_NEG_RESPONSE_FAILURE:
+ p2p_go_neg_resp_failure_cb(p2p, success);
+ break;
+ case P2P_PENDING_GO_NEG_CONFIRM:
+ p2p_go_neg_conf_cb(p2p, result);
+ break;
+ case P2P_PENDING_SD:
+ p2p_sd_cb(p2p, success);
+ break;
+ case P2P_PENDING_PD:
+ p2p_prov_disc_cb(p2p, success);
+ break;
+ case P2P_PENDING_INVITATION_REQUEST:
+ p2p_invitation_req_cb(p2p, success);
+ break;
+ case P2P_PENDING_INVITATION_RESPONSE:
+ p2p_invitation_resp_cb(p2p, success);
+ break;
+ case P2P_PENDING_DEV_DISC_REQUEST:
+ p2p_dev_disc_req_cb(p2p, success);
+ break;
+ case P2P_PENDING_DEV_DISC_RESPONSE:
+ p2p_dev_disc_resp_cb(p2p, success);
+ break;
+ case P2P_PENDING_GO_DISC_REQ:
+ p2p_go_disc_req_cb(p2p, success);
+ break;
+ }
+}
+
+
+void p2p_listen_cb(struct p2p_data *p2p, unsigned int freq,
+ unsigned int duration)
+{
+ if (freq == p2p->pending_client_disc_freq) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Client discoverability remain-awake completed");
+ p2p->pending_client_disc_freq = 0;
+ return;
+ }
+
+ if (freq != p2p->pending_listen_freq) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unexpected listen callback for freq=%u "
+ "duration=%u (pending_listen_freq=%u)",
+ freq, duration, p2p->pending_listen_freq);
+ return;
+ }
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Starting Listen timeout(%u,%u) on freq=%u based on "
+ "callback",
+ p2p->pending_listen_sec, p2p->pending_listen_usec,
+ p2p->pending_listen_freq);
+ p2p->in_listen = 1;
+ p2p->drv_in_listen = freq;
+ if (p2p->pending_listen_sec || p2p->pending_listen_usec) {
+ /*
+ * Add 20 msec extra wait to avoid race condition with driver
+ * remain-on-channel end event, i.e., give driver more time to
+ * complete the operation before our timeout expires.
+ */
+ p2p_set_timeout(p2p, p2p->pending_listen_sec,
+ p2p->pending_listen_usec + 20000);
+ }
+
+ p2p->pending_listen_freq = 0;
+}
+
+
+int p2p_listen_end(struct p2p_data *p2p, unsigned int freq)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Driver ended Listen "
+ "state (freq=%u)", freq);
+ p2p->drv_in_listen = 0;
+ if (p2p->in_listen)
+ return 0; /* Internal timeout will trigger the next step */
+
+ if (p2p->state == P2P_CONNECT_LISTEN && p2p->go_neg_peer) {
+ if (p2p->go_neg_peer->connect_reqs >= 120) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Timeout on sending GO Negotiation "
+ "Request without getting response");
+ p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1);
+ return 0;
+ }
+
+ p2p_set_state(p2p, P2P_CONNECT);
+ p2p_connect_send(p2p, p2p->go_neg_peer);
+ return 1;
+ } else if (p2p->state == P2P_SEARCH) {
+ if (p2p->p2p_scan_running) {
+ /*
+ * Search is already in progress. This can happen if
+ * an Action frame RX is reported immediately after
+ * the end of a remain-on-channel operation and the
+ * response frame to that is sent using an offchannel
+ * operation while in p2p_find. Avoid an attempt to
+ * restart a scan here.
+ */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: p2p_scan "
+ "already in progress - do not try to start a "
+ "new one");
+ return 1;
+ }
+ p2p_search(p2p);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static void p2p_timeout_connect(struct p2p_data *p2p)
+{
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+ p2p_set_state(p2p, P2P_CONNECT_LISTEN);
+ p2p_listen_in_find(p2p);
+}
+
+
+static void p2p_timeout_connect_listen(struct p2p_data *p2p)
+{
+ if (p2p->go_neg_peer) {
+ if (p2p->drv_in_listen) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Driver is "
+ "still in Listen state; wait for it to "
+ "complete");
+ return;
+ }
+
+ if (p2p->go_neg_peer->connect_reqs >= 120) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Timeout on sending GO Negotiation "
+ "Request without getting response");
+ p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1);
+ return;
+ }
+
+ p2p_set_state(p2p, P2P_CONNECT);
+ p2p_connect_send(p2p, p2p->go_neg_peer);
+ } else
+ p2p_set_state(p2p, P2P_IDLE);
+}
+
+
+static void p2p_timeout_wait_peer_connect(struct p2p_data *p2p)
+{
+ /*
+ * TODO: could remain constantly in Listen state for some time if there
+ * are no other concurrent uses for the radio. For now, go to listen
+ * state once per second to give other uses a chance to use the radio.
+ */
+ p2p_set_state(p2p, P2P_WAIT_PEER_IDLE);
+ p2p_set_timeout(p2p, 0, 500000);
+}
+
+
+static void p2p_timeout_wait_peer_idle(struct p2p_data *p2p)
+{
+ struct p2p_device *dev = p2p->go_neg_peer;
+
+ if (dev == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unknown GO Neg peer - stop GO Neg wait");
+ return;
+ }
+
+ dev->wait_count++;
+ if (dev->wait_count >= 120) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Timeout on waiting peer to become ready for GO "
+ "Negotiation");
+ p2p_go_neg_failed(p2p, dev, -1);
+ return;
+ }
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Go to Listen state while waiting for the peer to become "
+ "ready for GO Negotiation");
+ p2p_set_state(p2p, P2P_WAIT_PEER_CONNECT);
+ p2p_listen_in_find(p2p);
+}
+
+
+static void p2p_timeout_sd_during_find(struct p2p_data *p2p)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Service Discovery Query timeout");
+ if (p2p->sd_peer) {
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+ p2p->sd_peer->flags &= ~P2P_DEV_SD_SCHEDULE;
+ p2p->sd_peer = NULL;
+ }
+ p2p_continue_find(p2p);
+}
+
+
+static void p2p_timeout_prov_disc_during_find(struct p2p_data *p2p)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Provision Discovery Request timeout");
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+ p2p_continue_find(p2p);
+}
+
+
+static void p2p_timeout_prov_disc_req(struct p2p_data *p2p)
+{
+ p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+
+ /*
+ * For user initiated PD requests that we have not gotten any responses
+ * for while in IDLE state, we retry them a couple of times before
+ * giving up.
+ */
+ if (!p2p->user_initiated_pd)
+ return;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: User initiated Provision Discovery Request timeout");
+
+ if (p2p->pd_retries) {
+ p2p->pd_retries--;
+ p2p_retry_pd(p2p);
+ } else {
+ if (p2p->cfg->prov_disc_fail)
+ p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx,
+ p2p->pending_pd_devaddr,
+ P2P_PROV_DISC_TIMEOUT);
+ p2p_reset_pending_pd(p2p);
+ }
+}
+
+
+static void p2p_timeout_invite(struct p2p_data *p2p)
+{
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+ p2p_set_state(p2p, P2P_INVITE_LISTEN);
+ if (p2p->inv_role == P2P_INVITE_ROLE_ACTIVE_GO) {
+ /*
+ * Better remain on operating channel instead of listen channel
+ * when running a group.
+ */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Inviting in "
+ "active GO role - wait on operating channel");
+ p2p_set_timeout(p2p, 0, 100000);
+ return;
+ }
+ p2p_listen_in_find(p2p);
+}
+
+
+static void p2p_timeout_invite_listen(struct p2p_data *p2p)
+{
+ if (p2p->invite_peer && p2p->invite_peer->invitation_reqs < 100) {
+ p2p_set_state(p2p, P2P_INVITE);
+ p2p_invite_send(p2p, p2p->invite_peer,
+ p2p->invite_go_dev_addr);
+ } else {
+ if (p2p->invite_peer) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Invitation Request retry limit reached");
+ if (p2p->cfg->invitation_result)
+ p2p->cfg->invitation_result(
+ p2p->cfg->cb_ctx, -1, NULL);
+ }
+ p2p_set_state(p2p, P2P_IDLE);
+ }
+}
+
+
+static void p2p_state_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct p2p_data *p2p = eloop_ctx;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Timeout (state=%s)",
+ p2p_state_txt(p2p->state));
+
+ p2p->in_listen = 0;
+
+ switch (p2p->state) {
+ case P2P_IDLE:
+ /* Check if we timed out waiting for PD req */
+ if (p2p->pending_action_state == P2P_PENDING_PD)
+ p2p_timeout_prov_disc_req(p2p);
+ break;
+ case P2P_SEARCH:
+ /* Check if we timed out waiting for PD req */
+ if (p2p->pending_action_state == P2P_PENDING_PD)
+ p2p_timeout_prov_disc_req(p2p);
+ p2p_search(p2p);
+ break;
+ case P2P_CONNECT:
+ p2p_timeout_connect(p2p);
+ break;
+ case P2P_CONNECT_LISTEN:
+ p2p_timeout_connect_listen(p2p);
+ break;
+ case P2P_GO_NEG:
+ break;
+ case P2P_LISTEN_ONLY:
+ /* Check if we timed out waiting for PD req */
+ if (p2p->pending_action_state == P2P_PENDING_PD)
+ p2p_timeout_prov_disc_req(p2p);
+
+ if (p2p->ext_listen_only) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Extended Listen Timing - Listen State "
+ "completed");
+ p2p->ext_listen_only = 0;
+ p2p_set_state(p2p, P2P_IDLE);
+ }
+ break;
+ case P2P_WAIT_PEER_CONNECT:
+ p2p_timeout_wait_peer_connect(p2p);
+ break;
+ case P2P_WAIT_PEER_IDLE:
+ p2p_timeout_wait_peer_idle(p2p);
+ break;
+ case P2P_SD_DURING_FIND:
+ p2p_timeout_sd_during_find(p2p);
+ break;
+ case P2P_PROVISIONING:
+ break;
+ case P2P_PD_DURING_FIND:
+ p2p_timeout_prov_disc_during_find(p2p);
+ break;
+ case P2P_INVITE:
+ p2p_timeout_invite(p2p);
+ break;
+ case P2P_INVITE_LISTEN:
+ p2p_timeout_invite_listen(p2p);
+ break;
+ case P2P_SEARCH_WHEN_READY:
+ break;
+ }
+}
+
+
+int p2p_reject(struct p2p_data *p2p, const u8 *peer_addr)
+{
+ struct p2p_device *dev;
+
+ dev = p2p_get_device(p2p, peer_addr);
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Local request to reject "
+ "connection attempts by peer " MACSTR, MAC2STR(peer_addr));
+ if (dev == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR
+ " unknown", MAC2STR(peer_addr));
+ return -1;
+ }
+ dev->status = P2P_SC_FAIL_REJECTED_BY_USER;
+ dev->flags |= P2P_DEV_USER_REJECTED;
+ return 0;
+}
+
+
+const char * p2p_wps_method_text(enum p2p_wps_method method)
+{
+ switch (method) {
+ case WPS_NOT_READY:
+ return "not-ready";
+ case WPS_PIN_DISPLAY:
+ return "Display";
+ case WPS_PIN_KEYPAD:
+ return "Keypad";
+ case WPS_PBC:
+ return "PBC";
+ }
+
+ return "??";
+}
+
+
+static const char * p2p_go_state_text(enum p2p_go_state go_state)
+{
+ switch (go_state) {
+ case UNKNOWN_GO:
+ return "unknown";
+ case LOCAL_GO:
+ return "local";
+ case REMOTE_GO:
+ return "remote";
+ }
+
+ return "??";
+}
+
+
+const struct p2p_peer_info * p2p_get_peer_info(struct p2p_data *p2p,
+ const u8 *addr, int next)
+{
+ struct p2p_device *dev;
+
+ if (addr)
+ dev = p2p_get_device(p2p, addr);
+ else
+ dev = dl_list_first(&p2p->devices, struct p2p_device, list);
+
+ if (dev && next) {
+ dev = dl_list_first(&dev->list, struct p2p_device, list);
+ if (&dev->list == &p2p->devices)
+ dev = NULL;
+ }
+
+ if (dev == NULL)
+ return NULL;
+
+ return &dev->info;
+}
+
+
+int p2p_get_peer_info_txt(const struct p2p_peer_info *info,
+ char *buf, size_t buflen)
+{
+ struct p2p_device *dev;
+ int res;
+ char *pos, *end;
+ struct os_time now;
+
+ if (info == NULL)
+ return -1;
+
+ dev = (struct p2p_device *) (((u8 *) info) -
+ offsetof(struct p2p_device, info));
+
+ pos = buf;
+ end = buf + buflen;
+
+ os_get_time(&now);
+ res = os_snprintf(pos, end - pos,
+ "age=%d\n"
+ "listen_freq=%d\n"
+ "wps_method=%s\n"
+ "interface_addr=" MACSTR "\n"
+ "member_in_go_dev=" MACSTR "\n"
+ "member_in_go_iface=" MACSTR "\n"
+ "go_neg_req_sent=%d\n"
+ "go_state=%s\n"
+ "dialog_token=%u\n"
+ "intended_addr=" MACSTR "\n"
+ "country=%c%c\n"
+ "oper_freq=%d\n"
+ "req_config_methods=0x%x\n"
+ "flags=%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n"
+ "status=%d\n"
+ "wait_count=%u\n"
+ "invitation_reqs=%u\n",
+ (int) (now.sec - dev->last_seen.sec),
+ dev->listen_freq,
+ p2p_wps_method_text(dev->wps_method),
+ MAC2STR(dev->interface_addr),
+ MAC2STR(dev->member_in_go_dev),
+ MAC2STR(dev->member_in_go_iface),
+ dev->go_neg_req_sent,
+ p2p_go_state_text(dev->go_state),
+ dev->dialog_token,
+ MAC2STR(dev->intended_addr),
+ dev->country[0] ? dev->country[0] : '_',
+ dev->country[1] ? dev->country[1] : '_',
+ dev->oper_freq,
+ dev->req_config_methods,
+ dev->flags & P2P_DEV_PROBE_REQ_ONLY ?
+ "[PROBE_REQ_ONLY]" : "",
+ dev->flags & P2P_DEV_REPORTED ? "[REPORTED]" : "",
+ dev->flags & P2P_DEV_NOT_YET_READY ?
+ "[NOT_YET_READY]" : "",
+ dev->flags & P2P_DEV_SD_INFO ? "[SD_INFO]" : "",
+ dev->flags & P2P_DEV_SD_SCHEDULE ? "[SD_SCHEDULE]" :
+ "",
+ dev->flags & P2P_DEV_PD_PEER_DISPLAY ?
+ "[PD_PEER_DISPLAY]" : "",
+ dev->flags & P2P_DEV_PD_PEER_KEYPAD ?
+ "[PD_PEER_KEYPAD]" : "",
+ dev->flags & P2P_DEV_USER_REJECTED ?
+ "[USER_REJECTED]" : "",
+ dev->flags & P2P_DEV_PEER_WAITING_RESPONSE ?
+ "[PEER_WAITING_RESPONSE]" : "",
+ dev->flags & P2P_DEV_PREFER_PERSISTENT_GROUP ?
+ "[PREFER_PERSISTENT_GROUP]" : "",
+ dev->flags & P2P_DEV_WAIT_GO_NEG_RESPONSE ?
+ "[WAIT_GO_NEG_RESPONSE]" : "",
+ dev->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM ?
+ "[WAIT_GO_NEG_CONFIRM]" : "",
+ dev->flags & P2P_DEV_GROUP_CLIENT_ONLY ?
+ "[GROUP_CLIENT_ONLY]" : "",
+ dev->flags & P2P_DEV_FORCE_FREQ ?
+ "[FORCE_FREQ]" : "",
+ dev->flags & P2P_DEV_PD_FOR_JOIN ?
+ "[PD_FOR_JOIN]" : "",
+ dev->status,
+ dev->wait_count,
+ dev->invitation_reqs);
+ if (res < 0 || res >= end - pos)
+ return pos - buf;
+ pos += res;
+
+ if (dev->ext_listen_period) {
+ res = os_snprintf(pos, end - pos,
+ "ext_listen_period=%u\n"
+ "ext_listen_interval=%u\n",
+ dev->ext_listen_period,
+ dev->ext_listen_interval);
+ if (res < 0 || res >= end - pos)
+ return pos - buf;
+ pos += res;
+ }
+
+ if (dev->oper_ssid_len) {
+ res = os_snprintf(pos, end - pos,
+ "oper_ssid=%s\n",
+ wpa_ssid_txt(dev->oper_ssid,
+ dev->oper_ssid_len));
+ if (res < 0 || res >= end - pos)
+ return pos - buf;
+ pos += res;
+ }
+
+ return pos - buf;
+}
+
+
+int p2p_peer_known(struct p2p_data *p2p, const u8 *addr)
+{
+ return p2p_get_device(p2p, addr) != NULL;
+}
+
+
+void p2p_set_client_discoverability(struct p2p_data *p2p, int enabled)
+{
+ if (enabled) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Client "
+ "discoverability enabled");
+ p2p->dev_capab |= P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
+ } else {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Client "
+ "discoverability disabled");
+ p2p->dev_capab &= ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
+ }
+}
+
+
+static struct wpabuf * p2p_build_presence_req(u32 duration1, u32 interval1,
+ u32 duration2, u32 interval2)
+{
+ struct wpabuf *req;
+ struct p2p_noa_desc desc1, desc2, *ptr1 = NULL, *ptr2 = NULL;
+ u8 *len;
+
+ req = wpabuf_alloc(100);
+ if (req == NULL)
+ return NULL;
+
+ if (duration1 || interval1) {
+ os_memset(&desc1, 0, sizeof(desc1));
+ desc1.count_type = 1;
+ desc1.duration = duration1;
+ desc1.interval = interval1;
+ ptr1 = &desc1;
+
+ if (duration2 || interval2) {
+ os_memset(&desc2, 0, sizeof(desc2));
+ desc2.count_type = 2;
+ desc2.duration = duration2;
+ desc2.interval = interval2;
+ ptr2 = &desc2;
+ }
+ }
+
+ p2p_buf_add_action_hdr(req, P2P_PRESENCE_REQ, 1);
+ len = p2p_buf_add_ie_hdr(req);
+ p2p_buf_add_noa(req, 0, 0, 0, ptr1, ptr2);
+ p2p_buf_update_ie_hdr(req, len);
+
+ return req;
+}
+
+
+int p2p_presence_req(struct p2p_data *p2p, const u8 *go_interface_addr,
+ const u8 *own_interface_addr, unsigned int freq,
+ u32 duration1, u32 interval1, u32 duration2,
+ u32 interval2)
+{
+ struct wpabuf *req;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Send Presence Request to "
+ "GO " MACSTR " (own interface " MACSTR ") freq=%u dur1=%u "
+ "int1=%u dur2=%u int2=%u",
+ MAC2STR(go_interface_addr), MAC2STR(own_interface_addr),
+ freq, duration1, interval1, duration2, interval2);
+
+ req = p2p_build_presence_req(duration1, interval1, duration2,
+ interval2);
+ if (req == NULL)
+ return -1;
+
+ p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+ if (p2p_send_action(p2p, freq, go_interface_addr, own_interface_addr,
+ go_interface_addr,
+ wpabuf_head(req), wpabuf_len(req), 200) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to send Action frame");
+ }
+ wpabuf_free(req);
+
+ return 0;
+}
+
+
+static struct wpabuf * p2p_build_presence_resp(u8 status, const u8 *noa,
+ size_t noa_len, u8 dialog_token)
+{
+ struct wpabuf *resp;
+ u8 *len;
+
+ resp = wpabuf_alloc(100 + noa_len);
+ if (resp == NULL)
+ return NULL;
+
+ p2p_buf_add_action_hdr(resp, P2P_PRESENCE_RESP, dialog_token);
+ len = p2p_buf_add_ie_hdr(resp);
+ p2p_buf_add_status(resp, status);
+ if (noa) {
+ wpabuf_put_u8(resp, P2P_ATTR_NOTICE_OF_ABSENCE);
+ wpabuf_put_le16(resp, noa_len);
+ wpabuf_put_data(resp, noa, noa_len);
+ } else
+ p2p_buf_add_noa(resp, 0, 0, 0, NULL, NULL);
+ p2p_buf_update_ie_hdr(resp, len);
+
+ return resp;
+}
+
+
+static void p2p_process_presence_req(struct p2p_data *p2p, const u8 *da,
+ const u8 *sa, const u8 *data, size_t len,
+ int rx_freq)
+{
+ struct p2p_message msg;
+ u8 status;
+ struct wpabuf *resp;
+ size_t g;
+ struct p2p_group *group = NULL;
+ int parsed = 0;
+ u8 noa[50];
+ int noa_len;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Received P2P Action - P2P Presence Request");
+
+ for (g = 0; g < p2p->num_groups; g++) {
+ if (os_memcmp(da, p2p_group_get_interface_addr(p2p->groups[g]),
+ ETH_ALEN) == 0) {
+ group = p2p->groups[g];
+ break;
+ }
+ }
+ if (group == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Ignore P2P Presence Request for unknown group "
+ MACSTR, MAC2STR(da));
+ return;
+ }
+
+ if (p2p_parse(data, len, &msg) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to parse P2P Presence Request");
+ status = P2P_SC_FAIL_INVALID_PARAMS;
+ goto fail;
+ }
+ parsed = 1;
+
+ if (msg.noa == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No NoA attribute in P2P Presence Request");
+ status = P2P_SC_FAIL_INVALID_PARAMS;
+ goto fail;
+ }
+
+ status = p2p_group_presence_req(group, sa, msg.noa, msg.noa_len);
+
+fail:
+ if (p2p->cfg->get_noa)
+ noa_len = p2p->cfg->get_noa(p2p->cfg->cb_ctx, da, noa,
+ sizeof(noa));
+ else
+ noa_len = -1;
+ resp = p2p_build_presence_resp(status, noa_len > 0 ? noa : NULL,
+ noa_len > 0 ? noa_len : 0,
+ msg.dialog_token);
+ if (parsed)
+ p2p_parse_free(&msg);
+ if (resp == NULL)
+ return;
+
+ p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+ if (p2p_send_action(p2p, rx_freq, sa, da, da,
+ wpabuf_head(resp), wpabuf_len(resp), 200) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to send Action frame");
+ }
+ wpabuf_free(resp);
+}
+
+
+static void p2p_process_presence_resp(struct p2p_data *p2p, const u8 *da,
+ const u8 *sa, const u8 *data, size_t len)
+{
+ struct p2p_message msg;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Received P2P Action - P2P Presence Response");
+
+ if (p2p_parse(data, len, &msg) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to parse P2P Presence Response");
+ return;
+ }
+
+ if (msg.status == NULL || msg.noa == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No Status or NoA attribute in P2P Presence "
+ "Response");
+ p2p_parse_free(&msg);
+ return;
+ }
+
+ if (*msg.status) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: P2P Presence Request was rejected: status %u",
+ *msg.status);
+ p2p_parse_free(&msg);
+ return;
+ }
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: P2P Presence Request was accepted");
+ wpa_hexdump(MSG_DEBUG, "P2P: P2P Presence Response - NoA",
+ msg.noa, msg.noa_len);
+ /* TODO: process NoA */
+ p2p_parse_free(&msg);
+}
+
+
+static void p2p_ext_listen_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct p2p_data *p2p = eloop_ctx;
+
+ if (p2p->ext_listen_interval) {
+ /* Schedule next extended listen timeout */
+ eloop_register_timeout(p2p->ext_listen_interval_sec,
+ p2p->ext_listen_interval_usec,
+ p2p_ext_listen_timeout, p2p, NULL);
+ }
+
+ if (p2p->state == P2P_LISTEN_ONLY && p2p->ext_listen_only) {
+ /*
+ * This should not really happen, but it looks like the Listen
+ * command may fail is something else (e.g., a scan) was
+ * running at an inconvenient time. As a workaround, allow new
+ * Extended Listen operation to be started.
+ */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Previous "
+ "Extended Listen operation had not been completed - "
+ "try again");
+ p2p->ext_listen_only = 0;
+ p2p_set_state(p2p, P2P_IDLE);
+ }
+
+ if (p2p->state != P2P_IDLE) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Skip Extended "
+ "Listen timeout in active state (%s)",
+ p2p_state_txt(p2p->state));
+ return;
+ }
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Extended Listen timeout");
+ p2p->ext_listen_only = 1;
+ if (p2p_listen(p2p, p2p->ext_listen_period) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Failed to start "
+ "Listen state for Extended Listen Timing");
+ p2p->ext_listen_only = 0;
+ }
+}
+
+
+int p2p_ext_listen(struct p2p_data *p2p, unsigned int period,
+ unsigned int interval)
+{
+ if (period > 65535 || interval > 65535 || period > interval ||
+ (period == 0 && interval > 0) || (period > 0 && interval == 0)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Invalid Extended Listen Timing request: "
+ "period=%u interval=%u", period, interval);
+ return -1;
+ }
+
+ eloop_cancel_timeout(p2p_ext_listen_timeout, p2p, NULL);
+
+ if (interval == 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Disabling Extended Listen Timing");
+ p2p->ext_listen_period = 0;
+ p2p->ext_listen_interval = 0;
+ return 0;
+ }
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Enabling Extended Listen Timing: period %u msec, "
+ "interval %u msec", period, interval);
+ p2p->ext_listen_period = period;
+ p2p->ext_listen_interval = interval;
+ p2p->ext_listen_interval_sec = interval / 1000;
+ p2p->ext_listen_interval_usec = (interval % 1000) * 1000;
+
+ eloop_register_timeout(p2p->ext_listen_interval_sec,
+ p2p->ext_listen_interval_usec,
+ p2p_ext_listen_timeout, p2p, NULL);
+
+ return 0;
+}
+
+
+void p2p_deauth_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code,
+ const u8 *ie, size_t ie_len)
+{
+ struct p2p_message msg;
+
+ if (bssid == NULL || ie == NULL)
+ return;
+
+ os_memset(&msg, 0, sizeof(msg));
+ if (p2p_parse_ies(ie, ie_len, &msg))
+ return;
+ if (msg.minor_reason_code == NULL)
+ return;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_INFO,
+ "P2P: Deauthentication notification BSSID " MACSTR
+ " reason_code=%u minor_reason_code=%u",
+ MAC2STR(bssid), reason_code, *msg.minor_reason_code);
+
+ p2p_parse_free(&msg);
+}
+
+
+void p2p_disassoc_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code,
+ const u8 *ie, size_t ie_len)
+{
+ struct p2p_message msg;
+
+ if (bssid == NULL || ie == NULL)
+ return;
+
+ os_memset(&msg, 0, sizeof(msg));
+ if (p2p_parse_ies(ie, ie_len, &msg))
+ return;
+ if (msg.minor_reason_code == NULL)
+ return;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_INFO,
+ "P2P: Disassociation notification BSSID " MACSTR
+ " reason_code=%u minor_reason_code=%u",
+ MAC2STR(bssid), reason_code, *msg.minor_reason_code);
+
+ p2p_parse_free(&msg);
+}
+
+
+void p2p_set_managed_oper(struct p2p_data *p2p, int enabled)
+{
+ if (enabled) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Managed P2P "
+ "Device operations enabled");
+ p2p->dev_capab |= P2P_DEV_CAPAB_INFRA_MANAGED;
+ } else {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Managed P2P "
+ "Device operations disabled");
+ p2p->dev_capab &= ~P2P_DEV_CAPAB_INFRA_MANAGED;
+ }
+}
+
+
+int p2p_set_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel)
+{
+ if (p2p_channel_to_freq(p2p->cfg->country, reg_class, channel) < 0)
+ return -1;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Set Listen channel: "
+ "reg_class %u channel %u", reg_class, channel);
+ p2p->cfg->reg_class = reg_class;
+ p2p->cfg->channel = channel;
+
+ return 0;
+}
+
+
+int p2p_set_ssid_postfix(struct p2p_data *p2p, const u8 *postfix, size_t len)
+{
+ wpa_hexdump_ascii(MSG_DEBUG, "P2P: New SSID postfix", postfix, len);
+ if (postfix == NULL) {
+ p2p->cfg->ssid_postfix_len = 0;
+ return 0;
+ }
+ if (len > sizeof(p2p->cfg->ssid_postfix))
+ return -1;
+ os_memcpy(p2p->cfg->ssid_postfix, postfix, len);
+ p2p->cfg->ssid_postfix_len = len;
+ return 0;
+}
+
+
+int p2p_set_oper_channel(struct p2p_data *p2p, u8 op_reg_class, u8 op_channel,
+ int cfg_op_channel)
+{
+ if (p2p_channel_to_freq(p2p->cfg->country, op_reg_class, op_channel)
+ < 0)
+ return -1;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_INFO, "P2P: Set Operating channel: "
+ "reg_class %u channel %u", op_reg_class, op_channel);
+ p2p->cfg->op_reg_class = op_reg_class;
+ p2p->cfg->op_channel = op_channel;
+ p2p->cfg->cfg_op_channel = cfg_op_channel;
+ return 0;
+}
+
+
+int p2p_set_pref_chan(struct p2p_data *p2p, unsigned int num_pref_chan,
+ const struct p2p_channel *pref_chan)
+{
+ struct p2p_channel *n;
+
+ if (pref_chan) {
+ n = os_malloc(num_pref_chan * sizeof(struct p2p_channel));
+ if (n == NULL)
+ return -1;
+ os_memcpy(n, pref_chan,
+ num_pref_chan * sizeof(struct p2p_channel));
+ } else
+ n = NULL;
+
+ os_free(p2p->cfg->pref_chan);
+ p2p->cfg->pref_chan = n;
+ p2p->cfg->num_pref_chan = num_pref_chan;
+
+ return 0;
+}
+
+
+int p2p_get_interface_addr(struct p2p_data *p2p, const u8 *dev_addr,
+ u8 *iface_addr)
+{
+ struct p2p_device *dev = p2p_get_device(p2p, dev_addr);
+ if (dev == NULL || is_zero_ether_addr(dev->interface_addr))
+ return -1;
+ os_memcpy(iface_addr, dev->interface_addr, ETH_ALEN);
+ return 0;
+}
+
+
+int p2p_get_dev_addr(struct p2p_data *p2p, const u8 *iface_addr,
+ u8 *dev_addr)
+{
+ struct p2p_device *dev = p2p_get_device_interface(p2p, iface_addr);
+ if (dev == NULL)
+ return -1;
+ os_memcpy(dev_addr, dev->info.p2p_device_addr, ETH_ALEN);
+ return 0;
+}
+
+
+void p2p_set_peer_filter(struct p2p_data *p2p, const u8 *addr)
+{
+ os_memcpy(p2p->peer_filter, addr, ETH_ALEN);
+ if (is_zero_ether_addr(p2p->peer_filter))
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Disable peer "
+ "filter");
+ else
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Enable peer "
+ "filter for " MACSTR, MAC2STR(p2p->peer_filter));
+}
+
+
+void p2p_set_cross_connect(struct p2p_data *p2p, int enabled)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Cross connection %s",
+ enabled ? "enabled" : "disabled");
+ if (p2p->cross_connect == enabled)
+ return;
+ p2p->cross_connect = enabled;
+ /* TODO: may need to tear down any action group where we are GO(?) */
+}
+
+
+int p2p_get_oper_freq(struct p2p_data *p2p, const u8 *iface_addr)
+{
+ struct p2p_device *dev = p2p_get_device_interface(p2p, iface_addr);
+ if (dev == NULL)
+ return -1;
+ if (dev->oper_freq <= 0)
+ return -1;
+ return dev->oper_freq;
+}
+
+
+void p2p_set_intra_bss_dist(struct p2p_data *p2p, int enabled)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Intra BSS distribution %s",
+ enabled ? "enabled" : "disabled");
+ p2p->cfg->p2p_intra_bss = enabled;
+}
+
+
+void p2p_update_channel_list(struct p2p_data *p2p, struct p2p_channels *chan)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Update channel list");
+ os_memcpy(&p2p->cfg->channels, chan, sizeof(struct p2p_channels));
+}
+
+
+int p2p_send_action(struct p2p_data *p2p, unsigned int freq, const u8 *dst,
+ const u8 *src, const u8 *bssid, const u8 *buf,
+ size_t len, unsigned int wait_time)
+{
+ if (p2p->p2p_scan_running) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Delay Action "
+ "frame TX until p2p_scan completes");
+ if (p2p->after_scan_tx) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Dropped "
+ "previous pending Action frame TX");
+ os_free(p2p->after_scan_tx);
+ }
+ p2p->after_scan_tx = os_malloc(sizeof(*p2p->after_scan_tx) +
+ len);
+ if (p2p->after_scan_tx == NULL)
+ return -1;
+ p2p->after_scan_tx->freq = freq;
+ os_memcpy(p2p->after_scan_tx->dst, dst, ETH_ALEN);
+ os_memcpy(p2p->after_scan_tx->src, src, ETH_ALEN);
+ os_memcpy(p2p->after_scan_tx->bssid, bssid, ETH_ALEN);
+ p2p->after_scan_tx->len = len;
+ p2p->after_scan_tx->wait_time = wait_time;
+ os_memcpy(p2p->after_scan_tx + 1, buf, len);
+ return 0;
+ }
+
+ return p2p->cfg->send_action(p2p->cfg->cb_ctx, freq, dst, src, bssid,
+ buf, len, wait_time);
+}
+
+
+void p2p_set_best_channels(struct p2p_data *p2p, int freq_24, int freq_5,
+ int freq_overall)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Best channel: 2.4 GHz: %d,"
+ " 5 GHz: %d, overall: %d", freq_24, freq_5, freq_overall);
+ p2p->best_freq_24 = freq_24;
+ p2p->best_freq_5 = freq_5;
+ p2p->best_freq_overall = freq_overall;
+}
+
+
+const u8 * p2p_get_go_neg_peer(struct p2p_data *p2p)
+{
+ if (p2p == NULL || p2p->go_neg_peer == NULL)
+ return NULL;
+ return p2p->go_neg_peer->info.p2p_device_addr;
+}
+
+
+const struct p2p_peer_info *
+p2p_get_peer_found(struct p2p_data *p2p, const u8 *addr, int next)
+{
+ struct p2p_device *dev;
+
+ if (addr) {
+ dev = p2p_get_device(p2p, addr);
+ if (!dev)
+ return NULL;
+
+ if (!next) {
+ if (dev->flags & P2P_DEV_PROBE_REQ_ONLY)
+ return NULL;
+
+ return &dev->info;
+ } else {
+ do {
+ dev = dl_list_first(&dev->list,
+ struct p2p_device,
+ list);
+ if (&dev->list == &p2p->devices)
+ return NULL;
+ } while (dev->flags & P2P_DEV_PROBE_REQ_ONLY);
+ }
+ } else {
+ dev = dl_list_first(&p2p->devices, struct p2p_device, list);
+ if (!dev)
+ return NULL;
+ while (dev->flags & P2P_DEV_PROBE_REQ_ONLY) {
+ dev = dl_list_first(&dev->list,
+ struct p2p_device,
+ list);
+ if (&dev->list == &p2p->devices)
+ return NULL;
+ }
+ }
+
+ return &dev->info;
+}
+
+
+int p2p_in_progress(struct p2p_data *p2p)
+{
+ if (p2p == NULL)
+ return 0;
+ return p2p->state != P2P_IDLE && p2p->state != P2P_PROVISIONING;
+}
--- /dev/null
+/*
+ * Wi-Fi Direct - P2P module
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef P2P_H
+#define P2P_H
+
+/**
+ * P2P_MAX_REG_CLASSES - Maximum number of regulatory classes
+ */
+#define P2P_MAX_REG_CLASSES 10
+
+/**
+ * P2P_MAX_REG_CLASS_CHANNELS - Maximum number of channels per regulatory class
+ */
+#define P2P_MAX_REG_CLASS_CHANNELS 20
+
+/**
+ * struct p2p_channels - List of supported channels
+ */
+struct p2p_channels {
+ /**
+ * struct p2p_reg_class - Supported regulatory class
+ */
+ struct p2p_reg_class {
+ /**
+ * reg_class - Regulatory class (IEEE 802.11-2007, Annex J)
+ */
+ u8 reg_class;
+
+ /**
+ * channel - Supported channels
+ */
+ u8 channel[P2P_MAX_REG_CLASS_CHANNELS];
+
+ /**
+ * channels - Number of channel entries in use
+ */
+ size_t channels;
+ } reg_class[P2P_MAX_REG_CLASSES];
+
+ /**
+ * reg_classes - Number of reg_class entries in use
+ */
+ size_t reg_classes;
+};
+
+enum p2p_wps_method {
+ WPS_NOT_READY, WPS_PIN_DISPLAY, WPS_PIN_KEYPAD, WPS_PBC
+};
+
+/**
+ * struct p2p_go_neg_results - P2P Group Owner Negotiation results
+ */
+struct p2p_go_neg_results {
+ /**
+ * status - Negotiation result (Status Code)
+ *
+ * 0 (P2P_SC_SUCCESS) indicates success. Non-zero values indicate
+ * failed negotiation.
+ */
+ int status;
+
+ /**
+ * role_go - Whether local end is Group Owner
+ */
+ int role_go;
+
+ /**
+ * freq - Frequency of the group operational channel in MHz
+ */
+ int freq;
+
+ /**
+ * ssid - SSID of the group
+ */
+ u8 ssid[32];
+
+ /**
+ * ssid_len - Length of SSID in octets
+ */
+ size_t ssid_len;
+
+ /**
+ * passphrase - WPA2-Personal passphrase for the group (GO only)
+ */
+ char passphrase[64];
+
+ /**
+ * peer_device_addr - P2P Device Address of the peer
+ */
+ u8 peer_device_addr[ETH_ALEN];
+
+ /**
+ * peer_interface_addr - P2P Interface Address of the peer
+ */
+ u8 peer_interface_addr[ETH_ALEN];
+
+ /**
+ * wps_method - WPS method to be used during provisioning
+ */
+ enum p2p_wps_method wps_method;
+
+#define P2P_MAX_CHANNELS 50
+
+ /**
+ * freq_list - Zero-terminated list of possible operational channels
+ */
+ int freq_list[P2P_MAX_CHANNELS];
+
+ /**
+ * persistent_group - Whether the group should be made persistent
+ * 0 = not persistent
+ * 1 = persistent group without persistent reconnect
+ * 2 = persistent group with persistent reconnect
+ */
+ int persistent_group;
+
+ /**
+ * peer_config_timeout - Peer configuration timeout (in 10 msec units)
+ */
+ unsigned int peer_config_timeout;
+};
+
+struct p2p_data;
+
+enum p2p_scan_type {
+ P2P_SCAN_SOCIAL,
+ P2P_SCAN_FULL,
+ P2P_SCAN_SPECIFIC,
+ P2P_SCAN_SOCIAL_PLUS_ONE
+};
+
+#define P2P_MAX_WPS_VENDOR_EXT 10
+
+/**
+ * struct p2p_peer_info - P2P peer information
+ */
+struct p2p_peer_info {
+ /**
+ * p2p_device_addr - P2P Device Address of the peer
+ */
+ u8 p2p_device_addr[ETH_ALEN];
+
+ /**
+ * pri_dev_type - Primary Device Type
+ */
+ u8 pri_dev_type[8];
+
+ /**
+ * device_name - Device Name (0..32 octets encoded in UTF-8)
+ */
+ char device_name[33];
+
+ /**
+ * manufacturer - Manufacturer (0..64 octets encoded in UTF-8)
+ */
+ char manufacturer[65];
+
+ /**
+ * model_name - Model Name (0..32 octets encoded in UTF-8)
+ */
+ char model_name[33];
+
+ /**
+ * model_number - Model Number (0..32 octets encoded in UTF-8)
+ */
+ char model_number[33];
+
+ /**
+ * serial_number - Serial Number (0..32 octets encoded in UTF-8)
+ */
+ char serial_number[33];
+
+ /**
+ * level - Signal level
+ */
+ int level;
+
+ /**
+ * config_methods - WPS Configuration Methods
+ */
+ u16 config_methods;
+
+ /**
+ * dev_capab - Device Capabilities
+ */
+ u8 dev_capab;
+
+ /**
+ * group_capab - Group Capabilities
+ */
+ u8 group_capab;
+
+ /**
+ * wps_sec_dev_type_list - WPS secondary device type list
+ *
+ * This list includes from 0 to 16 Secondary Device Types as indicated
+ * by wps_sec_dev_type_list_len (8 * number of types).
+ */
+ u8 wps_sec_dev_type_list[128];
+
+ /**
+ * wps_sec_dev_type_list_len - Length of secondary device type list
+ */
+ size_t wps_sec_dev_type_list_len;
+
+ struct wpabuf *wps_vendor_ext[P2P_MAX_WPS_VENDOR_EXT];
+};
+
+enum p2p_prov_disc_status {
+ P2P_PROV_DISC_SUCCESS,
+ P2P_PROV_DISC_TIMEOUT,
+ P2P_PROV_DISC_REJECTED,
+};
+
+struct p2p_channel {
+ u8 op_class;
+ u8 chan;
+};
+
+/**
+ * struct p2p_config - P2P configuration
+ *
+ * This configuration is provided to the P2P module during initialization with
+ * p2p_init().
+ */
+struct p2p_config {
+ /**
+ * country - Country code to use in P2P operations
+ */
+ char country[3];
+
+ /**
+ * reg_class - Regulatory class for own listen channel
+ */
+ u8 reg_class;
+
+ /**
+ * channel - Own listen channel
+ */
+ u8 channel;
+
+ /**
+ * Regulatory class for own operational channel
+ */
+ u8 op_reg_class;
+
+ /**
+ * op_channel - Own operational channel
+ */
+ u8 op_channel;
+
+ /**
+ * cfg_op_channel - Whether op_channel is hardcoded in configuration
+ */
+ u8 cfg_op_channel;
+
+ /**
+ * channels - Own supported regulatory classes and channels
+ *
+ * List of supposerted channels per regulatory class. The regulatory
+ * classes are defined in IEEE Std 802.11-2007 Annex J and the
+ * numbering of the clases depends on the configured country code.
+ */
+ struct p2p_channels channels;
+
+ /**
+ * num_pref_chan - Number of pref_chan entries
+ */
+ unsigned int num_pref_chan;
+
+ /**
+ * pref_chan - Preferred channels for GO Negotiation
+ */
+ struct p2p_channel *pref_chan;
+
+ /**
+ * pri_dev_type - Primary Device Type (see WPS)
+ */
+ u8 pri_dev_type[8];
+
+ /**
+ * P2P_SEC_DEVICE_TYPES - Maximum number of secondary device types
+ */
+#define P2P_SEC_DEVICE_TYPES 5
+
+ /**
+ * sec_dev_type - Optional secondary device types
+ */
+ u8 sec_dev_type[P2P_SEC_DEVICE_TYPES][8];
+
+ /**
+ * num_sec_dev_types - Number of sec_dev_type entries
+ */
+ size_t num_sec_dev_types;
+
+ /**
+ * dev_addr - P2P Device Address
+ */
+ u8 dev_addr[ETH_ALEN];
+
+ /**
+ * dev_name - Device Name
+ */
+ char *dev_name;
+
+ char *manufacturer;
+ char *model_name;
+ char *model_number;
+ char *serial_number;
+
+ u8 uuid[16];
+ u16 config_methods;
+
+ /**
+ * concurrent_operations - Whether concurrent operations are supported
+ */
+ int concurrent_operations;
+
+ /**
+ * max_peers - Maximum number of discovered peers to remember
+ *
+ * If more peers are discovered, older entries will be removed to make
+ * room for the new ones.
+ */
+ size_t max_peers;
+
+ /**
+ * p2p_intra_bss - Intra BSS communication is supported
+ */
+ int p2p_intra_bss;
+
+ /**
+ * ssid_postfix - Postfix data to add to the SSID
+ *
+ * This data will be added to the end of the SSID after the
+ * DIRECT-<random two octets> prefix.
+ */
+ u8 ssid_postfix[32 - 9];
+
+ /**
+ * ssid_postfix_len - Length of the ssid_postfix data
+ */
+ size_t ssid_postfix_len;
+
+ /**
+ * msg_ctx - Context to use with wpa_msg() calls
+ */
+ void *msg_ctx;
+
+ /**
+ * cb_ctx - Context to use with callback functions
+ */
+ void *cb_ctx;
+
+
+ /* Callbacks to request lower layer driver operations */
+
+ /**
+ * p2p_scan - Request a P2P scan/search
+ * @ctx: Callback context from cb_ctx
+ * @type: Scan type
+ * @freq: Specific frequency (MHz) to scan or 0 for no restriction
+ * @num_req_dev_types: Number of requested device types
+ * @req_dev_types: Array containing requested device types
+ * @dev_id: Device ID to search for or %NULL to find all devices
+ * Returns: 0 on success, -1 on failure
+ *
+ * This callback function is used to request a P2P scan or search
+ * operation to be completed. Type type argument specifies which type
+ * of scan is to be done. @P2P_SCAN_SOCIAL indicates that only the
+ * social channels (1, 6, 11) should be scanned. @P2P_SCAN_FULL
+ * indicates that all channels are to be scanned. @P2P_SCAN_SPECIFIC
+ * request a scan of a single channel specified by freq.
+ * @P2P_SCAN_SOCIAL_PLUS_ONE request scan of all the social channels
+ * plus one extra channel specified by freq.
+ *
+ * The full scan is used for the initial scan to find group owners from
+ * all. The other types are used during search phase scan of the social
+ * channels (with potential variation if the Listen channel of the
+ * target peer is known or if other channels are scanned in steps).
+ *
+ * The scan results are returned after this call by calling
+ * p2p_scan_res_handler() for each scan result that has a P2P IE and
+ * then calling p2p_scan_res_handled() to indicate that all scan
+ * results have been indicated.
+ */
+ int (*p2p_scan)(void *ctx, enum p2p_scan_type type, int freq,
+ unsigned int num_req_dev_types,
+ const u8 *req_dev_types, const u8 *dev_id);
+
+ /**
+ * send_probe_resp - Transmit a Probe Response frame
+ * @ctx: Callback context from cb_ctx
+ * @buf: Probe Response frame (including the header and body)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to reply to Probe Request frames that were
+ * indicated with a call to p2p_probe_req_rx(). The response is to be
+ * sent on the same channel or to be dropped if the driver is not
+ * anymore listening to Probe Request frames.
+ *
+ * Alternatively, the responsibility for building the Probe Response
+ * frames in Listen state may be in another system component in which
+ * case this function need to be implemented (i.e., the function
+ * pointer can be %NULL). The WPS and P2P IEs to be added for Probe
+ * Response frames in such a case are available from the
+ * start_listen() callback. It should be noted that the received Probe
+ * Request frames must be indicated by calling p2p_probe_req_rx() even
+ * if this send_probe_resp() is not used.
+ */
+ int (*send_probe_resp)(void *ctx, const struct wpabuf *buf);
+
+ /**
+ * send_action - Transmit an Action frame
+ * @ctx: Callback context from cb_ctx
+ * @freq: Frequency in MHz for the channel on which to transmit
+ * @dst: Destination MAC address (Address 1)
+ * @src: Source MAC address (Address 2)
+ * @bssid: BSSID (Address 3)
+ * @buf: Frame body (starting from Category field)
+ * @len: Length of buf in octets
+ * @wait_time: How many msec to wait for a response frame
+ * Returns: 0 on success, -1 on failure
+ *
+ * The Action frame may not be transmitted immediately and the status
+ * of the transmission must be reported by calling
+ * p2p_send_action_cb() once the frame has either been transmitted or
+ * it has been dropped due to excessive retries or other failure to
+ * transmit.
+ */
+ int (*send_action)(void *ctx, unsigned int freq, const u8 *dst,
+ const u8 *src, const u8 *bssid, const u8 *buf,
+ size_t len, unsigned int wait_time);
+
+ /**
+ * send_action_done - Notify that Action frame sequence was completed
+ * @ctx: Callback context from cb_ctx
+ *
+ * This function is called when the Action frame sequence that was
+ * started with send_action() has been completed, i.e., when there is
+ * no need to wait for a response from the destination peer anymore.
+ */
+ void (*send_action_done)(void *ctx);
+
+ /**
+ * start_listen - Start Listen state
+ * @ctx: Callback context from cb_ctx
+ * @freq: Frequency of the listen channel in MHz
+ * @duration: Duration for the Listen state in milliseconds
+ * @probe_resp_ie: IE(s) to be added to Probe Response frames
+ * Returns: 0 on success, -1 on failure
+ *
+ * This Listen state may not start immediately since the driver may
+ * have other pending operations to complete first. Once the Listen
+ * state has started, p2p_listen_cb() must be called to notify the P2P
+ * module. Once the Listen state is stopped, p2p_listen_end() must be
+ * called to notify the P2P module that the driver is not in the Listen
+ * state anymore.
+ *
+ * If the send_probe_resp() is not used for generating the response,
+ * the IEs from probe_resp_ie need to be added to the end of the Probe
+ * Response frame body. If send_probe_resp() is used, the probe_resp_ie
+ * information can be ignored.
+ */
+ int (*start_listen)(void *ctx, unsigned int freq,
+ unsigned int duration,
+ const struct wpabuf *probe_resp_ie);
+ /**
+ * stop_listen - Stop Listen state
+ * @ctx: Callback context from cb_ctx
+ *
+ * This callback can be used to stop a Listen state operation that was
+ * previously requested with start_listen().
+ */
+ void (*stop_listen)(void *ctx);
+
+ /**
+ * get_noa - Get current Notice of Absence attribute payload
+ * @ctx: Callback context from cb_ctx
+ * @interface_addr: P2P Interface Address of the GO
+ * @buf: Buffer for returning NoA
+ * @buf_len: Buffer length in octets
+ * Returns: Number of octets used in buf, 0 to indicate no NoA is being
+ * advertized, or -1 on failure
+ *
+ * This function is used to fetch the current Notice of Absence
+ * attribute value from GO.
+ */
+ int (*get_noa)(void *ctx, const u8 *interface_addr, u8 *buf,
+ size_t buf_len);
+
+ /* Callbacks to notify events to upper layer management entity */
+
+ /**
+ * dev_found - Notification of a found P2P Device
+ * @ctx: Callback context from cb_ctx
+ * @addr: Source address of the message triggering this notification
+ * @info: P2P peer information
+ * @new_device: Inform if the peer is newly found
+ *
+ * This callback is used to notify that a new P2P Device has been
+ * found. This may happen, e.g., during Search state based on scan
+ * results or during Listen state based on receive Probe Request and
+ * Group Owner Negotiation Request.
+ */
+ void (*dev_found)(void *ctx, const u8 *addr,
+ const struct p2p_peer_info *info,
+ int new_device);
+
+ /**
+ * dev_lost - Notification of a lost P2P Device
+ * @ctx: Callback context from cb_ctx
+ * @dev_addr: P2P Device Address of the lost P2P Device
+ *
+ * This callback is used to notify that a P2P Device has been deleted.
+ */
+ void (*dev_lost)(void *ctx, const u8 *dev_addr);
+
+ /**
+ * go_neg_req_rx - Notification of a receive GO Negotiation Request
+ * @ctx: Callback context from cb_ctx
+ * @src: Source address of the message triggering this notification
+ * @dev_passwd_id: WPS Device Password ID
+ *
+ * This callback is used to notify that a P2P Device is requesting
+ * group owner negotiation with us, but we do not have all the
+ * necessary information to start GO Negotiation. This indicates that
+ * the local user has not authorized the connection yet by providing a
+ * PIN or PBC button press. This information can be provided with a
+ * call to p2p_connect().
+ */
+ void (*go_neg_req_rx)(void *ctx, const u8 *src, u16 dev_passwd_id);
+
+ /**
+ * go_neg_completed - Notification of GO Negotiation results
+ * @ctx: Callback context from cb_ctx
+ * @res: GO Negotiation results
+ *
+ * This callback is used to notify that Group Owner Negotiation has
+ * been completed. Non-zero struct p2p_go_neg_results::status indicates
+ * failed negotiation. In case of success, this function is responsible
+ * for creating a new group interface (or using the existing interface
+ * depending on driver features), setting up the group interface in
+ * proper mode based on struct p2p_go_neg_results::role_go and
+ * initializing WPS provisioning either as a Registrar (if GO) or as an
+ * Enrollee. Successful WPS provisioning must be indicated by calling
+ * p2p_wps_success_cb(). The callee is responsible for timing out group
+ * formation if WPS provisioning cannot be completed successfully
+ * within 15 seconds.
+ */
+ void (*go_neg_completed)(void *ctx, struct p2p_go_neg_results *res);
+
+ /**
+ * sd_request - Callback on Service Discovery Request
+ * @ctx: Callback context from cb_ctx
+ * @freq: Frequency (in MHz) of the channel
+ * @sa: Source address of the request
+ * @dialog_token: Dialog token
+ * @update_indic: Service Update Indicator from the source of request
+ * @tlvs: P2P Service Request TLV(s)
+ * @tlvs_len: Length of tlvs buffer in octets
+ *
+ * This callback is used to indicate reception of a service discovery
+ * request. Response to the query must be indicated by calling
+ * p2p_sd_response() with the context information from the arguments to
+ * this callback function.
+ *
+ * This callback handler can be set to %NULL to indicate that service
+ * discovery is not supported.
+ */
+ void (*sd_request)(void *ctx, int freq, const u8 *sa, u8 dialog_token,
+ u16 update_indic, const u8 *tlvs, size_t tlvs_len);
+
+ /**
+ * sd_response - Callback on Service Discovery Response
+ * @ctx: Callback context from cb_ctx
+ * @sa: Source address of the request
+ * @update_indic: Service Update Indicator from the source of response
+ * @tlvs: P2P Service Response TLV(s)
+ * @tlvs_len: Length of tlvs buffer in octets
+ *
+ * This callback is used to indicate reception of a service discovery
+ * response. This callback handler can be set to %NULL if no service
+ * discovery requests are used. The information provided with this call
+ * is replies to the queries scheduled with p2p_sd_request().
+ */
+ void (*sd_response)(void *ctx, const u8 *sa, u16 update_indic,
+ const u8 *tlvs, size_t tlvs_len);
+
+ /**
+ * prov_disc_req - Callback on Provisiong Discovery Request
+ * @ctx: Callback context from cb_ctx
+ * @peer: Source address of the request
+ * @config_methods: Requested WPS Config Method
+ * @dev_addr: P2P Device Address of the found P2P Device
+ * @pri_dev_type: Primary Device Type
+ * @dev_name: Device Name
+ * @supp_config_methods: Supported configuration Methods
+ * @dev_capab: Device Capabilities
+ * @group_capab: Group Capabilities
+ * @group_id: P2P Group ID (or %NULL if not included)
+ * @group_id_len: Length of P2P Group ID
+ *
+ * This callback is used to indicate reception of a Provision Discovery
+ * Request frame that the P2P module accepted.
+ */
+ void (*prov_disc_req)(void *ctx, const u8 *peer, u16 config_methods,
+ const u8 *dev_addr, const u8 *pri_dev_type,
+ const char *dev_name, u16 supp_config_methods,
+ u8 dev_capab, u8 group_capab,
+ const u8 *group_id, size_t group_id_len);
+
+ /**
+ * prov_disc_resp - Callback on Provisiong Discovery Response
+ * @ctx: Callback context from cb_ctx
+ * @peer: Source address of the response
+ * @config_methods: Value from p2p_prov_disc_req() or 0 on failure
+ *
+ * This callback is used to indicate reception of a Provision Discovery
+ * Response frame for a pending request scheduled with
+ * p2p_prov_disc_req(). This callback handler can be set to %NULL if
+ * provision discovery is not used.
+ */
+ void (*prov_disc_resp)(void *ctx, const u8 *peer, u16 config_methods);
+
+ /**
+ * prov_disc_fail - Callback on Provision Discovery failure
+ * @ctx: Callback context from cb_ctx
+ * @peer: Source address of the response
+ * @status: Cause of failure, will not be %P2P_PROV_DISC_SUCCESS
+ *
+ * This callback is used to indicate either a failure or no response
+ * to an earlier provision discovery request.
+ *
+ * This callback handler can be set to %NULL if provision discovery
+ * is not used or failures do not need to be indicated.
+ */
+ void (*prov_disc_fail)(void *ctx, const u8 *peer,
+ enum p2p_prov_disc_status status);
+
+ /**
+ * invitation_process - Optional callback for processing Invitations
+ * @ctx: Callback context from cb_ctx
+ * @sa: Source address of the Invitation Request
+ * @bssid: P2P Group BSSID from the request or %NULL if not included
+ * @go_dev_addr: GO Device Address from P2P Group ID
+ * @ssid: SSID from P2P Group ID
+ * @ssid_len: Length of ssid buffer in octets
+ * @go: Variable for returning whether the local end is GO in the group
+ * @group_bssid: Buffer for returning P2P Group BSSID (if local end GO)
+ * @force_freq: Variable for returning forced frequency for the group
+ * @persistent_group: Whether this is an invitation to reinvoke a
+ * persistent group (instead of invitation to join an active
+ * group)
+ * Returns: Status code (P2P_SC_*)
+ *
+ * This optional callback can be used to implement persistent reconnect
+ * by allowing automatic restarting of persistent groups without user
+ * interaction. If this callback is not implemented (i.e., is %NULL),
+ * the received Invitation Request frames are replied with
+ * %P2P_SC_REQ_RECEIVED status and indicated to upper layer with the
+ * invitation_result() callback.
+ *
+ * If the requested parameters are acceptable and the group is known,
+ * %P2P_SC_SUCCESS may be returned. If the requested group is unknown,
+ * %P2P_SC_FAIL_UNKNOWN_GROUP should be returned. %P2P_SC_REQ_RECEIVED
+ * can be returned if there is not enough data to provide immediate
+ * response, i.e., if some sort of user interaction is needed. The
+ * invitation_received() callback will be called in that case
+ * immediately after this call.
+ */
+ u8 (*invitation_process)(void *ctx, const u8 *sa, const u8 *bssid,
+ const u8 *go_dev_addr, const u8 *ssid,
+ size_t ssid_len, int *go, u8 *group_bssid,
+ int *force_freq, int persistent_group);
+
+ /**
+ * invitation_received - Callback on Invitation Request RX
+ * @ctx: Callback context from cb_ctx
+ * @sa: Source address of the Invitation Request
+ * @bssid: P2P Group BSSID or %NULL if not received
+ * @ssid: SSID of the group
+ * @ssid_len: Length of ssid in octets
+ * @go_dev_addr: GO Device Address
+ * @status: Response Status
+ * @op_freq: Operational frequency for the group
+ *
+ * This callback is used to indicate sending of an Invitation Response
+ * for a received Invitation Request. If status == 0 (success), the
+ * upper layer code is responsible for starting the group. status == 1
+ * indicates need to get user authorization for the group. Other status
+ * values indicate that the invitation request was rejected.
+ */
+ void (*invitation_received)(void *ctx, const u8 *sa, const u8 *bssid,
+ const u8 *ssid, size_t ssid_len,
+ const u8 *go_dev_addr, u8 status,
+ int op_freq);
+
+ /**
+ * invitation_result - Callback on Invitation result
+ * @ctx: Callback context from cb_ctx
+ * @status: Negotiation result (Status Code)
+ * @bssid: P2P Group BSSID or %NULL if not received
+ *
+ * This callback is used to indicate result of an Invitation procedure
+ * started with a call to p2p_invite(). The indicated status code is
+ * the value received from the peer in Invitation Response with 0
+ * (P2P_SC_SUCCESS) indicating success or -1 to indicate a timeout or a
+ * local failure in transmitting the Invitation Request.
+ */
+ void (*invitation_result)(void *ctx, int status, const u8 *bssid);
+
+ /**
+ * go_connected - Check whether we are connected to a GO
+ * @ctx: Callback context from cb_ctx
+ * @dev_addr: P2P Device Address of a GO
+ * Returns: 1 if we are connected as a P2P client to the specified GO
+ * or 0 if not.
+ */
+ int (*go_connected)(void *ctx, const u8 *dev_addr);
+};
+
+
+/* P2P module initialization/deinitialization */
+
+/**
+ * p2p_init - Initialize P2P module
+ * @cfg: P2P module configuration
+ * Returns: Pointer to private data or %NULL on failure
+ *
+ * This function is used to initialize global P2P module context (one per
+ * device). The P2P module will keep a copy of the configuration data, so the
+ * caller does not need to maintain this structure. However, the callback
+ * functions and the context parameters to them must be kept available until
+ * the P2P module is deinitialized with p2p_deinit().
+ */
+struct p2p_data * p2p_init(const struct p2p_config *cfg);
+
+/**
+ * p2p_deinit - Deinitialize P2P module
+ * @p2p: P2P module context from p2p_init()
+ */
+void p2p_deinit(struct p2p_data *p2p);
+
+/**
+ * p2p_flush - Flush P2P module state
+ * @p2p: P2P module context from p2p_init()
+ *
+ * This command removes the P2P module state like peer device entries.
+ */
+void p2p_flush(struct p2p_data *p2p);
+
+/**
+ * p2p_unauthorize - Unauthorize the specified peer device
+ * @p2p: P2P module context from p2p_init()
+ * @addr: P2P peer entry to be unauthorized
+ * Returns: 0 on success, -1 on failure
+ *
+ * This command removes any connection authorization from the specified P2P
+ * peer device address. This can be used, e.g., to cancel effect of a previous
+ * p2p_authorize() or p2p_connect() call that has not yet resulted in completed
+ * GO Negotiation.
+ */
+int p2p_unauthorize(struct p2p_data *p2p, const u8 *addr);
+
+/**
+ * p2p_set_dev_name - Set device name
+ * @p2p: P2P module context from p2p_init()
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to update the P2P module configuration with
+ * information that was not available at the time of the p2p_init() call.
+ */
+int p2p_set_dev_name(struct p2p_data *p2p, const char *dev_name);
+
+int p2p_set_manufacturer(struct p2p_data *p2p, const char *manufacturer);
+int p2p_set_model_name(struct p2p_data *p2p, const char *model_name);
+int p2p_set_model_number(struct p2p_data *p2p, const char *model_number);
+int p2p_set_serial_number(struct p2p_data *p2p, const char *serial_number);
+
+void p2p_set_config_methods(struct p2p_data *p2p, u16 config_methods);
+void p2p_set_uuid(struct p2p_data *p2p, const u8 *uuid);
+
+/**
+ * p2p_set_pri_dev_type - Set primary device type
+ * @p2p: P2P module context from p2p_init()
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to update the P2P module configuration with
+ * information that was not available at the time of the p2p_init() call.
+ */
+int p2p_set_pri_dev_type(struct p2p_data *p2p, const u8 *pri_dev_type);
+
+/**
+ * p2p_set_sec_dev_types - Set secondary device types
+ * @p2p: P2P module context from p2p_init()
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to update the P2P module configuration with
+ * information that was not available at the time of the p2p_init() call.
+ */
+int p2p_set_sec_dev_types(struct p2p_data *p2p, const u8 dev_types[][8],
+ size_t num_dev_types);
+
+int p2p_set_country(struct p2p_data *p2p, const char *country);
+
+
+/* Commands from upper layer management entity */
+
+enum p2p_discovery_type {
+ P2P_FIND_START_WITH_FULL,
+ P2P_FIND_ONLY_SOCIAL,
+ P2P_FIND_PROGRESSIVE
+};
+
+/**
+ * p2p_find - Start P2P Find (Device Discovery)
+ * @p2p: P2P module context from p2p_init()
+ * @timeout: Timeout for find operation in seconds or 0 for no timeout
+ * @type: Device Discovery type
+ * @num_req_dev_types: Number of requested device types
+ * @req_dev_types: Requested device types array, must be an array
+ * containing num_req_dev_types * WPS_DEV_TYPE_LEN bytes; %NULL if no
+ * requested device types.
+ * @dev_id: Device ID to search for or %NULL to find all devices
+ * Returns: 0 on success, -1 on failure
+ */
+int p2p_find(struct p2p_data *p2p, unsigned int timeout,
+ enum p2p_discovery_type type,
+ unsigned int num_req_dev_types, const u8 *req_dev_types,
+ const u8 *dev_id);
+
+/**
+ * p2p_stop_find - Stop P2P Find (Device Discovery)
+ * @p2p: P2P module context from p2p_init()
+ */
+void p2p_stop_find(struct p2p_data *p2p);
+
+/**
+ * p2p_stop_find_for_freq - Stop P2P Find for next oper on specific freq
+ * @p2p: P2P module context from p2p_init()
+ * @freq: Frequency in MHz for next operation
+ *
+ * This is like p2p_stop_find(), but Listen state is not stopped if we are
+ * already on the same frequency.
+ */
+void p2p_stop_find_for_freq(struct p2p_data *p2p, int freq);
+
+/**
+ * p2p_listen - Start P2P Listen state for specified duration
+ * @p2p: P2P module context from p2p_init()
+ * @timeout: Listen state duration in milliseconds
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to request the P2P module to keep the device
+ * discoverable on the listen channel for an extended set of time. At least in
+ * its current form, this is mainly used for testing purposes and may not be of
+ * much use for normal P2P operations.
+ */
+int p2p_listen(struct p2p_data *p2p, unsigned int timeout);
+
+/**
+ * p2p_connect - Start P2P group formation (GO negotiation)
+ * @p2p: P2P module context from p2p_init()
+ * @peer_addr: MAC address of the peer P2P client
+ * @wps_method: WPS method to be used in provisioning
+ * @go_intent: Local GO intent value (1..15)
+ * @own_interface_addr: Intended interface address to use with the group
+ * @force_freq: The only allowed channel frequency in MHz or 0
+ * @persistent_group: Whether to create a persistent group (0 = no, 1 =
+ * persistent group without persistent reconnect, 2 = persistent group with
+ * persistent reconnect)
+ * Returns: 0 on success, -1 on failure
+ */
+int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr,
+ enum p2p_wps_method wps_method,
+ int go_intent, const u8 *own_interface_addr,
+ unsigned int force_freq, int persistent_group);
+
+/**
+ * p2p_authorize - Authorize P2P group formation (GO negotiation)
+ * @p2p: P2P module context from p2p_init()
+ * @peer_addr: MAC address of the peer P2P client
+ * @wps_method: WPS method to be used in provisioning
+ * @go_intent: Local GO intent value (1..15)
+ * @own_interface_addr: Intended interface address to use with the group
+ * @force_freq: The only allowed channel frequency in MHz or 0
+ * @persistent_group: Whether to create a persistent group (0 = no, 1 =
+ * persistent group without persistent reconnect, 2 = persistent group with
+ * persistent reconnect)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This is like p2p_connect(), but the actual group negotiation is not
+ * initiated automatically, i.e., the other end is expected to do that.
+ */
+int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr,
+ enum p2p_wps_method wps_method,
+ int go_intent, const u8 *own_interface_addr,
+ unsigned int force_freq, int persistent_group);
+
+/**
+ * p2p_reject - Reject peer device (explicitly block connection attempts)
+ * @p2p: P2P module context from p2p_init()
+ * @peer_addr: MAC address of the peer P2P client
+ * Returns: 0 on success, -1 on failure
+ */
+int p2p_reject(struct p2p_data *p2p, const u8 *peer_addr);
+
+/**
+ * p2p_prov_disc_req - Send Provision Discovery Request
+ * @p2p: P2P module context from p2p_init()
+ * @peer_addr: MAC address of the peer P2P client
+ * @config_methods: WPS Config Methods value (only one bit set)
+ * @join: Whether this is used by a client joining an active group
+ * @force_freq: Forced TX frequency for the frame (mainly for the join case)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to request a discovered P2P peer to display a PIN
+ * (config_methods = WPS_CONFIG_DISPLAY) or be prepared to enter a PIN from us
+ * (config_methods = WPS_CONFIG_KEYPAD). The Provision Discovery Request frame
+ * is transmitted once immediately and if no response is received, the frame
+ * will be sent again whenever the target device is discovered during device
+ * dsicovery (start with a p2p_find() call). Response from the peer is
+ * indicated with the p2p_config::prov_disc_resp() callback.
+ */
+int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr,
+ u16 config_methods, int join, int force_freq);
+
+/**
+ * p2p_sd_request - Schedule a service discovery query
+ * @p2p: P2P module context from p2p_init()
+ * @dst: Destination peer or %NULL to apply for all peers
+ * @tlvs: P2P Service Query TLV(s)
+ * Returns: Reference to the query or %NULL on failure
+ *
+ * Response to the query is indicated with the p2p_config::sd_response()
+ * callback.
+ */
+void * p2p_sd_request(struct p2p_data *p2p, const u8 *dst,
+ const struct wpabuf *tlvs);
+
+/**
+ * p2p_sd_cancel_request - Cancel a pending service discovery query
+ * @p2p: P2P module context from p2p_init()
+ * @req: Query reference from p2p_sd_request()
+ * Returns: 0 if request for cancelled; -1 if not found
+ */
+int p2p_sd_cancel_request(struct p2p_data *p2p, void *req);
+
+/**
+ * p2p_sd_response - Send response to a service discovery query
+ * @p2p: P2P module context from p2p_init()
+ * @freq: Frequency from p2p_config::sd_request() callback
+ * @dst: Destination address from p2p_config::sd_request() callback
+ * @dialog_token: Dialog token from p2p_config::sd_request() callback
+ * @resp_tlvs: P2P Service Response TLV(s)
+ *
+ * This function is called as a response to the request indicated with
+ * p2p_config::sd_request() callback.
+ */
+void p2p_sd_response(struct p2p_data *p2p, int freq, const u8 *dst,
+ u8 dialog_token, const struct wpabuf *resp_tlvs);
+
+/**
+ * p2p_sd_service_update - Indicate a change in local services
+ * @p2p: P2P module context from p2p_init()
+ *
+ * This function needs to be called whenever there is a change in availability
+ * of the local services. This will increment the Service Update Indicator
+ * value which will be used in SD Request and Response frames.
+ */
+void p2p_sd_service_update(struct p2p_data *p2p);
+
+
+enum p2p_invite_role {
+ P2P_INVITE_ROLE_GO,
+ P2P_INVITE_ROLE_ACTIVE_GO,
+ P2P_INVITE_ROLE_CLIENT
+};
+
+/**
+ * p2p_invite - Invite a P2P Device into a group
+ * @p2p: P2P module context from p2p_init()
+ * @peer: Device Address of the peer P2P Device
+ * @role: Local role in the group
+ * @bssid: Group BSSID or %NULL if not known
+ * @ssid: Group SSID
+ * @ssid_len: Length of ssid in octets
+ * @force_freq: The only allowed channel frequency in MHz or 0
+ * @go_dev_addr: Forced GO Device Address or %NULL if none
+ * @persistent_group: Whether this is to reinvoke a persistent group
+ * Returns: 0 on success, -1 on failure
+ */
+int p2p_invite(struct p2p_data *p2p, const u8 *peer, enum p2p_invite_role role,
+ const u8 *bssid, const u8 *ssid, size_t ssid_len,
+ unsigned int force_freq, const u8 *go_dev_addr,
+ int persistent_group);
+
+/**
+ * p2p_presence_req - Request GO presence
+ * @p2p: P2P module context from p2p_init()
+ * @go_interface_addr: GO P2P Interface Address
+ * @own_interface_addr: Own P2P Interface Address for this group
+ * @freq: Group operating frequence (in MHz)
+ * @duration1: Preferred presence duration in microseconds
+ * @interval1: Preferred presence interval in microseconds
+ * @duration2: Acceptable presence duration in microseconds
+ * @interval2: Acceptable presence interval in microseconds
+ * Returns: 0 on success, -1 on failure
+ *
+ * If both duration and interval values are zero, the parameter pair is not
+ * specified (i.e., to remove Presence Request, use duration1 = interval1 = 0).
+ */
+int p2p_presence_req(struct p2p_data *p2p, const u8 *go_interface_addr,
+ const u8 *own_interface_addr, unsigned int freq,
+ u32 duration1, u32 interval1, u32 duration2,
+ u32 interval2);
+
+/**
+ * p2p_ext_listen - Set Extended Listen Timing
+ * @p2p: P2P module context from p2p_init()
+ * @freq: Group operating frequence (in MHz)
+ * @period: Availability period in milliseconds (1-65535; 0 to disable)
+ * @interval: Availability interval in milliseconds (1-65535; 0 to disable)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to enable or disable (period = interval = 0)
+ * Extended Listen Timing. When enabled, the P2P Device will become
+ * discoverable (go into Listen State) every @interval milliseconds for at
+ * least @period milliseconds.
+ */
+int p2p_ext_listen(struct p2p_data *p2p, unsigned int period,
+ unsigned int interval);
+
+/* Event notifications from upper layer management operations */
+
+/**
+ * p2p_wps_success_cb - Report successfully completed WPS provisioning
+ * @p2p: P2P module context from p2p_init()
+ * @mac_addr: Peer address
+ *
+ * This function is used to report successfully completed WPS provisioning
+ * during group formation in both GO/Registrar and client/Enrollee roles.
+ */
+void p2p_wps_success_cb(struct p2p_data *p2p, const u8 *mac_addr);
+
+/**
+ * p2p_group_formation_failed - Report failed WPS provisioning
+ * @p2p: P2P module context from p2p_init()
+ *
+ * This function is used to report failed group formation. This can happen
+ * either due to failed WPS provisioning or due to 15 second timeout during
+ * the provisioning phase.
+ */
+void p2p_group_formation_failed(struct p2p_data *p2p);
+
+/**
+ * p2p_get_provisioning_info - Get any stored provisioning info
+ * @p2p: P2P module context from p2p_init()
+ * @addr: Peer P2P Device Address
+ * Returns: WPS provisioning information (WPS config method) or 0 if no
+ * information is available
+ *
+ * This function is used to retrieve stored WPS provisioning info for the given
+ * peer.
+ */
+u16 p2p_get_provisioning_info(struct p2p_data *p2p, const u8 *addr);
+
+/**
+ * p2p_clear_provisioning_info - Clear any stored provisioning info
+ * @p2p: P2P module context from p2p_init()
+ * @iface_addr: Peer P2P Device Address
+ *
+ * This function is used to clear stored WPS provisioning info for the given
+ * peer.
+ */
+void p2p_clear_provisioning_info(struct p2p_data *p2p, const u8 *addr);
+
+
+/* Event notifications from lower layer driver operations */
+
+/**
+ * p2p_probe_req_rx - Report reception of a Probe Request frame
+ * @p2p: P2P module context from p2p_init()
+ * @addr: Source MAC address
+ * @dst: Destination MAC address if available or %NULL
+ * @bssid: BSSID if available or %NULL
+ * @ie: Information elements from the Probe Request frame body
+ * @ie_len: Length of ie buffer in octets
+ * Returns: 0 to indicate the frame was not processed or 1 if it was
+ */
+int p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
+ const u8 *bssid, const u8 *ie, size_t ie_len);
+
+/**
+ * p2p_rx_action - Report received Action frame
+ * @p2p: P2P module context from p2p_init()
+ * @da: Destination address of the received Action frame
+ * @sa: Source address of the received Action frame
+ * @bssid: Address 3 of the received Action frame
+ * @category: Category of the received Action frame
+ * @data: Action frame body after the Category field
+ * @len: Length of the data buffer in octets
+ * @freq: Frequency (in MHz) on which the frame was received
+ */
+void p2p_rx_action(struct p2p_data *p2p, const u8 *da, const u8 *sa,
+ const u8 *bssid, u8 category,
+ const u8 *data, size_t len, int freq);
+
+/**
+ * p2p_scan_res_handler - Indicate a P2P scan results
+ * @p2p: P2P module context from p2p_init()
+ * @bssid: BSSID of the scan result
+ * @freq: Frequency of the channel on which the device was found in MHz
+ * @level: Signal level (signal strength of the received Beacon/Probe Response
+ * frame)
+ * @ies: Pointer to IEs from the scan result
+ * @ies_len: Length of the ies buffer
+ * Returns: 0 to continue or 1 to stop scan result indication
+ *
+ * This function is called to indicate a scan result entry with P2P IE from a
+ * scan requested with struct p2p_config::p2p_scan(). This can be called during
+ * the actual scan process (i.e., whenever a new device is found) or as a
+ * sequence of calls after the full scan has been completed. The former option
+ * can result in optimized operations, but may not be supported by all
+ * driver/firmware designs. The ies buffer need to include at least the P2P IE,
+ * but it is recommended to include all IEs received from the device. The
+ * caller does not need to check that the IEs contain a P2P IE before calling
+ * this function since frames will be filtered internally if needed.
+ *
+ * This function will return 1 if it wants to stop scan result iteration (and
+ * scan in general if it is still in progress). This is used to allow faster
+ * start of a pending operation, e.g., to start a pending GO negotiation.
+ */
+int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq,
+ int level, const u8 *ies, size_t ies_len);
+
+/**
+ * p2p_scan_res_handled - Indicate end of scan results
+ * @p2p: P2P module context from p2p_init()
+ *
+ * This function is called to indicate that all P2P scan results from a scan
+ * have been reported with zero or more calls to p2p_scan_res_handler(). This
+ * function must be called as a response to successful
+ * struct p2p_config::p2p_scan() call if none of the p2p_scan_res_handler()
+ * calls stopped iteration.
+ */
+void p2p_scan_res_handled(struct p2p_data *p2p);
+
+enum p2p_send_action_result {
+ P2P_SEND_ACTION_SUCCESS /* Frame was send and acknowledged */,
+ P2P_SEND_ACTION_NO_ACK /* Frame was sent, but not acknowledged */,
+ P2P_SEND_ACTION_FAILED /* Frame was not sent due to a failure */
+};
+
+/**
+ * p2p_send_action_cb - Notify TX status of an Action frame
+ * @p2p: P2P module context from p2p_init()
+ * @freq: Channel frequency in MHz
+ * @dst: Destination MAC address (Address 1)
+ * @src: Source MAC address (Address 2)
+ * @bssid: BSSID (Address 3)
+ * @result: Result of the transmission attempt
+ *
+ * This function is used to indicate the result of an Action frame transmission
+ * that was requested with struct p2p_config::send_action() callback.
+ */
+void p2p_send_action_cb(struct p2p_data *p2p, unsigned int freq, const u8 *dst,
+ const u8 *src, const u8 *bssid,
+ enum p2p_send_action_result result);
+
+/**
+ * p2p_listen_cb - Indicate the start of a requested Listen state
+ * @p2p: P2P module context from p2p_init()
+ * @freq: Listen channel frequency in MHz
+ * @duration: Duration for the Listen state in milliseconds
+ *
+ * This function is used to indicate that a Listen state requested with
+ * struct p2p_config::start_listen() callback has started.
+ */
+void p2p_listen_cb(struct p2p_data *p2p, unsigned int freq,
+ unsigned int duration);
+
+/**
+ * p2p_listen_end - Indicate the end of a requested Listen state
+ * @p2p: P2P module context from p2p_init()
+ * @freq: Listen channel frequency in MHz
+ * Returns: 0 if no operations were started, 1 if an operation was started
+ *
+ * This function is used to indicate that a Listen state requested with
+ * struct p2p_config::start_listen() callback has ended.
+ */
+int p2p_listen_end(struct p2p_data *p2p, unsigned int freq);
+
+void p2p_deauth_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code,
+ const u8 *ie, size_t ie_len);
+
+void p2p_disassoc_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code,
+ const u8 *ie, size_t ie_len);
+
+
+/* Per-group P2P state for GO */
+
+struct p2p_group;
+
+/**
+ * struct p2p_group_config - P2P group configuration
+ *
+ * This configuration is provided to the P2P module during initialization of
+ * the per-group information with p2p_group_init().
+ */
+struct p2p_group_config {
+ /**
+ * persistent_group - Whether the group is persistent
+ * 0 = not a persistent group
+ * 1 = persistent group without persistent reconnect
+ * 2 = persistent group with persistent reconnect
+ */
+ int persistent_group;
+
+ /**
+ * interface_addr - P2P Interface Address of the group
+ */
+ u8 interface_addr[ETH_ALEN];
+
+ /**
+ * max_clients - Maximum number of clients in the group
+ */
+ unsigned int max_clients;
+
+ /**
+ * cb_ctx - Context to use with callback functions
+ */
+ void *cb_ctx;
+
+ /**
+ * ie_update - Notification of IE update
+ * @ctx: Callback context from cb_ctx
+ * @beacon_ies: P2P IE for Beacon frames or %NULL if no change
+ * @proberesp_ies: P2P Ie for Probe Response frames
+ *
+ * P2P module uses this callback function to notify whenever the P2P IE
+ * in Beacon or Probe Response frames should be updated based on group
+ * events.
+ *
+ * The callee is responsible for freeing the returned buffer(s) with
+ * wpabuf_free().
+ */
+ void (*ie_update)(void *ctx, struct wpabuf *beacon_ies,
+ struct wpabuf *proberesp_ies);
+
+ /**
+ * idle_update - Notification of changes in group idle state
+ * @ctx: Callback context from cb_ctx
+ * @idle: Whether the group is idle (no associated stations)
+ */
+ void (*idle_update)(void *ctx, int idle);
+};
+
+/**
+ * p2p_group_init - Initialize P2P group
+ * @p2p: P2P module context from p2p_init()
+ * @config: P2P group configuration (will be freed by p2p_group_deinit())
+ * Returns: Pointer to private data or %NULL on failure
+ *
+ * This function is used to initialize per-group P2P module context. Currently,
+ * this is only used to manage GO functionality and P2P clients do not need to
+ * create an instance of this per-group information.
+ */
+struct p2p_group * p2p_group_init(struct p2p_data *p2p,
+ struct p2p_group_config *config);
+
+/**
+ * p2p_group_deinit - Deinitialize P2P group
+ * @group: P2P group context from p2p_group_init()
+ */
+void p2p_group_deinit(struct p2p_group *group);
+
+/**
+ * p2p_group_notif_assoc - Notification of P2P client association with GO
+ * @group: P2P group context from p2p_group_init()
+ * @addr: Interface address of the P2P client
+ * @ie: IEs from the (Re)association Request frame
+ * @len: Length of the ie buffer in octets
+ * Returns: 0 on success, -1 on failure
+ */
+int p2p_group_notif_assoc(struct p2p_group *group, const u8 *addr,
+ const u8 *ie, size_t len);
+
+/**
+ * p2p_group_assoc_resp_ie - Build P2P IE for (re)association response
+ * @group: P2P group context from p2p_group_init()
+ * @status: Status value (P2P_SC_SUCCESS if association succeeded)
+ * Returns: P2P IE for (Re)association Response or %NULL on failure
+ *
+ * The caller is responsible for freeing the returned buffer with
+ * wpabuf_free().
+ */
+struct wpabuf * p2p_group_assoc_resp_ie(struct p2p_group *group, u8 status);
+
+/**
+ * p2p_group_notif_disassoc - Notification of P2P client disassociation from GO
+ * @group: P2P group context from p2p_group_init()
+ * @addr: Interface address of the P2P client
+ */
+void p2p_group_notif_disassoc(struct p2p_group *group, const u8 *addr);
+
+/**
+ * p2p_group_notif_formation_done - Notification of completed group formation
+ * @group: P2P group context from p2p_group_init()
+ */
+void p2p_group_notif_formation_done(struct p2p_group *group);
+
+/**
+ * p2p_group_notif_noa - Notification of NoA change
+ * @group: P2P group context from p2p_group_init()
+ * @noa: Notice of Absence attribute payload, %NULL if none
+ * @noa_len: Length of noa buffer in octets
+ * Returns: 0 on success, -1 on failure
+ *
+ * Notify the P2P group management about a new NoA contents. This will be
+ * inserted into the P2P IEs in Beacon and Probe Response frames with rest of
+ * the group information.
+ */
+int p2p_group_notif_noa(struct p2p_group *group, const u8 *noa,
+ size_t noa_len);
+
+/**
+ * p2p_group_match_dev_type - Match device types in group with requested type
+ * @group: P2P group context from p2p_group_init()
+ * @wps: WPS TLVs from Probe Request frame (concatenated WPS IEs)
+ * Returns: 1 on match, 0 on mismatch
+ *
+ * This function can be used to match the Requested Device Type attribute in
+ * WPS IE with the device types of a group member for deciding whether a GO
+ * should reply to a Probe Request frame. Match will be reported if the WPS IE
+ * is not requested any specific device type.
+ */
+int p2p_group_match_dev_type(struct p2p_group *group, struct wpabuf *wps);
+
+/**
+ * p2p_group_match_dev_id - Match P2P Device Address in group with requested device id
+ */
+int p2p_group_match_dev_id(struct p2p_group *group, struct wpabuf *p2p);
+
+/**
+ * p2p_group_go_discover - Send GO Discoverability Request to a group client
+ * @group: P2P group context from p2p_group_init()
+ * Returns: 0 on success (frame scheduled); -1 if client was not found
+ */
+int p2p_group_go_discover(struct p2p_group *group, const u8 *dev_id,
+ const u8 *searching_dev, int rx_freq);
+
+
+/* Generic helper functions */
+
+/**
+ * p2p_ie_text - Build text format description of P2P IE
+ * @p2p_ie: P2P IE
+ * @buf: Buffer for returning text
+ * @end: Pointer to the end of the buf area
+ * Returns: Number of octets written to the buffer or -1 on failure
+ *
+ * This function can be used to parse P2P IE contents into text format
+ * field=value lines.
+ */
+int p2p_ie_text(struct wpabuf *p2p_ie, char *buf, char *end);
+
+/**
+ * p2p_scan_result_text - Build text format description of P2P IE
+ * @ies: Information elements from scan results
+ * @ies_len: ies buffer length in octets
+ * @buf: Buffer for returning text
+ * @end: Pointer to the end of the buf area
+ * Returns: Number of octets written to the buffer or -1 on failure
+ *
+ * This function can be used to parse P2P IE contents into text format
+ * field=value lines.
+ */
+int p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf, char *end);
+
+/**
+ * p2p_parse_dev_addr - Parse P2P Device Address from P2P IE(s)
+ * @ies: Information elements from scan results
+ * @ies_len: ies buffer length in octets
+ * @dev_addr: Buffer for returning P2P Device Address
+ * Returns: 0 on success or -1 if P2P Device Address could not be parsed
+ */
+int p2p_parse_dev_addr(const u8 *ies, size_t ies_len, u8 *dev_addr);
+
+/**
+ * p2p_assoc_req_ie - Build P2P IE for (Re)Association Request frame
+ * @p2p: P2P module context from p2p_init()
+ * @bssid: BSSID
+ * @buf: Buffer for writing the P2P IE
+ * @len: Maximum buf length in octets
+ * @p2p_group: Whether this is for association with a P2P GO
+ * @p2p_ie: Reassembled P2P IE data from scan results or %NULL if none
+ * Returns: Number of octets written into buf or -1 on failure
+ */
+int p2p_assoc_req_ie(struct p2p_data *p2p, const u8 *bssid, u8 *buf,
+ size_t len, int p2p_group, struct wpabuf *p2p_ie);
+
+/**
+ * p2p_scan_ie - Build P2P IE for Probe Request
+ * @p2p: P2P module context from p2p_init()
+ * @ies: Buffer for writing P2P IE
+ * @dev_id: Device ID to search for or %NULL for any
+ */
+void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id);
+
+/**
+ * p2p_scan_ie_buf_len - Get maximum buffer length needed for p2p_scan_ie
+ * @p2p: P2P module context from p2p_init()
+ * Returns: Number of octets that p2p_scan_ie() may add to the buffer
+ */
+size_t p2p_scan_ie_buf_len(struct p2p_data *p2p);
+
+/**
+ * p2p_go_params - Generate random P2P group parameters
+ * @p2p: P2P module context from p2p_init()
+ * @params: Buffer for parameters
+ * Returns: 0 on success, -1 on failure
+ */
+int p2p_go_params(struct p2p_data *p2p, struct p2p_go_neg_results *params);
+
+/**
+ * p2p_get_group_capab - Get Group Capability from P2P IE data
+ * @p2p_ie: P2P IE(s) contents
+ * Returns: Group Capability
+ */
+u8 p2p_get_group_capab(const struct wpabuf *p2p_ie);
+
+/**
+ * p2p_get_cross_connect_disallowed - Does WLAN AP disallows cross connection
+ * @p2p_ie: P2P IE(s) contents
+ * Returns: 0 if cross connection is allow, 1 if not
+ */
+int p2p_get_cross_connect_disallowed(const struct wpabuf *p2p_ie);
+
+/**
+ * p2p_get_go_dev_addr - Get P2P Device Address from P2P IE data
+ * @p2p_ie: P2P IE(s) contents
+ * Returns: Pointer to P2P Device Address or %NULL if not included
+ */
+const u8 * p2p_get_go_dev_addr(const struct wpabuf *p2p_ie);
+
+/**
+ * p2p_get_peer_info - Get P2P peer information
+ * @p2p: P2P module context from p2p_init()
+ * @addr: P2P Device Address of the peer or %NULL to indicate the first peer
+ * @next: Whether to select the peer entry following the one indicated by addr
+ * Returns: Pointer to peer info or %NULL if not found
+ */
+const struct p2p_peer_info * p2p_get_peer_info(struct p2p_data *p2p,
+ const u8 *addr, int next);
+
+/**
+ * p2p_get_peer_info_txt - Get internal P2P peer information in text format
+ * @info: Pointer to P2P peer info from p2p_get_peer_info()
+ * @buf: Buffer for returning text
+ * @buflen: Maximum buffer length
+ * Returns: Number of octets written to the buffer or -1 on failure
+ *
+ * Note: This information is internal to the P2P module and subject to change.
+ * As such, this should not really be used by external programs for purposes
+ * other than debugging.
+ */
+int p2p_get_peer_info_txt(const struct p2p_peer_info *info,
+ char *buf, size_t buflen);
+
+/**
+ * p2p_peer_known - Check whether P2P peer is known
+ * @p2p: P2P module context from p2p_init()
+ * @addr: P2P Device Address of the peer
+ * Returns: 1 if the specified device is in the P2P peer table or 0 if not
+ */
+int p2p_peer_known(struct p2p_data *p2p, const u8 *addr);
+
+/**
+ * p2p_set_client_discoverability - Set client discoverability capability
+ * @p2p: P2P module context from p2p_init()
+ * @enabled: Whether client discoverability will be enabled
+ *
+ * This function can be used to disable (and re-enable) client discoverability.
+ * This capability is enabled by default and should not be disabled in normal
+ * use cases, i.e., this is mainly for testing purposes.
+ */
+void p2p_set_client_discoverability(struct p2p_data *p2p, int enabled);
+
+/**
+ * p2p_set_managed_oper - Set managed P2P Device operations capability
+ * @p2p: P2P module context from p2p_init()
+ * @enabled: Whether managed P2P Device operations will be enabled
+ */
+void p2p_set_managed_oper(struct p2p_data *p2p, int enabled);
+
+int p2p_set_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel);
+
+int p2p_set_ssid_postfix(struct p2p_data *p2p, const u8 *postfix, size_t len);
+
+int p2p_get_interface_addr(struct p2p_data *p2p, const u8 *dev_addr,
+ u8 *iface_addr);
+int p2p_get_dev_addr(struct p2p_data *p2p, const u8 *iface_addr,
+ u8 *dev_addr);
+
+void p2p_set_peer_filter(struct p2p_data *p2p, const u8 *addr);
+
+/**
+ * p2p_set_cross_connect - Set cross connection capability
+ * @p2p: P2P module context from p2p_init()
+ * @enabled: Whether cross connection will be enabled
+ */
+void p2p_set_cross_connect(struct p2p_data *p2p, int enabled);
+
+int p2p_get_oper_freq(struct p2p_data *p2p, const u8 *iface_addr);
+
+/**
+ * p2p_set_intra_bss_dist - Set intra BSS distribution
+ * @p2p: P2P module context from p2p_init()
+ * @enabled: Whether intra BSS distribution will be enabled
+ */
+void p2p_set_intra_bss_dist(struct p2p_data *p2p, int enabled);
+
+/**
+ * p2p_supported_freq - Check whether channel is supported for P2P
+ * @p2p: P2P module context from p2p_init()
+ * @freq: Channel frequency in MHz
+ * Returns: 0 if channel not usable for P2P, 1 if usable for P2P
+ */
+int p2p_supported_freq(struct p2p_data *p2p, unsigned int freq);
+
+void p2p_update_channel_list(struct p2p_data *p2p, struct p2p_channels *chan);
+
+/**
+ * p2p_set_best_channels - Update best channel information
+ * @p2p: P2P module context from p2p_init()
+ * @freq_24: Frequency (MHz) of best channel in 2.4 GHz band
+ * @freq_5: Frequency (MHz) of best channel in 5 GHz band
+ * @freq_overall: Frequency (MHz) of best channel overall
+ */
+void p2p_set_best_channels(struct p2p_data *p2p, int freq_24, int freq_5,
+ int freq_overall);
+
+const u8 * p2p_get_go_neg_peer(struct p2p_data *p2p);
+
+/**
+ * p2p_get_group_num_members - Get number of members in group
+ * @group: P2P group context from p2p_group_init()
+ * Returns: Number of members in the group
+ */
+unsigned int p2p_get_group_num_members(struct p2p_group *group);
+
+/**
+ * p2p_iterate_group_members - Iterate group members
+ * @group: P2P group context from p2p_group_init()
+ * @next: iteration pointer, must be a pointer to a void * that is set to %NULL
+ * on the first call and not modified later
+ * Returns: A P2P Interface Address for each call and %NULL for no more members
+ */
+const u8 * p2p_iterate_group_members(struct p2p_group *group, void **next);
+
+/**
+ * p2p_group_get_dev_addr - Get a P2P Device Address of a client in a group
+ * @group: P2P group context from p2p_group_init()
+ * @addr: P2P Interface Address of the client
+ * Returns: P2P Device Address of the client if found or %NULL if no match
+ * found
+ */
+const u8 * p2p_group_get_dev_addr(struct p2p_group *group, const u8 *addr);
+
+/**
+ * p2p_group_is_client_connected - Check whether a specific client is connected
+ * @group: P2P group context from p2p_group_init()
+ * @addr: P2P Device Address of the client
+ * Returns: 1 if client is connected or 0 if not
+ */
+int p2p_group_is_client_connected(struct p2p_group *group, const u8 *dev_addr);
+
+/**
+ * p2p_get_peer_found - Get P2P peer info structure of a found peer
+ * @p2p: P2P module context from p2p_init()
+ * @addr: P2P Device Address of the peer or %NULL to indicate the first peer
+ * @next: Whether to select the peer entry following the one indicated by addr
+ * Returns: The first P2P peer info available or %NULL if no such peer exists
+ */
+const struct p2p_peer_info *
+p2p_get_peer_found(struct p2p_data *p2p, const u8 *addr, int next);
+
+/**
+ * p2p_remove_wps_vendor_extensions - Remove WPS vendor extensions
+ * @p2p: P2P module context from p2p_init()
+ */
+void p2p_remove_wps_vendor_extensions(struct p2p_data *p2p);
+
+/**
+ * p2p_add_wps_vendor_extension - Add a WPS vendor extension
+ * @p2p: P2P module context from p2p_init()
+ * @vendor_ext: The vendor extensions to add
+ * Returns: 0 on success, -1 on failure
+ *
+ * The wpabuf structures in the array are owned by the P2P
+ * module after this call.
+ */
+int p2p_add_wps_vendor_extension(struct p2p_data *p2p,
+ const struct wpabuf *vendor_ext);
+
+/**
+ * p2p_set_oper_channel - Set the P2P operating channel
+ * @p2p: P2P module context from p2p_init()
+ * @op_reg_class: Operating regulatory class to set
+ * @op_channel: operating channel to set
+ * @cfg_op_channel : Whether op_channel is hardcoded in configuration
+ * Returns: 0 on success, -1 on failure
+ */
+int p2p_set_oper_channel(struct p2p_data *p2p, u8 op_reg_class, u8 op_channel,
+ int cfg_op_channel);
+
+/**
+ * p2p_set_pref_chan - Set P2P preferred channel list
+ * @p2p: P2P module context from p2p_init()
+ * @num_pref_chan: Number of entries in pref_chan list
+ * @pref_chan: Preferred channels or %NULL to remove preferences
+ * Returns: 0 on success, -1 on failure
+ */
+int p2p_set_pref_chan(struct p2p_data *p2p, unsigned int num_pref_chan,
+ const struct p2p_channel *pref_chan);
+
+/**
+ * p2p_in_progress - Check whether a P2P operation is progress
+ * @p2p: P2P module context from p2p_init()
+ * Returns: 0 if P2P module is idle or 1 if an operation is in progress
+ */
+int p2p_in_progress(struct p2p_data *p2p);
+
+/**
+ * p2p_other_scan_completed - Notify completion of non-P2P scan
+ * @p2p: P2P module context from p2p_init()
+ * Returns: 0 if P2P module is idle or 1 if an operation was started
+ */
+int p2p_other_scan_completed(struct p2p_data *p2p);
+
+const char * p2p_wps_method_text(enum p2p_wps_method method);
+
+#endif /* P2P_H */
--- /dev/null
+/*
+ * P2P - IE builder
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.h"
+#include "wps/wps_i.h"
+#include "p2p_i.h"
+
+
+void p2p_buf_add_action_hdr(struct wpabuf *buf, u8 subtype, u8 dialog_token)
+{
+ wpabuf_put_u8(buf, WLAN_ACTION_VENDOR_SPECIFIC);
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, P2P_OUI_TYPE);
+
+ wpabuf_put_u8(buf, subtype); /* OUI Subtype */
+ wpabuf_put_u8(buf, dialog_token);
+ wpa_printf(MSG_DEBUG, "P2P: * Dialog Token: %d", dialog_token);
+}
+
+
+void p2p_buf_add_public_action_hdr(struct wpabuf *buf, u8 subtype,
+ u8 dialog_token)
+{
+ wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC);
+ wpabuf_put_u8(buf, WLAN_PA_VENDOR_SPECIFIC);
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, P2P_OUI_TYPE);
+
+ wpabuf_put_u8(buf, subtype); /* OUI Subtype */
+ wpabuf_put_u8(buf, dialog_token);
+ wpa_printf(MSG_DEBUG, "P2P: * Dialog Token: %d", dialog_token);
+}
+
+
+u8 * p2p_buf_add_ie_hdr(struct wpabuf *buf)
+{
+ u8 *len;
+
+ /* P2P IE header */
+ wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+ len = wpabuf_put(buf, 1); /* IE length to be filled */
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, P2P_OUI_TYPE);
+ wpa_printf(MSG_DEBUG, "P2P: * P2P IE header");
+ return len;
+}
+
+
+void p2p_buf_update_ie_hdr(struct wpabuf *buf, u8 *len)
+{
+ /* Update P2P IE Length */
+ *len = (u8 *) wpabuf_put(buf, 0) - len - 1;
+}
+
+
+void p2p_buf_add_capability(struct wpabuf *buf, u8 dev_capab, u8 group_capab)
+{
+ /* P2P Capability */
+ wpabuf_put_u8(buf, P2P_ATTR_CAPABILITY);
+ wpabuf_put_le16(buf, 2);
+ wpabuf_put_u8(buf, dev_capab); /* Device Capabilities */
+ wpabuf_put_u8(buf, group_capab); /* Group Capabilities */
+ wpa_printf(MSG_DEBUG, "P2P: * Capability dev=%02x group=%02x",
+ dev_capab, group_capab);
+}
+
+
+void p2p_buf_add_go_intent(struct wpabuf *buf, u8 go_intent)
+{
+ /* Group Owner Intent */
+ wpabuf_put_u8(buf, P2P_ATTR_GROUP_OWNER_INTENT);
+ wpabuf_put_le16(buf, 1);
+ wpabuf_put_u8(buf, go_intent);
+ wpa_printf(MSG_DEBUG, "P2P: * GO Intent: Intent %u Tie breaker %u",
+ go_intent >> 1, go_intent & 0x01);
+}
+
+
+void p2p_buf_add_listen_channel(struct wpabuf *buf, const char *country,
+ u8 reg_class, u8 channel)
+{
+ /* Listen Channel */
+ wpabuf_put_u8(buf, P2P_ATTR_LISTEN_CHANNEL);
+ wpabuf_put_le16(buf, 5);
+ wpabuf_put_data(buf, country, 3);
+ wpabuf_put_u8(buf, reg_class); /* Regulatory Class */
+ wpabuf_put_u8(buf, channel); /* Channel Number */
+ wpa_printf(MSG_DEBUG, "P2P: * Listen Channel: Regulatory Class %u "
+ "Channel %u", reg_class, channel);
+}
+
+
+void p2p_buf_add_operating_channel(struct wpabuf *buf, const char *country,
+ u8 reg_class, u8 channel)
+{
+ /* Operating Channel */
+ wpabuf_put_u8(buf, P2P_ATTR_OPERATING_CHANNEL);
+ wpabuf_put_le16(buf, 5);
+ wpabuf_put_data(buf, country, 3);
+ wpabuf_put_u8(buf, reg_class); /* Regulatory Class */
+ wpabuf_put_u8(buf, channel); /* Channel Number */
+ wpa_printf(MSG_DEBUG, "P2P: * Operating Channel: Regulatory Class %u "
+ "Channel %u", reg_class, channel);
+}
+
+
+void p2p_buf_add_channel_list(struct wpabuf *buf, const char *country,
+ struct p2p_channels *chan)
+{
+ u8 *len;
+ size_t i;
+
+ /* Channel List */
+ wpabuf_put_u8(buf, P2P_ATTR_CHANNEL_LIST);
+ len = wpabuf_put(buf, 2); /* IE length to be filled */
+ wpabuf_put_data(buf, country, 3); /* Country String */
+
+ for (i = 0; i < chan->reg_classes; i++) {
+ struct p2p_reg_class *c = &chan->reg_class[i];
+ wpabuf_put_u8(buf, c->reg_class);
+ wpabuf_put_u8(buf, c->channels);
+ wpabuf_put_data(buf, c->channel, c->channels);
+ }
+
+ /* Update attribute length */
+ WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2);
+ wpa_printf(MSG_DEBUG, "P2P: * Channel List");
+}
+
+
+void p2p_buf_add_status(struct wpabuf *buf, u8 status)
+{
+ /* Status */
+ wpabuf_put_u8(buf, P2P_ATTR_STATUS);
+ wpabuf_put_le16(buf, 1);
+ wpabuf_put_u8(buf, status);
+ wpa_printf(MSG_DEBUG, "P2P: * Status: %d", status);
+}
+
+
+void p2p_buf_add_device_info(struct wpabuf *buf, struct p2p_data *p2p,
+ struct p2p_device *peer)
+{
+ u8 *len;
+ u16 methods;
+ size_t nlen, i;
+
+ /* P2P Device Info */
+ wpabuf_put_u8(buf, P2P_ATTR_DEVICE_INFO);
+ len = wpabuf_put(buf, 2); /* IE length to be filled */
+
+ /* P2P Device address */
+ wpabuf_put_data(buf, p2p->cfg->dev_addr, ETH_ALEN);
+
+ /* Config Methods */
+ methods = 0;
+ if (peer && peer->wps_method != WPS_NOT_READY) {
+ if (peer->wps_method == WPS_PBC)
+ methods |= WPS_CONFIG_PUSHBUTTON;
+ else if (peer->wps_method == WPS_PIN_DISPLAY ||
+ peer->wps_method == WPS_PIN_KEYPAD)
+ methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
+ } else if (p2p->cfg->config_methods) {
+ methods |= p2p->cfg->config_methods &
+ (WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_DISPLAY |
+ WPS_CONFIG_KEYPAD);
+ } else {
+ methods |= WPS_CONFIG_PUSHBUTTON;
+ methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
+ }
+ wpabuf_put_be16(buf, methods);
+
+ /* Primary Device Type */
+ wpabuf_put_data(buf, p2p->cfg->pri_dev_type,
+ sizeof(p2p->cfg->pri_dev_type));
+
+ /* Number of Secondary Device Types */
+ wpabuf_put_u8(buf, p2p->cfg->num_sec_dev_types);
+
+ /* Secondary Device Type List */
+ for (i = 0; i < p2p->cfg->num_sec_dev_types; i++)
+ wpabuf_put_data(buf, p2p->cfg->sec_dev_type[i],
+ WPS_DEV_TYPE_LEN);
+
+ /* Device Name */
+ nlen = p2p->cfg->dev_name ? os_strlen(p2p->cfg->dev_name) : 0;
+ wpabuf_put_be16(buf, ATTR_DEV_NAME);
+ wpabuf_put_be16(buf, nlen);
+ wpabuf_put_data(buf, p2p->cfg->dev_name, nlen);
+
+ /* Update attribute length */
+ WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2);
+ wpa_printf(MSG_DEBUG, "P2P: * Device Info");
+}
+
+
+void p2p_buf_add_device_id(struct wpabuf *buf, const u8 *dev_addr)
+{
+ /* P2P Device ID */
+ wpabuf_put_u8(buf, P2P_ATTR_DEVICE_ID);
+ wpabuf_put_le16(buf, ETH_ALEN);
+ wpabuf_put_data(buf, dev_addr, ETH_ALEN);
+ wpa_printf(MSG_DEBUG, "P2P: * Device ID: " MACSTR, MAC2STR(dev_addr));
+}
+
+
+void p2p_buf_add_config_timeout(struct wpabuf *buf, u8 go_timeout,
+ u8 client_timeout)
+{
+ /* Configuration Timeout */
+ wpabuf_put_u8(buf, P2P_ATTR_CONFIGURATION_TIMEOUT);
+ wpabuf_put_le16(buf, 2);
+ wpabuf_put_u8(buf, go_timeout);
+ wpabuf_put_u8(buf, client_timeout);
+ wpa_printf(MSG_DEBUG, "P2P: * Configuration Timeout: GO %d (*10ms) "
+ "client %d (*10ms)", go_timeout, client_timeout);
+}
+
+
+void p2p_buf_add_intended_addr(struct wpabuf *buf, const u8 *interface_addr)
+{
+ /* Intended P2P Interface Address */
+ wpabuf_put_u8(buf, P2P_ATTR_INTENDED_INTERFACE_ADDR);
+ wpabuf_put_le16(buf, ETH_ALEN);
+ wpabuf_put_data(buf, interface_addr, ETH_ALEN);
+ wpa_printf(MSG_DEBUG, "P2P: * Intended P2P Interface Address " MACSTR,
+ MAC2STR(interface_addr));
+}
+
+
+void p2p_buf_add_group_bssid(struct wpabuf *buf, const u8 *bssid)
+{
+ /* P2P Group BSSID */
+ wpabuf_put_u8(buf, P2P_ATTR_GROUP_BSSID);
+ wpabuf_put_le16(buf, ETH_ALEN);
+ wpabuf_put_data(buf, bssid, ETH_ALEN);
+ wpa_printf(MSG_DEBUG, "P2P: * P2P Group BSSID " MACSTR,
+ MAC2STR(bssid));
+}
+
+
+void p2p_buf_add_group_id(struct wpabuf *buf, const u8 *dev_addr,
+ const u8 *ssid, size_t ssid_len)
+{
+ /* P2P Group ID */
+ wpabuf_put_u8(buf, P2P_ATTR_GROUP_ID);
+ wpabuf_put_le16(buf, ETH_ALEN + ssid_len);
+ wpabuf_put_data(buf, dev_addr, ETH_ALEN);
+ wpabuf_put_data(buf, ssid, ssid_len);
+ wpa_printf(MSG_DEBUG, "P2P: * P2P Group ID " MACSTR,
+ MAC2STR(dev_addr));
+}
+
+
+void p2p_buf_add_invitation_flags(struct wpabuf *buf, u8 flags)
+{
+ /* Invitation Flags */
+ wpabuf_put_u8(buf, P2P_ATTR_INVITATION_FLAGS);
+ wpabuf_put_le16(buf, 1);
+ wpabuf_put_u8(buf, flags);
+ wpa_printf(MSG_DEBUG, "P2P: * Invitation Flags: bitmap 0x%x", flags);
+}
+
+
+static void p2p_buf_add_noa_desc(struct wpabuf *buf, struct p2p_noa_desc *desc)
+{
+ if (desc == NULL)
+ return;
+
+ wpabuf_put_u8(buf, desc->count_type);
+ wpabuf_put_le32(buf, desc->duration);
+ wpabuf_put_le32(buf, desc->interval);
+ wpabuf_put_le32(buf, desc->start_time);
+}
+
+
+void p2p_buf_add_noa(struct wpabuf *buf, u8 noa_index, u8 opp_ps, u8 ctwindow,
+ struct p2p_noa_desc *desc1, struct p2p_noa_desc *desc2)
+{
+ /* Notice of Absence */
+ wpabuf_put_u8(buf, P2P_ATTR_NOTICE_OF_ABSENCE);
+ wpabuf_put_le16(buf, 2 + (desc1 ? 13 : 0) + (desc2 ? 13 : 0));
+ wpabuf_put_u8(buf, noa_index);
+ wpabuf_put_u8(buf, (opp_ps ? 0x80 : 0) | (ctwindow & 0x7f));
+ p2p_buf_add_noa_desc(buf, desc1);
+ p2p_buf_add_noa_desc(buf, desc2);
+ wpa_printf(MSG_DEBUG, "P2P: * Notice of Absence");
+}
+
+
+void p2p_buf_add_ext_listen_timing(struct wpabuf *buf, u16 period,
+ u16 interval)
+{
+ /* Extended Listen Timing */
+ wpabuf_put_u8(buf, P2P_ATTR_EXT_LISTEN_TIMING);
+ wpabuf_put_le16(buf, 4);
+ wpabuf_put_le16(buf, period);
+ wpabuf_put_le16(buf, interval);
+ wpa_printf(MSG_DEBUG, "P2P: * Extended Listen Timing (period %u msec "
+ "interval %u msec)", period, interval);
+}
+
+
+void p2p_buf_add_p2p_interface(struct wpabuf *buf, struct p2p_data *p2p)
+{
+ /* P2P Interface */
+ wpabuf_put_u8(buf, P2P_ATTR_INTERFACE);
+ wpabuf_put_le16(buf, ETH_ALEN + 1 + ETH_ALEN);
+ /* P2P Device address */
+ wpabuf_put_data(buf, p2p->cfg->dev_addr, ETH_ALEN);
+ /*
+ * FIX: Fetch interface address list from driver. Do not include
+ * the P2P Device address if it is never used as interface address.
+ */
+ /* P2P Interface Address Count */
+ wpabuf_put_u8(buf, 1);
+ wpabuf_put_data(buf, p2p->cfg->dev_addr, ETH_ALEN);
+}
+
+
+static void p2p_add_wps_string(struct wpabuf *buf, enum wps_attribute attr,
+ const char *val)
+{
+ size_t len;
+
+ wpabuf_put_be16(buf, attr);
+ len = val ? os_strlen(val) : 0;
+#ifndef CONFIG_WPS_STRICT
+ if (len == 0) {
+ /*
+ * Some deployed WPS implementations fail to parse zeor-length
+ * attributes. As a workaround, send a space character if the
+ * device attribute string is empty.
+ */
+ wpabuf_put_be16(buf, 1);
+ wpabuf_put_u8(buf, ' ');
+ return;
+ }
+#endif /* CONFIG_WPS_STRICT */
+ wpabuf_put_be16(buf, len);
+ if (val)
+ wpabuf_put_data(buf, val, len);
+}
+
+
+void p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, u16 pw_id,
+ int all_attr)
+{
+ u8 *len;
+ int i;
+
+ wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+ len = wpabuf_put(buf, 1);
+ wpabuf_put_be32(buf, WPS_DEV_OUI_WFA);
+
+ wps_build_version(buf);
+
+ if (all_attr) {
+ wpabuf_put_be16(buf, ATTR_WPS_STATE);
+ wpabuf_put_be16(buf, 1);
+ wpabuf_put_u8(buf, WPS_STATE_NOT_CONFIGURED);
+ }
+
+ /* Device Password ID */
+ wpabuf_put_be16(buf, ATTR_DEV_PASSWORD_ID);
+ wpabuf_put_be16(buf, 2);
+ wpa_printf(MSG_DEBUG, "P2P: WPS IE Device Password ID: %d", pw_id);
+ wpabuf_put_be16(buf, pw_id);
+
+ if (all_attr) {
+ wpabuf_put_be16(buf, ATTR_RESPONSE_TYPE);
+ wpabuf_put_be16(buf, 1);
+ wpabuf_put_u8(buf, WPS_RESP_ENROLLEE_INFO);
+
+ wps_build_uuid_e(buf, p2p->cfg->uuid);
+ p2p_add_wps_string(buf, ATTR_MANUFACTURER,
+ p2p->cfg->manufacturer);
+ p2p_add_wps_string(buf, ATTR_MODEL_NAME, p2p->cfg->model_name);
+ p2p_add_wps_string(buf, ATTR_MODEL_NUMBER,
+ p2p->cfg->model_number);
+ p2p_add_wps_string(buf, ATTR_SERIAL_NUMBER,
+ p2p->cfg->serial_number);
+
+ wpabuf_put_be16(buf, ATTR_PRIMARY_DEV_TYPE);
+ wpabuf_put_be16(buf, WPS_DEV_TYPE_LEN);
+ wpabuf_put_data(buf, p2p->cfg->pri_dev_type, WPS_DEV_TYPE_LEN);
+
+ p2p_add_wps_string(buf, ATTR_DEV_NAME, p2p->cfg->dev_name);
+
+ wpabuf_put_be16(buf, ATTR_CONFIG_METHODS);
+ wpabuf_put_be16(buf, 2);
+ wpabuf_put_be16(buf, p2p->cfg->config_methods);
+ }
+
+ wps_build_wfa_ext(buf, 0, NULL, 0);
+
+ if (all_attr && p2p->cfg->num_sec_dev_types) {
+ wpabuf_put_be16(buf, ATTR_SECONDARY_DEV_TYPE_LIST);
+ wpabuf_put_be16(buf, WPS_DEV_TYPE_LEN *
+ p2p->cfg->num_sec_dev_types);
+ wpabuf_put_data(buf, p2p->cfg->sec_dev_type,
+ WPS_DEV_TYPE_LEN *
+ p2p->cfg->num_sec_dev_types);
+ }
+
+ /* Add the WPS vendor extensions */
+ for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
+ if (p2p->wps_vendor_ext[i] == NULL)
+ break;
+ if (wpabuf_tailroom(buf) <
+ 4 + wpabuf_len(p2p->wps_vendor_ext[i]))
+ continue;
+ wpabuf_put_be16(buf, ATTR_VENDOR_EXT);
+ wpabuf_put_be16(buf, wpabuf_len(p2p->wps_vendor_ext[i]));
+ wpabuf_put_buf(buf, p2p->wps_vendor_ext[i]);
+ }
+
+ p2p_buf_update_ie_hdr(buf, len);
+}
--- /dev/null
+/*
+ * Wi-Fi Direct - P2P Device Discoverability procedure
+ * Copyright (c) 2010, Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.h"
+#include "p2p_i.h"
+#include "p2p.h"
+
+
+static struct wpabuf * p2p_build_dev_disc_req(struct p2p_data *p2p,
+ struct p2p_device *go,
+ const u8 *dev_id)
+{
+ struct wpabuf *buf;
+ u8 *len;
+
+ buf = wpabuf_alloc(100);
+ if (buf == NULL)
+ return NULL;
+
+ go->dialog_token++;
+ if (go->dialog_token == 0)
+ go->dialog_token = 1;
+ p2p_buf_add_public_action_hdr(buf, P2P_DEV_DISC_REQ, go->dialog_token);
+
+ len = p2p_buf_add_ie_hdr(buf);
+ p2p_buf_add_device_id(buf, dev_id);
+ p2p_buf_add_group_id(buf, go->info.p2p_device_addr, go->oper_ssid,
+ go->oper_ssid_len);
+ p2p_buf_update_ie_hdr(buf, len);
+
+ return buf;
+}
+
+
+void p2p_dev_disc_req_cb(struct p2p_data *p2p, int success)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Device Discoverability Request TX callback: success=%d",
+ success);
+
+ if (!success) {
+ /*
+ * Use P2P find, if needed, to find the other device or to
+ * retry device discoverability.
+ */
+ p2p_set_state(p2p, P2P_CONNECT);
+ p2p_set_timeout(p2p, 0, 100000);
+ return;
+ }
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: GO acknowledged Device Discoverability Request - wait "
+ "for response");
+ /*
+ * TODO: is the remain-on-channel from Action frame TX long enough for
+ * most cases or should we try to increase its duration and/or start
+ * another remain-on-channel if needed once the previous one expires?
+ */
+}
+
+
+int p2p_send_dev_disc_req(struct p2p_data *p2p, struct p2p_device *dev)
+{
+ struct p2p_device *go;
+ struct wpabuf *req;
+
+ go = p2p_get_device(p2p, dev->member_in_go_dev);
+ if (go == NULL || dev->oper_freq <= 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Could not find peer entry for GO and frequency "
+ "to send Device Discoverability Request");
+ return -1;
+ }
+
+ req = p2p_build_dev_disc_req(p2p, go, dev->info.p2p_device_addr);
+ if (req == NULL)
+ return -1;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Sending Device Discoverability Request to GO " MACSTR
+ " for client " MACSTR,
+ MAC2STR(go->info.p2p_device_addr),
+ MAC2STR(dev->info.p2p_device_addr));
+
+ p2p->pending_client_disc_go = go;
+ os_memcpy(p2p->pending_client_disc_addr, dev->info.p2p_device_addr,
+ ETH_ALEN);
+ p2p->pending_action_state = P2P_PENDING_DEV_DISC_REQUEST;
+ if (p2p_send_action(p2p, dev->oper_freq, go->info.p2p_device_addr,
+ p2p->cfg->dev_addr, go->info.p2p_device_addr,
+ wpabuf_head(req), wpabuf_len(req), 1000) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to send Action frame");
+ wpabuf_free(req);
+ /* TODO: how to recover from failure? */
+ return -1;
+ }
+
+ wpabuf_free(req);
+
+ return 0;
+}
+
+
+static struct wpabuf * p2p_build_dev_disc_resp(u8 dialog_token, u8 status)
+{
+ struct wpabuf *buf;
+ u8 *len;
+
+ buf = wpabuf_alloc(100);
+ if (buf == NULL)
+ return NULL;
+
+ p2p_buf_add_public_action_hdr(buf, P2P_DEV_DISC_RESP, dialog_token);
+
+ len = p2p_buf_add_ie_hdr(buf);
+ p2p_buf_add_status(buf, status);
+ p2p_buf_update_ie_hdr(buf, len);
+
+ return buf;
+}
+
+
+void p2p_dev_disc_resp_cb(struct p2p_data *p2p, int success)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Device Discoverability Response TX callback: success=%d",
+ success);
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+}
+
+
+static void p2p_send_dev_disc_resp(struct p2p_data *p2p, u8 dialog_token,
+ const u8 *addr, int freq, u8 status)
+{
+ struct wpabuf *resp;
+
+ resp = p2p_build_dev_disc_resp(dialog_token, status);
+ if (resp == NULL)
+ return;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Sending Device Discoverability Response to " MACSTR
+ " (status %u freq %d)",
+ MAC2STR(addr), status, freq);
+
+ p2p->pending_action_state = P2P_PENDING_DEV_DISC_RESPONSE;
+ if (p2p_send_action(p2p, freq, addr, p2p->cfg->dev_addr,
+ p2p->cfg->dev_addr,
+ wpabuf_head(resp), wpabuf_len(resp), 200) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to send Action frame");
+ }
+
+ wpabuf_free(resp);
+}
+
+
+void p2p_process_dev_disc_req(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len, int rx_freq)
+{
+ struct p2p_message msg;
+ size_t g;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Received Device Discoverability Request from " MACSTR
+ " (freq=%d)", MAC2STR(sa), rx_freq);
+
+ if (p2p_parse(data, len, &msg))
+ return;
+
+ if (msg.dialog_token == 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Invalid Dialog Token 0 (must be nonzero) in "
+ "Device Discoverability Request");
+ p2p_send_dev_disc_resp(p2p, msg.dialog_token, sa, rx_freq,
+ P2P_SC_FAIL_INVALID_PARAMS);
+ p2p_parse_free(&msg);
+ return;
+ }
+
+ if (msg.device_id == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: P2P Device ID attribute missing from Device "
+ "Discoverability Request");
+ p2p_send_dev_disc_resp(p2p, msg.dialog_token, sa, rx_freq,
+ P2P_SC_FAIL_INVALID_PARAMS);
+ p2p_parse_free(&msg);
+ return;
+ }
+
+ for (g = 0; g < p2p->num_groups; g++) {
+ if (p2p_group_go_discover(p2p->groups[g], msg.device_id, sa,
+ rx_freq) == 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Scheduled "
+ "GO Discoverability Request for the target "
+ "device");
+ /*
+ * P2P group code will use a callback to indicate TX
+ * status, so that we can reply to the request once the
+ * target client has acknowledged the request or it has
+ * timed out.
+ */
+ p2p->pending_dev_disc_dialog_token = msg.dialog_token;
+ os_memcpy(p2p->pending_dev_disc_addr, sa, ETH_ALEN);
+ p2p->pending_dev_disc_freq = rx_freq;
+ p2p_parse_free(&msg);
+ return;
+ }
+ }
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Requested client "
+ "was not found in any group or did not support client "
+ "discoverability");
+ p2p_send_dev_disc_resp(p2p, msg.dialog_token, sa, rx_freq,
+ P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE);
+ p2p_parse_free(&msg);
+}
+
+
+void p2p_process_dev_disc_resp(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len)
+{
+ struct p2p_message msg;
+ struct p2p_device *go;
+ u8 status;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Received Device Discoverability Response from " MACSTR,
+ MAC2STR(sa));
+
+ go = p2p->pending_client_disc_go;
+ if (go == NULL ||
+ os_memcmp(sa, go->info.p2p_device_addr, ETH_ALEN) != 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Ignore unexpected "
+ "Device Discoverability Response");
+ return;
+ }
+
+ if (p2p_parse(data, len, &msg))
+ return;
+
+ if (msg.status == NULL) {
+ p2p_parse_free(&msg);
+ return;
+ }
+
+ if (msg.dialog_token != go->dialog_token) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Ignore Device "
+ "Discoverability Response with unexpected dialog "
+ "token %u (expected %u)",
+ msg.dialog_token, go->dialog_token);
+ p2p_parse_free(&msg);
+ return;
+ }
+
+ status = *msg.status;
+ p2p_parse_free(&msg);
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Device Discoverability Response status %u", status);
+
+ if (p2p->go_neg_peer == NULL ||
+ os_memcmp(p2p->pending_client_disc_addr,
+ p2p->go_neg_peer->info.p2p_device_addr, ETH_ALEN) != 0 ||
+ os_memcmp(p2p->go_neg_peer->member_in_go_dev,
+ go->info.p2p_device_addr, ETH_ALEN) != 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No pending "
+ "operation with the client discoverability peer "
+ "anymore");
+ return;
+ }
+
+ if (status == 0) {
+ /*
+ * Peer is expected to be awake for at least 100 TU; try to
+ * connect immediately.
+ */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Client discoverability request succeeded");
+ if (p2p->state == P2P_CONNECT) {
+ /*
+ * Change state to force the timeout to start in
+ * P2P_CONNECT again without going through the short
+ * Listen state.
+ */
+ p2p_set_state(p2p, P2P_CONNECT_LISTEN);
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+ }
+ p2p_set_timeout(p2p, 0, 0);
+ } else {
+ /*
+ * Client discoverability request failed; try to connect from
+ * timeout.
+ */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Client discoverability request failed");
+ p2p_set_timeout(p2p, 0, 500000);
+ }
+
+}
+
+
+void p2p_go_disc_req_cb(struct p2p_data *p2p, int success)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: GO Discoverability Request TX callback: success=%d",
+ success);
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+
+ if (p2p->pending_dev_disc_dialog_token == 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No pending Device "
+ "Discoverability Request");
+ return;
+ }
+
+ p2p_send_dev_disc_resp(p2p, p2p->pending_dev_disc_dialog_token,
+ p2p->pending_dev_disc_addr,
+ p2p->pending_dev_disc_freq,
+ success ? P2P_SC_SUCCESS :
+ P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE);
+
+ p2p->pending_dev_disc_dialog_token = 0;
+}
+
+
+void p2p_process_go_disc_req(struct p2p_data *p2p, const u8 *da, const u8 *sa,
+ const u8 *data, size_t len, int rx_freq)
+{
+ unsigned int tu;
+ struct wpabuf *ies;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Received GO Discoverability Request - remain awake for "
+ "100 TU");
+
+ ies = p2p_build_probe_resp_ies(p2p);
+ if (ies == NULL)
+ return;
+
+ /* Remain awake 100 TU on operating channel */
+ p2p->pending_client_disc_freq = rx_freq;
+ tu = 100;
+ if (p2p->cfg->start_listen(p2p->cfg->cb_ctx, rx_freq, 1024 * tu / 1000,
+ ies) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to start listen mode for client "
+ "discoverability");
+ }
+ wpabuf_free(ies);
+}
--- /dev/null
+/*
+ * Wi-Fi Direct - P2P Group Owner Negotiation
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.h"
+#include "wps/wps_defs.h"
+#include "p2p_i.h"
+#include "p2p.h"
+
+
+static int p2p_go_det(u8 own_intent, u8 peer_value)
+{
+ u8 peer_intent = peer_value >> 1;
+ if (own_intent == peer_intent) {
+ if (own_intent == P2P_MAX_GO_INTENT)
+ return -1; /* both devices want to become GO */
+
+ /* Use tie breaker bit to determine GO */
+ return (peer_value & 0x01) ? 0 : 1;
+ }
+
+ return own_intent > peer_intent;
+}
+
+
+int p2p_peer_channels_check(struct p2p_data *p2p, struct p2p_channels *own,
+ struct p2p_device *dev,
+ const u8 *channel_list, size_t channel_list_len)
+{
+ const u8 *pos, *end;
+ struct p2p_channels *ch;
+ size_t channels;
+ struct p2p_channels intersection;
+
+ ch = &dev->channels;
+ os_memset(ch, 0, sizeof(*ch));
+ pos = channel_list;
+ end = channel_list + channel_list_len;
+
+ if (end - pos < 3)
+ return -1;
+ os_memcpy(dev->country, pos, 3);
+ wpa_hexdump_ascii(MSG_DEBUG, "P2P: Peer country", pos, 3);
+ if (pos[2] != 0x04 && os_memcmp(pos, p2p->cfg->country, 2) != 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_INFO,
+ "P2P: Mismatching country (ours=%c%c peer's=%c%c)",
+ p2p->cfg->country[0], p2p->cfg->country[1],
+ pos[0], pos[1]);
+ return -1;
+ }
+ pos += 3;
+
+ while (pos + 2 < end) {
+ struct p2p_reg_class *cl = &ch->reg_class[ch->reg_classes];
+ cl->reg_class = *pos++;
+ if (pos + 1 + pos[0] > end) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_INFO,
+ "P2P: Invalid peer Channel List");
+ return -1;
+ }
+ channels = *pos++;
+ cl->channels = channels > P2P_MAX_REG_CLASS_CHANNELS ?
+ P2P_MAX_REG_CLASS_CHANNELS : channels;
+ os_memcpy(cl->channel, pos, cl->channels);
+ pos += channels;
+ ch->reg_classes++;
+ if (ch->reg_classes == P2P_MAX_REG_CLASSES)
+ break;
+ }
+
+ p2p_channels_intersect(own, &dev->channels, &intersection);
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Own reg_classes %d "
+ "peer reg_classes %d intersection reg_classes %d",
+ (int) own->reg_classes,
+ (int) dev->channels.reg_classes,
+ (int) intersection.reg_classes);
+ if (intersection.reg_classes == 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_INFO,
+ "P2P: No common channels found");
+ return -1;
+ }
+ return 0;
+}
+
+
+static int p2p_peer_channels(struct p2p_data *p2p, struct p2p_device *dev,
+ const u8 *channel_list, size_t channel_list_len)
+{
+ return p2p_peer_channels_check(p2p, &p2p->channels, dev,
+ channel_list, channel_list_len);
+}
+
+
+static u16 p2p_wps_method_pw_id(enum p2p_wps_method wps_method)
+{
+ switch (wps_method) {
+ case WPS_PIN_DISPLAY:
+ return DEV_PW_REGISTRAR_SPECIFIED;
+ case WPS_PIN_KEYPAD:
+ return DEV_PW_USER_SPECIFIED;
+ case WPS_PBC:
+ return DEV_PW_PUSHBUTTON;
+ default:
+ return DEV_PW_DEFAULT;
+ }
+}
+
+
+static const char * p2p_wps_method_str(enum p2p_wps_method wps_method)
+{
+ switch (wps_method) {
+ case WPS_PIN_DISPLAY:
+ return "Display";
+ case WPS_PIN_KEYPAD:
+ return "Keypad";
+ case WPS_PBC:
+ return "PBC";
+ default:
+ return "??";
+ }
+}
+
+
+static struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p,
+ struct p2p_device *peer)
+{
+ struct wpabuf *buf;
+ u8 *len;
+ u8 group_capab;
+
+ buf = wpabuf_alloc(1000);
+ if (buf == NULL)
+ return NULL;
+
+ peer->dialog_token++;
+ if (peer->dialog_token == 0)
+ peer->dialog_token = 1;
+ p2p_buf_add_public_action_hdr(buf, P2P_GO_NEG_REQ, peer->dialog_token);
+
+ len = p2p_buf_add_ie_hdr(buf);
+ group_capab = 0;
+ if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP) {
+ group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP;
+ if (peer->flags & P2P_DEV_PREFER_PERSISTENT_RECONN)
+ group_capab |= P2P_GROUP_CAPAB_PERSISTENT_RECONN;
+ }
+ if (p2p->cross_connect)
+ group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
+ if (p2p->cfg->p2p_intra_bss)
+ group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST;
+ p2p_buf_add_capability(buf, p2p->dev_capab, group_capab);
+ p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) |
+ p2p->next_tie_breaker);
+ p2p->next_tie_breaker = !p2p->next_tie_breaker;
+ p2p_buf_add_config_timeout(buf, 100, 20);
+ p2p_buf_add_listen_channel(buf, p2p->cfg->country, p2p->cfg->reg_class,
+ p2p->cfg->channel);
+ if (p2p->ext_listen_interval)
+ p2p_buf_add_ext_listen_timing(buf, p2p->ext_listen_period,
+ p2p->ext_listen_interval);
+ p2p_buf_add_intended_addr(buf, p2p->intended_addr);
+ p2p_buf_add_channel_list(buf, p2p->cfg->country, &p2p->channels);
+ p2p_buf_add_device_info(buf, p2p, peer);
+ p2p_buf_add_operating_channel(buf, p2p->cfg->country,
+ p2p->op_reg_class, p2p->op_channel);
+ p2p_buf_update_ie_hdr(buf, len);
+
+ /* WPS IE with Device Password ID attribute */
+ p2p_build_wps_ie(p2p, buf, p2p_wps_method_pw_id(peer->wps_method), 0);
+
+ return buf;
+}
+
+
+int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev)
+{
+ struct wpabuf *req;
+ int freq;
+
+ freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq;
+ if (freq <= 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No Listen/Operating frequency known for the "
+ "peer " MACSTR " to send GO Negotiation Request",
+ MAC2STR(dev->info.p2p_device_addr));
+ return -1;
+ }
+
+ req = p2p_build_go_neg_req(p2p, dev);
+ if (req == NULL)
+ return -1;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Sending GO Negotiation Request");
+ p2p_set_state(p2p, P2P_CONNECT);
+ p2p->pending_action_state = P2P_PENDING_GO_NEG_REQUEST;
+ p2p->go_neg_peer = dev;
+ dev->flags |= P2P_DEV_WAIT_GO_NEG_RESPONSE;
+ dev->connect_reqs++;
+ if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr,
+ p2p->cfg->dev_addr, dev->info.p2p_device_addr,
+ wpabuf_head(req), wpabuf_len(req), 200) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to send Action frame");
+ /* Use P2P find to recover and retry */
+ p2p_set_timeout(p2p, 0, 0);
+ }
+
+ wpabuf_free(req);
+
+ return 0;
+}
+
+
+static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p,
+ struct p2p_device *peer,
+ u8 dialog_token, u8 status,
+ u8 tie_breaker)
+{
+ struct wpabuf *buf;
+ u8 *len;
+ u8 group_capab;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Building GO Negotiation Response");
+ buf = wpabuf_alloc(1000);
+ if (buf == NULL)
+ return NULL;
+
+ p2p_buf_add_public_action_hdr(buf, P2P_GO_NEG_RESP, dialog_token);
+
+ len = p2p_buf_add_ie_hdr(buf);
+ p2p_buf_add_status(buf, status);
+ group_capab = 0;
+ if (peer && peer->go_state == LOCAL_GO) {
+ if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP) {
+ group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP;
+ if (peer->flags & P2P_DEV_PREFER_PERSISTENT_RECONN)
+ group_capab |=
+ P2P_GROUP_CAPAB_PERSISTENT_RECONN;
+ }
+ if (p2p->cross_connect)
+ group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
+ if (p2p->cfg->p2p_intra_bss)
+ group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST;
+ }
+ p2p_buf_add_capability(buf, p2p->dev_capab, group_capab);
+ p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) | tie_breaker);
+ p2p_buf_add_config_timeout(buf, 100, 20);
+ if (peer && peer->go_state == REMOTE_GO) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Omit Operating "
+ "Channel attribute");
+ } else {
+ p2p_buf_add_operating_channel(buf, p2p->cfg->country,
+ p2p->op_reg_class,
+ p2p->op_channel);
+ }
+ p2p_buf_add_intended_addr(buf, p2p->intended_addr);
+ if (status || peer == NULL) {
+ p2p_buf_add_channel_list(buf, p2p->cfg->country,
+ &p2p->channels);
+ } else if (peer->go_state == REMOTE_GO) {
+ p2p_buf_add_channel_list(buf, p2p->cfg->country,
+ &p2p->channels);
+ } else {
+ struct p2p_channels res;
+ p2p_channels_intersect(&p2p->channels, &peer->channels,
+ &res);
+ p2p_buf_add_channel_list(buf, p2p->cfg->country, &res);
+ }
+ p2p_buf_add_device_info(buf, p2p, peer);
+ if (peer && peer->go_state == LOCAL_GO) {
+ p2p_buf_add_group_id(buf, p2p->cfg->dev_addr, p2p->ssid,
+ p2p->ssid_len);
+ }
+ p2p_buf_update_ie_hdr(buf, len);
+
+ /* WPS IE with Device Password ID attribute */
+ p2p_build_wps_ie(p2p, buf,
+ p2p_wps_method_pw_id(peer ? peer->wps_method :
+ WPS_NOT_READY), 0);
+
+ return buf;
+}
+
+
+static void p2p_reselect_channel(struct p2p_data *p2p,
+ struct p2p_channels *intersection)
+{
+ struct p2p_reg_class *cl;
+ int freq;
+ u8 op_reg_class, op_channel;
+ unsigned int i;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Selected operating "
+ "channel (reg_class %u channel %u) not acceptable to the "
+ "peer", p2p->op_reg_class, p2p->op_channel);
+
+ /* First, try to pick the best channel from another band */
+ freq = p2p_channel_to_freq(p2p->cfg->country, p2p->op_reg_class,
+ p2p->op_channel);
+ if (freq >= 2400 && freq < 2500 && p2p->best_freq_5 > 0 &&
+ p2p_freq_to_channel(p2p->cfg->country, p2p->best_freq_5,
+ &op_reg_class, &op_channel) == 0 &&
+ p2p_channels_includes(intersection, op_reg_class, op_channel)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Pick best 5 GHz "
+ "channel (reg_class %u channel %u) from intersection",
+ op_reg_class, op_channel);
+ p2p->op_reg_class = op_reg_class;
+ p2p->op_channel = op_channel;
+ return;
+ }
+
+ if (freq >= 4900 && freq < 6000 && p2p->best_freq_24 > 0 &&
+ p2p_freq_to_channel(p2p->cfg->country, p2p->best_freq_24,
+ &op_reg_class, &op_channel) == 0 &&
+ p2p_channels_includes(intersection, op_reg_class, op_channel)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Pick best 2.4 GHz "
+ "channel (reg_class %u channel %u) from intersection",
+ op_reg_class, op_channel);
+ p2p->op_reg_class = op_reg_class;
+ p2p->op_channel = op_channel;
+ return;
+ }
+
+ /* Select channel with highest preference if the peer supports it */
+ for (i = 0; p2p->cfg->pref_chan && i < p2p->cfg->num_pref_chan; i++) {
+ if (p2p_channels_includes(intersection,
+ p2p->cfg->pref_chan[i].op_class,
+ p2p->cfg->pref_chan[i].chan)) {
+ p2p->op_reg_class = p2p->cfg->pref_chan[i].op_class;
+ p2p->op_channel = p2p->cfg->pref_chan[i].chan;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Pick "
+ "highest preferred chnnel (op_class %u "
+ "channel %u) from intersection",
+ p2p->op_reg_class, p2p->op_channel);
+ return;
+ }
+ }
+
+ /*
+ * Fall back to whatever is included in the channel intersection since
+ * no better options seems to be available.
+ */
+ cl = &intersection->reg_class[0];
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Pick another channel "
+ "(reg_class %u channel %u) from intersection",
+ cl->reg_class, cl->channel[0]);
+ p2p->op_reg_class = cl->reg_class;
+ p2p->op_channel = cl->channel[0];
+}
+
+
+void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len, int rx_freq)
+{
+ struct p2p_device *dev = NULL;
+ struct wpabuf *resp;
+ struct p2p_message msg;
+ u8 status = P2P_SC_FAIL_INVALID_PARAMS;
+ int tie_breaker = 0;
+ int freq;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Received GO Negotiation Request from " MACSTR
+ "(freq=%d)", MAC2STR(sa), rx_freq);
+
+ if (p2p_parse(data, len, &msg))
+ return;
+
+ if (!msg.capability) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Mandatory Capability attribute missing from GO "
+ "Negotiation Request");
+#ifdef CONFIG_P2P_STRICT
+ goto fail;
+#endif /* CONFIG_P2P_STRICT */
+ }
+
+ if (msg.go_intent)
+ tie_breaker = *msg.go_intent & 0x01;
+ else {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Mandatory GO Intent attribute missing from GO "
+ "Negotiation Request");
+#ifdef CONFIG_P2P_STRICT
+ goto fail;
+#endif /* CONFIG_P2P_STRICT */
+ }
+
+ if (!msg.config_timeout) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Mandatory Configuration Timeout attribute "
+ "missing from GO Negotiation Request");
+#ifdef CONFIG_P2P_STRICT
+ goto fail;
+#endif /* CONFIG_P2P_STRICT */
+ }
+
+ if (!msg.listen_channel) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No Listen Channel attribute received");
+ goto fail;
+ }
+ if (!msg.operating_channel) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No Operating Channel attribute received");
+ goto fail;
+ }
+ if (!msg.channel_list) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No Channel List attribute received");
+ goto fail;
+ }
+ if (!msg.intended_addr) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No Intended P2P Interface Address attribute "
+ "received");
+ goto fail;
+ }
+ if (!msg.p2p_device_info) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No P2P Device Info attribute received");
+ goto fail;
+ }
+
+ if (os_memcmp(msg.p2p_device_addr, sa, ETH_ALEN) != 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unexpected GO Negotiation Request SA=" MACSTR
+ " != dev_addr=" MACSTR,
+ MAC2STR(sa), MAC2STR(msg.p2p_device_addr));
+ goto fail;
+ }
+
+ dev = p2p_get_device(p2p, sa);
+
+ if (msg.status && *msg.status) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unexpected Status attribute (%d) in GO "
+ "Negotiation Request", *msg.status);
+ goto fail;
+ }
+
+ if (dev == NULL)
+ dev = p2p_add_dev_from_go_neg_req(p2p, sa, &msg);
+ else if (dev->flags & P2P_DEV_PROBE_REQ_ONLY)
+ p2p_add_dev_info(p2p, sa, dev, &msg);
+ if (dev && dev->flags & P2P_DEV_USER_REJECTED) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: User has rejected this peer");
+ status = P2P_SC_FAIL_REJECTED_BY_USER;
+ } else if (dev == NULL || dev->wps_method == WPS_NOT_READY) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Not ready for GO negotiation with " MACSTR,
+ MAC2STR(sa));
+ status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
+ if (dev)
+ dev->flags |= P2P_DEV_PEER_WAITING_RESPONSE;
+ p2p->cfg->go_neg_req_rx(p2p->cfg->cb_ctx, sa,
+ msg.dev_password_id);
+ } else if (p2p->go_neg_peer && p2p->go_neg_peer != dev) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Already in Group Formation with another peer");
+ status = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
+ } else {
+ int go;
+
+ if (!p2p->go_neg_peer) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting "
+ "GO Negotiation with previously authorized "
+ "peer");
+ if (!(dev->flags & P2P_DEV_FORCE_FREQ)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Use default channel settings");
+ p2p->op_reg_class = p2p->cfg->op_reg_class;
+ p2p->op_channel = p2p->cfg->op_channel;
+ os_memcpy(&p2p->channels, &p2p->cfg->channels,
+ sizeof(struct p2p_channels));
+ } else {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Use previously configured "
+ "forced channel settings");
+ }
+ }
+
+ dev->flags &= ~P2P_DEV_NOT_YET_READY;
+
+ if (!msg.go_intent) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No GO Intent attribute received");
+ goto fail;
+ }
+ if ((*msg.go_intent >> 1) > P2P_MAX_GO_INTENT) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Invalid GO Intent value (%u) received",
+ *msg.go_intent >> 1);
+ goto fail;
+ }
+
+ if (dev->go_neg_req_sent &&
+ os_memcmp(sa, p2p->cfg->dev_addr, ETH_ALEN) > 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Do not reply since peer has higher "
+ "address and GO Neg Request already sent");
+ p2p_parse_free(&msg);
+ return;
+ }
+
+ go = p2p_go_det(p2p->go_intent, *msg.go_intent);
+ if (go < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Incompatible GO Intent");
+ status = P2P_SC_FAIL_BOTH_GO_INTENT_15;
+ goto fail;
+ }
+
+ if (p2p_peer_channels(p2p, dev, msg.channel_list,
+ msg.channel_list_len) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No common channels found");
+ status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+ goto fail;
+ }
+
+ switch (msg.dev_password_id) {
+ case DEV_PW_REGISTRAR_SPECIFIED:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: PIN from peer Display");
+ if (dev->wps_method != WPS_PIN_KEYPAD) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: We have wps_method=%s -> "
+ "incompatible",
+ p2p_wps_method_str(dev->wps_method));
+ status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+ goto fail;
+ }
+ break;
+ case DEV_PW_USER_SPECIFIED:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Peer entered PIN on Keypad");
+ if (dev->wps_method != WPS_PIN_DISPLAY) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: We have wps_method=%s -> "
+ "incompatible",
+ p2p_wps_method_str(dev->wps_method));
+ status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+ goto fail;
+ }
+ break;
+ case DEV_PW_PUSHBUTTON:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Peer using pushbutton");
+ if (dev->wps_method != WPS_PBC) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: We have wps_method=%s -> "
+ "incompatible",
+ p2p_wps_method_str(dev->wps_method));
+ status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+ goto fail;
+ }
+ break;
+ default:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported Device Password ID %d",
+ msg.dev_password_id);
+ status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+ goto fail;
+ }
+
+ if (go) {
+ struct p2p_channels intersection;
+ size_t i;
+ p2p_channels_intersect(&p2p->channels, &dev->channels,
+ &intersection);
+ if (intersection.reg_classes == 0 ||
+ intersection.reg_class[0].channels == 0) {
+ status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No common channels found");
+ goto fail;
+ }
+ for (i = 0; i < intersection.reg_classes; i++) {
+ struct p2p_reg_class *c;
+ c = &intersection.reg_class[i];
+ wpa_printf(MSG_DEBUG, "P2P: reg_class %u",
+ c->reg_class);
+ wpa_hexdump(MSG_DEBUG, "P2P: channels",
+ c->channel, c->channels);
+ }
+ if (!p2p_channels_includes(&intersection,
+ p2p->op_reg_class,
+ p2p->op_channel))
+ p2p_reselect_channel(p2p, &intersection);
+
+ if (!p2p->ssid_set) {
+ p2p_build_ssid(p2p, p2p->ssid, &p2p->ssid_len);
+ p2p->ssid_set = 1;
+ }
+ }
+
+ dev->go_state = go ? LOCAL_GO : REMOTE_GO;
+ dev->oper_freq = p2p_channel_to_freq((const char *)
+ msg.operating_channel,
+ msg.operating_channel[3],
+ msg.operating_channel[4]);
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer operating "
+ "channel preference: %d MHz", dev->oper_freq);
+
+ if (msg.config_timeout) {
+ dev->go_timeout = msg.config_timeout[0];
+ dev->client_timeout = msg.config_timeout[1];
+ }
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: GO Negotiation with " MACSTR, MAC2STR(sa));
+ if (p2p->state != P2P_IDLE)
+ p2p_stop_find_for_freq(p2p, rx_freq);
+ p2p_set_state(p2p, P2P_GO_NEG);
+ p2p_clear_timeout(p2p);
+ dev->dialog_token = msg.dialog_token;
+ os_memcpy(dev->intended_addr, msg.intended_addr, ETH_ALEN);
+ p2p->go_neg_peer = dev;
+ status = P2P_SC_SUCCESS;
+ }
+
+fail:
+ if (dev)
+ dev->status = status;
+ resp = p2p_build_go_neg_resp(p2p, dev, msg.dialog_token, status,
+ !tie_breaker);
+ p2p_parse_free(&msg);
+ if (resp == NULL)
+ return;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Sending GO Negotiation Response");
+ if (rx_freq > 0)
+ freq = rx_freq;
+ else
+ freq = p2p_channel_to_freq(p2p->cfg->country,
+ p2p->cfg->reg_class,
+ p2p->cfg->channel);
+ if (freq < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unknown regulatory class/channel");
+ wpabuf_free(resp);
+ return;
+ }
+ if (status == P2P_SC_SUCCESS) {
+ p2p->pending_action_state = P2P_PENDING_GO_NEG_RESPONSE;
+ dev->flags |= P2P_DEV_WAIT_GO_NEG_CONFIRM;
+ } else
+ p2p->pending_action_state =
+ P2P_PENDING_GO_NEG_RESPONSE_FAILURE;
+ if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr,
+ p2p->cfg->dev_addr,
+ wpabuf_head(resp), wpabuf_len(resp), 200) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to send Action frame");
+ }
+
+ wpabuf_free(resp);
+}
+
+
+static struct wpabuf * p2p_build_go_neg_conf(struct p2p_data *p2p,
+ struct p2p_device *peer,
+ u8 dialog_token, u8 status,
+ const u8 *resp_chan, int go)
+{
+ struct wpabuf *buf;
+ u8 *len;
+ struct p2p_channels res;
+ u8 group_capab;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Building GO Negotiation Confirm");
+ buf = wpabuf_alloc(1000);
+ if (buf == NULL)
+ return NULL;
+
+ p2p_buf_add_public_action_hdr(buf, P2P_GO_NEG_CONF, dialog_token);
+
+ len = p2p_buf_add_ie_hdr(buf);
+ p2p_buf_add_status(buf, status);
+ group_capab = 0;
+ if (peer->go_state == LOCAL_GO) {
+ if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP) {
+ group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP;
+ if (peer->flags & P2P_DEV_PREFER_PERSISTENT_RECONN)
+ group_capab |=
+ P2P_GROUP_CAPAB_PERSISTENT_RECONN;
+ }
+ if (p2p->cross_connect)
+ group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
+ if (p2p->cfg->p2p_intra_bss)
+ group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST;
+ }
+ p2p_buf_add_capability(buf, p2p->dev_capab, group_capab);
+ if (go || resp_chan == NULL)
+ p2p_buf_add_operating_channel(buf, p2p->cfg->country,
+ p2p->op_reg_class,
+ p2p->op_channel);
+ else
+ p2p_buf_add_operating_channel(buf, (const char *) resp_chan,
+ resp_chan[3], resp_chan[4]);
+ p2p_channels_intersect(&p2p->channels, &peer->channels, &res);
+ p2p_buf_add_channel_list(buf, p2p->cfg->country, &res);
+ if (go) {
+ p2p_buf_add_group_id(buf, p2p->cfg->dev_addr, p2p->ssid,
+ p2p->ssid_len);
+ }
+ p2p_buf_update_ie_hdr(buf, len);
+
+ return buf;
+}
+
+
+void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len, int rx_freq)
+{
+ struct p2p_device *dev;
+ struct wpabuf *conf;
+ int go = -1;
+ struct p2p_message msg;
+ u8 status = P2P_SC_SUCCESS;
+ int freq;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Received GO Negotiation Response from " MACSTR
+ " (freq=%d)", MAC2STR(sa), rx_freq);
+ dev = p2p_get_device(p2p, sa);
+ if (dev == NULL || dev->wps_method == WPS_NOT_READY ||
+ dev != p2p->go_neg_peer) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Not ready for GO negotiation with " MACSTR,
+ MAC2STR(sa));
+ return;
+ }
+
+ if (p2p_parse(data, len, &msg))
+ return;
+
+ if (!(dev->flags & P2P_DEV_WAIT_GO_NEG_RESPONSE)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Was not expecting GO Negotiation Response - "
+ "ignore");
+ p2p_parse_free(&msg);
+ return;
+ }
+ dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE;
+
+ if (msg.dialog_token != dev->dialog_token) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unexpected Dialog Token %u (expected %u)",
+ msg.dialog_token, dev->dialog_token);
+ p2p_parse_free(&msg);
+ return;
+ }
+
+ if (!msg.status) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No Status attribute received");
+ status = P2P_SC_FAIL_INVALID_PARAMS;
+ goto fail;
+ }
+ if (*msg.status) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: GO Negotiation rejected: status %d",
+ *msg.status);
+ dev->go_neg_req_sent = 0;
+ if (*msg.status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Wait for the peer to become ready for "
+ "GO Negotiation");
+ dev->flags |= P2P_DEV_NOT_YET_READY;
+ dev->wait_count = 0;
+ p2p_set_state(p2p, P2P_WAIT_PEER_IDLE);
+ p2p_set_timeout(p2p, 0, 0);
+ } else {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Stop GO Negotiation attempt");
+ p2p_go_neg_failed(p2p, dev, *msg.status);
+ }
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+ p2p_parse_free(&msg);
+ return;
+ }
+
+ if (!msg.capability) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Mandatory Capability attribute missing from GO "
+ "Negotiation Response");
+#ifdef CONFIG_P2P_STRICT
+ status = P2P_SC_FAIL_INVALID_PARAMS;
+ goto fail;
+#endif /* CONFIG_P2P_STRICT */
+ }
+
+ if (!msg.p2p_device_info) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Mandatory P2P Device Info attribute missing "
+ "from GO Negotiation Response");
+#ifdef CONFIG_P2P_STRICT
+ status = P2P_SC_FAIL_INVALID_PARAMS;
+ goto fail;
+#endif /* CONFIG_P2P_STRICT */
+ }
+
+ if (!msg.intended_addr) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No Intended P2P Interface Address attribute "
+ "received");
+ status = P2P_SC_FAIL_INVALID_PARAMS;
+ goto fail;
+ }
+
+ if (!msg.go_intent) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No GO Intent attribute received");
+ status = P2P_SC_FAIL_INVALID_PARAMS;
+ goto fail;
+ }
+ if ((*msg.go_intent >> 1) > P2P_MAX_GO_INTENT) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Invalid GO Intent value (%u) received",
+ *msg.go_intent >> 1);
+ status = P2P_SC_FAIL_INVALID_PARAMS;
+ goto fail;
+ }
+
+ go = p2p_go_det(p2p->go_intent, *msg.go_intent);
+ if (go < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Incompatible GO Intent");
+ status = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+ goto fail;
+ }
+
+ if (!go && msg.group_id) {
+ /* Store SSID for Provisioning step */
+ p2p->ssid_len = msg.group_id_len - ETH_ALEN;
+ os_memcpy(p2p->ssid, msg.group_id + ETH_ALEN, p2p->ssid_len);
+ } else if (!go) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Mandatory P2P Group ID attribute missing from "
+ "GO Negotiation Response");
+ p2p->ssid_len = 0;
+#ifdef CONFIG_P2P_STRICT
+ status = P2P_SC_FAIL_INVALID_PARAMS;
+ goto fail;
+#endif /* CONFIG_P2P_STRICT */
+ }
+
+ if (!msg.config_timeout) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Mandatory Configuration Timeout attribute "
+ "missing from GO Negotiation Response");
+#ifdef CONFIG_P2P_STRICT
+ status = P2P_SC_FAIL_INVALID_PARAMS;
+ goto fail;
+#endif /* CONFIG_P2P_STRICT */
+ } else {
+ dev->go_timeout = msg.config_timeout[0];
+ dev->client_timeout = msg.config_timeout[1];
+ }
+
+ if (!msg.operating_channel && !go) {
+ /*
+ * Note: P2P Client may omit Operating Channel attribute to
+ * indicate it does not have a preference.
+ */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No Operating Channel attribute received");
+ status = P2P_SC_FAIL_INVALID_PARAMS;
+ goto fail;
+ }
+ if (!msg.channel_list) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No Channel List attribute received");
+ status = P2P_SC_FAIL_INVALID_PARAMS;
+ goto fail;
+ }
+
+ if (p2p_peer_channels(p2p, dev, msg.channel_list,
+ msg.channel_list_len) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No common channels found");
+ status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+ goto fail;
+ }
+
+ if (msg.operating_channel) {
+ dev->oper_freq = p2p_channel_to_freq((const char *)
+ msg.operating_channel,
+ msg.operating_channel[3],
+ msg.operating_channel[4]);
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer operating "
+ "channel preference: %d MHz", dev->oper_freq);
+ } else
+ dev->oper_freq = 0;
+
+ switch (msg.dev_password_id) {
+ case DEV_PW_REGISTRAR_SPECIFIED:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: PIN from peer Display");
+ if (dev->wps_method != WPS_PIN_KEYPAD) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: We have wps_method=%s -> "
+ "incompatible",
+ p2p_wps_method_str(dev->wps_method));
+ status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+ goto fail;
+ }
+ break;
+ case DEV_PW_USER_SPECIFIED:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Peer entered PIN on Keypad");
+ if (dev->wps_method != WPS_PIN_DISPLAY) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: We have wps_method=%s -> "
+ "incompatible",
+ p2p_wps_method_str(dev->wps_method));
+ status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+ goto fail;
+ }
+ break;
+ case DEV_PW_PUSHBUTTON:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Peer using pushbutton");
+ if (dev->wps_method != WPS_PBC) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: We have wps_method=%s -> "
+ "incompatible",
+ p2p_wps_method_str(dev->wps_method));
+ status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+ goto fail;
+ }
+ break;
+ default:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported Device Password ID %d",
+ msg.dev_password_id);
+ status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+ goto fail;
+ }
+
+ if (go) {
+ struct p2p_channels intersection;
+ size_t i;
+ p2p_channels_intersect(&p2p->channels, &dev->channels,
+ &intersection);
+ if (intersection.reg_classes == 0 ||
+ intersection.reg_class[0].channels == 0) {
+ status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No common channels found");
+ goto fail;
+ }
+ for (i = 0; i < intersection.reg_classes; i++) {
+ struct p2p_reg_class *c;
+ c = &intersection.reg_class[i];
+ wpa_printf(MSG_DEBUG, "P2P: reg_class %u",
+ c->reg_class);
+ wpa_hexdump(MSG_DEBUG, "P2P: channels",
+ c->channel, c->channels);
+ }
+ if (!p2p_channels_includes(&intersection, p2p->op_reg_class,
+ p2p->op_channel))
+ p2p_reselect_channel(p2p, &intersection);
+
+ if (!p2p->ssid_set) {
+ p2p_build_ssid(p2p, p2p->ssid, &p2p->ssid_len);
+ p2p->ssid_set = 1;
+ }
+ }
+
+ p2p_set_state(p2p, P2P_GO_NEG);
+ p2p_clear_timeout(p2p);
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: GO Negotiation with " MACSTR, MAC2STR(sa));
+ os_memcpy(dev->intended_addr, msg.intended_addr, ETH_ALEN);
+
+fail:
+ conf = p2p_build_go_neg_conf(p2p, dev, msg.dialog_token, status,
+ msg.operating_channel, go);
+ p2p_parse_free(&msg);
+ if (conf == NULL)
+ return;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Sending GO Negotiation Confirm");
+ if (status == P2P_SC_SUCCESS) {
+ p2p->pending_action_state = P2P_PENDING_GO_NEG_CONFIRM;
+ dev->go_state = go ? LOCAL_GO : REMOTE_GO;
+ } else
+ p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+ if (rx_freq > 0)
+ freq = rx_freq;
+ else
+ freq = dev->listen_freq;
+ if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, sa,
+ wpabuf_head(conf), wpabuf_len(conf), 200) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to send Action frame");
+ p2p_go_neg_failed(p2p, dev, -1);
+ }
+ wpabuf_free(conf);
+}
+
+
+void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len)
+{
+ struct p2p_device *dev;
+ struct p2p_message msg;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Received GO Negotiation Confirm from " MACSTR,
+ MAC2STR(sa));
+ dev = p2p_get_device(p2p, sa);
+ if (dev == NULL || dev->wps_method == WPS_NOT_READY ||
+ dev != p2p->go_neg_peer) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Not ready for GO negotiation with " MACSTR,
+ MAC2STR(sa));
+ return;
+ }
+
+ if (p2p->pending_action_state == P2P_PENDING_GO_NEG_RESPONSE) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Stopped waiting "
+ "for TX status on GO Negotiation Response since we "
+ "already received Confirmation");
+ p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+ }
+
+ if (p2p_parse(data, len, &msg))
+ return;
+
+ if (!(dev->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Was not expecting GO Negotiation Confirm - "
+ "ignore");
+ return;
+ }
+ dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM;
+
+ if (msg.dialog_token != dev->dialog_token) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unexpected Dialog Token %u (expected %u)",
+ msg.dialog_token, dev->dialog_token);
+ p2p_parse_free(&msg);
+ return;
+ }
+
+ if (!msg.status) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No Status attribute received");
+ p2p_parse_free(&msg);
+ return;
+ }
+ if (*msg.status) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: GO Negotiation rejected: status %d",
+ *msg.status);
+ p2p_parse_free(&msg);
+ return;
+ }
+
+ if (dev->go_state == REMOTE_GO && msg.group_id) {
+ /* Store SSID for Provisioning step */
+ p2p->ssid_len = msg.group_id_len - ETH_ALEN;
+ os_memcpy(p2p->ssid, msg.group_id + ETH_ALEN, p2p->ssid_len);
+ } else if (dev->go_state == REMOTE_GO) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Mandatory P2P Group ID attribute missing from "
+ "GO Negotiation Confirmation");
+ p2p->ssid_len = 0;
+#ifdef CONFIG_P2P_STRICT
+ p2p_parse_free(&msg);
+ return;
+#endif /* CONFIG_P2P_STRICT */
+ }
+
+ if (!msg.operating_channel) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Mandatory Operating Channel attribute missing "
+ "from GO Negotiation Confirmation");
+#ifdef CONFIG_P2P_STRICT
+ p2p_parse_free(&msg);
+ return;
+#endif /* CONFIG_P2P_STRICT */
+ }
+
+ if (!msg.channel_list) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Mandatory Operating Channel attribute missing "
+ "from GO Negotiation Confirmation");
+#ifdef CONFIG_P2P_STRICT
+ p2p_parse_free(&msg);
+ return;
+#endif /* CONFIG_P2P_STRICT */
+ }
+
+ p2p_parse_free(&msg);
+
+ if (dev->go_state == UNKNOWN_GO) {
+ /*
+ * This should not happen since GO negotiation has already
+ * been completed.
+ */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unexpected GO Neg state - do not know which end "
+ "becomes GO");
+ return;
+ }
+
+ p2p_go_complete(p2p, dev);
+}
--- /dev/null
+/*
+ * Wi-Fi Direct - P2P group operations
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "wps/wps_defs.h"
+#include "wps/wps_i.h"
+#include "p2p_i.h"
+#include "p2p.h"
+
+
+struct p2p_group_member {
+ struct p2p_group_member *next;
+ u8 addr[ETH_ALEN]; /* P2P Interface Address */
+ u8 dev_addr[ETH_ALEN]; /* P2P Device Address */
+ struct wpabuf *p2p_ie;
+ struct wpabuf *client_info;
+ u8 dev_capab;
+};
+
+/**
+ * struct p2p_group - Internal P2P module per-group data
+ */
+struct p2p_group {
+ struct p2p_data *p2p;
+ struct p2p_group_config *cfg;
+ struct p2p_group_member *members;
+ unsigned int num_members;
+ int group_formation;
+ int beacon_update;
+ struct wpabuf *noa;
+};
+
+
+static void p2p_group_update_ies(struct p2p_group *group);
+
+
+struct p2p_group * p2p_group_init(struct p2p_data *p2p,
+ struct p2p_group_config *config)
+{
+ struct p2p_group *group, **groups;
+
+ group = os_zalloc(sizeof(*group));
+ if (group == NULL)
+ return NULL;
+
+ groups = os_realloc(p2p->groups, (p2p->num_groups + 1) *
+ sizeof(struct p2p_group *));
+ if (groups == NULL) {
+ os_free(group);
+ return NULL;
+ }
+ groups[p2p->num_groups++] = group;
+ p2p->groups = groups;
+
+ group->p2p = p2p;
+ group->cfg = config;
+ group->group_formation = 1;
+ group->beacon_update = 1;
+ p2p_group_update_ies(group);
+ group->cfg->idle_update(group->cfg->cb_ctx, 1);
+
+ return group;
+}
+
+
+static void p2p_group_free_member(struct p2p_group_member *m)
+{
+ wpabuf_free(m->p2p_ie);
+ wpabuf_free(m->client_info);
+ os_free(m);
+}
+
+
+static void p2p_group_free_members(struct p2p_group *group)
+{
+ struct p2p_group_member *m, *prev;
+ m = group->members;
+ group->members = NULL;
+ group->num_members = 0;
+ while (m) {
+ prev = m;
+ m = m->next;
+ p2p_group_free_member(prev);
+ }
+}
+
+
+void p2p_group_deinit(struct p2p_group *group)
+{
+ size_t g;
+ struct p2p_data *p2p;
+
+ if (group == NULL)
+ return;
+
+ p2p = group->p2p;
+
+ for (g = 0; g < p2p->num_groups; g++) {
+ if (p2p->groups[g] == group) {
+ while (g + 1 < p2p->num_groups) {
+ p2p->groups[g] = p2p->groups[g + 1];
+ g++;
+ }
+ p2p->num_groups--;
+ break;
+ }
+ }
+
+ p2p_group_free_members(group);
+ os_free(group->cfg);
+ wpabuf_free(group->noa);
+ os_free(group);
+}
+
+
+static void p2p_client_info(struct wpabuf *ie, struct p2p_group_member *m)
+{
+ if (m->client_info == NULL)
+ return;
+ if (wpabuf_tailroom(ie) < wpabuf_len(m->client_info) + 1)
+ return;
+ wpabuf_put_buf(ie, m->client_info);
+}
+
+
+static void p2p_group_add_common_ies(struct p2p_group *group,
+ struct wpabuf *ie)
+{
+ u8 dev_capab = 0, group_capab = 0;
+
+ /* P2P Capability */
+ dev_capab |= P2P_DEV_CAPAB_SERVICE_DISCOVERY;
+ dev_capab |= P2P_DEV_CAPAB_INVITATION_PROCEDURE;
+ group_capab |= P2P_GROUP_CAPAB_GROUP_OWNER;
+ if (group->cfg->persistent_group) {
+ group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP;
+ if (group->cfg->persistent_group == 2)
+ group_capab |= P2P_GROUP_CAPAB_PERSISTENT_RECONN;
+ }
+ if (group->p2p->cfg->p2p_intra_bss)
+ group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST;
+ if (group->group_formation)
+ group_capab |= P2P_GROUP_CAPAB_GROUP_FORMATION;
+ if (group->p2p->cross_connect)
+ group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
+ if (group->num_members >= group->cfg->max_clients)
+ group_capab |= P2P_GROUP_CAPAB_GROUP_LIMIT;
+ p2p_buf_add_capability(ie, dev_capab, group_capab);
+}
+
+
+static void p2p_group_add_noa(struct wpabuf *ie, struct wpabuf *noa)
+{
+ if (noa == NULL)
+ return;
+ /* Notice of Absence */
+ wpabuf_put_u8(ie, P2P_ATTR_NOTICE_OF_ABSENCE);
+ wpabuf_put_le16(ie, wpabuf_len(noa));
+ wpabuf_put_buf(ie, noa);
+}
+
+
+static struct wpabuf * p2p_group_build_beacon_ie(struct p2p_group *group)
+{
+ struct wpabuf *ie;
+ u8 *len;
+
+ ie = wpabuf_alloc(257);
+ if (ie == NULL)
+ return NULL;
+
+ len = p2p_buf_add_ie_hdr(ie);
+ p2p_group_add_common_ies(group, ie);
+ p2p_buf_add_device_id(ie, group->p2p->cfg->dev_addr);
+ p2p_group_add_noa(ie, group->noa);
+ p2p_buf_update_ie_hdr(ie, len);
+
+ return ie;
+}
+
+
+static struct wpabuf * p2p_group_build_probe_resp_ie(struct p2p_group *group)
+{
+ u8 *group_info;
+ struct wpabuf *ie;
+ struct p2p_group_member *m;
+ u8 *len;
+
+ ie = wpabuf_alloc(257);
+ if (ie == NULL)
+ return NULL;
+
+ len = p2p_buf_add_ie_hdr(ie);
+
+ p2p_group_add_common_ies(group, ie);
+ p2p_group_add_noa(ie, group->noa);
+
+ /* P2P Device Info */
+ p2p_buf_add_device_info(ie, group->p2p, NULL);
+
+ /* P2P Group Info */
+ group_info = wpabuf_put(ie, 0);
+ wpabuf_put_u8(ie, P2P_ATTR_GROUP_INFO);
+ wpabuf_put_le16(ie, 0); /* Length to be filled */
+ for (m = group->members; m; m = m->next)
+ p2p_client_info(ie, m);
+ WPA_PUT_LE16(group_info + 1,
+ (u8 *) wpabuf_put(ie, 0) - group_info - 3);
+
+ p2p_buf_update_ie_hdr(ie, len);
+ return ie;
+}
+
+
+static void p2p_group_update_ies(struct p2p_group *group)
+{
+ struct wpabuf *beacon_ie;
+ struct wpabuf *probe_resp_ie;
+
+ probe_resp_ie = p2p_group_build_probe_resp_ie(group);
+ if (probe_resp_ie == NULL)
+ return;
+ wpa_hexdump_buf(MSG_MSGDUMP, "P2P: Update GO Probe Response P2P IE",
+ probe_resp_ie);
+
+ if (group->beacon_update) {
+ beacon_ie = p2p_group_build_beacon_ie(group);
+ if (beacon_ie)
+ group->beacon_update = 0;
+ wpa_hexdump_buf(MSG_MSGDUMP, "P2P: Update GO Beacon P2P IE",
+ beacon_ie);
+ } else
+ beacon_ie = NULL;
+
+ group->cfg->ie_update(group->cfg->cb_ctx, beacon_ie, probe_resp_ie);
+}
+
+
+/**
+ * p2p_build_client_info - Build P2P Client Info Descriptor
+ * @addr: MAC address of the peer device
+ * @p2p_ie: P2P IE from (Re)Association Request
+ * @dev_capab: Buffer for returning Device Capability
+ * @dev_addr: Buffer for returning P2P Device Address
+ * Returns: P2P Client Info Descriptor or %NULL on failure
+ *
+ * This function builds P2P Client Info Descriptor based on the information
+ * available from (Re)Association Request frame. Group owner can use this to
+ * build the P2P Group Info attribute for Probe Response frames.
+ */
+static struct wpabuf * p2p_build_client_info(const u8 *addr,
+ struct wpabuf *p2p_ie,
+ u8 *dev_capab, u8 *dev_addr)
+{
+ const u8 *spos;
+ struct p2p_message msg;
+ u8 *len_pos;
+ struct wpabuf *buf;
+
+ if (p2p_ie == NULL)
+ return NULL;
+
+ os_memset(&msg, 0, sizeof(msg));
+ if (p2p_parse_p2p_ie(p2p_ie, &msg) ||
+ msg.capability == NULL || msg.p2p_device_info == NULL)
+ return NULL;
+
+ buf = wpabuf_alloc(ETH_ALEN + 1 + 1 + msg.p2p_device_info_len);
+ if (buf == NULL)
+ return NULL;
+
+ *dev_capab = msg.capability[0];
+ os_memcpy(dev_addr, msg.p2p_device_addr, ETH_ALEN);
+
+ spos = msg.p2p_device_info; /* P2P Device address */
+
+ /* P2P Client Info Descriptor */
+ /* Length to be set */
+ len_pos = wpabuf_put(buf, 1);
+ /* P2P Device address */
+ wpabuf_put_data(buf, spos, ETH_ALEN);
+ /* P2P Interface address */
+ wpabuf_put_data(buf, addr, ETH_ALEN);
+ /* Device Capability Bitmap */
+ wpabuf_put_u8(buf, msg.capability[0]);
+ /*
+ * Config Methods, Primary Device Type, Number of Secondary Device
+ * Types, Secondary Device Type List, Device Name copied from
+ * Device Info
+ */
+ wpabuf_put_data(buf, spos + ETH_ALEN,
+ msg.p2p_device_info_len - ETH_ALEN);
+
+ *len_pos = wpabuf_len(buf) - 1;
+
+
+ return buf;
+}
+
+
+static int p2p_group_remove_member(struct p2p_group *group, const u8 *addr)
+{
+ struct p2p_group_member *m, *prev;
+
+ if (group == NULL)
+ return 0;
+
+ m = group->members;
+ prev = NULL;
+ while (m) {
+ if (os_memcmp(m->addr, addr, ETH_ALEN) == 0)
+ break;
+ prev = m;
+ m = m->next;
+ }
+
+ if (m == NULL)
+ return 0;
+
+ if (prev)
+ prev->next = m->next;
+ else
+ group->members = m->next;
+ p2p_group_free_member(m);
+ group->num_members--;
+
+ return 1;
+}
+
+
+int p2p_group_notif_assoc(struct p2p_group *group, const u8 *addr,
+ const u8 *ie, size_t len)
+{
+ struct p2p_group_member *m;
+
+ if (group == NULL)
+ return -1;
+
+ m = os_zalloc(sizeof(*m));
+ if (m == NULL)
+ return -1;
+ os_memcpy(m->addr, addr, ETH_ALEN);
+ m->p2p_ie = ieee802_11_vendor_ie_concat(ie, len, P2P_IE_VENDOR_TYPE);
+ if (m->p2p_ie) {
+ m->client_info = p2p_build_client_info(addr, m->p2p_ie,
+ &m->dev_capab,
+ m->dev_addr);
+ }
+
+ p2p_group_remove_member(group, addr);
+
+ m->next = group->members;
+ group->members = m;
+ group->num_members++;
+ wpa_msg(group->p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Add client " MACSTR
+ " to group (p2p=%d client_info=%d); num_members=%u/%u",
+ MAC2STR(addr), m->p2p_ie ? 1 : 0, m->client_info ? 1 : 0,
+ group->num_members, group->cfg->max_clients);
+ if (group->num_members == group->cfg->max_clients)
+ group->beacon_update = 1;
+ p2p_group_update_ies(group);
+ if (group->num_members == 1)
+ group->cfg->idle_update(group->cfg->cb_ctx, 0);
+
+ return 0;
+}
+
+
+struct wpabuf * p2p_group_assoc_resp_ie(struct p2p_group *group, u8 status)
+{
+ struct wpabuf *resp;
+ u8 *rlen;
+
+ /*
+ * (Re)Association Response - P2P IE
+ * Status attribute (shall be present when association request is
+ * denied)
+ * Extended Listen Timing (may be present)
+ */
+ resp = wpabuf_alloc(20);
+ if (resp == NULL)
+ return NULL;
+ rlen = p2p_buf_add_ie_hdr(resp);
+ if (status != P2P_SC_SUCCESS)
+ p2p_buf_add_status(resp, status);
+ p2p_buf_update_ie_hdr(resp, rlen);
+
+ return resp;
+}
+
+
+void p2p_group_notif_disassoc(struct p2p_group *group, const u8 *addr)
+{
+ if (p2p_group_remove_member(group, addr)) {
+ wpa_msg(group->p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Remove "
+ "client " MACSTR " from group; num_members=%u/%u",
+ MAC2STR(addr), group->num_members,
+ group->cfg->max_clients);
+ if (group->num_members == group->cfg->max_clients - 1)
+ group->beacon_update = 1;
+ p2p_group_update_ies(group);
+ if (group->num_members == 0)
+ group->cfg->idle_update(group->cfg->cb_ctx, 1);
+ }
+}
+
+
+/**
+ * p2p_match_dev_type_member - Match client device type with requested type
+ * @m: Group member
+ * @wps: WPS TLVs from Probe Request frame (concatenated WPS IEs)
+ * Returns: 1 on match, 0 on mismatch
+ *
+ * This function can be used to match the Requested Device Type attribute in
+ * WPS IE with the device types of a group member for deciding whether a GO
+ * should reply to a Probe Request frame.
+ */
+static int p2p_match_dev_type_member(struct p2p_group_member *m,
+ struct wpabuf *wps)
+{
+ const u8 *pos, *end;
+ struct wps_parse_attr attr;
+ u8 num_sec;
+
+ if (m->client_info == NULL || wps == NULL)
+ return 0;
+
+ pos = wpabuf_head(m->client_info);
+ end = pos + wpabuf_len(m->client_info);
+
+ pos += 1 + 2 * ETH_ALEN + 1 + 2;
+ if (end - pos < WPS_DEV_TYPE_LEN + 1)
+ return 0;
+
+ if (wps_parse_msg(wps, &attr))
+ return 1; /* assume no Requested Device Type attributes */
+
+ if (attr.num_req_dev_type == 0)
+ return 1; /* no Requested Device Type attributes -> match */
+
+ if (dev_type_list_match(pos, attr.req_dev_type, attr.num_req_dev_type))
+ return 1; /* Match with client Primary Device Type */
+
+ pos += WPS_DEV_TYPE_LEN;
+ num_sec = *pos++;
+ if (end - pos < num_sec * WPS_DEV_TYPE_LEN)
+ return 0;
+ while (num_sec > 0) {
+ num_sec--;
+ if (dev_type_list_match(pos, attr.req_dev_type,
+ attr.num_req_dev_type))
+ return 1; /* Match with client Secondary Device Type */
+ pos += WPS_DEV_TYPE_LEN;
+ }
+
+ /* No matching device type found */
+ return 0;
+}
+
+
+int p2p_group_match_dev_type(struct p2p_group *group, struct wpabuf *wps)
+{
+ struct p2p_group_member *m;
+
+ if (p2p_match_dev_type(group->p2p, wps))
+ return 1; /* Match with own device type */
+
+ for (m = group->members; m; m = m->next) {
+ if (p2p_match_dev_type_member(m, wps))
+ return 1; /* Match with group client device type */
+ }
+
+ /* No match with Requested Device Type */
+ return 0;
+}
+
+
+int p2p_group_match_dev_id(struct p2p_group *group, struct wpabuf *p2p)
+{
+ struct p2p_group_member *m;
+ struct p2p_message msg;
+
+ os_memset(&msg, 0, sizeof(msg));
+ if (p2p_parse_p2p_ie(p2p, &msg))
+ return 1; /* Failed to parse - assume no filter on Device ID */
+
+ if (!msg.device_id)
+ return 1; /* No filter on Device ID */
+
+ if (os_memcmp(msg.device_id, group->p2p->cfg->dev_addr, ETH_ALEN) == 0)
+ return 1; /* Match with our P2P Device Address */
+
+ for (m = group->members; m; m = m->next) {
+ if (os_memcmp(msg.device_id, m->dev_addr, ETH_ALEN) == 0)
+ return 1; /* Match with group client P2P Device Address */
+ }
+
+ /* No match with Device ID */
+ return 0;
+}
+
+
+void p2p_group_notif_formation_done(struct p2p_group *group)
+{
+ if (group == NULL)
+ return;
+ group->group_formation = 0;
+ group->beacon_update = 1;
+ p2p_group_update_ies(group);
+}
+
+
+int p2p_group_notif_noa(struct p2p_group *group, const u8 *noa,
+ size_t noa_len)
+{
+ if (noa == NULL) {
+ wpabuf_free(group->noa);
+ group->noa = NULL;
+ } else {
+ if (group->noa) {
+ if (wpabuf_size(group->noa) >= noa_len) {
+ group->noa->used = 0;
+ wpabuf_put_data(group->noa, noa, noa_len);
+ } else {
+ wpabuf_free(group->noa);
+ group->noa = NULL;
+ }
+ }
+
+ if (!group->noa) {
+ group->noa = wpabuf_alloc_copy(noa, noa_len);
+ if (group->noa == NULL)
+ return -1;
+ }
+ }
+
+ group->beacon_update = 1;
+ p2p_group_update_ies(group);
+ return 0;
+}
+
+
+static struct p2p_group_member * p2p_group_get_client(struct p2p_group *group,
+ const u8 *dev_id)
+{
+ struct p2p_group_member *m;
+
+ for (m = group->members; m; m = m->next) {
+ if (os_memcmp(dev_id, m->dev_addr, ETH_ALEN) == 0)
+ return m;
+ }
+
+ return NULL;
+}
+
+
+static struct p2p_group_member * p2p_group_get_client_iface(
+ struct p2p_group *group, const u8 *interface_addr)
+{
+ struct p2p_group_member *m;
+
+ for (m = group->members; m; m = m->next) {
+ if (os_memcmp(interface_addr, m->addr, ETH_ALEN) == 0)
+ return m;
+ }
+
+ return NULL;
+}
+
+
+const u8 * p2p_group_get_dev_addr(struct p2p_group *group, const u8 *addr)
+{
+ struct p2p_group_member *m;
+
+ if (group == NULL)
+ return NULL;
+ m = p2p_group_get_client_iface(group, addr);
+ if (m && !is_zero_ether_addr(m->dev_addr))
+ return m->dev_addr;
+ return NULL;
+}
+
+
+static struct wpabuf * p2p_build_go_disc_req(void)
+{
+ struct wpabuf *buf;
+
+ buf = wpabuf_alloc(100);
+ if (buf == NULL)
+ return NULL;
+
+ p2p_buf_add_action_hdr(buf, P2P_GO_DISC_REQ, 0);
+
+ return buf;
+}
+
+
+int p2p_group_go_discover(struct p2p_group *group, const u8 *dev_id,
+ const u8 *searching_dev, int rx_freq)
+{
+ struct p2p_group_member *m;
+ struct wpabuf *req;
+ struct p2p_data *p2p = group->p2p;
+ int freq;
+
+ m = p2p_group_get_client(group, dev_id);
+ if (m == NULL || m->client_info == NULL) {
+ wpa_printf(MSG_DEBUG, "P2P: Requested client was not in this "
+ "group " MACSTR,
+ MAC2STR(group->cfg->interface_addr));
+ return -1;
+ }
+
+ if (!(m->dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) {
+ wpa_printf(MSG_DEBUG, "P2P: Requested client does not support "
+ "client discoverability");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "P2P: Schedule GO Discoverability Request to be "
+ "sent to " MACSTR, MAC2STR(dev_id));
+
+ req = p2p_build_go_disc_req();
+ if (req == NULL)
+ return -1;
+
+ /* TODO: Should really use group operating frequency here */
+ freq = rx_freq;
+
+ p2p->pending_action_state = P2P_PENDING_GO_DISC_REQ;
+ if (p2p->cfg->send_action(p2p->cfg->cb_ctx, freq, m->addr,
+ group->cfg->interface_addr,
+ group->cfg->interface_addr,
+ wpabuf_head(req), wpabuf_len(req), 200) < 0)
+ {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to send Action frame");
+ }
+
+ wpabuf_free(req);
+
+ return 0;
+}
+
+
+const u8 * p2p_group_get_interface_addr(struct p2p_group *group)
+{
+ return group->cfg->interface_addr;
+}
+
+
+u8 p2p_group_presence_req(struct p2p_group *group,
+ const u8 *client_interface_addr,
+ const u8 *noa, size_t noa_len)
+{
+ struct p2p_group_member *m;
+ u8 curr_noa[50];
+ int curr_noa_len;
+
+ m = p2p_group_get_client_iface(group, client_interface_addr);
+ if (m == NULL || m->client_info == NULL) {
+ wpa_printf(MSG_DEBUG, "P2P: Client was not in this group");
+ return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "P2P: Presence Request NoA", noa, noa_len);
+
+ if (group->p2p->cfg->get_noa)
+ curr_noa_len = group->p2p->cfg->get_noa(
+ group->p2p->cfg->cb_ctx, group->cfg->interface_addr,
+ curr_noa, sizeof(curr_noa));
+ else
+ curr_noa_len = -1;
+ if (curr_noa_len < 0)
+ wpa_printf(MSG_DEBUG, "P2P: Failed to fetch current NoA");
+ else if (curr_noa_len == 0)
+ wpa_printf(MSG_DEBUG, "P2P: No NoA being advertized");
+ else
+ wpa_hexdump(MSG_DEBUG, "P2P: Current NoA", curr_noa,
+ curr_noa_len);
+
+ /* TODO: properly process request and store copy */
+ if (curr_noa_len > 0 || curr_noa_len == -1)
+ return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
+
+ return P2P_SC_SUCCESS;
+}
+
+
+unsigned int p2p_get_group_num_members(struct p2p_group *group)
+{
+ return group->num_members;
+}
+
+
+const u8 * p2p_iterate_group_members(struct p2p_group *group, void **next)
+{
+ struct p2p_group_member *iter = *next;
+
+ if (!iter)
+ iter = group->members;
+ else
+ iter = iter->next;
+
+ *next = iter;
+
+ if (!iter)
+ return NULL;
+
+ return iter->addr;
+}
+
+
+int p2p_group_is_client_connected(struct p2p_group *group, const u8 *dev_addr)
+{
+ struct p2p_group_member *m;
+
+ for (m = group->members; m; m = m->next) {
+ if (os_memcmp(m->dev_addr, dev_addr, ETH_ALEN) == 0)
+ return 1;
+ }
+
+ return 0;
+}
--- /dev/null
+/*
+ * P2P - Internal definitions for P2P module
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef P2P_I_H
+#define P2P_I_H
+
+#include "utils/list.h"
+#include "p2p.h"
+
+enum p2p_go_state {
+ UNKNOWN_GO,
+ LOCAL_GO,
+ REMOTE_GO
+};
+
+/**
+ * struct p2p_device - P2P Device data (internal to P2P module)
+ */
+struct p2p_device {
+ struct dl_list list;
+ struct os_time last_seen;
+ int listen_freq;
+ enum p2p_wps_method wps_method;
+
+ struct p2p_peer_info info;
+
+ /*
+ * If the peer was discovered based on an interface address (e.g., GO
+ * from Beacon/Probe Response), the interface address is stored here.
+ * p2p_device_addr must still be set in such a case to the unique
+ * identifier for the P2P Device.
+ */
+ u8 interface_addr[ETH_ALEN];
+
+ /*
+ * P2P Device Address of the GO in whose group this P2P Device is a
+ * client.
+ */
+ u8 member_in_go_dev[ETH_ALEN];
+
+ /*
+ * P2P Interface Address of the GO in whose group this P2P Device is a
+ * client.
+ */
+ u8 member_in_go_iface[ETH_ALEN];
+
+ int go_neg_req_sent;
+ enum p2p_go_state go_state;
+ u8 dialog_token;
+ u8 intended_addr[ETH_ALEN];
+
+ char country[3];
+ struct p2p_channels channels;
+ int oper_freq;
+ u8 oper_ssid[32];
+ size_t oper_ssid_len;
+
+ /**
+ * req_config_methods - Pending provisioning discovery methods
+ */
+ u16 req_config_methods;
+
+ /**
+ * wps_prov_info - Stored provisioning WPS config method
+ *
+ * This is used to store pending WPS config method between Provisioning
+ * Discovery and connection to a running group.
+ */
+ u16 wps_prov_info;
+
+#define P2P_DEV_PROBE_REQ_ONLY BIT(0)
+#define P2P_DEV_REPORTED BIT(1)
+#define P2P_DEV_NOT_YET_READY BIT(2)
+#define P2P_DEV_SD_INFO BIT(3)
+#define P2P_DEV_SD_SCHEDULE BIT(4)
+#define P2P_DEV_PD_PEER_DISPLAY BIT(5)
+#define P2P_DEV_PD_PEER_KEYPAD BIT(6)
+#define P2P_DEV_USER_REJECTED BIT(7)
+#define P2P_DEV_PEER_WAITING_RESPONSE BIT(8)
+#define P2P_DEV_PREFER_PERSISTENT_GROUP BIT(9)
+#define P2P_DEV_WAIT_GO_NEG_RESPONSE BIT(10)
+#define P2P_DEV_WAIT_GO_NEG_CONFIRM BIT(11)
+#define P2P_DEV_GROUP_CLIENT_ONLY BIT(12)
+#define P2P_DEV_FORCE_FREQ BIT(13)
+#define P2P_DEV_PD_FOR_JOIN BIT(14)
+#define P2P_DEV_REPORTED_ONCE BIT(15)
+#define P2P_DEV_PREFER_PERSISTENT_RECONN BIT(16)
+ unsigned int flags;
+
+ int status; /* enum p2p_status_code */
+ unsigned int wait_count;
+ unsigned int connect_reqs;
+ unsigned int invitation_reqs;
+
+ u16 ext_listen_period;
+ u16 ext_listen_interval;
+
+ u8 go_timeout;
+ u8 client_timeout;
+};
+
+struct p2p_sd_query {
+ struct p2p_sd_query *next;
+ u8 peer[ETH_ALEN];
+ int for_all_peers;
+ struct wpabuf *tlvs;
+};
+
+struct p2p_pending_action_tx {
+ unsigned int freq;
+ u8 dst[ETH_ALEN];
+ u8 src[ETH_ALEN];
+ u8 bssid[ETH_ALEN];
+ size_t len;
+ unsigned int wait_time;
+ /* Followed by len octets of the frame */
+};
+
+/**
+ * struct p2p_data - P2P module data (internal to P2P module)
+ */
+struct p2p_data {
+ /**
+ * cfg - P2P module configuration
+ *
+ * This is included in the same memory allocation with the
+ * struct p2p_data and as such, must not be freed separately.
+ */
+ struct p2p_config *cfg;
+
+ /**
+ * state - The current P2P state
+ */
+ enum p2p_state {
+ /**
+ * P2P_IDLE - Idle
+ */
+ P2P_IDLE,
+
+ /**
+ * P2P_SEARCH - Search (Device Discovery)
+ */
+ P2P_SEARCH,
+
+ /**
+ * P2P_CONNECT - Trying to start GO Negotiation
+ */
+ P2P_CONNECT,
+
+ /**
+ * P2P_CONNECT_LISTEN - Listen during GO Negotiation start
+ */
+ P2P_CONNECT_LISTEN,
+
+ /**
+ * P2P_GO_NEG - In GO Negotiation
+ */
+ P2P_GO_NEG,
+
+ /**
+ * P2P_LISTEN_ONLY - Listen only
+ */
+ P2P_LISTEN_ONLY,
+
+ /**
+ * P2P_WAIT_PEER_CONNECT - Waiting peer in List for GO Neg
+ */
+ P2P_WAIT_PEER_CONNECT,
+
+ /**
+ * P2P_WAIT_PEER_IDLE - Waiting peer idle for GO Neg
+ */
+ P2P_WAIT_PEER_IDLE,
+
+ /**
+ * P2P_SD_DURING_FIND - Service Discovery during find
+ */
+ P2P_SD_DURING_FIND,
+
+ /**
+ * P2P_PROVISIONING - Provisioning (during group formation)
+ */
+ P2P_PROVISIONING,
+
+ /**
+ * P2P_PD_DURING_FIND - Provision Discovery during find
+ */
+ P2P_PD_DURING_FIND,
+
+ /**
+ * P2P_INVITE - Trying to start Invite
+ */
+ P2P_INVITE,
+
+ /**
+ * P2P_INVITE_LISTEN - Listen during Invite
+ */
+ P2P_INVITE_LISTEN,
+
+ /**
+ * P2P_SEARCH_WHEN_READY - Waiting to start Search
+ */
+ P2P_SEARCH_WHEN_READY,
+ } state;
+
+ /**
+ * min_disc_int - minDiscoverableInterval
+ */
+ int min_disc_int;
+
+ /**
+ * max_disc_int - maxDiscoverableInterval
+ */
+ int max_disc_int;
+
+ /**
+ * devices - List of known P2P Device peers
+ */
+ struct dl_list devices;
+
+ /**
+ * go_neg_peer - Pointer to GO Negotiation peer
+ */
+ struct p2p_device *go_neg_peer;
+
+ /**
+ * invite_peer - Pointer to Invite peer
+ */
+ struct p2p_device *invite_peer;
+
+ const u8 *invite_go_dev_addr;
+ u8 invite_go_dev_addr_buf[ETH_ALEN];
+
+ /**
+ * sd_peer - Pointer to Service Discovery peer
+ */
+ struct p2p_device *sd_peer;
+
+ /**
+ * sd_query - Pointer to Service Discovery query
+ */
+ struct p2p_sd_query *sd_query;
+
+ /* GO Negotiation data */
+
+ /**
+ * intended_addr - Local Intended P2P Interface Address
+ *
+ * This address is used during group owner negotiation as the Intended
+ * P2P Interface Address and the group interface will be created with
+ * address as the local address in case of successfully completed
+ * negotiation.
+ */
+ u8 intended_addr[ETH_ALEN];
+
+ /**
+ * go_intent - Local GO Intent to be used during GO Negotiation
+ */
+ u8 go_intent;
+
+ /**
+ * next_tie_breaker - Next tie-breaker value to use in GO Negotiation
+ */
+ u8 next_tie_breaker;
+
+ /**
+ * ssid - Selected SSID for GO Negotiation (if local end will be GO)
+ */
+ u8 ssid[32];
+
+ /**
+ * ssid_len - ssid length in octets
+ */
+ size_t ssid_len;
+
+ /**
+ * ssid_set - Whether SSID is already set for GO Negotiation
+ */
+ int ssid_set;
+
+ /**
+ * Regulatory class for own operational channel
+ */
+ u8 op_reg_class;
+
+ /**
+ * op_channel - Own operational channel
+ */
+ u8 op_channel;
+
+ /**
+ * channels - Own supported regulatory classes and channels
+ *
+ * List of supposerted channels per regulatory class. The regulatory
+ * classes are defined in IEEE Std 802.11-2007 Annex J and the
+ * numbering of the clases depends on the configured country code.
+ */
+ struct p2p_channels channels;
+
+ enum p2p_pending_action_state {
+ P2P_NO_PENDING_ACTION,
+ P2P_PENDING_GO_NEG_REQUEST,
+ P2P_PENDING_GO_NEG_RESPONSE,
+ P2P_PENDING_GO_NEG_RESPONSE_FAILURE,
+ P2P_PENDING_GO_NEG_CONFIRM,
+ P2P_PENDING_SD,
+ P2P_PENDING_PD,
+ P2P_PENDING_INVITATION_REQUEST,
+ P2P_PENDING_INVITATION_RESPONSE,
+ P2P_PENDING_DEV_DISC_REQUEST,
+ P2P_PENDING_DEV_DISC_RESPONSE,
+ P2P_PENDING_GO_DISC_REQ
+ } pending_action_state;
+
+ unsigned int pending_listen_freq;
+ unsigned int pending_listen_sec;
+ unsigned int pending_listen_usec;
+
+ u8 dev_capab;
+
+ int in_listen;
+ int drv_in_listen;
+
+ /**
+ * sd_queries - Pending service discovery queries
+ */
+ struct p2p_sd_query *sd_queries;
+
+ /**
+ * srv_update_indic - Service Update Indicator for local services
+ */
+ u16 srv_update_indic;
+
+ struct wpabuf *sd_resp; /* Fragmented SD response */
+ u8 sd_resp_addr[ETH_ALEN];
+ u8 sd_resp_dialog_token;
+ size_t sd_resp_pos; /* Offset in sd_resp */
+ u8 sd_frag_id;
+
+ struct wpabuf *sd_rx_resp; /* Reassembled SD response */
+ u16 sd_rx_update_indic;
+
+ /* P2P Invitation data */
+ enum p2p_invite_role inv_role;
+ u8 inv_bssid[ETH_ALEN];
+ int inv_bssid_set;
+ u8 inv_ssid[32];
+ size_t inv_ssid_len;
+ u8 inv_sa[ETH_ALEN];
+ u8 inv_group_bssid[ETH_ALEN];
+ u8 *inv_group_bssid_ptr;
+ u8 inv_go_dev_addr[ETH_ALEN];
+ u8 inv_status;
+ int inv_op_freq;
+ int inv_persistent;
+
+ enum p2p_discovery_type find_type;
+ unsigned int last_p2p_find_timeout;
+ u8 last_prog_scan_class;
+ u8 last_prog_scan_chan;
+ int p2p_scan_running;
+ enum p2p_after_scan {
+ P2P_AFTER_SCAN_NOTHING,
+ P2P_AFTER_SCAN_LISTEN,
+ P2P_AFTER_SCAN_CONNECT
+ } start_after_scan;
+ u8 after_scan_peer[ETH_ALEN];
+ struct p2p_pending_action_tx *after_scan_tx;
+
+ /* Requested device types for find/search */
+ unsigned int num_req_dev_types;
+ u8 *req_dev_types;
+ u8 *find_dev_id;
+ u8 find_dev_id_buf[ETH_ALEN];
+
+ struct p2p_group **groups;
+ size_t num_groups;
+
+ struct p2p_device *pending_client_disc_go;
+ u8 pending_client_disc_addr[ETH_ALEN];
+ u8 pending_dev_disc_dialog_token;
+ u8 pending_dev_disc_addr[ETH_ALEN];
+ int pending_dev_disc_freq;
+ unsigned int pending_client_disc_freq;
+
+ int ext_listen_only;
+ unsigned int ext_listen_period;
+ unsigned int ext_listen_interval;
+ unsigned int ext_listen_interval_sec;
+ unsigned int ext_listen_interval_usec;
+
+ u8 peer_filter[ETH_ALEN];
+
+ int cross_connect;
+
+ int best_freq_24;
+ int best_freq_5;
+ int best_freq_overall;
+
+ /**
+ * wps_vendor_ext - WPS Vendor Extensions to add
+ */
+ struct wpabuf *wps_vendor_ext[P2P_MAX_WPS_VENDOR_EXT];
+
+ /*
+ * user_initiated_pd - Whether a PD request is user initiated or not.
+ */
+ u8 user_initiated_pd;
+
+ /*
+ * Keep track of which peer a given PD request was sent to.
+ * Used to raise a timeout alert in case there is no response.
+ */
+ u8 pending_pd_devaddr[ETH_ALEN];
+
+ /*
+ * Retry counter for provision discovery requests when issued
+ * in IDLE state.
+ */
+ int pd_retries;
+};
+
+/**
+ * struct p2p_message - Parsed P2P message (or P2P IE)
+ */
+struct p2p_message {
+ struct wpabuf *p2p_attributes;
+ struct wpabuf *wps_attributes;
+
+ u8 dialog_token;
+
+ const u8 *capability;
+ const u8 *go_intent;
+ const u8 *status;
+ const u8 *listen_channel;
+ const u8 *operating_channel;
+ const u8 *channel_list;
+ u8 channel_list_len;
+ const u8 *config_timeout;
+ const u8 *intended_addr;
+ const u8 *group_bssid;
+ const u8 *invitation_flags;
+
+ const u8 *group_info;
+ size_t group_info_len;
+
+ const u8 *group_id;
+ size_t group_id_len;
+
+ const u8 *device_id;
+
+ const u8 *manageability;
+
+ const u8 *noa;
+ size_t noa_len;
+
+ const u8 *ext_listen_timing;
+
+ const u8 *minor_reason_code;
+
+ /* P2P Device Info */
+ const u8 *p2p_device_info;
+ size_t p2p_device_info_len;
+ const u8 *p2p_device_addr;
+ const u8 *pri_dev_type;
+ u8 num_sec_dev_types;
+ char device_name[33];
+ u16 config_methods;
+
+ /* WPS IE */
+ u16 dev_password_id;
+ u16 wps_config_methods;
+ const u8 *wps_pri_dev_type;
+ const u8 *wps_sec_dev_type_list;
+ size_t wps_sec_dev_type_list_len;
+ const u8 *wps_vendor_ext[P2P_MAX_WPS_VENDOR_EXT];
+ size_t wps_vendor_ext_len[P2P_MAX_WPS_VENDOR_EXT];
+ const u8 *manufacturer;
+ size_t manufacturer_len;
+ const u8 *model_name;
+ size_t model_name_len;
+ const u8 *model_number;
+ size_t model_number_len;
+ const u8 *serial_number;
+ size_t serial_number_len;
+
+ /* DS Parameter Set IE */
+ const u8 *ds_params;
+
+ /* SSID IE */
+ const u8 *ssid;
+};
+
+
+#define P2P_MAX_GROUP_ENTRIES 50
+
+struct p2p_group_info {
+ unsigned int num_clients;
+ struct p2p_client_info {
+ const u8 *p2p_device_addr;
+ const u8 *p2p_interface_addr;
+ u8 dev_capab;
+ u16 config_methods;
+ const u8 *pri_dev_type;
+ u8 num_sec_dev_types;
+ const u8 *sec_dev_types;
+ const char *dev_name;
+ size_t dev_name_len;
+ } client[P2P_MAX_GROUP_ENTRIES];
+};
+
+
+/* p2p_utils.c */
+int p2p_random(char *buf, size_t len);
+int p2p_channel_to_freq(const char *country, int reg_class, int channel);
+int p2p_freq_to_channel(const char *country, unsigned int freq, u8 *reg_class,
+ u8 *channel);
+void p2p_channels_intersect(const struct p2p_channels *a,
+ const struct p2p_channels *b,
+ struct p2p_channels *res);
+int p2p_channels_includes(const struct p2p_channels *channels, u8 reg_class,
+ u8 channel);
+
+/* p2p_parse.c */
+int p2p_parse_p2p_ie(const struct wpabuf *buf, struct p2p_message *msg);
+int p2p_parse_ies(const u8 *data, size_t len, struct p2p_message *msg);
+int p2p_parse(const u8 *data, size_t len, struct p2p_message *msg);
+void p2p_parse_free(struct p2p_message *msg);
+int p2p_attr_text(struct wpabuf *data, char *buf, char *end);
+int p2p_group_info_parse(const u8 *gi, size_t gi_len,
+ struct p2p_group_info *info);
+
+/* p2p_build.c */
+
+struct p2p_noa_desc {
+ u8 count_type;
+ u32 duration;
+ u32 interval;
+ u32 start_time;
+};
+
+/* p2p_group.c */
+const u8 * p2p_group_get_interface_addr(struct p2p_group *group);
+u8 p2p_group_presence_req(struct p2p_group *group,
+ const u8 *client_interface_addr,
+ const u8 *noa, size_t noa_len);
+
+
+void p2p_buf_add_action_hdr(struct wpabuf *buf, u8 subtype, u8 dialog_token);
+void p2p_buf_add_public_action_hdr(struct wpabuf *buf, u8 subtype,
+ u8 dialog_token);
+u8 * p2p_buf_add_ie_hdr(struct wpabuf *buf);
+void p2p_buf_add_status(struct wpabuf *buf, u8 status);
+void p2p_buf_add_device_info(struct wpabuf *buf, struct p2p_data *p2p,
+ struct p2p_device *peer);
+void p2p_buf_add_device_id(struct wpabuf *buf, const u8 *dev_addr);
+void p2p_buf_update_ie_hdr(struct wpabuf *buf, u8 *len);
+void p2p_buf_add_capability(struct wpabuf *buf, u8 dev_capab, u8 group_capab);
+void p2p_buf_add_go_intent(struct wpabuf *buf, u8 go_intent);
+void p2p_buf_add_listen_channel(struct wpabuf *buf, const char *country,
+ u8 reg_class, u8 channel);
+void p2p_buf_add_operating_channel(struct wpabuf *buf, const char *country,
+ u8 reg_class, u8 channel);
+void p2p_buf_add_channel_list(struct wpabuf *buf, const char *country,
+ struct p2p_channels *chan);
+void p2p_buf_add_config_timeout(struct wpabuf *buf, u8 go_timeout,
+ u8 client_timeout);
+void p2p_buf_add_intended_addr(struct wpabuf *buf, const u8 *interface_addr);
+void p2p_buf_add_group_bssid(struct wpabuf *buf, const u8 *bssid);
+void p2p_buf_add_group_id(struct wpabuf *buf, const u8 *dev_addr,
+ const u8 *ssid, size_t ssid_len);
+void p2p_buf_add_invitation_flags(struct wpabuf *buf, u8 flags);
+void p2p_buf_add_noa(struct wpabuf *buf, u8 noa_index, u8 opp_ps, u8 ctwindow,
+ struct p2p_noa_desc *desc1, struct p2p_noa_desc *desc2);
+void p2p_buf_add_ext_listen_timing(struct wpabuf *buf, u16 period,
+ u16 interval);
+void p2p_buf_add_p2p_interface(struct wpabuf *buf, struct p2p_data *p2p);
+void p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, u16 pw_id,
+ int all_attr);
+
+/* p2p_sd.c */
+struct p2p_sd_query * p2p_pending_sd_req(struct p2p_data *p2p,
+ struct p2p_device *dev);
+void p2p_free_sd_queries(struct p2p_data *p2p);
+void p2p_rx_gas_initial_req(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len, int rx_freq);
+void p2p_rx_gas_initial_resp(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len, int rx_freq);
+void p2p_rx_gas_comeback_req(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len, int rx_freq);
+void p2p_rx_gas_comeback_resp(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len, int rx_freq);
+int p2p_start_sd(struct p2p_data *p2p, struct p2p_device *dev);
+
+/* p2p_go_neg.c */
+int p2p_peer_channels_check(struct p2p_data *p2p, struct p2p_channels *own,
+ struct p2p_device *dev,
+ const u8 *channel_list, size_t channel_list_len);
+void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len, int rx_freq);
+void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len, int rx_freq);
+void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len);
+int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev);
+
+/* p2p_pd.c */
+void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len, int rx_freq);
+void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len);
+int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev,
+ int join, int force_freq);
+void p2p_reset_pending_pd(struct p2p_data *p2p);
+
+/* p2p_invitation.c */
+void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len, int rx_freq);
+void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len);
+int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev,
+ const u8 *go_dev_addr);
+void p2p_invitation_req_cb(struct p2p_data *p2p, int success);
+void p2p_invitation_resp_cb(struct p2p_data *p2p, int success);
+
+/* p2p_dev_disc.c */
+void p2p_process_dev_disc_req(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len, int rx_freq);
+void p2p_dev_disc_req_cb(struct p2p_data *p2p, int success);
+int p2p_send_dev_disc_req(struct p2p_data *p2p, struct p2p_device *dev);
+void p2p_dev_disc_resp_cb(struct p2p_data *p2p, int success);
+void p2p_process_dev_disc_resp(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len);
+void p2p_go_disc_req_cb(struct p2p_data *p2p, int success);
+void p2p_process_go_disc_req(struct p2p_data *p2p, const u8 *da, const u8 *sa,
+ const u8 *data, size_t len, int rx_freq);
+
+/* p2p.c */
+void p2p_set_state(struct p2p_data *p2p, int new_state);
+void p2p_set_timeout(struct p2p_data *p2p, unsigned int sec,
+ unsigned int usec);
+void p2p_clear_timeout(struct p2p_data *p2p);
+void p2p_continue_find(struct p2p_data *p2p);
+struct p2p_device * p2p_add_dev_from_go_neg_req(struct p2p_data *p2p,
+ const u8 *addr,
+ struct p2p_message *msg);
+void p2p_add_dev_info(struct p2p_data *p2p, const u8 *addr,
+ struct p2p_device *dev, struct p2p_message *msg);
+int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, int level,
+ const u8 *ies, size_t ies_len);
+struct p2p_device * p2p_get_device(struct p2p_data *p2p, const u8 *addr);
+struct p2p_device * p2p_get_device_interface(struct p2p_data *p2p,
+ const u8 *addr);
+void p2p_go_neg_failed(struct p2p_data *p2p, struct p2p_device *peer,
+ int status);
+void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer);
+int p2p_match_dev_type(struct p2p_data *p2p, struct wpabuf *wps);
+int dev_type_list_match(const u8 *dev_type, const u8 *req_dev_type[],
+ size_t num_req_dev_type);
+struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p);
+void p2p_build_ssid(struct p2p_data *p2p, u8 *ssid, size_t *ssid_len);
+int p2p_send_action(struct p2p_data *p2p, unsigned int freq, const u8 *dst,
+ const u8 *src, const u8 *bssid, const u8 *buf,
+ size_t len, unsigned int wait_time);
+void p2p_stop_listen_for_freq(struct p2p_data *p2p, int freq);
+
+#endif /* P2P_I_H */
--- /dev/null
+/*
+ * Wi-Fi Direct - P2P Invitation procedure
+ * Copyright (c) 2010, Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.h"
+#include "p2p_i.h"
+#include "p2p.h"
+
+
+static struct wpabuf * p2p_build_invitation_req(struct p2p_data *p2p,
+ struct p2p_device *peer,
+ const u8 *go_dev_addr)
+{
+ struct wpabuf *buf;
+ u8 *len;
+ const u8 *dev_addr;
+
+ buf = wpabuf_alloc(1000);
+ if (buf == NULL)
+ return NULL;
+
+ peer->dialog_token++;
+ if (peer->dialog_token == 0)
+ peer->dialog_token = 1;
+ p2p_buf_add_public_action_hdr(buf, P2P_INVITATION_REQ,
+ peer->dialog_token);
+
+ len = p2p_buf_add_ie_hdr(buf);
+ if (p2p->inv_role == P2P_INVITE_ROLE_ACTIVE_GO || !p2p->inv_persistent)
+ p2p_buf_add_config_timeout(buf, 0, 0);
+ else
+ p2p_buf_add_config_timeout(buf, 100, 20);
+ p2p_buf_add_invitation_flags(buf, p2p->inv_persistent ?
+ P2P_INVITATION_FLAGS_TYPE : 0);
+ p2p_buf_add_operating_channel(buf, p2p->cfg->country,
+ p2p->op_reg_class, p2p->op_channel);
+ if (p2p->inv_bssid_set)
+ p2p_buf_add_group_bssid(buf, p2p->inv_bssid);
+ p2p_buf_add_channel_list(buf, p2p->cfg->country, &p2p->channels);
+ if (go_dev_addr)
+ dev_addr = go_dev_addr;
+ else if (p2p->inv_role == P2P_INVITE_ROLE_CLIENT)
+ dev_addr = peer->info.p2p_device_addr;
+ else
+ dev_addr = p2p->cfg->dev_addr;
+ p2p_buf_add_group_id(buf, dev_addr, p2p->inv_ssid, p2p->inv_ssid_len);
+ p2p_buf_add_device_info(buf, p2p, peer);
+ p2p_buf_update_ie_hdr(buf, len);
+
+ return buf;
+}
+
+
+static struct wpabuf * p2p_build_invitation_resp(struct p2p_data *p2p,
+ struct p2p_device *peer,
+ u8 dialog_token, u8 status,
+ const u8 *group_bssid,
+ u8 reg_class, u8 channel,
+ struct p2p_channels *channels)
+{
+ struct wpabuf *buf;
+ u8 *len;
+
+ buf = wpabuf_alloc(1000);
+ if (buf == NULL)
+ return NULL;
+
+ p2p_buf_add_public_action_hdr(buf, P2P_INVITATION_RESP,
+ dialog_token);
+
+ len = p2p_buf_add_ie_hdr(buf);
+ p2p_buf_add_status(buf, status);
+ p2p_buf_add_config_timeout(buf, 0, 0); /* FIX */
+ if (reg_class && channel)
+ p2p_buf_add_operating_channel(buf, p2p->cfg->country,
+ reg_class, channel);
+ if (group_bssid)
+ p2p_buf_add_group_bssid(buf, group_bssid);
+ if (channels)
+ p2p_buf_add_channel_list(buf, p2p->cfg->country, channels);
+ p2p_buf_update_ie_hdr(buf, len);
+
+ return buf;
+}
+
+
+void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len, int rx_freq)
+{
+ struct p2p_device *dev;
+ struct p2p_message msg;
+ struct wpabuf *resp = NULL;
+ u8 status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
+ int freq;
+ int go = 0;
+ u8 group_bssid[ETH_ALEN], *bssid;
+ int op_freq = 0;
+ u8 reg_class = 0, channel = 0;
+ struct p2p_channels intersection, *channels = NULL;
+ int persistent;
+
+ os_memset(group_bssid, 0, sizeof(group_bssid));
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Received Invitation Request from " MACSTR " (freq=%d)",
+ MAC2STR(sa), rx_freq);
+
+ if (p2p_parse(data, len, &msg))
+ return;
+
+ dev = p2p_get_device(p2p, sa);
+ if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Invitation Request from unknown peer "
+ MACSTR, MAC2STR(sa));
+
+ if (p2p_add_device(p2p, sa, rx_freq, 0, data + 1, len - 1)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Invitation Request add device failed "
+ MACSTR, MAC2STR(sa));
+ status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
+ goto fail;
+ }
+
+ dev = p2p_get_device(p2p, sa);
+ if (dev == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Reject Invitation Request from unknown "
+ "peer " MACSTR, MAC2STR(sa));
+ status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
+ goto fail;
+ }
+ }
+
+ if (!msg.group_id || !msg.channel_list) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Mandatory attribute missing in Invitation "
+ "Request from " MACSTR, MAC2STR(sa));
+ status = P2P_SC_FAIL_INVALID_PARAMS;
+ goto fail;
+ }
+
+ if (msg.invitation_flags)
+ persistent = *msg.invitation_flags & P2P_INVITATION_FLAGS_TYPE;
+ else {
+ /* Invitation Flags is a mandatory attribute starting from P2P
+ * spec 1.06. As a backwards compatibility mechanism, assume
+ * the request was for a persistent group if the attribute is
+ * missing.
+ */
+ wpa_printf(MSG_DEBUG, "P2P: Mandatory Invitation Flags "
+ "attribute missing from Invitation Request");
+ persistent = 1;
+ }
+
+ if (p2p_peer_channels_check(p2p, &p2p->cfg->channels, dev,
+ msg.channel_list, msg.channel_list_len) <
+ 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No common channels found");
+ status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+ goto fail;
+ }
+
+ if (p2p->cfg->invitation_process) {
+ status = p2p->cfg->invitation_process(
+ p2p->cfg->cb_ctx, sa, msg.group_bssid, msg.group_id,
+ msg.group_id + ETH_ALEN, msg.group_id_len - ETH_ALEN,
+ &go, group_bssid, &op_freq, persistent);
+ }
+
+ if (op_freq) {
+ if (p2p_freq_to_channel(p2p->cfg->country, op_freq,
+ ®_class, &channel) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unknown forced freq %d MHz from "
+ "invitation_process()", op_freq);
+ status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+ goto fail;
+ }
+
+ p2p_channels_intersect(&p2p->cfg->channels, &dev->channels,
+ &intersection);
+ if (!p2p_channels_includes(&intersection, reg_class, channel))
+ {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: forced freq %d MHz not in the supported "
+ "channels interaction", op_freq);
+ status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+ goto fail;
+ }
+
+ if (status == P2P_SC_SUCCESS)
+ channels = &intersection;
+ } else {
+ op_freq = p2p_channel_to_freq(p2p->cfg->country,
+ p2p->cfg->op_reg_class,
+ p2p->cfg->op_channel);
+ if (op_freq < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unknown operational channel "
+ "(country=%c%c reg_class=%u channel=%u)",
+ p2p->cfg->country[0], p2p->cfg->country[1],
+ p2p->cfg->op_reg_class, p2p->cfg->op_channel);
+ status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+ goto fail;
+ }
+
+ p2p_channels_intersect(&p2p->cfg->channels, &dev->channels,
+ &intersection);
+ if (status == P2P_SC_SUCCESS) {
+ reg_class = p2p->cfg->op_reg_class;
+ channel = p2p->cfg->op_channel;
+ channels = &intersection;
+ }
+ }
+
+fail:
+ if (go && status == P2P_SC_SUCCESS && !is_zero_ether_addr(group_bssid))
+ bssid = group_bssid;
+ else
+ bssid = NULL;
+ resp = p2p_build_invitation_resp(p2p, dev, msg.dialog_token, status,
+ bssid, reg_class, channel, channels);
+
+ if (resp == NULL)
+ goto out;
+
+ if (rx_freq > 0)
+ freq = rx_freq;
+ else
+ freq = p2p_channel_to_freq(p2p->cfg->country,
+ p2p->cfg->reg_class,
+ p2p->cfg->channel);
+ if (freq < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unknown regulatory class/channel");
+ goto out;
+ }
+
+ /*
+ * Store copy of invitation data to be used when processing TX status
+ * callback for the Acton frame.
+ */
+ os_memcpy(p2p->inv_sa, sa, ETH_ALEN);
+ if (msg.group_bssid) {
+ os_memcpy(p2p->inv_group_bssid, msg.group_bssid, ETH_ALEN);
+ p2p->inv_group_bssid_ptr = p2p->inv_group_bssid;
+ } else
+ p2p->inv_group_bssid_ptr = NULL;
+ if (msg.group_id_len - ETH_ALEN <= 32) {
+ os_memcpy(p2p->inv_ssid, msg.group_id + ETH_ALEN,
+ msg.group_id_len - ETH_ALEN);
+ p2p->inv_ssid_len = msg.group_id_len - ETH_ALEN;
+ }
+ os_memcpy(p2p->inv_go_dev_addr, msg.group_id, ETH_ALEN);
+ p2p->inv_status = status;
+ p2p->inv_op_freq = op_freq;
+
+ p2p->pending_action_state = P2P_PENDING_INVITATION_RESPONSE;
+ if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr,
+ p2p->cfg->dev_addr,
+ wpabuf_head(resp), wpabuf_len(resp), 200) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to send Action frame");
+ }
+
+out:
+ wpabuf_free(resp);
+ p2p_parse_free(&msg);
+}
+
+
+void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len)
+{
+ struct p2p_device *dev;
+ struct p2p_message msg;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Received Invitation Response from " MACSTR,
+ MAC2STR(sa));
+
+ dev = p2p_get_device(p2p, sa);
+ if (dev == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Ignore Invitation Response from unknown peer "
+ MACSTR, MAC2STR(sa));
+ return;
+ }
+
+ if (dev != p2p->invite_peer) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Ignore unexpected Invitation Response from peer "
+ MACSTR, MAC2STR(sa));
+ return;
+ }
+
+ if (p2p_parse(data, len, &msg))
+ return;
+
+ if (!msg.status) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Mandatory Status attribute missing in "
+ "Invitation Response from " MACSTR, MAC2STR(sa));
+ p2p_parse_free(&msg);
+ return;
+ }
+
+ if (p2p->cfg->invitation_result)
+ p2p->cfg->invitation_result(p2p->cfg->cb_ctx, *msg.status,
+ msg.group_bssid);
+
+ p2p_parse_free(&msg);
+
+ p2p_clear_timeout(p2p);
+ p2p_set_state(p2p, P2P_IDLE);
+ p2p->invite_peer = NULL;
+}
+
+
+int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev,
+ const u8 *go_dev_addr)
+{
+ struct wpabuf *req;
+ int freq;
+
+ freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq;
+ if (freq <= 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No Listen/Operating frequency known for the "
+ "peer " MACSTR " to send Invitation Request",
+ MAC2STR(dev->info.p2p_device_addr));
+ return -1;
+ }
+
+ req = p2p_build_invitation_req(p2p, dev, go_dev_addr);
+ if (req == NULL)
+ return -1;
+ if (p2p->state != P2P_IDLE)
+ p2p_stop_listen_for_freq(p2p, freq);
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Sending Invitation Request");
+ p2p_set_state(p2p, P2P_INVITE);
+ p2p->pending_action_state = P2P_PENDING_INVITATION_REQUEST;
+ p2p->invite_peer = dev;
+ dev->invitation_reqs++;
+ if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr,
+ p2p->cfg->dev_addr, dev->info.p2p_device_addr,
+ wpabuf_head(req), wpabuf_len(req), 200) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to send Action frame");
+ /* Use P2P find to recover and retry */
+ p2p_set_timeout(p2p, 0, 0);
+ }
+
+ wpabuf_free(req);
+
+ return 0;
+}
+
+
+void p2p_invitation_req_cb(struct p2p_data *p2p, int success)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Invitation Request TX callback: success=%d", success);
+
+ if (p2p->invite_peer == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No pending Invite");
+ return;
+ }
+
+ /*
+ * Use P2P find, if needed, to find the other device from its listen
+ * channel.
+ */
+ p2p_set_state(p2p, P2P_INVITE);
+ p2p_set_timeout(p2p, 0, 100000);
+}
+
+
+void p2p_invitation_resp_cb(struct p2p_data *p2p, int success)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Invitation Response TX callback: success=%d", success);
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+
+ if (success && p2p->cfg->invitation_received) {
+ p2p->cfg->invitation_received(p2p->cfg->cb_ctx,
+ p2p->inv_sa,
+ p2p->inv_group_bssid_ptr,
+ p2p->inv_ssid, p2p->inv_ssid_len,
+ p2p->inv_go_dev_addr,
+ p2p->inv_status,
+ p2p->inv_op_freq);
+ }
+}
+
+
+int p2p_invite(struct p2p_data *p2p, const u8 *peer, enum p2p_invite_role role,
+ const u8 *bssid, const u8 *ssid, size_t ssid_len,
+ unsigned int force_freq, const u8 *go_dev_addr,
+ int persistent_group)
+{
+ struct p2p_device *dev;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Request to invite peer " MACSTR " role=%d persistent=%d "
+ "force_freq=%u",
+ MAC2STR(peer), role, persistent_group, force_freq);
+ if (bssid)
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Invitation for BSSID " MACSTR, MAC2STR(bssid));
+ if (go_dev_addr) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Invitation for GO Device Address " MACSTR,
+ MAC2STR(go_dev_addr));
+ os_memcpy(p2p->invite_go_dev_addr_buf, go_dev_addr, ETH_ALEN);
+ p2p->invite_go_dev_addr = p2p->invite_go_dev_addr_buf;
+ } else
+ p2p->invite_go_dev_addr = NULL;
+ wpa_hexdump_ascii(MSG_DEBUG, "P2P: Invitation for SSID",
+ ssid, ssid_len);
+
+ dev = p2p_get_device(p2p, peer);
+ if (dev == NULL || (dev->listen_freq <= 0 && dev->oper_freq <= 0)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Cannot invite unknown P2P Device " MACSTR,
+ MAC2STR(peer));
+ return -1;
+ }
+
+ if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) {
+ if (!(dev->info.dev_capab &
+ P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Cannot invite a P2P Device " MACSTR
+ " that is in a group and is not discoverable",
+ MAC2STR(peer));
+ }
+ /* TODO: use device discoverability request through GO */
+ }
+
+ dev->invitation_reqs = 0;
+
+ if (force_freq) {
+ if (p2p_freq_to_channel(p2p->cfg->country, force_freq,
+ &p2p->op_reg_class, &p2p->op_channel) <
+ 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported frequency %u MHz",
+ force_freq);
+ return -1;
+ }
+ p2p->channels.reg_classes = 1;
+ p2p->channels.reg_class[0].channels = 1;
+ p2p->channels.reg_class[0].reg_class = p2p->op_reg_class;
+ p2p->channels.reg_class[0].channel[0] = p2p->op_channel;
+ } else {
+ p2p->op_reg_class = p2p->cfg->op_reg_class;
+ p2p->op_channel = p2p->cfg->op_channel;
+ os_memcpy(&p2p->channels, &p2p->cfg->channels,
+ sizeof(struct p2p_channels));
+ }
+
+ if (p2p->state != P2P_IDLE)
+ p2p_stop_find(p2p);
+
+ p2p->inv_role = role;
+ p2p->inv_bssid_set = bssid != NULL;
+ if (bssid)
+ os_memcpy(p2p->inv_bssid, bssid, ETH_ALEN);
+ os_memcpy(p2p->inv_ssid, ssid, ssid_len);
+ p2p->inv_ssid_len = ssid_len;
+ p2p->inv_persistent = persistent_group;
+ return p2p_invite_send(p2p, dev, go_dev_addr);
+}
--- /dev/null
+/*
+ * P2P - IE parser
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "wps/wps_i.h"
+#include "p2p_i.h"
+
+
+static int p2p_parse_attribute(u8 id, const u8 *data, u16 len,
+ struct p2p_message *msg)
+{
+ const u8 *pos;
+ size_t i, nlen;
+ char devtype[WPS_DEV_TYPE_BUFSIZE];
+
+ switch (id) {
+ case P2P_ATTR_CAPABILITY:
+ if (len < 2) {
+ wpa_printf(MSG_DEBUG, "P2P: Too short Capability "
+ "attribute (length %d)", len);
+ return -1;
+ }
+ msg->capability = data;
+ wpa_printf(MSG_DEBUG, "P2P: * Device Capability %02x "
+ "Group Capability %02x",
+ data[0], data[1]);
+ break;
+ case P2P_ATTR_DEVICE_ID:
+ if (len < ETH_ALEN) {
+ wpa_printf(MSG_DEBUG, "P2P: Too short Device ID "
+ "attribute (length %d)", len);
+ return -1;
+ }
+ msg->device_id = data;
+ wpa_printf(MSG_DEBUG, "P2P: * Device ID " MACSTR,
+ MAC2STR(msg->device_id));
+ break;
+ case P2P_ATTR_GROUP_OWNER_INTENT:
+ if (len < 1) {
+ wpa_printf(MSG_DEBUG, "P2P: Too short GO Intent "
+ "attribute (length %d)", len);
+ return -1;
+ }
+ msg->go_intent = data;
+ wpa_printf(MSG_DEBUG, "P2P: * GO Intent: Intent %u "
+ "Tie breaker %u", data[0] >> 1, data[0] & 0x01);
+ break;
+ case P2P_ATTR_STATUS:
+ if (len < 1) {
+ wpa_printf(MSG_DEBUG, "P2P: Too short Status "
+ "attribute (length %d)", len);
+ return -1;
+ }
+ msg->status = data;
+ wpa_printf(MSG_DEBUG, "P2P: * Status: %d", data[0]);
+ break;
+ case P2P_ATTR_LISTEN_CHANNEL:
+ if (len == 0) {
+ wpa_printf(MSG_DEBUG, "P2P: * Listen Channel: Ignore "
+ "null channel");
+ break;
+ }
+ if (len < 5) {
+ wpa_printf(MSG_DEBUG, "P2P: Too short Listen Channel "
+ "attribute (length %d)", len);
+ return -1;
+ }
+ msg->listen_channel = data;
+ wpa_printf(MSG_DEBUG, "P2P: * Listen Channel: "
+ "Country %c%c(0x%02x) Regulatory "
+ "Class %d Channel Number %d", data[0], data[1],
+ data[2], data[3], data[4]);
+ break;
+ case P2P_ATTR_OPERATING_CHANNEL:
+ if (len == 0) {
+ wpa_printf(MSG_DEBUG, "P2P: * Operating Channel: "
+ "Ignore null channel");
+ break;
+ }
+ if (len < 5) {
+ wpa_printf(MSG_DEBUG, "P2P: Too short Operating "
+ "Channel attribute (length %d)", len);
+ return -1;
+ }
+ msg->operating_channel = data;
+ wpa_printf(MSG_DEBUG, "P2P: * Operating Channel: "
+ "Country %c%c(0x%02x) Regulatory "
+ "Class %d Channel Number %d", data[0], data[1],
+ data[2], data[3], data[4]);
+ break;
+ case P2P_ATTR_CHANNEL_LIST:
+ if (len < 3) {
+ wpa_printf(MSG_DEBUG, "P2P: Too short Channel List "
+ "attribute (length %d)", len);
+ return -1;
+ }
+ msg->channel_list = data;
+ msg->channel_list_len = len;
+ wpa_printf(MSG_DEBUG, "P2P: * Channel List: Country String "
+ "'%c%c(0x%02x)'", data[0], data[1], data[2]);
+ wpa_hexdump(MSG_MSGDUMP, "P2P: Channel List",
+ msg->channel_list, msg->channel_list_len);
+ break;
+ case P2P_ATTR_GROUP_INFO:
+ msg->group_info = data;
+ msg->group_info_len = len;
+ wpa_printf(MSG_DEBUG, "P2P: * Group Info");
+ break;
+ case P2P_ATTR_DEVICE_INFO:
+ if (len < ETH_ALEN + 2 + 8 + 1) {
+ wpa_printf(MSG_DEBUG, "P2P: Too short Device Info "
+ "attribute (length %d)", len);
+ return -1;
+ }
+ msg->p2p_device_info = data;
+ msg->p2p_device_info_len = len;
+ pos = data;
+ msg->p2p_device_addr = pos;
+ pos += ETH_ALEN;
+ msg->config_methods = WPA_GET_BE16(pos);
+ pos += 2;
+ msg->pri_dev_type = pos;
+ pos += 8;
+ msg->num_sec_dev_types = *pos++;
+ if (msg->num_sec_dev_types * 8 > data + len - pos) {
+ wpa_printf(MSG_DEBUG, "P2P: Device Info underflow");
+ return -1;
+ }
+ pos += msg->num_sec_dev_types * 8;
+ if (data + len - pos < 4) {
+ wpa_printf(MSG_DEBUG, "P2P: Invalid Device Name "
+ "length %d", (int) (data + len - pos));
+ return -1;
+ }
+ if (WPA_GET_BE16(pos) != ATTR_DEV_NAME) {
+ wpa_hexdump(MSG_DEBUG, "P2P: Unexpected Device Name "
+ "header", pos, 4);
+ return -1;
+ }
+ pos += 2;
+ nlen = WPA_GET_BE16(pos);
+ pos += 2;
+ if (data + len - pos < (int) nlen || nlen > 32) {
+ wpa_printf(MSG_DEBUG, "P2P: Invalid Device Name "
+ "length %d (buf len %d)", (int) nlen,
+ (int) (data + len - pos));
+ return -1;
+ }
+ os_memcpy(msg->device_name, pos, nlen);
+ msg->device_name[nlen] = '\0';
+ for (i = 0; i < nlen; i++) {
+ if (msg->device_name[i] == '\0')
+ break;
+ if (msg->device_name[i] > 0 &&
+ msg->device_name[i] < 32)
+ msg->device_name[i] = '_';
+ }
+ wpa_printf(MSG_DEBUG, "P2P: * Device Info: addr " MACSTR
+ " primary device type %s device name '%s' "
+ "config methods 0x%x",
+ MAC2STR(msg->p2p_device_addr),
+ wps_dev_type_bin2str(msg->pri_dev_type, devtype,
+ sizeof(devtype)),
+ msg->device_name, msg->config_methods);
+ break;
+ case P2P_ATTR_CONFIGURATION_TIMEOUT:
+ if (len < 2) {
+ wpa_printf(MSG_DEBUG, "P2P: Too short Configuration "
+ "Timeout attribute (length %d)", len);
+ return -1;
+ }
+ msg->config_timeout = data;
+ wpa_printf(MSG_DEBUG, "P2P: * Configuration Timeout");
+ break;
+ case P2P_ATTR_INTENDED_INTERFACE_ADDR:
+ if (len < ETH_ALEN) {
+ wpa_printf(MSG_DEBUG, "P2P: Too short Intended P2P "
+ "Interface Address attribute (length %d)",
+ len);
+ return -1;
+ }
+ msg->intended_addr = data;
+ wpa_printf(MSG_DEBUG, "P2P: * Intended P2P Interface Address: "
+ MACSTR, MAC2STR(msg->intended_addr));
+ break;
+ case P2P_ATTR_GROUP_BSSID:
+ if (len < ETH_ALEN) {
+ wpa_printf(MSG_DEBUG, "P2P: Too short P2P Group BSSID "
+ "attribute (length %d)", len);
+ return -1;
+ }
+ msg->group_bssid = data;
+ wpa_printf(MSG_DEBUG, "P2P: * P2P Group BSSID: " MACSTR,
+ MAC2STR(msg->group_bssid));
+ break;
+ case P2P_ATTR_GROUP_ID:
+ if (len < ETH_ALEN || len > ETH_ALEN + 32) {
+ wpa_printf(MSG_DEBUG, "P2P: Invalid P2P Group ID "
+ "attribute length %d", len);
+ return -1;
+ }
+ msg->group_id = data;
+ msg->group_id_len = len;
+ wpa_printf(MSG_DEBUG, "P2P: * P2P Group ID: Device Address "
+ MACSTR, MAC2STR(msg->group_id));
+ wpa_hexdump_ascii(MSG_DEBUG, "P2P: * P2P Group ID: SSID",
+ msg->group_id + ETH_ALEN,
+ msg->group_id_len - ETH_ALEN);
+ break;
+ case P2P_ATTR_INVITATION_FLAGS:
+ if (len < 1) {
+ wpa_printf(MSG_DEBUG, "P2P: Too short Invitation "
+ "Flag attribute (length %d)", len);
+ return -1;
+ }
+ msg->invitation_flags = data;
+ wpa_printf(MSG_DEBUG, "P2P: * Invitation Flags: bitmap 0x%x",
+ data[0]);
+ break;
+ case P2P_ATTR_MANAGEABILITY:
+ if (len < 1) {
+ wpa_printf(MSG_DEBUG, "P2P: Too short Manageability "
+ "attribute (length %d)", len);
+ return -1;
+ }
+ msg->manageability = data;
+ wpa_printf(MSG_DEBUG, "P2P: * Manageability: bitmap 0x%x",
+ data[0]);
+ break;
+ case P2P_ATTR_NOTICE_OF_ABSENCE:
+ if (len < 2) {
+ wpa_printf(MSG_DEBUG, "P2P: Too short Notice of "
+ "Absence attribute (length %d)", len);
+ return -1;
+ }
+ msg->noa = data;
+ msg->noa_len = len;
+ wpa_printf(MSG_DEBUG, "P2P: * Notice of Absence");
+ break;
+ case P2P_ATTR_EXT_LISTEN_TIMING:
+ if (len < 4) {
+ wpa_printf(MSG_DEBUG, "P2P: Too short Extended Listen "
+ "Timing attribute (length %d)", len);
+ return -1;
+ }
+ msg->ext_listen_timing = data;
+ wpa_printf(MSG_DEBUG, "P2P: * Extended Listen Timing "
+ "(period %u msec interval %u msec)",
+ WPA_GET_LE16(msg->ext_listen_timing),
+ WPA_GET_LE16(msg->ext_listen_timing + 2));
+ break;
+ case P2P_ATTR_MINOR_REASON_CODE:
+ if (len < 1) {
+ wpa_printf(MSG_DEBUG, "P2P: Too short Minor Reason "
+ "Code attribute (length %d)", len);
+ return -1;
+ }
+ msg->minor_reason_code = data;
+ wpa_printf(MSG_DEBUG, "P2P: * Minor Reason Code: %u",
+ *msg->minor_reason_code);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "P2P: Skipped unknown attribute %d "
+ "(length %d)", id, len);
+ break;
+ }
+
+ return 0;
+}
+
+
+/**
+ * p2p_parse_p2p_ie - Parse P2P IE
+ * @buf: Concatenated P2P IE(s) payload
+ * @msg: Buffer for returning parsed attributes
+ * Returns: 0 on success, -1 on failure
+ *
+ * Note: Caller is responsible for clearing the msg data structure before
+ * calling this function.
+ */
+int p2p_parse_p2p_ie(const struct wpabuf *buf, struct p2p_message *msg)
+{
+ const u8 *pos = wpabuf_head_u8(buf);
+ const u8 *end = pos + wpabuf_len(buf);
+
+ wpa_printf(MSG_DEBUG, "P2P: Parsing P2P IE");
+
+ while (pos < end) {
+ u16 attr_len;
+ if (pos + 2 >= end) {
+ wpa_printf(MSG_DEBUG, "P2P: Invalid P2P attribute");
+ return -1;
+ }
+ attr_len = WPA_GET_LE16(pos + 1);
+ wpa_printf(MSG_DEBUG, "P2P: Attribute %d length %u",
+ pos[0], attr_len);
+ if (pos + 3 + attr_len > end) {
+ wpa_printf(MSG_DEBUG, "P2P: Attribute underflow "
+ "(len=%u left=%d)",
+ attr_len, (int) (end - pos - 3));
+ wpa_hexdump(MSG_MSGDUMP, "P2P: Data", pos, end - pos);
+ return -1;
+ }
+ if (p2p_parse_attribute(pos[0], pos + 3, attr_len, msg))
+ return -1;
+ pos += 3 + attr_len;
+ }
+
+ return 0;
+}
+
+
+static int p2p_parse_wps_ie(const struct wpabuf *buf, struct p2p_message *msg)
+{
+ struct wps_parse_attr attr;
+ int i;
+
+ wpa_printf(MSG_DEBUG, "P2P: Parsing WPS IE");
+ if (wps_parse_msg(buf, &attr))
+ return -1;
+ if (attr.dev_name && attr.dev_name_len < sizeof(msg->device_name) &&
+ !msg->device_name[0])
+ os_memcpy(msg->device_name, attr.dev_name, attr.dev_name_len);
+ if (attr.config_methods) {
+ msg->wps_config_methods =
+ WPA_GET_BE16(attr.config_methods);
+ wpa_printf(MSG_DEBUG, "P2P: Config Methods (WPS): 0x%x",
+ msg->wps_config_methods);
+ }
+ if (attr.dev_password_id) {
+ msg->dev_password_id = WPA_GET_BE16(attr.dev_password_id);
+ wpa_printf(MSG_DEBUG, "P2P: Device Password ID: %d",
+ msg->dev_password_id);
+ }
+ if (attr.primary_dev_type) {
+ char devtype[WPS_DEV_TYPE_BUFSIZE];
+ msg->wps_pri_dev_type = attr.primary_dev_type;
+ wpa_printf(MSG_DEBUG, "P2P: Primary Device Type (WPS): %s",
+ wps_dev_type_bin2str(msg->wps_pri_dev_type, devtype,
+ sizeof(devtype)));
+ }
+ if (attr.sec_dev_type_list) {
+ msg->wps_sec_dev_type_list = attr.sec_dev_type_list;
+ msg->wps_sec_dev_type_list_len = attr.sec_dev_type_list_len;
+ }
+
+ for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
+ msg->wps_vendor_ext[i] = attr.vendor_ext[i];
+ msg->wps_vendor_ext_len[i] = attr.vendor_ext_len[i];
+ }
+
+ msg->manufacturer = attr.manufacturer;
+ msg->manufacturer_len = attr.manufacturer_len;
+ msg->model_name = attr.model_name;
+ msg->model_name_len = attr.model_name_len;
+ msg->model_number = attr.model_number;
+ msg->model_number_len = attr.model_number_len;
+ msg->serial_number = attr.serial_number;
+ msg->serial_number_len = attr.serial_number_len;
+
+ return 0;
+}
+
+
+/**
+ * p2p_parse_ies - Parse P2P message IEs (both WPS and P2P IE)
+ * @data: IEs from the message
+ * @len: Length of data buffer in octets
+ * @msg: Buffer for returning parsed attributes
+ * Returns: 0 on success, -1 on failure
+ *
+ * Note: Caller is responsible for clearing the msg data structure before
+ * calling this function.
+ *
+ * Note: Caller must free temporary memory allocations by calling
+ * p2p_parse_free() when the parsed data is not needed anymore.
+ */
+int p2p_parse_ies(const u8 *data, size_t len, struct p2p_message *msg)
+{
+ struct ieee802_11_elems elems;
+
+ ieee802_11_parse_elems(data, len, &elems, 0);
+ if (elems.ds_params && elems.ds_params_len >= 1)
+ msg->ds_params = elems.ds_params;
+ if (elems.ssid)
+ msg->ssid = elems.ssid - 2;
+
+ msg->wps_attributes = ieee802_11_vendor_ie_concat(data, len,
+ WPS_DEV_OUI_WFA);
+ if (msg->wps_attributes &&
+ p2p_parse_wps_ie(msg->wps_attributes, msg)) {
+ p2p_parse_free(msg);
+ return -1;
+ }
+
+ msg->p2p_attributes = ieee802_11_vendor_ie_concat(data, len,
+ P2P_IE_VENDOR_TYPE);
+ if (msg->p2p_attributes &&
+ p2p_parse_p2p_ie(msg->p2p_attributes, msg)) {
+ wpa_printf(MSG_DEBUG, "P2P: Failed to parse P2P IE data");
+ if (msg->p2p_attributes)
+ wpa_hexdump_buf(MSG_MSGDUMP, "P2P: P2P IE data",
+ msg->p2p_attributes);
+ p2p_parse_free(msg);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * p2p_parse - Parse a P2P Action frame contents
+ * @data: Action frame payload after Category and Code fields
+ * @len: Length of data buffer in octets
+ * @msg: Buffer for returning parsed attributes
+ * Returns: 0 on success, -1 on failure
+ *
+ * Note: Caller must free temporary memory allocations by calling
+ * p2p_parse_free() when the parsed data is not needed anymore.
+ */
+int p2p_parse(const u8 *data, size_t len, struct p2p_message *msg)
+{
+ os_memset(msg, 0, sizeof(*msg));
+ wpa_printf(MSG_DEBUG, "P2P: Parsing the received message");
+ if (len < 1) {
+ wpa_printf(MSG_DEBUG, "P2P: No Dialog Token in the message");
+ return -1;
+ }
+ msg->dialog_token = data[0];
+ wpa_printf(MSG_DEBUG, "P2P: * Dialog Token: %d", msg->dialog_token);
+
+ return p2p_parse_ies(data + 1, len - 1, msg);
+}
+
+
+/**
+ * p2p_parse_free - Free temporary data from P2P parsing
+ * @msg: Parsed attributes
+ */
+void p2p_parse_free(struct p2p_message *msg)
+{
+ wpabuf_free(msg->p2p_attributes);
+ msg->p2p_attributes = NULL;
+ wpabuf_free(msg->wps_attributes);
+ msg->wps_attributes = NULL;
+}
+
+
+int p2p_group_info_parse(const u8 *gi, size_t gi_len,
+ struct p2p_group_info *info)
+{
+ const u8 *g, *gend;
+
+ os_memset(info, 0, sizeof(*info));
+ if (gi == NULL)
+ return 0;
+
+ g = gi;
+ gend = gi + gi_len;
+ while (g < gend) {
+ struct p2p_client_info *cli;
+ const u8 *t, *cend;
+ int count;
+
+ cli = &info->client[info->num_clients];
+ cend = g + 1 + g[0];
+ if (cend > gend)
+ return -1; /* invalid data */
+ /* g at start of P2P Client Info Descriptor */
+ /* t at Device Capability Bitmap */
+ t = g + 1 + 2 * ETH_ALEN;
+ if (t > cend)
+ return -1; /* invalid data */
+ cli->p2p_device_addr = g + 1;
+ cli->p2p_interface_addr = g + 1 + ETH_ALEN;
+ cli->dev_capab = t[0];
+
+ if (t + 1 + 2 + 8 + 1 > cend)
+ return -1; /* invalid data */
+
+ cli->config_methods = WPA_GET_BE16(&t[1]);
+ cli->pri_dev_type = &t[3];
+
+ t += 1 + 2 + 8;
+ /* t at Number of Secondary Device Types */
+ cli->num_sec_dev_types = *t++;
+ if (t + 8 * cli->num_sec_dev_types > cend)
+ return -1; /* invalid data */
+ cli->sec_dev_types = t;
+ t += 8 * cli->num_sec_dev_types;
+
+ /* t at Device Name in WPS TLV format */
+ if (t + 2 + 2 > cend)
+ return -1; /* invalid data */
+ if (WPA_GET_BE16(t) != ATTR_DEV_NAME)
+ return -1; /* invalid Device Name TLV */
+ t += 2;
+ count = WPA_GET_BE16(t);
+ t += 2;
+ if (count > cend - t)
+ return -1; /* invalid Device Name TLV */
+ if (count >= 32)
+ count = 32;
+ cli->dev_name = (const char *) t;
+ cli->dev_name_len = count;
+
+ g = cend;
+
+ info->num_clients++;
+ if (info->num_clients == P2P_MAX_GROUP_ENTRIES)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int p2p_group_info_text(const u8 *gi, size_t gi_len, char *buf,
+ char *end)
+{
+ char *pos = buf;
+ int ret;
+ struct p2p_group_info info;
+ unsigned int i;
+
+ if (p2p_group_info_parse(gi, gi_len, &info) < 0)
+ return 0;
+
+ for (i = 0; i < info.num_clients; i++) {
+ struct p2p_client_info *cli;
+ char name[33];
+ char devtype[WPS_DEV_TYPE_BUFSIZE];
+ u8 s;
+ int count;
+
+ cli = &info.client[i];
+ ret = os_snprintf(pos, end - pos, "p2p_group_client: "
+ "dev=" MACSTR " iface=" MACSTR,
+ MAC2STR(cli->p2p_device_addr),
+ MAC2STR(cli->p2p_interface_addr));
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+
+ ret = os_snprintf(pos, end - pos,
+ " dev_capab=0x%x config_methods=0x%x "
+ "dev_type=%s",
+ cli->dev_capab, cli->config_methods,
+ wps_dev_type_bin2str(cli->pri_dev_type,
+ devtype,
+ sizeof(devtype)));
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+
+ for (s = 0; s < cli->num_sec_dev_types; s++) {
+ ret = os_snprintf(pos, end - pos, " dev_type=%s",
+ wps_dev_type_bin2str(
+ &cli->sec_dev_types[s * 8],
+ devtype, sizeof(devtype)));
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ }
+
+ os_memcpy(name, cli->dev_name, cli->dev_name_len);
+ name[cli->dev_name_len] = '\0';
+ count = (int) cli->dev_name_len - 1;
+ while (count >= 0) {
+ if (name[count] > 0 && name[count] < 32)
+ name[count] = '_';
+ count--;
+ }
+
+ ret = os_snprintf(pos, end - pos, " dev_name='%s'\n", name);
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ }
+
+ return pos - buf;
+}
+
+
+/**
+ * p2p_attr_text - Build text format description of P2P IE attributes
+ * @data: P2P IE contents
+ * @buf: Buffer for returning text
+ * @end: Pointer to the end of the buf area
+ * Returns: Number of octets written to the buffer or -1 on faikure
+ *
+ * This function can be used to parse P2P IE contents into text format
+ * field=value lines.
+ */
+int p2p_attr_text(struct wpabuf *data, char *buf, char *end)
+{
+ struct p2p_message msg;
+ char *pos = buf;
+ int ret;
+
+ os_memset(&msg, 0, sizeof(msg));
+ if (p2p_parse_p2p_ie(data, &msg))
+ return -1;
+
+ if (msg.capability) {
+ ret = os_snprintf(pos, end - pos,
+ "p2p_dev_capab=0x%x\n"
+ "p2p_group_capab=0x%x\n",
+ msg.capability[0], msg.capability[1]);
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ }
+
+ if (msg.pri_dev_type) {
+ char devtype[WPS_DEV_TYPE_BUFSIZE];
+ ret = os_snprintf(pos, end - pos,
+ "p2p_primary_device_type=%s\n",
+ wps_dev_type_bin2str(msg.pri_dev_type,
+ devtype,
+ sizeof(devtype)));
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ }
+
+ ret = os_snprintf(pos, end - pos, "p2p_device_name=%s\n",
+ msg.device_name);
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+
+ if (msg.p2p_device_addr) {
+ ret = os_snprintf(pos, end - pos, "p2p_device_addr=" MACSTR
+ "\n",
+ MAC2STR(msg.p2p_device_addr));
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ }
+
+ ret = os_snprintf(pos, end - pos, "p2p_config_methods=0x%x\n",
+ msg.config_methods);
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+
+ ret = p2p_group_info_text(msg.group_info, msg.group_info_len,
+ pos, end);
+ if (ret < 0)
+ return pos - buf;
+ pos += ret;
+
+ return pos - buf;
+}
+
+
+int p2p_get_cross_connect_disallowed(const struct wpabuf *p2p_ie)
+{
+ struct p2p_message msg;
+
+ os_memset(&msg, 0, sizeof(msg));
+ if (p2p_parse_p2p_ie(p2p_ie, &msg))
+ return 0;
+
+ if (!msg.manageability)
+ return 0;
+
+ return !(msg.manageability[0] & P2P_MAN_CROSS_CONNECTION_PERMITTED);
+}
+
+
+u8 p2p_get_group_capab(const struct wpabuf *p2p_ie)
+{
+ struct p2p_message msg;
+
+ os_memset(&msg, 0, sizeof(msg));
+ if (p2p_parse_p2p_ie(p2p_ie, &msg))
+ return 0;
+
+ if (!msg.capability)
+ return 0;
+
+ return msg.capability[1];
+}
+
+
+const u8 * p2p_get_go_dev_addr(const struct wpabuf *p2p_ie)
+{
+ struct p2p_message msg;
+
+ os_memset(&msg, 0, sizeof(msg));
+ if (p2p_parse_p2p_ie(p2p_ie, &msg))
+ return NULL;
+
+ if (msg.p2p_device_addr)
+ return msg.p2p_device_addr;
+ if (msg.device_id)
+ return msg.device_id;
+
+ return NULL;
+}
--- /dev/null
+/*
+ * Wi-Fi Direct - P2P provision discovery
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.h"
+#include "wps/wps_defs.h"
+#include "p2p_i.h"
+#include "p2p.h"
+
+
+/*
+ * Number of retries to attempt for provision discovery requests during IDLE
+ * state in case the peer is not listening.
+ */
+#define MAX_PROV_DISC_REQ_RETRIES 10
+
+
+static void p2p_build_wps_ie_config_methods(struct wpabuf *buf,
+ u16 config_methods)
+{
+ u8 *len;
+ wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+ len = wpabuf_put(buf, 1);
+ wpabuf_put_be32(buf, WPS_DEV_OUI_WFA);
+
+ /* Config Methods */
+ wpabuf_put_be16(buf, ATTR_CONFIG_METHODS);
+ wpabuf_put_be16(buf, 2);
+ wpabuf_put_be16(buf, config_methods);
+
+ p2p_buf_update_ie_hdr(buf, len);
+}
+
+
+static struct wpabuf * p2p_build_prov_disc_req(struct p2p_data *p2p,
+ u8 dialog_token,
+ u16 config_methods,
+ struct p2p_device *go)
+{
+ struct wpabuf *buf;
+ u8 *len;
+
+ buf = wpabuf_alloc(1000);
+ if (buf == NULL)
+ return NULL;
+
+ p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_REQ, dialog_token);
+
+ len = p2p_buf_add_ie_hdr(buf);
+ p2p_buf_add_capability(buf, p2p->dev_capab, 0);
+ p2p_buf_add_device_info(buf, p2p, NULL);
+ if (go) {
+ p2p_buf_add_group_id(buf, go->info.p2p_device_addr,
+ go->oper_ssid, go->oper_ssid_len);
+ }
+ p2p_buf_update_ie_hdr(buf, len);
+
+ /* WPS IE with Config Methods attribute */
+ p2p_build_wps_ie_config_methods(buf, config_methods);
+
+ return buf;
+}
+
+
+static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p,
+ u8 dialog_token,
+ u16 config_methods)
+{
+ struct wpabuf *buf;
+
+ buf = wpabuf_alloc(100);
+ if (buf == NULL)
+ return NULL;
+
+ p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_RESP, dialog_token);
+
+ /* WPS IE with Config Methods attribute */
+ p2p_build_wps_ie_config_methods(buf, config_methods);
+
+ return buf;
+}
+
+
+void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len, int rx_freq)
+{
+ struct p2p_message msg;
+ struct p2p_device *dev;
+ int freq;
+ int reject = 1;
+ struct wpabuf *resp;
+
+ if (p2p_parse(data, len, &msg))
+ return;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Received Provision Discovery Request from " MACSTR
+ " with config methods 0x%x (freq=%d)",
+ MAC2STR(sa), msg.wps_config_methods, rx_freq);
+
+ dev = p2p_get_device(p2p, sa);
+ if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Provision Discovery Request from "
+ "unknown peer " MACSTR, MAC2STR(sa));
+ if (p2p_add_device(p2p, sa, rx_freq, 0, data + 1, len - 1)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Provision Discovery Request add device "
+ "failed " MACSTR, MAC2STR(sa));
+ }
+ }
+
+ if (!(msg.wps_config_methods &
+ (WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD |
+ WPS_CONFIG_PUSHBUTTON))) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Unsupported "
+ "Config Methods in Provision Discovery Request");
+ goto out;
+ }
+
+ if (dev)
+ dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY |
+ P2P_DEV_PD_PEER_KEYPAD);
+ if (msg.wps_config_methods & WPS_CONFIG_DISPLAY) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR
+ " requested us to show a PIN on display", MAC2STR(sa));
+ if (dev)
+ dev->flags |= P2P_DEV_PD_PEER_KEYPAD;
+ } else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR
+ " requested us to write its PIN using keypad",
+ MAC2STR(sa));
+ if (dev)
+ dev->flags |= P2P_DEV_PD_PEER_DISPLAY;
+ }
+
+ reject = 0;
+
+out:
+ resp = p2p_build_prov_disc_resp(p2p, msg.dialog_token,
+ reject ? 0 : msg.wps_config_methods);
+ if (resp == NULL) {
+ p2p_parse_free(&msg);
+ return;
+ }
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Sending Provision Discovery Response");
+ if (rx_freq > 0)
+ freq = rx_freq;
+ else
+ freq = p2p_channel_to_freq(p2p->cfg->country,
+ p2p->cfg->reg_class,
+ p2p->cfg->channel);
+ if (freq < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unknown regulatory class/channel");
+ wpabuf_free(resp);
+ p2p_parse_free(&msg);
+ return;
+ }
+ p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+ if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr,
+ p2p->cfg->dev_addr,
+ wpabuf_head(resp), wpabuf_len(resp), 200) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to send Action frame");
+ }
+
+ wpabuf_free(resp);
+
+ if (!reject && p2p->cfg->prov_disc_req) {
+ const u8 *dev_addr = sa;
+ if (msg.p2p_device_addr)
+ dev_addr = msg.p2p_device_addr;
+ p2p->cfg->prov_disc_req(p2p->cfg->cb_ctx, sa,
+ msg.wps_config_methods,
+ dev_addr, msg.pri_dev_type,
+ msg.device_name, msg.config_methods,
+ msg.capability ? msg.capability[0] : 0,
+ msg.capability ? msg.capability[1] :
+ 0,
+ msg.group_id, msg.group_id_len);
+ }
+ p2p_parse_free(&msg);
+}
+
+
+void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len)
+{
+ struct p2p_message msg;
+ struct p2p_device *dev;
+ u16 report_config_methods = 0;
+
+ if (p2p_parse(data, len, &msg))
+ return;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Received Provisioning Discovery Response from " MACSTR
+ " with config methods 0x%x",
+ MAC2STR(sa), msg.wps_config_methods);
+
+ dev = p2p_get_device(p2p, sa);
+ if (dev == NULL || !dev->req_config_methods) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Ignore Provisioning Discovery Response from "
+ MACSTR " with no pending request", MAC2STR(sa));
+ p2p_parse_free(&msg);
+ return;
+ }
+
+ if (p2p->pending_action_state == P2P_PENDING_PD) {
+ os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN);
+ p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+ }
+
+ if (dev->dialog_token != msg.dialog_token) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Ignore Provisioning Discovery Response with "
+ "unexpected Dialog Token %u (expected %u)",
+ msg.dialog_token, dev->dialog_token);
+ p2p_parse_free(&msg);
+ return;
+ }
+
+ /*
+ * If the response is from the peer to whom a user initiated request
+ * was sent earlier, we reset that state info here.
+ */
+ if (p2p->user_initiated_pd &&
+ os_memcmp(p2p->pending_pd_devaddr, sa, ETH_ALEN) == 0)
+ p2p_reset_pending_pd(p2p);
+
+ if (msg.wps_config_methods != dev->req_config_methods) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer rejected "
+ "our Provisioning Discovery Request");
+ if (p2p->cfg->prov_disc_fail)
+ p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, sa,
+ P2P_PROV_DISC_REJECTED);
+ p2p_parse_free(&msg);
+ goto out;
+ }
+
+ report_config_methods = dev->req_config_methods;
+ dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY |
+ P2P_DEV_PD_PEER_KEYPAD);
+ if (dev->req_config_methods & WPS_CONFIG_DISPLAY) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR
+ " accepted to show a PIN on display", MAC2STR(sa));
+ dev->flags |= P2P_DEV_PD_PEER_DISPLAY;
+ } else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR
+ " accepted to write our PIN using keypad",
+ MAC2STR(sa));
+ dev->flags |= P2P_DEV_PD_PEER_KEYPAD;
+ }
+
+ /* Store the provisioning info */
+ dev->wps_prov_info = msg.wps_config_methods;
+
+ p2p_parse_free(&msg);
+
+out:
+ dev->req_config_methods = 0;
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+ if (p2p->cfg->prov_disc_resp)
+ p2p->cfg->prov_disc_resp(p2p->cfg->cb_ctx, sa,
+ report_config_methods);
+}
+
+
+int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev,
+ int join, int force_freq)
+{
+ struct wpabuf *req;
+ int freq;
+
+ if (force_freq > 0)
+ freq = force_freq;
+ else
+ freq = dev->listen_freq > 0 ? dev->listen_freq :
+ dev->oper_freq;
+ if (freq <= 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No Listen/Operating frequency known for the "
+ "peer " MACSTR " to send Provision Discovery Request",
+ MAC2STR(dev->info.p2p_device_addr));
+ return -1;
+ }
+
+ if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) {
+ if (!(dev->info.dev_capab &
+ P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Cannot use PD with P2P Device " MACSTR
+ " that is in a group and is not discoverable",
+ MAC2STR(dev->info.p2p_device_addr));
+ return -1;
+ }
+ /* TODO: use device discoverability request through GO */
+ }
+
+ dev->dialog_token++;
+ if (dev->dialog_token == 0)
+ dev->dialog_token = 1;
+ req = p2p_build_prov_disc_req(p2p, dev->dialog_token,
+ dev->req_config_methods,
+ join ? dev : NULL);
+ if (req == NULL)
+ return -1;
+
+ if (p2p->state != P2P_IDLE)
+ p2p_stop_listen_for_freq(p2p, freq);
+ p2p->pending_action_state = P2P_PENDING_PD;
+ if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr,
+ p2p->cfg->dev_addr, dev->info.p2p_device_addr,
+ wpabuf_head(req), wpabuf_len(req), 200) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to send Action frame");
+ wpabuf_free(req);
+ return -1;
+ }
+
+ os_memcpy(p2p->pending_pd_devaddr, dev->info.p2p_device_addr, ETH_ALEN);
+
+ wpabuf_free(req);
+ return 0;
+}
+
+
+int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr,
+ u16 config_methods, int join, int force_freq)
+{
+ struct p2p_device *dev;
+
+ dev = p2p_get_device(p2p, peer_addr);
+ if (dev == NULL)
+ dev = p2p_get_device_interface(p2p, peer_addr);
+ if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Provision "
+ "Discovery Request destination " MACSTR
+ " not yet known", MAC2STR(peer_addr));
+ return -1;
+ }
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Provision Discovery "
+ "Request with " MACSTR " (config methods 0x%x)",
+ MAC2STR(peer_addr), config_methods);
+ if (config_methods == 0)
+ return -1;
+
+ /* Reset provisioning info */
+ dev->wps_prov_info = 0;
+
+ dev->req_config_methods = config_methods;
+ if (join)
+ dev->flags |= P2P_DEV_PD_FOR_JOIN;
+ else
+ dev->flags &= ~P2P_DEV_PD_FOR_JOIN;
+
+ if (p2p->state != P2P_IDLE && p2p->state != P2P_SEARCH &&
+ p2p->state != P2P_LISTEN_ONLY) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Busy with other "
+ "operations; postpone Provision Discovery Request "
+ "with " MACSTR " (config methods 0x%x)",
+ MAC2STR(peer_addr), config_methods);
+ return 0;
+ }
+
+ /*
+ * We use the join param as a cue to differentiate between user
+ * initiated PD request and one issued during finds (internal).
+ */
+ p2p->user_initiated_pd = !join;
+
+ /* Also set some retries to attempt in case of IDLE state */
+ if (p2p->user_initiated_pd && p2p->state == P2P_IDLE)
+ p2p->pd_retries = MAX_PROV_DISC_REQ_RETRIES;
+
+ return p2p_send_prov_disc_req(p2p, dev, join, force_freq);
+}
+
+
+void p2p_reset_pending_pd(struct p2p_data *p2p)
+{
+ struct p2p_device *dev;
+
+ dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
+ if (os_memcmp(p2p->pending_pd_devaddr,
+ dev->info.p2p_device_addr, ETH_ALEN))
+ continue;
+ if (!dev->req_config_methods)
+ continue;
+ if (dev->flags & P2P_DEV_PD_FOR_JOIN)
+ continue;
+ /* Reset the config methods of the device */
+ dev->req_config_methods = 0;
+ }
+
+ p2p->user_initiated_pd = 0;
+ os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN);
+ p2p->pd_retries = 0;
+}
--- /dev/null
+/*
+ * Wi-Fi Direct - P2P service discovery
+ * Copyright (c) 2009, Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/gas.h"
+#include "p2p_i.h"
+#include "p2p.h"
+
+
+struct p2p_sd_query * p2p_pending_sd_req(struct p2p_data *p2p,
+ struct p2p_device *dev)
+{
+ struct p2p_sd_query *q;
+
+ if (!(dev->info.dev_capab & P2P_DEV_CAPAB_SERVICE_DISCOVERY))
+ return 0; /* peer does not support SD */
+
+ for (q = p2p->sd_queries; q; q = q->next) {
+ if (q->for_all_peers && !(dev->flags & P2P_DEV_SD_INFO))
+ return q;
+ if (!q->for_all_peers &&
+ os_memcmp(q->peer, dev->info.p2p_device_addr, ETH_ALEN) ==
+ 0)
+ return q;
+ }
+
+ return NULL;
+}
+
+
+static int p2p_unlink_sd_query(struct p2p_data *p2p,
+ struct p2p_sd_query *query)
+{
+ struct p2p_sd_query *q, *prev;
+ q = p2p->sd_queries;
+ prev = NULL;
+ while (q) {
+ if (q == query) {
+ if (prev)
+ prev->next = q->next;
+ else
+ p2p->sd_queries = q->next;
+ if (p2p->sd_query == query)
+ p2p->sd_query = NULL;
+ return 1;
+ }
+ prev = q;
+ q = q->next;
+ }
+ return 0;
+}
+
+
+static void p2p_free_sd_query(struct p2p_sd_query *q)
+{
+ if (q == NULL)
+ return;
+ wpabuf_free(q->tlvs);
+ os_free(q);
+}
+
+
+void p2p_free_sd_queries(struct p2p_data *p2p)
+{
+ struct p2p_sd_query *q, *prev;
+ q = p2p->sd_queries;
+ p2p->sd_queries = NULL;
+ while (q) {
+ prev = q;
+ q = q->next;
+ p2p_free_sd_query(prev);
+ }
+}
+
+
+static struct wpabuf * p2p_build_sd_query(u16 update_indic,
+ struct wpabuf *tlvs)
+{
+ struct wpabuf *buf;
+ u8 *len_pos;
+
+ buf = gas_anqp_build_initial_req(0, 100 + wpabuf_len(tlvs));
+ if (buf == NULL)
+ return NULL;
+
+ /* ANQP Query Request Frame */
+ len_pos = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, P2P_OUI_TYPE);
+ wpabuf_put_le16(buf, update_indic); /* Service Update Indicator */
+ wpabuf_put_buf(buf, tlvs);
+ gas_anqp_set_element_len(buf, len_pos);
+
+ gas_anqp_set_len(buf);
+
+ return buf;
+}
+
+
+static void p2p_send_gas_comeback_req(struct p2p_data *p2p, const u8 *dst,
+ u8 dialog_token, int freq)
+{
+ struct wpabuf *req;
+
+ req = gas_build_comeback_req(dialog_token);
+ if (req == NULL)
+ return;
+
+ p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+ if (p2p_send_action(p2p, freq, dst, p2p->cfg->dev_addr, dst,
+ wpabuf_head(req), wpabuf_len(req), 200) < 0)
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to send Action frame");
+
+ wpabuf_free(req);
+}
+
+
+static struct wpabuf * p2p_build_sd_response(u8 dialog_token, u16 status_code,
+ u16 comeback_delay,
+ u16 update_indic,
+ const struct wpabuf *tlvs)
+{
+ struct wpabuf *buf;
+ u8 *len_pos;
+
+ buf = gas_anqp_build_initial_resp(dialog_token, status_code,
+ comeback_delay,
+ 100 + (tlvs ? wpabuf_len(tlvs) : 0));
+ if (buf == NULL)
+ return NULL;
+
+ if (tlvs) {
+ /* ANQP Query Response Frame */
+ len_pos = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, P2P_OUI_TYPE);
+ /* Service Update Indicator */
+ wpabuf_put_le16(buf, update_indic);
+ wpabuf_put_buf(buf, tlvs);
+ gas_anqp_set_element_len(buf, len_pos);
+ }
+
+ gas_anqp_set_len(buf);
+
+ return buf;
+}
+
+
+static struct wpabuf * p2p_build_gas_comeback_resp(u8 dialog_token,
+ u16 status_code,
+ u16 update_indic,
+ const u8 *data, size_t len,
+ u8 frag_id, u8 more,
+ u16 total_len)
+{
+ struct wpabuf *buf;
+
+ buf = gas_anqp_build_comeback_resp(dialog_token, status_code, frag_id,
+ more, 0, 100 + len);
+ if (buf == NULL)
+ return NULL;
+
+ if (frag_id == 0) {
+ /* ANQP Query Response Frame */
+ wpabuf_put_le16(buf, ANQP_VENDOR_SPECIFIC); /* Info ID */
+ wpabuf_put_le16(buf, 3 + 1 + 2 + total_len);
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, P2P_OUI_TYPE);
+ /* Service Update Indicator */
+ wpabuf_put_le16(buf, update_indic);
+ }
+
+ wpabuf_put_data(buf, data, len);
+ gas_anqp_set_len(buf);
+
+ return buf;
+}
+
+
+int p2p_start_sd(struct p2p_data *p2p, struct p2p_device *dev)
+{
+ struct wpabuf *req;
+ int ret = 0;
+ struct p2p_sd_query *query;
+ int freq;
+
+ freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq;
+ if (freq <= 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No Listen/Operating frequency known for the "
+ "peer " MACSTR " to send SD Request",
+ MAC2STR(dev->info.p2p_device_addr));
+ return -1;
+ }
+
+ query = p2p_pending_sd_req(p2p, dev);
+ if (query == NULL)
+ return -1;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Start Service Discovery with " MACSTR,
+ MAC2STR(dev->info.p2p_device_addr));
+
+ req = p2p_build_sd_query(p2p->srv_update_indic, query->tlvs);
+ if (req == NULL)
+ return -1;
+
+ p2p->sd_peer = dev;
+ p2p->sd_query = query;
+ p2p->pending_action_state = P2P_PENDING_SD;
+
+ if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr,
+ p2p->cfg->dev_addr, dev->info.p2p_device_addr,
+ wpabuf_head(req), wpabuf_len(req), 5000) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to send Action frame");
+ ret = -1;
+ }
+
+ wpabuf_free(req);
+
+ return ret;
+}
+
+
+void p2p_rx_gas_initial_req(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len, int rx_freq)
+{
+ const u8 *pos = data;
+ const u8 *end = data + len;
+ const u8 *next;
+ u8 dialog_token;
+ u16 slen;
+ int freq;
+ u16 update_indic;
+
+
+ if (p2p->cfg->sd_request == NULL)
+ return;
+
+ if (rx_freq > 0)
+ freq = rx_freq;
+ else
+ freq = p2p_channel_to_freq(p2p->cfg->country,
+ p2p->cfg->reg_class,
+ p2p->cfg->channel);
+ if (freq < 0)
+ return;
+
+ if (len < 1 + 2)
+ return;
+
+ dialog_token = *pos++;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: GAS Initial Request from " MACSTR " (dialog token %u, "
+ "freq %d)",
+ MAC2STR(sa), dialog_token, rx_freq);
+
+ if (*pos != WLAN_EID_ADV_PROTO) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unexpected IE in GAS Initial Request: %u", *pos);
+ return;
+ }
+ pos++;
+
+ slen = *pos++;
+ next = pos + slen;
+ if (next > end || slen < 2) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Invalid IE in GAS Initial Request");
+ return;
+ }
+ pos++; /* skip QueryRespLenLimit and PAME-BI */
+
+ if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported GAS advertisement protocol id %u",
+ *pos);
+ return;
+ }
+
+ pos = next;
+ /* Query Request */
+ if (pos + 2 > end)
+ return;
+ slen = WPA_GET_LE16(pos);
+ pos += 2;
+ if (pos + slen > end)
+ return;
+ end = pos + slen;
+
+ /* ANQP Query Request */
+ if (pos + 4 > end)
+ return;
+ if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported ANQP Info ID %u", WPA_GET_LE16(pos));
+ return;
+ }
+ pos += 2;
+
+ slen = WPA_GET_LE16(pos);
+ pos += 2;
+ if (pos + slen > end || slen < 3 + 1) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Invalid ANQP Query Request length");
+ return;
+ }
+
+ if (WPA_GET_BE24(pos) != OUI_WFA) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported ANQP OUI %06x", WPA_GET_BE24(pos));
+ return;
+ }
+ pos += 3;
+
+ if (*pos != P2P_OUI_TYPE) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported ANQP vendor type %u", *pos);
+ return;
+ }
+ pos++;
+
+ if (pos + 2 > end)
+ return;
+ update_indic = WPA_GET_LE16(pos);
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Service Update Indicator: %u", update_indic);
+ pos += 2;
+
+ p2p->cfg->sd_request(p2p->cfg->cb_ctx, freq, sa, dialog_token,
+ update_indic, pos, end - pos);
+ /* the response will be indicated with a call to p2p_sd_response() */
+}
+
+
+void p2p_sd_response(struct p2p_data *p2p, int freq, const u8 *dst,
+ u8 dialog_token, const struct wpabuf *resp_tlvs)
+{
+ struct wpabuf *resp;
+
+ /* TODO: fix the length limit to match with the maximum frame length */
+ if (wpabuf_len(resp_tlvs) > 1400) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: SD response long "
+ "enough to require fragmentation");
+ if (p2p->sd_resp) {
+ /*
+ * TODO: Could consider storing the fragmented response
+ * separately for each peer to avoid having to drop old
+ * one if there is more than one pending SD query.
+ * Though, that would eat more memory, so there are
+ * also benefits to just using a single buffer.
+ */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Drop "
+ "previous SD response");
+ wpabuf_free(p2p->sd_resp);
+ }
+ os_memcpy(p2p->sd_resp_addr, dst, ETH_ALEN);
+ p2p->sd_resp_dialog_token = dialog_token;
+ p2p->sd_resp = wpabuf_dup(resp_tlvs);
+ p2p->sd_resp_pos = 0;
+ p2p->sd_frag_id = 0;
+ resp = p2p_build_sd_response(dialog_token, WLAN_STATUS_SUCCESS,
+ 1, p2p->srv_update_indic, NULL);
+ } else {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: SD response fits "
+ "in initial response");
+ resp = p2p_build_sd_response(dialog_token,
+ WLAN_STATUS_SUCCESS, 0,
+ p2p->srv_update_indic, resp_tlvs);
+ }
+ if (resp == NULL)
+ return;
+
+ p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+ if (p2p_send_action(p2p, freq, dst, p2p->cfg->dev_addr,
+ p2p->cfg->dev_addr,
+ wpabuf_head(resp), wpabuf_len(resp), 200) < 0)
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to send Action frame");
+
+ wpabuf_free(resp);
+}
+
+
+void p2p_rx_gas_initial_resp(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len, int rx_freq)
+{
+ const u8 *pos = data;
+ const u8 *end = data + len;
+ const u8 *next;
+ u8 dialog_token;
+ u16 status_code;
+ u16 comeback_delay;
+ u16 slen;
+ u16 update_indic;
+
+ if (p2p->state != P2P_SD_DURING_FIND || p2p->sd_peer == NULL ||
+ os_memcmp(sa, p2p->sd_peer->info.p2p_device_addr, ETH_ALEN) != 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Ignore unexpected GAS Initial Response from "
+ MACSTR, MAC2STR(sa));
+ return;
+ }
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+ p2p_clear_timeout(p2p);
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Received GAS Initial Response from " MACSTR " (len=%d)",
+ MAC2STR(sa), (int) len);
+
+ if (len < 5 + 2) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Too short GAS Initial Response frame");
+ return;
+ }
+
+ dialog_token = *pos++;
+ /* TODO: check dialog_token match */
+ status_code = WPA_GET_LE16(pos);
+ pos += 2;
+ comeback_delay = WPA_GET_LE16(pos);
+ pos += 2;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: dialog_token=%u status_code=%u comeback_delay=%u",
+ dialog_token, status_code, comeback_delay);
+ if (status_code) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Service Discovery failed: status code %u",
+ status_code);
+ return;
+ }
+
+ if (*pos != WLAN_EID_ADV_PROTO) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unexpected IE in GAS Initial Response: %u",
+ *pos);
+ return;
+ }
+ pos++;
+
+ slen = *pos++;
+ next = pos + slen;
+ if (next > end || slen < 2) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Invalid IE in GAS Initial Response");
+ return;
+ }
+ pos++; /* skip QueryRespLenLimit and PAME-BI */
+
+ if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported GAS advertisement protocol id %u",
+ *pos);
+ return;
+ }
+
+ pos = next;
+ /* Query Response */
+ if (pos + 2 > end) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Too short Query "
+ "Response");
+ return;
+ }
+ slen = WPA_GET_LE16(pos);
+ pos += 2;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Query Response Length: %d",
+ slen);
+ if (pos + slen > end) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Not enough Query "
+ "Response data");
+ return;
+ }
+ end = pos + slen;
+
+ if (comeback_delay) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Fragmented "
+ "response - request fragments");
+ if (p2p->sd_rx_resp) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Drop "
+ "old SD reassembly buffer");
+ wpabuf_free(p2p->sd_rx_resp);
+ p2p->sd_rx_resp = NULL;
+ }
+ p2p_send_gas_comeback_req(p2p, sa, dialog_token, rx_freq);
+ return;
+ }
+
+ /* ANQP Query Response */
+ if (pos + 4 > end)
+ return;
+ if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported ANQP Info ID %u", WPA_GET_LE16(pos));
+ return;
+ }
+ pos += 2;
+
+ slen = WPA_GET_LE16(pos);
+ pos += 2;
+ if (pos + slen > end || slen < 3 + 1) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Invalid ANQP Query Response length");
+ return;
+ }
+
+ if (WPA_GET_BE24(pos) != OUI_WFA) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported ANQP OUI %06x", WPA_GET_BE24(pos));
+ return;
+ }
+ pos += 3;
+
+ if (*pos != P2P_OUI_TYPE) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported ANQP vendor type %u", *pos);
+ return;
+ }
+ pos++;
+
+ if (pos + 2 > end)
+ return;
+ update_indic = WPA_GET_LE16(pos);
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Service Update Indicator: %u", update_indic);
+ pos += 2;
+
+ p2p->sd_peer->flags |= P2P_DEV_SD_INFO;
+ p2p->sd_peer->flags &= ~P2P_DEV_SD_SCHEDULE;
+ p2p->sd_peer = NULL;
+
+ if (p2p->sd_query) {
+ if (!p2p->sd_query->for_all_peers) {
+ struct p2p_sd_query *q;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Remove completed SD query %p",
+ p2p->sd_query);
+ q = p2p->sd_query;
+ p2p_unlink_sd_query(p2p, p2p->sd_query);
+ p2p_free_sd_query(q);
+ }
+ p2p->sd_query = NULL;
+ }
+
+ if (p2p->cfg->sd_response)
+ p2p->cfg->sd_response(p2p->cfg->cb_ctx, sa, update_indic,
+ pos, end - pos);
+ p2p_continue_find(p2p);
+}
+
+
+void p2p_rx_gas_comeback_req(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len, int rx_freq)
+{
+ struct wpabuf *resp;
+ u8 dialog_token;
+ size_t frag_len;
+ int more = 0;
+
+ wpa_hexdump(MSG_DEBUG, "P2P: RX GAS Comeback Request", data, len);
+ if (len < 1)
+ return;
+ dialog_token = *data;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Dialog Token: %u",
+ dialog_token);
+ if (dialog_token != p2p->sd_resp_dialog_token) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No pending SD "
+ "response fragment for dialog token %u", dialog_token);
+ return;
+ }
+
+ if (p2p->sd_resp == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No pending SD "
+ "response fragment available");
+ return;
+ }
+ if (os_memcmp(sa, p2p->sd_resp_addr, ETH_ALEN) != 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No pending SD "
+ "response fragment for " MACSTR, MAC2STR(sa));
+ return;
+ }
+
+ frag_len = wpabuf_len(p2p->sd_resp) - p2p->sd_resp_pos;
+ if (frag_len > 1400) {
+ frag_len = 1400;
+ more = 1;
+ }
+ resp = p2p_build_gas_comeback_resp(dialog_token, WLAN_STATUS_SUCCESS,
+ p2p->srv_update_indic,
+ wpabuf_head_u8(p2p->sd_resp) +
+ p2p->sd_resp_pos, frag_len,
+ p2p->sd_frag_id, more,
+ wpabuf_len(p2p->sd_resp));
+ if (resp == NULL)
+ return;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Send GAS Comeback "
+ "Response (frag_id %d more=%d frag_len=%d)",
+ p2p->sd_frag_id, more, (int) frag_len);
+ p2p->sd_frag_id++;
+ p2p->sd_resp_pos += frag_len;
+
+ if (more) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: %d more bytes "
+ "remain to be sent",
+ (int) (wpabuf_len(p2p->sd_resp) - p2p->sd_resp_pos));
+ } else {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: All fragments of "
+ "SD response sent");
+ wpabuf_free(p2p->sd_resp);
+ p2p->sd_resp = NULL;
+ }
+
+ p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+ if (p2p_send_action(p2p, rx_freq, sa, p2p->cfg->dev_addr,
+ p2p->cfg->dev_addr,
+ wpabuf_head(resp), wpabuf_len(resp), 200) < 0)
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to send Action frame");
+
+ wpabuf_free(resp);
+}
+
+
+void p2p_rx_gas_comeback_resp(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len, int rx_freq)
+{
+ const u8 *pos = data;
+ const u8 *end = data + len;
+ const u8 *next;
+ u8 dialog_token;
+ u16 status_code;
+ u8 frag_id;
+ u8 more_frags;
+ u16 comeback_delay;
+ u16 slen;
+
+ wpa_hexdump(MSG_DEBUG, "P2P: RX GAS Comeback Response", data, len);
+
+ if (p2p->state != P2P_SD_DURING_FIND || p2p->sd_peer == NULL ||
+ os_memcmp(sa, p2p->sd_peer->info.p2p_device_addr, ETH_ALEN) != 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Ignore unexpected GAS Comeback Response from "
+ MACSTR, MAC2STR(sa));
+ return;
+ }
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+ p2p_clear_timeout(p2p);
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Received GAS Comeback Response from " MACSTR " (len=%d)",
+ MAC2STR(sa), (int) len);
+
+ if (len < 6 + 2) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Too short GAS Comeback Response frame");
+ return;
+ }
+
+ dialog_token = *pos++;
+ /* TODO: check dialog_token match */
+ status_code = WPA_GET_LE16(pos);
+ pos += 2;
+ frag_id = *pos & 0x7f;
+ more_frags = (*pos & 0x80) >> 7;
+ pos++;
+ comeback_delay = WPA_GET_LE16(pos);
+ pos += 2;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: dialog_token=%u status_code=%u frag_id=%d more_frags=%d "
+ "comeback_delay=%u",
+ dialog_token, status_code, frag_id, more_frags,
+ comeback_delay);
+ /* TODO: check frag_id match */
+ if (status_code) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Service Discovery failed: status code %u",
+ status_code);
+ return;
+ }
+
+ if (*pos != WLAN_EID_ADV_PROTO) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unexpected IE in GAS Comeback Response: %u",
+ *pos);
+ return;
+ }
+ pos++;
+
+ slen = *pos++;
+ next = pos + slen;
+ if (next > end || slen < 2) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Invalid IE in GAS Comeback Response");
+ return;
+ }
+ pos++; /* skip QueryRespLenLimit and PAME-BI */
+
+ if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported GAS advertisement protocol id %u",
+ *pos);
+ return;
+ }
+
+ pos = next;
+ /* Query Response */
+ if (pos + 2 > end) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Too short Query "
+ "Response");
+ return;
+ }
+ slen = WPA_GET_LE16(pos);
+ pos += 2;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Query Response Length: %d",
+ slen);
+ if (pos + slen > end) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Not enough Query "
+ "Response data");
+ return;
+ }
+ if (slen == 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No Query Response "
+ "data");
+ return;
+ }
+ end = pos + slen;
+
+ if (p2p->sd_rx_resp) {
+ /*
+ * ANQP header is only included in the first fragment; rest of
+ * the fragments start with continue TLVs.
+ */
+ goto skip_nqp_header;
+ }
+
+ /* ANQP Query Response */
+ if (pos + 4 > end)
+ return;
+ if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported ANQP Info ID %u", WPA_GET_LE16(pos));
+ return;
+ }
+ pos += 2;
+
+ slen = WPA_GET_LE16(pos);
+ pos += 2;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: ANQP Query Response "
+ "length: %u", slen);
+ if (slen < 3 + 1) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Invalid ANQP Query Response length");
+ return;
+ }
+ if (pos + 4 > end)
+ return;
+
+ if (WPA_GET_BE24(pos) != OUI_WFA) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported ANQP OUI %06x", WPA_GET_BE24(pos));
+ return;
+ }
+ pos += 3;
+
+ if (*pos != P2P_OUI_TYPE) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported ANQP vendor type %u", *pos);
+ return;
+ }
+ pos++;
+
+ if (pos + 2 > end)
+ return;
+ p2p->sd_rx_update_indic = WPA_GET_LE16(pos);
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Service Update Indicator: %u", p2p->sd_rx_update_indic);
+ pos += 2;
+
+skip_nqp_header:
+ if (wpabuf_resize(&p2p->sd_rx_resp, end - pos) < 0)
+ return;
+ wpabuf_put_data(p2p->sd_rx_resp, pos, end - pos);
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Current SD reassembly "
+ "buffer length: %u",
+ (unsigned int) wpabuf_len(p2p->sd_rx_resp));
+
+ if (more_frags) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: More fragments "
+ "remains");
+ /* TODO: what would be a good size limit? */
+ if (wpabuf_len(p2p->sd_rx_resp) > 64000) {
+ wpabuf_free(p2p->sd_rx_resp);
+ p2p->sd_rx_resp = NULL;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Too long "
+ "SD response - drop it");
+ return;
+ }
+ p2p_send_gas_comeback_req(p2p, sa, dialog_token, rx_freq);
+ return;
+ }
+
+ p2p->sd_peer->flags |= P2P_DEV_SD_INFO;
+ p2p->sd_peer->flags &= ~P2P_DEV_SD_SCHEDULE;
+ p2p->sd_peer = NULL;
+
+ if (p2p->sd_query) {
+ if (!p2p->sd_query->for_all_peers) {
+ struct p2p_sd_query *q;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Remove completed SD query %p",
+ p2p->sd_query);
+ q = p2p->sd_query;
+ p2p_unlink_sd_query(p2p, p2p->sd_query);
+ p2p_free_sd_query(q);
+ }
+ p2p->sd_query = NULL;
+ }
+
+ if (p2p->cfg->sd_response)
+ p2p->cfg->sd_response(p2p->cfg->cb_ctx, sa,
+ p2p->sd_rx_update_indic,
+ wpabuf_head(p2p->sd_rx_resp),
+ wpabuf_len(p2p->sd_rx_resp));
+ wpabuf_free(p2p->sd_rx_resp);
+ p2p->sd_rx_resp = NULL;
+
+ p2p_continue_find(p2p);
+}
+
+
+void * p2p_sd_request(struct p2p_data *p2p, const u8 *dst,
+ const struct wpabuf *tlvs)
+{
+ struct p2p_sd_query *q;
+
+ q = os_zalloc(sizeof(*q));
+ if (q == NULL)
+ return NULL;
+
+ if (dst)
+ os_memcpy(q->peer, dst, ETH_ALEN);
+ else
+ q->for_all_peers = 1;
+
+ q->tlvs = wpabuf_dup(tlvs);
+ if (q->tlvs == NULL) {
+ p2p_free_sd_query(q);
+ return NULL;
+ }
+
+ q->next = p2p->sd_queries;
+ p2p->sd_queries = q;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Added SD Query %p", q);
+
+ return q;
+}
+
+
+void p2p_sd_service_update(struct p2p_data *p2p)
+{
+ p2p->srv_update_indic++;
+}
+
+
+int p2p_sd_cancel_request(struct p2p_data *p2p, void *req)
+{
+ if (p2p_unlink_sd_query(p2p, req)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Cancel pending SD query %p", req);
+ p2p_free_sd_query(req);
+ return 0;
+ }
+ return -1;
+}
--- /dev/null
+/*
+ * P2P - generic helper functions
+ * Copyright (c) 2009, Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "p2p_i.h"
+
+
+/**
+ * p2p_random - Generate random string for SSID and passphrase
+ * @buf: Buffer for returning the result
+ * @len: Number of octets to write to the buffer
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function generates a random string using the following character set:
+ * 'A'-'Z', 'a'-'z', '0'-'9'.
+ */
+int p2p_random(char *buf, size_t len)
+{
+ u8 val;
+ size_t i;
+ u8 letters = 'Z' - 'A' + 1;
+ u8 numbers = 10;
+
+ if (os_get_random((unsigned char *) buf, len))
+ return -1;
+ /* Character set: 'A'-'Z', 'a'-'z', '0'-'9' */
+ for (i = 0; i < len; i++) {
+ val = buf[i];
+ val %= 2 * letters + numbers;
+ if (val < letters)
+ buf[i] = 'A' + val;
+ else if (val < 2 * letters)
+ buf[i] = 'a' + (val - letters);
+ else
+ buf[i] = '0' + (val - 2 * letters);
+ }
+
+ return 0;
+}
+
+
+static int p2p_channel_to_freq_j4(int reg_class, int channel)
+{
+ /* Table J-4 in P802.11REVmb/D4.0 - Global operating classes */
+ /* TODO: more regulatory classes */
+ switch (reg_class) {
+ case 81:
+ /* channels 1..13 */
+ if (channel < 1 || channel > 13)
+ return -1;
+ return 2407 + 5 * channel;
+ case 82:
+ /* channel 14 */
+ if (channel != 14)
+ return -1;
+ return 2414 + 5 * channel;
+ case 83: /* channels 1..9; 40 MHz */
+ case 84: /* channels 5..13; 40 MHz */
+ if (channel < 1 || channel > 13)
+ return -1;
+ return 2407 + 5 * channel;
+ case 115: /* channels 36,40,44,48; indoor only */
+ case 118: /* channels 52,56,60,64; dfs */
+ if (channel < 36 || channel > 64)
+ return -1;
+ return 5000 + 5 * channel;
+ case 124: /* channels 149,153,157,161 */
+ case 125: /* channels 149,153,157,161,165,169 */
+ if (channel < 149 || channel > 161)
+ return -1;
+ return 5000 + 5 * channel;
+ case 116: /* channels 36,44; 40 MHz; indoor only */
+ case 117: /* channels 40,48; 40 MHz; indoor only */
+ case 119: /* channels 52,60; 40 MHz; dfs */
+ case 120: /* channels 56,64; 40 MHz; dfs */
+ if (channel < 36 || channel > 64)
+ return -1;
+ return 5000 + 5 * channel;
+ case 126: /* channels 149,157; 40 MHz */
+ case 127: /* channels 153,161; 40 MHz */
+ if (channel < 149 || channel > 161)
+ return -1;
+ return 5000 + 5 * channel;
+ }
+ return -1;
+}
+
+
+/**
+ * p2p_channel_to_freq - Convert channel info to frequency
+ * @country: Country code
+ * @reg_class: Regulatory class
+ * @channel: Channel number
+ * Returns: Frequency in MHz or -1 if the specified channel is unknown
+ */
+int p2p_channel_to_freq(const char *country, int reg_class, int channel)
+{
+ if (country[2] == 0x04)
+ return p2p_channel_to_freq_j4(reg_class, channel);
+
+ /* These are mainly for backwards compatibility; to be removed */
+ switch (reg_class) {
+ case 1: /* US/1, EU/1, JP/1 = 5 GHz, channels 36,40,44,48 */
+ if (channel < 36 || channel > 48)
+ return -1;
+ return 5000 + 5 * channel;
+ case 3: /* US/3 = 5 GHz, channels 149,153,157,161 */
+ case 5: /* US/5 = 5 GHz, channels 149,153,157,161 */
+ if (channel < 149 || channel > 161)
+ return -1;
+ return 5000 + 5 * channel;
+ case 4: /* EU/4 = 2.407 GHz, channels 1..13 */
+ case 12: /* US/12 = 2.407 GHz, channels 1..11 */
+ case 30: /* JP/30 = 2.407 GHz, channels 1..13 */
+ if (channel < 1 || channel > 13)
+ return -1;
+ return 2407 + 5 * channel;
+ case 31: /* JP/31 = 2.414 GHz, channel 14 */
+ if (channel != 14)
+ return -1;
+ return 2414 + 5 * channel;
+ }
+
+ return -1;
+}
+
+
+/**
+ * p2p_freq_to_channel - Convert frequency into channel info
+ * @country: Country code
+ * @reg_class: Buffer for returning regulatory class
+ * @channel: Buffer for returning channel number
+ * Returns: 0 on success, -1 if the specified frequency is unknown
+ */
+int p2p_freq_to_channel(const char *country, unsigned int freq, u8 *reg_class,
+ u8 *channel)
+{
+ /* TODO: more operating classes */
+ if (freq >= 2412 && freq <= 2472) {
+ *reg_class = 81; /* 2.407 GHz, channels 1..13 */
+ *channel = (freq - 2407) / 5;
+ return 0;
+ }
+
+ if (freq == 2484) {
+ *reg_class = 82; /* channel 14 */
+ *channel = 14;
+ return 0;
+ }
+
+ if (freq >= 5180 && freq <= 5240) {
+ *reg_class = 115; /* 5 GHz, channels 36..48 */
+ *channel = (freq - 5000) / 5;
+ return 0;
+ }
+
+ if (freq >= 5745 && freq <= 5805) {
+ *reg_class = 124; /* 5 GHz, channels 149..161 */
+ *channel = (freq - 5000) / 5;
+ return 0;
+ }
+
+ return -1;
+}
+
+
+static void p2p_reg_class_intersect(const struct p2p_reg_class *a,
+ const struct p2p_reg_class *b,
+ struct p2p_reg_class *res)
+{
+ size_t i, j;
+
+ res->reg_class = a->reg_class;
+
+ for (i = 0; i < a->channels; i++) {
+ for (j = 0; j < b->channels; j++) {
+ if (a->channel[i] != b->channel[j])
+ continue;
+ res->channel[res->channels] = a->channel[i];
+ res->channels++;
+ if (res->channels == P2P_MAX_REG_CLASS_CHANNELS)
+ return;
+ }
+ }
+}
+
+
+/**
+ * p2p_channels_intersect - Intersection of supported channel lists
+ * @a: First set of supported channels
+ * @b: Second set of supported channels
+ * @res: Data structure for returning the intersection of support channels
+ *
+ * This function can be used to find a common set of supported channels. Both
+ * input channels sets are assumed to use the same country code. If different
+ * country codes are used, the regulatory class numbers may not be matched
+ * correctly and results are undefined.
+ */
+void p2p_channels_intersect(const struct p2p_channels *a,
+ const struct p2p_channels *b,
+ struct p2p_channels *res)
+{
+ size_t i, j;
+
+ os_memset(res, 0, sizeof(*res));
+
+ for (i = 0; i < a->reg_classes; i++) {
+ const struct p2p_reg_class *a_reg = &a->reg_class[i];
+ for (j = 0; j < b->reg_classes; j++) {
+ const struct p2p_reg_class *b_reg = &b->reg_class[j];
+ if (a_reg->reg_class != b_reg->reg_class)
+ continue;
+ p2p_reg_class_intersect(
+ a_reg, b_reg,
+ &res->reg_class[res->reg_classes]);
+ if (res->reg_class[res->reg_classes].channels) {
+ res->reg_classes++;
+ if (res->reg_classes == P2P_MAX_REG_CLASSES)
+ return;
+ }
+ }
+ }
+}
+
+
+/**
+ * p2p_channels_includes - Check whether a channel is included in the list
+ * @channels: List of supported channels
+ * @reg_class: Regulatory class of the channel to search
+ * @channel: Channel number of the channel to search
+ * Returns: 1 if channel was found or 0 if not
+ */
+int p2p_channels_includes(const struct p2p_channels *channels, u8 reg_class,
+ u8 channel)
+{
+ size_t i, j;
+ for (i = 0; i < channels->reg_classes; i++) {
+ const struct p2p_reg_class *reg = &channels->reg_class[i];
+ if (reg->reg_class != reg_class)
+ continue;
+ for (j = 0; j < reg->channels; j++) {
+ if (reg->channel[j] == channel)
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+int p2p_supported_freq(struct p2p_data *p2p, unsigned int freq)
+{
+ u8 op_reg_class, op_channel;
+ if (p2p_freq_to_channel(p2p->cfg->country, freq,
+ &op_reg_class, &op_channel) < 0)
+ return 0;
+ return p2p_channels_includes(&p2p->cfg->channels, op_reg_class,
+ op_channel);
+}
const u8 *secret, size_t secret_len)
{
u8 buf[128];
- int padlen, i;
- size_t buf_len, pos;
+ size_t padlen, i, buf_len, pos;
const u8 *addr[2];
size_t len[2];
u8 hash[16];
buf_len = data_len;
padlen = data_len % 16;
- if (padlen) {
+ if (padlen && data_len < sizeof(buf)) {
padlen = 16 - padlen;
os_memset(buf + data_len, 0, padlen);
buf_len += padlen;
radius_client_list_add(radius, msg, msg_type, shared_secret,
shared_secret_len, addr);
- return res;
+ return 0;
}
return count;
}
+
+
+void radius_client_reconfig(struct radius_client_data *radius,
+ struct hostapd_radius_servers *conf)
+{
+ if (radius)
+ radius->conf = conf;
+}
const u8 *addr);
int radius_client_get_mib(struct radius_client_data *radius, char *buf,
size_t buflen);
+void radius_client_reconfig(struct radius_client_data *radius,
+ struct hostapd_radius_servers *conf);
#endif /* RADIUS_CLIENT_H */
int tnc;
/**
+ * pwd_group - The D-H group assigned for EAP-pwd
+ *
+ * If EAP-pwd is not used it can be set to zero.
+ */
+ u16 pwd_group;
+
+ /**
* wps - Wi-Fi Protected Setup context
*
* If WPS is used with an external RADIUS server (which is quite
eap_conf.eap_sim_aka_result_ind = data->eap_sim_aka_result_ind;
eap_conf.tnc = data->tnc;
eap_conf.wps = data->wps;
+ eap_conf.pwd_group = data->pwd_group;
sess->eap = eap_server_sm_init(sess, &radius_server_eapol_cb,
&eap_conf);
if (sess->eap == NULL) {
data->eap_sim_aka_result_ind = conf->eap_sim_aka_result_ind;
data->tnc = conf->tnc;
data->wps = conf->wps;
+ data->pwd_group = conf->pwd_group;
if (conf->eap_req_id_text) {
data->eap_req_id_text = os_malloc(conf->eap_req_id_text_len);
if (data->eap_req_id_text) {
int tnc;
/**
+ * pwd_group - EAP-pwd D-H group
+ *
+ * This is used to select which D-H group to use with EAP-pwd.
+ */
+ u16 pwd_group;
+
+ /**
* wps - Wi-Fi Protected Setup context
*
* If WPS is used with an external RADIUS server (which is quite
#include "eloop.h"
#include "crypto/sha1.h"
#include "crypto/sha256.h"
+#include "crypto/random.h"
#include "common/ieee802_11_defs.h"
#include "wpa.h"
#include "wpa_i.h"
peerkey->use_sha256 = 1;
#endif /* CONFIG_IEEE80211W */
- if (os_get_random(peerkey->pnonce, WPA_NONCE_LEN)) {
+ if (random_get_bytes(peerkey->pnonce, WPA_NONCE_LEN)) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Failed to get random data for PNonce");
wpa_supplicant_peerkey_free(sm, peerkey);
wpa_add_kde((u8 *) (msg + 1), RSN_KEY_DATA_PMKID,
peerkey->smkid, PMKID_LEN);
- if (os_get_random(peerkey->inonce, WPA_NONCE_LEN)) {
+ if (random_get_bytes(peerkey->inonce, WPA_NONCE_LEN)) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"RSN: Failed to get random data for INonce (STK)");
os_free(mbuf);
return;
}
- if (os_get_random(peerkey->pnonce, WPA_NONCE_LEN)) {
+ if (random_get_bytes(peerkey->pnonce, WPA_NONCE_LEN)) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"RSN: Failed to get random data for PNonce");
return;
WPA_REPLAY_COUNTER_LEN);
inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN);
- if (os_get_random(peerkey->inonce, WPA_NONCE_LEN)) {
+ if (random_get_bytes(peerkey->inonce, WPA_NONCE_LEN)) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Failed to get random data for INonce");
os_free(rbuf);
peerkey = peerkey->next;
os_free(prev);
}
+ sm->peerkey = NULL;
}
struct rsn_pmksa_cache_entry *entry,
int replace)
{
+ wpa_sm_remove_pmkid(pmksa->sm, entry->aa, entry->pmkid);
pmksa->pmksa_count--;
pmksa->free_cb(entry, pmksa->ctx, replace);
_pmksa_cache_free_entry(entry);
wpa_printf(MSG_DEBUG, "RSN: Replace PMKSA entry for "
"the current AP");
pmksa_cache_free_entry(pmksa, pos, 1);
+
+ /*
+ * If OKC is used, there may be other PMKSA cache
+ * entries based on the same PMK. These needs to be
+ * flushed so that a new entry can be created based on
+ * the new PMK.
+ */
+ pmksa_cache_flush(pmksa, network_ctx);
break;
}
prev = pos;
wpa_printf(MSG_DEBUG, "RSN: removed the oldest PMKSA cache "
"entry (for " MACSTR ") to make room for new one",
MAC2STR(pos->aa));
- wpa_sm_remove_pmkid(pmksa->sm, pos->aa, pos->pmkid);
pmksa_cache_free_entry(pmksa, pos, 0);
}
/**
+ * pmksa_cache_flush - Flush PMKSA cache entries for a specific network
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
+ * @network_ctx: Network configuration context or %NULL to flush all entries
+ */
+void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx)
+{
+ struct rsn_pmksa_cache_entry *entry, *prev = NULL, *tmp;
+ int removed = 0;
+
+ entry = pmksa->pmksa;
+ while (entry) {
+ if (entry->network_ctx == network_ctx || network_ctx == NULL) {
+ wpa_printf(MSG_DEBUG, "RSN: Flush PMKSA cache entry "
+ "for " MACSTR, MAC2STR(entry->aa));
+ if (prev)
+ prev->next = entry->next;
+ else
+ pmksa->pmksa = entry->next;
+ tmp = entry;
+ entry = entry->next;
+ pmksa_cache_free_entry(pmksa, tmp, 0);
+ removed++;
+ } else {
+ prev = entry;
+ entry = entry->next;
+ }
+ }
+ if (removed)
+ pmksa_cache_set_expiration(pmksa);
+}
+
+
+/**
* pmksa_cache_deinit - Free all entries in PMKSA cache
* @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
*/
}
-/**
- * pmksa_cache_notify_reconfig - Reconfiguration notification for PMKSA cache
- * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
- *
- * Clear references to old data structures when wpa_supplicant is reconfigured.
- */
-void pmksa_cache_notify_reconfig(struct rsn_pmksa_cache *pmksa)
-{
- struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
- while (entry) {
- entry->network_ctx = NULL;
- entry = entry->next;
- }
-}
-
-
static struct rsn_pmksa_cache_entry *
pmksa_cache_clone_entry(struct rsn_pmksa_cache *pmksa,
const struct rsn_pmksa_cache_entry *old_entry,
{
struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
+ wpa_printf(MSG_DEBUG, "RSN: Consider " MACSTR " for OKC", MAC2STR(aa));
if (network_ctx == NULL)
return NULL;
while (entry) {
struct rsn_pmksa_cache_entry *
pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
const u8 *aa, const u8 *spa, void *network_ctx, int akmp);
-void pmksa_cache_notify_reconfig(struct rsn_pmksa_cache *pmksa);
struct rsn_pmksa_cache_entry * pmksa_cache_get_current(struct wpa_sm *sm);
void pmksa_cache_clear_current(struct wpa_sm *sm);
int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid,
struct rsn_pmksa_cache_entry *
pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa,
void *network_ctx, const u8 *aa);
+void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx);
#else /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */
return NULL;
}
-static inline void pmksa_cache_notify_reconfig(struct rsn_pmksa_cache *pmksa)
-{
-}
-
static inline void pmksa_cache_clear_current(struct wpa_sm *sm)
{
}
return -1;
}
+static inline void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa,
+ void *network_ctx)
+{
+}
+
#endif /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */
#endif /* PMKSA_CACHE_H */
--- /dev/null
+/*
+ * wpa_supplicant - TDLS
+ * Copyright (c) 2010-2011, Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/os.h"
+#include "common/ieee802_11_defs.h"
+#include "crypto/sha256.h"
+#include "crypto/crypto.h"
+#include "crypto/aes_wrap.h"
+#include "rsn_supp/wpa.h"
+#include "rsn_supp/wpa_ie.h"
+#include "rsn_supp/wpa_i.h"
+#include "drivers/driver.h"
+#include "l2_packet/l2_packet.h"
+
+#ifdef CONFIG_TDLS_TESTING
+#define TDLS_TESTING_LONG_FRAME BIT(0)
+#define TDLS_TESTING_ALT_RSN_IE BIT(1)
+#define TDLS_TESTING_DIFF_BSSID BIT(2)
+#define TDLS_TESTING_SHORT_LIFETIME BIT(3)
+#define TDLS_TESTING_WRONG_LIFETIME_RESP BIT(4)
+#define TDLS_TESTING_WRONG_LIFETIME_CONF BIT(5)
+#define TDLS_TESTING_LONG_LIFETIME BIT(6)
+#define TDLS_TESTING_CONCURRENT_INIT BIT(7)
+#define TDLS_TESTING_NO_TPK_EXPIRATION BIT(8)
+#define TDLS_TESTING_DECLINE_RESP BIT(9)
+#define TDLS_TESTING_IGNORE_AP_PROHIBIT BIT(10)
+unsigned int tdls_testing = 0;
+#endif /* CONFIG_TDLS_TESTING */
+
+#define TPK_LIFETIME 43200 /* 12 hours */
+#define TPK_RETRY_COUNT 3
+#define TPK_TIMEOUT 5000 /* in milliseconds */
+
+#define TDLS_MIC_LEN 16
+
+#define TDLS_TIMEOUT_LEN 4
+
+struct wpa_tdls_ftie {
+ u8 ie_type; /* FTIE */
+ u8 ie_len;
+ u8 mic_ctrl[2];
+ u8 mic[TDLS_MIC_LEN];
+ u8 Anonce[WPA_NONCE_LEN]; /* Responder Nonce in TDLS */
+ u8 Snonce[WPA_NONCE_LEN]; /* Initiator Nonce in TDLS */
+ /* followed by optional elements */
+} STRUCT_PACKED;
+
+struct wpa_tdls_timeoutie {
+ u8 ie_type; /* Timeout IE */
+ u8 ie_len;
+ u8 interval_type;
+ u8 value[TDLS_TIMEOUT_LEN];
+} STRUCT_PACKED;
+
+struct wpa_tdls_lnkid {
+ u8 ie_type; /* Link Identifier IE */
+ u8 ie_len;
+ u8 bssid[ETH_ALEN];
+ u8 init_sta[ETH_ALEN];
+ u8 resp_sta[ETH_ALEN];
+} STRUCT_PACKED;
+
+/* TDLS frame headers as per IEEE Std 802.11z-2010 */
+struct wpa_tdls_frame {
+ u8 payloadtype; /* IEEE80211_TDLS_RFTYPE */
+ u8 category; /* Category */
+ u8 action; /* Action (enum tdls_frame_type) */
+} STRUCT_PACKED;
+
+static u8 * wpa_add_tdls_timeoutie(u8 *pos, u8 *ie, size_t ie_len, u32 tsecs);
+static void wpa_tdls_tpk_retry_timeout(void *eloop_ctx, void *timeout_ctx);
+static void wpa_tdls_peer_free(struct wpa_sm *sm, struct wpa_tdls_peer *peer);
+
+
+#define TDLS_MAX_IE_LEN 80
+#define IEEE80211_MAX_SUPP_RATES 32
+
+struct wpa_tdls_peer {
+ struct wpa_tdls_peer *next;
+ int initiator; /* whether this end was initiator for TDLS setup */
+ u8 addr[ETH_ALEN]; /* other end MAC address */
+ u8 inonce[WPA_NONCE_LEN]; /* Initiator Nonce */
+ u8 rnonce[WPA_NONCE_LEN]; /* Responder Nonce */
+ u8 rsnie_i[TDLS_MAX_IE_LEN]; /* Initiator RSN IE */
+ size_t rsnie_i_len;
+ u8 rsnie_p[TDLS_MAX_IE_LEN]; /* Peer RSN IE */
+ size_t rsnie_p_len;
+ u32 lifetime;
+ int cipher; /* Selected cipher (WPA_CIPHER_*) */
+ u8 dtoken;
+
+ struct tpk {
+ u8 kck[16]; /* TPK-KCK */
+ u8 tk[16]; /* TPK-TK; assuming only CCMP will be used */
+ } tpk;
+ int tpk_set;
+ int tpk_success;
+
+ struct tpk_timer {
+ u8 dest[ETH_ALEN];
+ int count; /* Retry Count */
+ int timer; /* Timeout in milliseconds */
+ u8 action_code; /* TDLS frame type */
+ u8 dialog_token;
+ u16 status_code;
+ int buf_len; /* length of TPK message for retransmission */
+ u8 *buf; /* buffer for TPK message */
+ } sm_tmr;
+
+ u16 capability;
+
+ u8 supp_rates[IEEE80211_MAX_SUPP_RATES];
+ size_t supp_rates_len;
+};
+
+
+static int wpa_tdls_get_privacy(struct wpa_sm *sm)
+{
+ /*
+ * Get info needed from supplicant to check if the current BSS supports
+ * security. Other than OPEN mode, rest are considered secured
+ * WEP/WPA/WPA2 hence TDLS frames are processed for TPK handshake.
+ */
+ return sm->pairwise_cipher != WPA_CIPHER_NONE;
+}
+
+
+static u8 * wpa_add_ie(u8 *pos, const u8 *ie, size_t ie_len)
+{
+ os_memcpy(pos, ie, ie_len);
+ return pos + ie_len;
+}
+
+
+static int wpa_tdls_del_key(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
+{
+ if (wpa_sm_set_key(sm, WPA_ALG_NONE, peer->addr,
+ 0, 0, NULL, 0, NULL, 0) < 0) {
+ wpa_printf(MSG_WARNING, "TDLS: Failed to delete TPK-TK from "
+ "the driver");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int wpa_tdls_set_key(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
+{
+ u8 key_len;
+ u8 rsc[6];
+ enum wpa_alg alg;
+
+ os_memset(rsc, 0, 6);
+
+ switch (peer->cipher) {
+ case WPA_CIPHER_CCMP:
+ alg = WPA_ALG_CCMP;
+ key_len = 16;
+ break;
+ case WPA_CIPHER_NONE:
+ wpa_printf(MSG_DEBUG, "TDLS: Pairwise Cipher Suite: "
+ "NONE - do not use pairwise keys");
+ return -1;
+ default:
+ wpa_printf(MSG_WARNING, "TDLS: Unsupported pairwise cipher %d",
+ sm->pairwise_cipher);
+ return -1;
+ }
+
+ if (wpa_sm_set_key(sm, alg, peer->addr, -1, 1,
+ rsc, sizeof(rsc), peer->tpk.tk, key_len) < 0) {
+ wpa_printf(MSG_WARNING, "TDLS: Failed to set TPK to the "
+ "driver");
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wpa_tdls_send_tpk_msg(struct wpa_sm *sm, const u8 *dst,
+ u8 action_code, u8 dialog_token,
+ u16 status_code, const u8 *buf, size_t len)
+{
+ return wpa_sm_send_tdls_mgmt(sm, dst, action_code, dialog_token,
+ status_code, buf, len);
+}
+
+
+static int wpa_tdls_tpk_send(struct wpa_sm *sm, const u8 *dest, u8 action_code,
+ u8 dialog_token, u16 status_code,
+ const u8 *msg, size_t msg_len)
+{
+ struct wpa_tdls_peer *peer;
+
+ wpa_printf(MSG_DEBUG, "TDLS: TPK send dest=" MACSTR " action_code=%u "
+ "dialog_token=%u status_code=%u msg_len=%u",
+ MAC2STR(dest), action_code, dialog_token, status_code,
+ (unsigned int) msg_len);
+
+ if (wpa_tdls_send_tpk_msg(sm, dest, action_code, dialog_token,
+ status_code, msg, msg_len)) {
+ wpa_printf(MSG_INFO, "TDLS: Failed to send message "
+ "(action_code=%u)", action_code);
+ return -1;
+ }
+
+ if (action_code == WLAN_TDLS_SETUP_CONFIRM ||
+ action_code == WLAN_TDLS_TEARDOWN ||
+ action_code == WLAN_TDLS_DISCOVERY_REQUEST ||
+ action_code == WLAN_TDLS_DISCOVERY_RESPONSE)
+ return 0; /* No retries */
+
+ for (peer = sm->tdls; peer; peer = peer->next) {
+ if (os_memcmp(peer->addr, dest, ETH_ALEN) == 0)
+ break;
+ }
+
+ if (peer == NULL) {
+ wpa_printf(MSG_INFO, "TDLS: No matching entry found for "
+ "retry " MACSTR, MAC2STR(dest));
+ return 0;
+ }
+
+ eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer);
+
+ peer->sm_tmr.count = TPK_RETRY_COUNT;
+ peer->sm_tmr.timer = TPK_TIMEOUT;
+
+ /* Copy message to resend on timeout */
+ os_memcpy(peer->sm_tmr.dest, dest, ETH_ALEN);
+ peer->sm_tmr.action_code = action_code;
+ peer->sm_tmr.dialog_token = dialog_token;
+ peer->sm_tmr.status_code = status_code;
+ peer->sm_tmr.buf_len = msg_len;
+ os_free(peer->sm_tmr.buf);
+ peer->sm_tmr.buf = os_malloc(msg_len);
+ if (peer->sm_tmr.buf == NULL)
+ return -1;
+ os_memcpy(peer->sm_tmr.buf, msg, msg_len);
+
+ wpa_printf(MSG_DEBUG, "TDLS: Retry timeout registered "
+ "(action_code=%u)", action_code);
+ eloop_register_timeout(peer->sm_tmr.timer / 1000, 0,
+ wpa_tdls_tpk_retry_timeout, sm, peer);
+ return 0;
+}
+
+
+static int wpa_tdls_do_teardown(struct wpa_sm *sm, struct wpa_tdls_peer *peer,
+ u16 reason_code, int free_peer)
+{
+ int ret;
+
+ if (sm->tdls_external_setup) {
+ ret = wpa_tdls_send_teardown(sm, peer->addr, reason_code);
+
+ /* disable the link after teardown was sent */
+ wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr);
+ } else {
+ ret = wpa_sm_tdls_oper(sm, TDLS_TEARDOWN, peer->addr);
+ }
+
+ if (sm->tdls_external_setup || free_peer)
+ wpa_tdls_peer_free(sm, peer);
+
+ return ret;
+}
+
+
+static void wpa_tdls_tpk_retry_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+
+ struct wpa_sm *sm = eloop_ctx;
+ struct wpa_tdls_peer *peer = timeout_ctx;
+
+ if (peer->sm_tmr.count) {
+ peer->sm_tmr.count--;
+ peer->sm_tmr.timer = TPK_TIMEOUT;
+
+ wpa_printf(MSG_INFO, "TDLS: Retrying sending of message "
+ "(action_code=%u)",
+ peer->sm_tmr.action_code);
+
+ if (peer->sm_tmr.buf == NULL) {
+ wpa_printf(MSG_INFO, "TDLS: No retry buffer available "
+ "for action_code=%u",
+ peer->sm_tmr.action_code);
+ eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm,
+ peer);
+ return;
+ }
+
+ /* resend TPK Handshake Message to Peer */
+ if (wpa_tdls_send_tpk_msg(sm, peer->sm_tmr.dest,
+ peer->sm_tmr.action_code,
+ peer->sm_tmr.dialog_token,
+ peer->sm_tmr.status_code,
+ peer->sm_tmr.buf,
+ peer->sm_tmr.buf_len)) {
+ wpa_printf(MSG_INFO, "TDLS: Failed to retry "
+ "transmission");
+ }
+
+ eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer);
+ eloop_register_timeout(peer->sm_tmr.timer / 1000, 0,
+ wpa_tdls_tpk_retry_timeout, sm, peer);
+ } else {
+ eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer);
+
+ wpa_printf(MSG_DEBUG, "TDLS: Sending Teardown Request");
+ wpa_tdls_do_teardown(sm, peer,
+ WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED, 1);
+ }
+}
+
+
+static void wpa_tdls_tpk_retry_timeout_cancel(struct wpa_sm *sm,
+ struct wpa_tdls_peer *peer,
+ u8 action_code)
+{
+ if (action_code == peer->sm_tmr.action_code) {
+ wpa_printf(MSG_DEBUG, "TDLS: Retry timeout cancelled for "
+ "action_code=%u", action_code);
+
+ /* Cancel Timeout registered */
+ eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer);
+
+ /* free all resources meant for retry */
+ os_free(peer->sm_tmr.buf);
+ peer->sm_tmr.buf = NULL;
+
+ peer->sm_tmr.count = 0;
+ peer->sm_tmr.timer = 0;
+ peer->sm_tmr.buf_len = 0;
+ peer->sm_tmr.action_code = 0xff;
+ } else {
+ wpa_printf(MSG_INFO, "TDLS: Error in cancelling retry timeout "
+ "(Unknown action_code=%u)", action_code);
+ }
+}
+
+
+static void wpa_tdls_generate_tpk(struct wpa_tdls_peer *peer,
+ const u8 *own_addr, const u8 *bssid)
+{
+ u8 key_input[SHA256_MAC_LEN];
+ const u8 *nonce[2];
+ size_t len[2];
+ u8 data[3 * ETH_ALEN];
+
+ /* IEEE Std 802.11z-2010 8.5.9.1:
+ * TPK-Key-Input = SHA-256(min(SNonce, ANonce) || max(SNonce, ANonce))
+ */
+ len[0] = WPA_NONCE_LEN;
+ len[1] = WPA_NONCE_LEN;
+ if (os_memcmp(peer->inonce, peer->rnonce, WPA_NONCE_LEN) < 0) {
+ nonce[0] = peer->inonce;
+ nonce[1] = peer->rnonce;
+ } else {
+ nonce[0] = peer->rnonce;
+ nonce[1] = peer->inonce;
+ }
+ wpa_hexdump(MSG_DEBUG, "TDLS: min(Nonce)", nonce[0], WPA_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "TDLS: max(Nonce)", nonce[1], WPA_NONCE_LEN);
+ sha256_vector(2, nonce, len, key_input);
+ wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-Key-Input",
+ key_input, SHA256_MAC_LEN);
+
+ /*
+ * TPK-Key-Data = KDF-N_KEY(TPK-Key-Input, "TDLS PMK",
+ * min(MAC_I, MAC_R) || max(MAC_I, MAC_R) || BSSID || N_KEY)
+ * TODO: is N_KEY really included in KDF Context and if so, in which
+ * presentation format (little endian 16-bit?) is it used? It gets
+ * added by the KDF anyway..
+ */
+
+ if (os_memcmp(own_addr, peer->addr, ETH_ALEN) < 0) {
+ os_memcpy(data, own_addr, ETH_ALEN);
+ os_memcpy(data + ETH_ALEN, peer->addr, ETH_ALEN);
+ } else {
+ os_memcpy(data, peer->addr, ETH_ALEN);
+ os_memcpy(data + ETH_ALEN, own_addr, ETH_ALEN);
+ }
+ os_memcpy(data + 2 * ETH_ALEN, bssid, ETH_ALEN);
+ wpa_hexdump(MSG_DEBUG, "TDLS: KDF Context", data, sizeof(data));
+
+ sha256_prf(key_input, SHA256_MAC_LEN, "TDLS PMK", data, sizeof(data),
+ (u8 *) &peer->tpk, sizeof(peer->tpk));
+ wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-KCK",
+ peer->tpk.kck, sizeof(peer->tpk.kck));
+ wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-TK",
+ peer->tpk.tk, sizeof(peer->tpk.tk));
+ peer->tpk_set = 1;
+}
+
+
+/**
+ * wpa_tdls_ftie_mic - Calculate TDLS FTIE MIC
+ * @kck: TPK-KCK
+ * @lnkid: Pointer to the beginning of Link Identifier IE
+ * @rsnie: Pointer to the beginning of RSN IE used for handshake
+ * @timeoutie: Pointer to the beginning of Timeout IE used for handshake
+ * @ftie: Pointer to the beginning of FT IE
+ * @mic: Pointer for writing MIC
+ *
+ * Calculate MIC for TDLS frame.
+ */
+static int wpa_tdls_ftie_mic(const u8 *kck, u8 trans_seq, const u8 *lnkid,
+ const u8 *rsnie, const u8 *timeoutie,
+ const u8 *ftie, u8 *mic)
+{
+ u8 *buf, *pos;
+ struct wpa_tdls_ftie *_ftie;
+ const struct wpa_tdls_lnkid *_lnkid;
+ int ret;
+ int len = 2 * ETH_ALEN + 1 + 2 + lnkid[1] + 2 + rsnie[1] +
+ 2 + timeoutie[1] + 2 + ftie[1];
+ buf = os_zalloc(len);
+ if (!buf) {
+ wpa_printf(MSG_WARNING, "TDLS: No memory for MIC calculation");
+ return -1;
+ }
+
+ pos = buf;
+ _lnkid = (const struct wpa_tdls_lnkid *) lnkid;
+ /* 1) TDLS initiator STA MAC address */
+ os_memcpy(pos, _lnkid->init_sta, ETH_ALEN);
+ pos += ETH_ALEN;
+ /* 2) TDLS responder STA MAC address */
+ os_memcpy(pos, _lnkid->resp_sta, ETH_ALEN);
+ pos += ETH_ALEN;
+ /* 3) Transaction Sequence number */
+ *pos++ = trans_seq;
+ /* 4) Link Identifier IE */
+ os_memcpy(pos, lnkid, 2 + lnkid[1]);
+ pos += 2 + lnkid[1];
+ /* 5) RSN IE */
+ os_memcpy(pos, rsnie, 2 + rsnie[1]);
+ pos += 2 + rsnie[1];
+ /* 6) Timeout Interval IE */
+ os_memcpy(pos, timeoutie, 2 + timeoutie[1]);
+ pos += 2 + timeoutie[1];
+ /* 7) FTIE, with the MIC field of the FTIE set to 0 */
+ os_memcpy(pos, ftie, 2 + ftie[1]);
+ _ftie = (struct wpa_tdls_ftie *) pos;
+ os_memset(_ftie->mic, 0, TDLS_MIC_LEN);
+ pos += 2 + ftie[1];
+
+ wpa_hexdump(MSG_DEBUG, "TDLS: Data for FTIE MIC", buf, pos - buf);
+ wpa_hexdump_key(MSG_DEBUG, "TDLS: KCK", kck, 16);
+ ret = omac1_aes_128(kck, buf, pos - buf, mic);
+ os_free(buf);
+ wpa_hexdump(MSG_DEBUG, "TDLS: FTIE MIC", mic, 16);
+ return ret;
+}
+
+
+/**
+ * wpa_tdls_key_mic_teardown - Calculate TDLS FTIE MIC for Teardown frame
+ * @kck: TPK-KCK
+ * @trans_seq: Transaction Sequence Number (4 - Teardown)
+ * @rcode: Reason code for Teardown
+ * @dtoken: Dialog Token used for that particular link
+ * @lnkid: Pointer to the beginning of Link Identifier IE
+ * @ftie: Pointer to the beginning of FT IE
+ * @mic: Pointer for writing MIC
+ *
+ * Calculate MIC for TDLS frame.
+ */
+static int wpa_tdls_key_mic_teardown(const u8 *kck, u8 trans_seq, u16 rcode,
+ u8 dtoken, const u8 *lnkid,
+ const u8 *ftie, u8 *mic)
+{
+ u8 *buf, *pos;
+ struct wpa_tdls_ftie *_ftie;
+ int ret;
+ int len;
+
+ if (lnkid == NULL)
+ return -1;
+
+ len = 2 + lnkid[1] + sizeof(rcode) + sizeof(dtoken) +
+ sizeof(trans_seq) + 2 + ftie[1];
+
+ buf = os_zalloc(len);
+ if (!buf) {
+ wpa_printf(MSG_WARNING, "TDLS: No memory for MIC calculation");
+ return -1;
+ }
+
+ pos = buf;
+ /* 1) Link Identifier IE */
+ os_memcpy(pos, lnkid, 2 + lnkid[1]);
+ pos += 2 + lnkid[1];
+ /* 2) Reason Code */
+ WPA_PUT_LE16(pos, rcode);
+ pos += sizeof(rcode);
+ /* 3) Dialog token */
+ *pos++ = dtoken;
+ /* 4) Transaction Sequence number */
+ *pos++ = trans_seq;
+ /* 7) FTIE, with the MIC field of the FTIE set to 0 */
+ os_memcpy(pos, ftie, 2 + ftie[1]);
+ _ftie = (struct wpa_tdls_ftie *) pos;
+ os_memset(_ftie->mic, 0, TDLS_MIC_LEN);
+ pos += 2 + ftie[1];
+
+ wpa_hexdump(MSG_DEBUG, "TDLS: Data for FTIE MIC", buf, pos - buf);
+ wpa_hexdump_key(MSG_DEBUG, "TDLS: KCK", kck, 16);
+ ret = omac1_aes_128(kck, buf, pos - buf, mic);
+ os_free(buf);
+ wpa_hexdump(MSG_DEBUG, "TDLS: FTIE MIC", mic, 16);
+ return ret;
+}
+
+
+static int wpa_supplicant_verify_tdls_mic(u8 trans_seq,
+ struct wpa_tdls_peer *peer,
+ const u8 *lnkid, const u8 *timeoutie,
+ const struct wpa_tdls_ftie *ftie)
+{
+ u8 mic[16];
+
+ if (peer->tpk_set) {
+ wpa_tdls_ftie_mic(peer->tpk.kck, trans_seq, lnkid,
+ peer->rsnie_p, timeoutie, (u8 *) ftie,
+ mic);
+ if (os_memcmp(mic, ftie->mic, 16) != 0) {
+ wpa_printf(MSG_INFO, "TDLS: Invalid MIC in FTIE - "
+ "dropping packet");
+ wpa_hexdump(MSG_DEBUG, "TDLS: Received MIC",
+ ftie->mic, 16);
+ wpa_hexdump(MSG_DEBUG, "TDLS: Calculated MIC",
+ mic, 16);
+ return -1;
+ }
+ } else {
+ wpa_printf(MSG_WARNING, "TDLS: Could not verify TDLS MIC, "
+ "TPK not set - dropping packet");
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wpa_supplicant_verify_tdls_mic_teardown(
+ u8 trans_seq, u16 rcode, u8 dtoken, struct wpa_tdls_peer *peer,
+ const u8 *lnkid, const struct wpa_tdls_ftie *ftie)
+{
+ u8 mic[16];
+
+ if (peer->tpk_set) {
+ wpa_tdls_key_mic_teardown(peer->tpk.kck, trans_seq, rcode,
+ dtoken, lnkid, (u8 *) ftie, mic);
+ if (os_memcmp(mic, ftie->mic, 16) != 0) {
+ wpa_printf(MSG_INFO, "TDLS: Invalid MIC in Teardown - "
+ "dropping packet");
+ return -1;
+ }
+ } else {
+ wpa_printf(MSG_INFO, "TDLS: Could not verify TDLS Teardown "
+ "MIC, TPK not set - dropping packet");
+ return -1;
+ }
+ return 0;
+}
+
+
+static void wpa_tdls_tpk_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_sm *sm = eloop_ctx;
+ struct wpa_tdls_peer *peer = timeout_ctx;
+
+ /*
+ * On TPK lifetime expiration, we have an option of either tearing down
+ * the direct link or trying to re-initiate it. The selection of what
+ * to do is not strictly speaking controlled by our role in the expired
+ * link, but for now, use that to select whether to renew or tear down
+ * the link.
+ */
+
+ if (peer->initiator) {
+ wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime expired for " MACSTR
+ " - try to renew", MAC2STR(peer->addr));
+ wpa_tdls_start(sm, peer->addr);
+ } else {
+ wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime expired for " MACSTR
+ " - tear down", MAC2STR(peer->addr));
+ wpa_tdls_do_teardown(sm, peer,
+ WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED, 1);
+ }
+}
+
+
+static void wpa_tdls_peer_free(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
+{
+ wpa_printf(MSG_DEBUG, "TDLS: Clear state for peer " MACSTR,
+ MAC2STR(peer->addr));
+ eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer);
+ eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer);
+ peer->initiator = 0;
+ os_free(peer->sm_tmr.buf);
+ peer->sm_tmr.buf = NULL;
+ peer->rsnie_i_len = peer->rsnie_p_len = 0;
+ peer->cipher = 0;
+ peer->tpk_set = peer->tpk_success = 0;
+ os_memset(&peer->tpk, 0, sizeof(peer->tpk));
+ os_memset(peer->inonce, 0, WPA_NONCE_LEN);
+ os_memset(peer->rnonce, 0, WPA_NONCE_LEN);
+}
+
+
+static void wpa_tdls_linkid(struct wpa_sm *sm, struct wpa_tdls_peer *peer,
+ struct wpa_tdls_lnkid *lnkid)
+{
+ lnkid->ie_type = WLAN_EID_LINK_ID;
+ lnkid->ie_len = 3 * ETH_ALEN;
+ os_memcpy(lnkid->bssid, sm->bssid, ETH_ALEN);
+ if (peer->initiator) {
+ os_memcpy(lnkid->init_sta, sm->own_addr, ETH_ALEN);
+ os_memcpy(lnkid->resp_sta, peer->addr, ETH_ALEN);
+ } else {
+ os_memcpy(lnkid->init_sta, peer->addr, ETH_ALEN);
+ os_memcpy(lnkid->resp_sta, sm->own_addr, ETH_ALEN);
+ }
+}
+
+
+int wpa_tdls_send_teardown(struct wpa_sm *sm, const u8 *addr, u16 reason_code)
+{
+ struct wpa_tdls_peer *peer;
+ struct wpa_tdls_ftie *ftie;
+ struct wpa_tdls_lnkid lnkid;
+ u8 dialog_token;
+ u8 *rbuf, *pos;
+ int ielen;
+
+ if (sm->tdls_disabled || !sm->tdls_supported)
+ return -1;
+
+ /* Find the node and free from the list */
+ for (peer = sm->tdls; peer; peer = peer->next) {
+ if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
+ break;
+ }
+
+ if (peer == NULL) {
+ wpa_printf(MSG_INFO, "TDLS: No matching entry found for "
+ "Teardown " MACSTR, MAC2STR(addr));
+ return 0;
+ }
+
+ dialog_token = peer->dtoken;
+
+ wpa_printf(MSG_DEBUG, "TDLS: TDLS Teardown for " MACSTR,
+ MAC2STR(addr));
+
+ ielen = 0;
+ if (wpa_tdls_get_privacy(sm) && peer->tpk_set && peer->tpk_success) {
+ /* To add FTIE for Teardown request and compute MIC */
+ ielen += sizeof(*ftie);
+#ifdef CONFIG_TDLS_TESTING
+ if (tdls_testing & TDLS_TESTING_LONG_FRAME)
+ ielen += 170;
+#endif /* CONFIG_TDLS_TESTING */
+ }
+
+ rbuf = os_zalloc(ielen + 1);
+ if (rbuf == NULL)
+ return -1;
+ pos = rbuf;
+
+ if (!wpa_tdls_get_privacy(sm) || !peer->tpk_set || !peer->tpk_success)
+ goto skip_ies;
+
+ ftie = (struct wpa_tdls_ftie *) pos;
+ ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION;
+ /* Using the recent nonce which should be for CONFIRM frame */
+ os_memcpy(ftie->Anonce, peer->rnonce, WPA_NONCE_LEN);
+ os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN);
+ ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2;
+ pos = (u8 *) (ftie + 1);
+#ifdef CONFIG_TDLS_TESTING
+ if (tdls_testing & TDLS_TESTING_LONG_FRAME) {
+ wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to "
+ "FTIE");
+ ftie->ie_len += 170;
+ *pos++ = 255; /* FTIE subelem */
+ *pos++ = 168; /* FTIE subelem length */
+ pos += 168;
+ }
+#endif /* CONFIG_TDLS_TESTING */
+ wpa_hexdump(MSG_DEBUG, "TDLS: FTIE for TDLS Teardown handshake",
+ (u8 *) ftie, pos - (u8 *) ftie);
+
+ /* compute MIC before sending */
+ wpa_tdls_linkid(sm, peer, &lnkid);
+ wpa_tdls_key_mic_teardown(peer->tpk.kck, 4, reason_code,
+ dialog_token, (u8 *) &lnkid, (u8 *) ftie,
+ ftie->mic);
+
+skip_ies:
+ /* TODO: register for a Timeout handler, if Teardown is not received at
+ * the other end, then try again another time */
+
+ /* request driver to send Teardown using this FTIE */
+ wpa_tdls_tpk_send(sm, addr, WLAN_TDLS_TEARDOWN, 0,
+ WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED, rbuf,
+ pos - rbuf);
+ os_free(rbuf);
+
+ /* clear the Peerkey statemachine */
+ wpa_tdls_peer_free(sm, peer);
+
+ return 0;
+}
+
+
+int wpa_tdls_teardown_link(struct wpa_sm *sm, const u8 *addr, u16 reason_code)
+{
+ struct wpa_tdls_peer *peer;
+
+ if (sm->tdls_disabled || !sm->tdls_supported)
+ return -1;
+
+ for (peer = sm->tdls; peer; peer = peer->next) {
+ if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
+ break;
+ }
+
+ if (peer == NULL) {
+ wpa_printf(MSG_DEBUG, "TDLS: Could not find peer " MACSTR
+ " for link Teardown", MAC2STR(addr));
+ return -1;
+ }
+
+ if (!peer->tpk_success) {
+ wpa_printf(MSG_DEBUG, "TDLS: Peer " MACSTR
+ " not connected - cannot Teardown link", MAC2STR(addr));
+ return -1;
+ }
+
+ return wpa_tdls_do_teardown(sm, peer, reason_code, 0);
+}
+
+
+void wpa_tdls_disable_link(struct wpa_sm *sm, const u8 *addr)
+{
+ struct wpa_tdls_peer *peer;
+
+ for (peer = sm->tdls; peer; peer = peer->next) {
+ if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
+ break;
+ }
+
+ if (peer) {
+ wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, addr);
+ wpa_tdls_peer_free(sm, peer);
+ }
+}
+
+
+static int wpa_tdls_recv_teardown(struct wpa_sm *sm, const u8 *src_addr,
+ const u8 *buf, size_t len)
+{
+ struct wpa_tdls_peer *peer = NULL;
+ struct wpa_tdls_ftie *ftie;
+ struct wpa_tdls_lnkid *lnkid;
+ struct wpa_eapol_ie_parse kde;
+ u16 reason_code;
+ const u8 *pos;
+ int ielen;
+
+ /* Find the node and free from the list */
+ for (peer = sm->tdls; peer; peer = peer->next) {
+ if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0)
+ break;
+ }
+
+ if (peer == NULL) {
+ wpa_printf(MSG_INFO, "TDLS: No matching entry found for "
+ "Teardown " MACSTR, MAC2STR(src_addr));
+ return 0;
+ }
+
+ pos = buf;
+ pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */;
+
+ reason_code = WPA_GET_LE16(pos);
+ pos += 2;
+
+ wpa_printf(MSG_DEBUG, "TDLS: TDLS Teardown Request from " MACSTR
+ " (reason code %u)", MAC2STR(src_addr), reason_code);
+
+ ielen = len - (pos - buf); /* start of IE in buf */
+ if (wpa_supplicant_parse_ies((const u8 *) pos, ielen, &kde) < 0) {
+ wpa_printf(MSG_INFO, "TDLS: Failed to parse IEs in Teardown");
+ return -1;
+ }
+
+ if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) {
+ wpa_printf(MSG_INFO, "TDLS: No Link Identifier IE in TDLS "
+ "Teardown");
+ return -1;
+ }
+ lnkid = (struct wpa_tdls_lnkid *) kde.lnkid;
+
+ if (!wpa_tdls_get_privacy(sm) || !peer->tpk_set || !peer->tpk_success)
+ goto skip_ftie;
+
+ if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie)) {
+ wpa_printf(MSG_INFO, "TDLS: No FTIE in TDLS Teardown");
+ return -1;
+ }
+
+ ftie = (struct wpa_tdls_ftie *) kde.ftie;
+
+ /* Process MIC check to see if TDLS Teardown is right */
+ if (wpa_supplicant_verify_tdls_mic_teardown(4, reason_code,
+ peer->dtoken, peer,
+ (u8 *) lnkid, ftie) < 0) {
+ wpa_printf(MSG_DEBUG, "TDLS: MIC failure for TDLS "
+ "Teardown Request from " MACSTR, MAC2STR(src_addr));
+ return -1;
+ }
+
+skip_ftie:
+ /*
+ * Request the driver to disable the direct link and clear associated
+ * keys.
+ */
+ wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr);
+
+ /* clear the Peerkey statemachine */
+ wpa_tdls_peer_free(sm, peer);
+
+ return 0;
+}
+
+
+/**
+ * wpa_tdls_send_error - To send suitable TDLS status response with
+ * appropriate status code mentioning reason for error/failure.
+ * @dst - MAC addr of Peer station
+ * @tdls_action - TDLS frame type for which error code is sent
+ * @status - status code mentioning reason
+ */
+
+static int wpa_tdls_send_error(struct wpa_sm *sm, const u8 *dst,
+ u8 tdls_action, u8 dialog_token, u16 status)
+{
+ wpa_printf(MSG_DEBUG, "TDLS: Sending error to " MACSTR
+ " (action=%u status=%u)",
+ MAC2STR(dst), tdls_action, status);
+ return wpa_tdls_tpk_send(sm, dst, tdls_action, dialog_token, status,
+ NULL, 0);
+}
+
+
+static struct wpa_tdls_peer *
+wpa_tdls_add_peer(struct wpa_sm *sm, const u8 *addr)
+{
+ struct wpa_tdls_peer *peer;
+
+ wpa_printf(MSG_INFO, "TDLS: Creating peer entry for " MACSTR,
+ MAC2STR(addr));
+
+ peer = os_zalloc(sizeof(*peer));
+ if (peer == NULL)
+ return NULL;
+
+ os_memcpy(peer->addr, addr, ETH_ALEN);
+ peer->next = sm->tdls;
+ sm->tdls = peer;
+
+ return peer;
+}
+
+
+static int wpa_tdls_send_tpk_m1(struct wpa_sm *sm,
+ struct wpa_tdls_peer *peer)
+{
+ size_t buf_len;
+ struct wpa_tdls_timeoutie timeoutie;
+ u16 rsn_capab;
+ struct wpa_tdls_ftie *ftie;
+ u8 *rbuf, *pos, *count_pos;
+ u16 count;
+ struct rsn_ie_hdr *hdr;
+
+ if (!wpa_tdls_get_privacy(sm)) {
+ wpa_printf(MSG_DEBUG, "TDLS: No security used on the link");
+ peer->rsnie_i_len = 0;
+ goto skip_rsnie;
+ }
+
+ /*
+ * TPK Handshake Message 1:
+ * FTIE: ANonce=0, SNonce=initiator nonce MIC=0, DataKDs=(RSNIE_I,
+ * Timeout Interval IE))
+ */
+
+ /* Filling RSN IE */
+ hdr = (struct rsn_ie_hdr *) peer->rsnie_i;
+ hdr->elem_id = WLAN_EID_RSN;
+ WPA_PUT_LE16(hdr->version, RSN_VERSION);
+
+ pos = (u8 *) (hdr + 1);
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
+ pos += RSN_SELECTOR_LEN;
+ count_pos = pos;
+ pos += 2;
+
+ count = 0;
+
+ /*
+ * AES-CCMP is the default Encryption preferred for TDLS, so
+ * RSN IE is filled only with CCMP CIPHER
+ * Note: TKIP is not used to encrypt TDLS link.
+ *
+ * Regardless of the cipher used on the AP connection, select CCMP
+ * here.
+ */
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
+ pos += RSN_SELECTOR_LEN;
+ count++;
+
+ WPA_PUT_LE16(count_pos, count);
+
+ WPA_PUT_LE16(pos, 1);
+ pos += 2;
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE);
+ pos += RSN_SELECTOR_LEN;
+
+ rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED;
+ rsn_capab |= RSN_NUM_REPLAY_COUNTERS_16 << 2;
+#ifdef CONFIG_TDLS_TESTING
+ if (tdls_testing & TDLS_TESTING_ALT_RSN_IE) {
+ wpa_printf(MSG_DEBUG, "TDLS: Use alternative RSN IE for "
+ "testing");
+ rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED;
+ }
+#endif /* CONFIG_TDLS_TESTING */
+ WPA_PUT_LE16(pos, rsn_capab);
+ pos += 2;
+#ifdef CONFIG_TDLS_TESTING
+ if (tdls_testing & TDLS_TESTING_ALT_RSN_IE) {
+ /* Number of PMKIDs */
+ *pos++ = 0x00;
+ *pos++ = 0x00;
+ }
+#endif /* CONFIG_TDLS_TESTING */
+
+ hdr->len = (pos - peer->rsnie_i) - 2;
+ peer->rsnie_i_len = pos - peer->rsnie_i;
+ wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE for TPK handshake",
+ peer->rsnie_i, peer->rsnie_i_len);
+
+skip_rsnie:
+ buf_len = 0;
+ if (wpa_tdls_get_privacy(sm))
+ buf_len += peer->rsnie_i_len + sizeof(struct wpa_tdls_ftie) +
+ sizeof(struct wpa_tdls_timeoutie);
+#ifdef CONFIG_TDLS_TESTING
+ if (wpa_tdls_get_privacy(sm) &&
+ (tdls_testing & TDLS_TESTING_LONG_FRAME))
+ buf_len += 170;
+ if (tdls_testing & TDLS_TESTING_DIFF_BSSID)
+ buf_len += sizeof(struct wpa_tdls_lnkid);
+#endif /* CONFIG_TDLS_TESTING */
+ rbuf = os_zalloc(buf_len + 1);
+ if (rbuf == NULL) {
+ wpa_tdls_peer_free(sm, peer);
+ return -1;
+ }
+ pos = rbuf;
+
+ if (!wpa_tdls_get_privacy(sm))
+ goto skip_ies;
+
+ /* Initiator RSN IE */
+ pos = wpa_add_ie(pos, peer->rsnie_i, peer->rsnie_i_len);
+
+ ftie = (struct wpa_tdls_ftie *) pos;
+ ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION;
+ ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2;
+
+ if (os_get_random(peer->inonce, WPA_NONCE_LEN)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "TDLS: Failed to get random data for initiator Nonce");
+ os_free(rbuf);
+ wpa_tdls_peer_free(sm, peer);
+ return -1;
+ }
+ wpa_hexdump(MSG_DEBUG, "TDLS: Initiator Nonce for TPK handshake",
+ peer->inonce, WPA_NONCE_LEN);
+ os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN);
+
+ wpa_hexdump(MSG_DEBUG, "TDLS: FTIE for TPK Handshake M1",
+ (u8 *) ftie, sizeof(struct wpa_tdls_ftie));
+
+ pos = (u8 *) (ftie + 1);
+
+#ifdef CONFIG_TDLS_TESTING
+ if (tdls_testing & TDLS_TESTING_LONG_FRAME) {
+ wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to "
+ "FTIE");
+ ftie->ie_len += 170;
+ *pos++ = 255; /* FTIE subelem */
+ *pos++ = 168; /* FTIE subelem length */
+ pos += 168;
+ }
+#endif /* CONFIG_TDLS_TESTING */
+
+ /* Lifetime */
+ peer->lifetime = TPK_LIFETIME;
+#ifdef CONFIG_TDLS_TESTING
+ if (tdls_testing & TDLS_TESTING_SHORT_LIFETIME) {
+ wpa_printf(MSG_DEBUG, "TDLS: Testing - use short TPK "
+ "lifetime");
+ peer->lifetime = 301;
+ }
+ if (tdls_testing & TDLS_TESTING_LONG_LIFETIME) {
+ wpa_printf(MSG_DEBUG, "TDLS: Testing - use long TPK "
+ "lifetime");
+ peer->lifetime = 0xffffffff;
+ }
+#endif /* CONFIG_TDLS_TESTING */
+ pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie,
+ sizeof(timeoutie), peer->lifetime);
+ wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds", peer->lifetime);
+
+skip_ies:
+
+#ifdef CONFIG_TDLS_TESTING
+ if (tdls_testing & TDLS_TESTING_DIFF_BSSID) {
+ wpa_printf(MSG_DEBUG, "TDLS: Testing - use incorrect BSSID in "
+ "Link Identifier");
+ struct wpa_tdls_lnkid *l = (struct wpa_tdls_lnkid *) pos;
+ wpa_tdls_linkid(sm, peer, l);
+ l->bssid[5] ^= 0x01;
+ pos += sizeof(*l);
+ }
+#endif /* CONFIG_TDLS_TESTING */
+
+ wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Request / TPK "
+ "Handshake Message 1 (peer " MACSTR ")",
+ MAC2STR(peer->addr));
+
+ wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_SETUP_REQUEST, 1, 0,
+ rbuf, pos - rbuf);
+ os_free(rbuf);
+
+ return 0;
+}
+
+
+static int wpa_tdls_send_tpk_m2(struct wpa_sm *sm,
+ const unsigned char *src_addr, u8 dtoken,
+ struct wpa_tdls_lnkid *lnkid,
+ const struct wpa_tdls_peer *peer)
+{
+ u8 *rbuf, *pos;
+ size_t buf_len;
+ u32 lifetime;
+ struct wpa_tdls_timeoutie timeoutie;
+ struct wpa_tdls_ftie *ftie;
+
+ buf_len = 0;
+ if (wpa_tdls_get_privacy(sm)) {
+ /* Peer RSN IE, FTIE(Initiator Nonce, Responder Nonce),
+ * Lifetime */
+ buf_len += peer->rsnie_i_len + sizeof(struct wpa_tdls_ftie) +
+ sizeof(struct wpa_tdls_timeoutie);
+#ifdef CONFIG_TDLS_TESTING
+ if (tdls_testing & TDLS_TESTING_LONG_FRAME)
+ buf_len += 170;
+#endif /* CONFIG_TDLS_TESTING */
+ }
+
+ rbuf = os_zalloc(buf_len + 1);
+ if (rbuf == NULL)
+ return -1;
+ pos = rbuf;
+
+ if (!wpa_tdls_get_privacy(sm))
+ goto skip_ies;
+
+ /* Peer RSN IE */
+ pos = wpa_add_ie(pos, peer->rsnie_p, peer->rsnie_p_len);
+
+ ftie = (struct wpa_tdls_ftie *) pos;
+ ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION;
+ /* TODO: ftie->mic_control to set 2-RESPONSE */
+ os_memcpy(ftie->Anonce, peer->rnonce, WPA_NONCE_LEN);
+ os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN);
+ ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2;
+ wpa_hexdump(MSG_DEBUG, "TDLS: FTIE for TPK M2",
+ (u8 *) ftie, sizeof(*ftie));
+
+ pos = (u8 *) (ftie + 1);
+
+#ifdef CONFIG_TDLS_TESTING
+ if (tdls_testing & TDLS_TESTING_LONG_FRAME) {
+ wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to "
+ "FTIE");
+ ftie->ie_len += 170;
+ *pos++ = 255; /* FTIE subelem */
+ *pos++ = 168; /* FTIE subelem length */
+ pos += 168;
+ }
+#endif /* CONFIG_TDLS_TESTING */
+
+ /* Lifetime */
+ lifetime = peer->lifetime;
+#ifdef CONFIG_TDLS_TESTING
+ if (tdls_testing & TDLS_TESTING_WRONG_LIFETIME_RESP) {
+ wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong TPK "
+ "lifetime in response");
+ lifetime++;
+ }
+#endif /* CONFIG_TDLS_TESTING */
+ pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie,
+ sizeof(timeoutie), lifetime);
+ wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds from initiator",
+ lifetime);
+
+ /* compute MIC before sending */
+ wpa_tdls_ftie_mic(peer->tpk.kck, 2, (u8 *) lnkid, peer->rsnie_p,
+ (u8 *) &timeoutie, (u8 *) ftie, ftie->mic);
+
+skip_ies:
+ wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE, dtoken, 0,
+ rbuf, pos - rbuf);
+ os_free(rbuf);
+
+ return 0;
+}
+
+
+static int wpa_tdls_send_tpk_m3(struct wpa_sm *sm,
+ const unsigned char *src_addr, u8 dtoken,
+ struct wpa_tdls_lnkid *lnkid,
+ const struct wpa_tdls_peer *peer)
+{
+ u8 *rbuf, *pos;
+ size_t buf_len;
+ struct wpa_tdls_ftie *ftie;
+ struct wpa_tdls_timeoutie timeoutie;
+ u32 lifetime;
+
+ buf_len = 0;
+ if (wpa_tdls_get_privacy(sm)) {
+ /* Peer RSN IE, FTIE(Initiator Nonce, Responder Nonce),
+ * Lifetime */
+ buf_len += peer->rsnie_i_len + sizeof(struct wpa_tdls_ftie) +
+ sizeof(struct wpa_tdls_timeoutie);
+#ifdef CONFIG_TDLS_TESTING
+ if (tdls_testing & TDLS_TESTING_LONG_FRAME)
+ buf_len += 170;
+#endif /* CONFIG_TDLS_TESTING */
+ }
+
+ rbuf = os_zalloc(buf_len + 1);
+ if (rbuf == NULL)
+ return -1;
+ pos = rbuf;
+
+ if (!wpa_tdls_get_privacy(sm))
+ goto skip_ies;
+
+ /* Peer RSN IE */
+ pos = wpa_add_ie(pos, peer->rsnie_p, peer->rsnie_p_len);
+
+ ftie = (struct wpa_tdls_ftie *) pos;
+ ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION;
+ /*TODO: ftie->mic_control to set 3-CONFIRM */
+ os_memcpy(ftie->Anonce, peer->rnonce, WPA_NONCE_LEN);
+ os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN);
+ ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2;
+
+ pos = (u8 *) (ftie + 1);
+
+#ifdef CONFIG_TDLS_TESTING
+ if (tdls_testing & TDLS_TESTING_LONG_FRAME) {
+ wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to "
+ "FTIE");
+ ftie->ie_len += 170;
+ *pos++ = 255; /* FTIE subelem */
+ *pos++ = 168; /* FTIE subelem length */
+ pos += 168;
+ }
+#endif /* CONFIG_TDLS_TESTING */
+
+ /* Lifetime */
+ lifetime = peer->lifetime;
+#ifdef CONFIG_TDLS_TESTING
+ if (tdls_testing & TDLS_TESTING_WRONG_LIFETIME_CONF) {
+ wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong TPK "
+ "lifetime in confirm");
+ lifetime++;
+ }
+#endif /* CONFIG_TDLS_TESTING */
+ pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie,
+ sizeof(timeoutie), lifetime);
+ wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds",
+ lifetime);
+
+ /* compute MIC before sending */
+ wpa_tdls_ftie_mic(peer->tpk.kck, 3, (u8 *) lnkid, peer->rsnie_p,
+ (u8 *) &timeoutie, (u8 *) ftie, ftie->mic);
+
+skip_ies:
+ wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM, dtoken, 0,
+ rbuf, pos - rbuf);
+ os_free(rbuf);
+
+ return 0;
+}
+
+
+static int wpa_tdls_send_discovery_response(struct wpa_sm *sm,
+ struct wpa_tdls_peer *peer,
+ u8 dialog_token)
+{
+ wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Discovery Response "
+ "(peer " MACSTR ")", MAC2STR(peer->addr));
+
+ return wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_DISCOVERY_RESPONSE,
+ dialog_token, 0, NULL, 0);
+}
+
+
+static int
+wpa_tdls_process_discovery_request(struct wpa_sm *sm, const u8 *addr,
+ const u8 *buf, size_t len)
+{
+ struct wpa_eapol_ie_parse kde;
+ const struct wpa_tdls_lnkid *lnkid;
+ struct wpa_tdls_peer *peer;
+ size_t min_req_len = sizeof(struct wpa_tdls_frame) +
+ 1 /* dialog token */ + sizeof(struct wpa_tdls_lnkid);
+ u8 dialog_token;
+
+ wpa_printf(MSG_DEBUG, "TDLS: Discovery Request from " MACSTR,
+ MAC2STR(addr));
+
+ if (len < min_req_len) {
+ wpa_printf(MSG_DEBUG, "TDLS Discovery Request is too short: "
+ "%d", (int) len);
+ return -1;
+ }
+
+ dialog_token = buf[sizeof(struct wpa_tdls_frame)];
+
+ if (wpa_supplicant_parse_ies(buf + sizeof(struct wpa_tdls_frame) + 1,
+ len - (sizeof(struct wpa_tdls_frame) + 1),
+ &kde) < 0)
+ return -1;
+
+ if (!kde.lnkid) {
+ wpa_printf(MSG_DEBUG, "TDLS: Link ID not found in Discovery "
+ "Request");
+ return -1;
+ }
+
+ lnkid = (const struct wpa_tdls_lnkid *) kde.lnkid;
+
+ if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG, "TDLS: Discovery Request from different "
+ " BSS " MACSTR, MAC2STR(lnkid->bssid));
+ return -1;
+ }
+
+ peer = wpa_tdls_add_peer(sm, addr);
+ if (peer == NULL)
+ return -1;
+
+ return wpa_tdls_send_discovery_response(sm, peer, dialog_token);
+}
+
+
+int wpa_tdls_send_discovery_request(struct wpa_sm *sm, const u8 *addr)
+{
+ if (sm->tdls_disabled || !sm->tdls_supported)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "TDLS: Sending Discovery Request to peer "
+ MACSTR, MAC2STR(addr));
+ return wpa_tdls_tpk_send(sm, addr, WLAN_TDLS_DISCOVERY_REQUEST,
+ 1, 0, NULL, 0);
+}
+
+
+static int copy_supp_rates(const struct wpa_eapol_ie_parse *kde,
+ struct wpa_tdls_peer *peer)
+{
+ if (!kde->supp_rates) {
+ wpa_printf(MSG_DEBUG, "TDLS: No supported rates received");
+ return -1;
+ }
+
+ peer->supp_rates_len = kde->supp_rates_len - 2;
+ if (peer->supp_rates_len > IEEE80211_MAX_SUPP_RATES)
+ peer->supp_rates_len = IEEE80211_MAX_SUPP_RATES;
+ os_memcpy(peer->supp_rates, kde->supp_rates + 2, peer->supp_rates_len);
+
+ if (kde->ext_supp_rates) {
+ int clen = kde->ext_supp_rates_len - 2;
+ if (peer->supp_rates_len + clen > IEEE80211_MAX_SUPP_RATES)
+ clen = IEEE80211_MAX_SUPP_RATES - peer->supp_rates_len;
+ os_memcpy(peer->supp_rates + peer->supp_rates_len,
+ kde->ext_supp_rates + 2, clen);
+ peer->supp_rates_len += clen;
+ }
+
+ return 0;
+}
+
+
+static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr,
+ const u8 *buf, size_t len)
+{
+ struct wpa_tdls_peer *peer;
+ struct wpa_eapol_ie_parse kde;
+ struct wpa_ie_data ie;
+ int cipher;
+ const u8 *cpos;
+ struct wpa_tdls_ftie *ftie = NULL;
+ struct wpa_tdls_timeoutie *timeoutie;
+ struct wpa_tdls_lnkid *lnkid;
+ u32 lifetime = 0;
+#if 0
+ struct rsn_ie_hdr *hdr;
+ u8 *pos;
+ u16 rsn_capab;
+ u16 rsn_ver;
+#endif
+ u8 dtoken;
+ u16 ielen;
+ u16 status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ int tdls_prohibited = sm->tdls_prohibited;
+ int existing_peer = 0;
+
+ if (len < 3 + 3)
+ return -1;
+
+ cpos = buf;
+ cpos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */;
+
+ /* driver had already verified the frame format */
+ dtoken = *cpos++; /* dialog token */
+
+ wpa_printf(MSG_INFO, "TDLS: Dialog Token in TPK M1 %d", dtoken);
+
+ for (peer = sm->tdls; peer; peer = peer->next) {
+ if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0) {
+ existing_peer = 1;
+ break;
+ }
+ }
+
+ if (peer == NULL) {
+ peer = wpa_tdls_add_peer(sm, src_addr);
+ if (peer == NULL)
+ goto error;
+ }
+
+ /* capability information */
+ peer->capability = WPA_GET_LE16(cpos);
+ cpos += 2;
+
+ ielen = len - (cpos - buf); /* start of IE in buf */
+ if (wpa_supplicant_parse_ies(cpos, ielen, &kde) < 0) {
+ wpa_printf(MSG_INFO, "TDLS: Failed to parse IEs in TPK M1");
+ goto error;
+ }
+
+ if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) {
+ wpa_printf(MSG_INFO, "TDLS: No valid Link Identifier IE in "
+ "TPK M1");
+ goto error;
+ }
+ wpa_hexdump(MSG_DEBUG, "TDLS: Link ID Received from TPK M1",
+ kde.lnkid, kde.lnkid_len);
+ lnkid = (struct wpa_tdls_lnkid *) kde.lnkid;
+ if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) {
+ wpa_printf(MSG_INFO, "TDLS: TPK M1 from diff BSS");
+ status = WLAN_STATUS_NOT_IN_SAME_BSS;
+ goto error;
+ }
+
+ wpa_printf(MSG_DEBUG, "TDLS: TPK M1 - TPK initiator " MACSTR,
+ MAC2STR(src_addr));
+
+ if (copy_supp_rates(&kde, peer) < 0)
+ goto error;
+
+#ifdef CONFIG_TDLS_TESTING
+ if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT) {
+ for (peer = sm->tdls; peer; peer = peer->next) {
+ if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0)
+ break;
+ }
+ if (peer == NULL) {
+ peer = wpa_tdls_add_peer(sm, src_addr);
+ if (peer == NULL)
+ goto error;
+ }
+ wpa_printf(MSG_DEBUG, "TDLS: Testing concurrent initiation of "
+ "TDLS setup - send own request");
+ peer->initiator = 1;
+ wpa_tdls_send_tpk_m1(sm, peer);
+ }
+
+ if ((tdls_testing & TDLS_TESTING_IGNORE_AP_PROHIBIT) &&
+ tdls_prohibited) {
+ wpa_printf(MSG_DEBUG, "TDLS: Testing - ignore AP prohibition "
+ "on TDLS");
+ tdls_prohibited = 0;
+ }
+#endif /* CONFIG_TDLS_TESTING */
+
+ if (tdls_prohibited) {
+ wpa_printf(MSG_INFO, "TDLS: TDLS prohibited in this BSS");
+ status = WLAN_STATUS_REQUEST_DECLINED;
+ goto error;
+ }
+
+ if (!wpa_tdls_get_privacy(sm)) {
+ if (kde.rsn_ie) {
+ wpa_printf(MSG_INFO, "TDLS: RSN IE in TPK M1 while "
+ "security is disabled");
+ status = WLAN_STATUS_SECURITY_DISABLED;
+ goto error;
+ }
+ goto skip_rsn;
+ }
+
+ if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie) ||
+ kde.rsn_ie == NULL) {
+ wpa_printf(MSG_INFO, "TDLS: No FTIE or RSN IE in TPK M1");
+ status = WLAN_STATUS_INVALID_PARAMETERS;
+ goto error;
+ }
+
+ if (kde.rsn_ie_len > TDLS_MAX_IE_LEN) {
+ wpa_printf(MSG_INFO, "TDLS: Too long Initiator RSN IE in "
+ "TPK M1");
+ status = WLAN_STATUS_INVALID_RSNIE;
+ goto error;
+ }
+
+ if (wpa_parse_wpa_ie_rsn(kde.rsn_ie, kde.rsn_ie_len, &ie) < 0) {
+ wpa_printf(MSG_INFO, "TDLS: Failed to parse RSN IE in TPK M1");
+ status = WLAN_STATUS_INVALID_RSNIE;
+ goto error;
+ }
+
+ cipher = ie.pairwise_cipher;
+ if (cipher & WPA_CIPHER_CCMP) {
+ wpa_printf(MSG_DEBUG, "TDLS: Using CCMP for direct link");
+ cipher = WPA_CIPHER_CCMP;
+ } else {
+ wpa_printf(MSG_INFO, "TDLS: No acceptable cipher in TPK M1");
+ status = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
+ goto error;
+ }
+
+ if ((ie.capabilities &
+ (WPA_CAPABILITY_NO_PAIRWISE | WPA_CAPABILITY_PEERKEY_ENABLED)) !=
+ WPA_CAPABILITY_PEERKEY_ENABLED) {
+ wpa_printf(MSG_INFO, "TDLS: Invalid RSN Capabilities in "
+ "TPK M1");
+ status = WLAN_STATUS_INVALID_RSN_IE_CAPAB;
+ goto error;
+ }
+
+ /* Lifetime */
+ if (kde.key_lifetime == NULL) {
+ wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M1");
+ status = WLAN_STATUS_UNACCEPTABLE_LIFETIME;
+ goto error;
+ }
+ timeoutie = (struct wpa_tdls_timeoutie *) kde.key_lifetime;
+ lifetime = WPA_GET_LE32(timeoutie->value);
+ wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds", lifetime);
+ if (lifetime < 300) {
+ wpa_printf(MSG_INFO, "TDLS: Too short TPK lifetime");
+ status = WLAN_STATUS_UNACCEPTABLE_LIFETIME;
+ goto error;
+ }
+
+skip_rsn:
+ /* If found, use existing entry instead of adding a new one;
+ * how to handle the case where both ends initiate at the
+ * same time? */
+ if (existing_peer) {
+ if (peer->tpk_success) {
+ wpa_printf(MSG_DEBUG, "TDLS: TDLS Setup Request while "
+ "direct link is enabled - tear down the "
+ "old link first");
+#if 0
+ /* TODO: Disabling the link would be more proper
+ * operation here, but it seems to trigger a race with
+ * some drivers handling the new request frame. */
+ wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr);
+#else
+ if (sm->tdls_external_setup)
+ wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK,
+ src_addr);
+ else
+ wpa_tdls_del_key(sm, peer);
+#endif
+ wpa_tdls_peer_free(sm, peer);
+ }
+
+ /*
+ * An entry is already present, so check if we already sent a
+ * TDLS Setup Request. If so, compare MAC addresses and let the
+ * STA with the lower MAC address continue as the initiator.
+ * The other negotiation is terminated.
+ */
+ if (peer->initiator) {
+ if (os_memcmp(sm->own_addr, src_addr, ETH_ALEN) < 0) {
+ wpa_printf(MSG_DEBUG, "TDLS: Discard request "
+ "from peer with higher address "
+ MACSTR, MAC2STR(src_addr));
+ return -1;
+ } else {
+ wpa_printf(MSG_DEBUG, "TDLS: Accept request "
+ "from peer with lower address "
+ MACSTR " (terminate previously "
+ "initiated negotiation",
+ MAC2STR(src_addr));
+ wpa_tdls_peer_free(sm, peer);
+ }
+ }
+ }
+
+#ifdef CONFIG_TDLS_TESTING
+ if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT) {
+ if (os_memcmp(sm->own_addr, peer->addr, ETH_ALEN) < 0) {
+ /*
+ * The request frame from us is going to win, so do not
+ * replace information based on this request frame from
+ * the peer.
+ */
+ goto skip_rsn_check;
+ }
+ }
+#endif /* CONFIG_TDLS_TESTING */
+
+ peer->initiator = 0; /* Need to check */
+ peer->dtoken = dtoken;
+
+ if (!wpa_tdls_get_privacy(sm)) {
+ peer->rsnie_i_len = 0;
+ peer->rsnie_p_len = 0;
+ peer->cipher = WPA_CIPHER_NONE;
+ goto skip_rsn_check;
+ }
+
+ ftie = (struct wpa_tdls_ftie *) kde.ftie;
+ os_memcpy(peer->inonce, ftie->Snonce, WPA_NONCE_LEN);
+ os_memcpy(peer->rsnie_i, kde.rsn_ie, kde.rsn_ie_len);
+ peer->rsnie_i_len = kde.rsn_ie_len;
+ peer->cipher = cipher;
+
+ if (os_get_random(peer->rnonce, WPA_NONCE_LEN)) {
+ wpa_msg(sm->ctx->ctx, MSG_WARNING,
+ "TDLS: Failed to get random data for responder nonce");
+ wpa_tdls_peer_free(sm, peer);
+ goto error;
+ }
+
+#if 0
+ /* get version info from RSNIE received from Peer */
+ hdr = (struct rsn_ie_hdr *) kde.rsn_ie;
+ rsn_ver = WPA_GET_LE16(hdr->version);
+
+ /* use min(peer's version, out version) */
+ if (rsn_ver > RSN_VERSION)
+ rsn_ver = RSN_VERSION;
+
+ hdr = (struct rsn_ie_hdr *) peer->rsnie_p;
+
+ hdr->elem_id = WLAN_EID_RSN;
+ WPA_PUT_LE16(hdr->version, rsn_ver);
+ pos = (u8 *) (hdr + 1);
+
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
+ pos += RSN_SELECTOR_LEN;
+ /* Include only the selected cipher in pairwise cipher suite */
+ WPA_PUT_LE16(pos, 1);
+ pos += 2;
+ if (cipher == WPA_CIPHER_CCMP)
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
+ pos += RSN_SELECTOR_LEN;
+
+ WPA_PUT_LE16(pos, 1);
+ pos += 2;
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE);
+ pos += RSN_SELECTOR_LEN;
+
+ rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED;
+ rsn_capab |= RSN_NUM_REPLAY_COUNTERS_16 << 2;
+ WPA_PUT_LE16(pos, rsn_capab);
+ pos += 2;
+
+ hdr->len = (pos - peer->rsnie_p) - 2;
+ peer->rsnie_p_len = pos - peer->rsnie_p;
+#endif
+
+ /* temp fix: validation of RSNIE later */
+ os_memcpy(peer->rsnie_p, peer->rsnie_i, peer->rsnie_i_len);
+ peer->rsnie_p_len = peer->rsnie_i_len;
+
+ wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE for TPK handshake",
+ peer->rsnie_p, peer->rsnie_p_len);
+
+ peer->lifetime = lifetime;
+
+ wpa_tdls_generate_tpk(peer, sm->own_addr, sm->bssid);
+
+skip_rsn_check:
+ /* add the peer to the driver as a "setup in progress" peer */
+ wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, NULL, 0);
+
+ wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Response / TPK M2");
+ if (wpa_tdls_send_tpk_m2(sm, src_addr, dtoken, lnkid, peer) < 0) {
+ wpa_tdls_disable_link(sm, peer->addr);
+ goto error;
+ }
+
+ return 0;
+
+error:
+ wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE, dtoken,
+ status);
+ return -1;
+}
+
+
+static void wpa_tdls_enable_link(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
+{
+ peer->tpk_success = 1;
+ eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer);
+ if (wpa_tdls_get_privacy(sm)) {
+ u32 lifetime = peer->lifetime;
+ /*
+ * Start the initiator process a bit earlier to avoid race
+ * condition with the responder sending teardown request.
+ */
+ if (lifetime > 3 && peer->initiator)
+ lifetime -= 3;
+ eloop_register_timeout(lifetime, 0, wpa_tdls_tpk_timeout,
+ sm, peer);
+#ifdef CONFIG_TDLS_TESTING
+ if (tdls_testing & TDLS_TESTING_NO_TPK_EXPIRATION) {
+ wpa_printf(MSG_DEBUG, "TDLS: Testing - disable TPK "
+ "expiration");
+ eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer);
+ }
+#endif /* CONFIG_TDLS_TESTING */
+ }
+
+ /* add supported rates and capabilities to the TDLS peer */
+ wpa_sm_tdls_peer_addset(sm, peer->addr, 0, peer->capability,
+ peer->supp_rates, peer->supp_rates_len);
+
+ wpa_sm_tdls_oper(sm, TDLS_ENABLE_LINK, peer->addr);
+}
+
+
+static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr,
+ const u8 *buf, size_t len)
+{
+ struct wpa_tdls_peer *peer;
+ struct wpa_eapol_ie_parse kde;
+ struct wpa_ie_data ie;
+ int cipher;
+ struct wpa_tdls_ftie *ftie;
+ struct wpa_tdls_timeoutie *timeoutie;
+ struct wpa_tdls_lnkid *lnkid;
+ u32 lifetime;
+ u8 dtoken;
+ int ielen;
+ u16 status;
+ const u8 *pos;
+
+ wpa_printf(MSG_DEBUG, "TDLS: Received TDLS Setup Response / TPK M2 "
+ "(Peer " MACSTR ")", MAC2STR(src_addr));
+ for (peer = sm->tdls; peer; peer = peer->next) {
+ if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0)
+ break;
+ }
+ if (peer == NULL) {
+ wpa_printf(MSG_INFO, "TDLS: No matching peer found for "
+ "TPK M2: " MACSTR, MAC2STR(src_addr));
+ return -1;
+ }
+ wpa_tdls_tpk_retry_timeout_cancel(sm, peer, WLAN_TDLS_SETUP_REQUEST);
+
+ if (len < 3 + 2 + 1)
+ return -1;
+ pos = buf;
+ pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */;
+ status = WPA_GET_LE16(pos);
+ pos += 2 /* status code */;
+
+ if (status != WLAN_STATUS_SUCCESS) {
+ wpa_printf(MSG_INFO, "TDLS: Status code in TPK M2: %u",
+ status);
+ if (sm->tdls_external_setup)
+ wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr);
+ return -1;
+ }
+
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+ /* TODO: need to verify dialog token matches here or in kernel */
+ dtoken = *pos++; /* dialog token */
+
+ wpa_printf(MSG_DEBUG, "TDLS: Dialog Token in TPK M2 %d", dtoken);
+
+ if (len < 3 + 2 + 1 + 2)
+ return -1;
+
+ /* capability information */
+ peer->capability = WPA_GET_LE16(pos);
+ pos += 2;
+
+ ielen = len - (pos - buf); /* start of IE in buf */
+ if (wpa_supplicant_parse_ies(pos, ielen, &kde) < 0) {
+ wpa_printf(MSG_INFO, "TDLS: Failed to parse IEs in TPK M2");
+ goto error;
+ }
+
+#ifdef CONFIG_TDLS_TESTING
+ if (tdls_testing & TDLS_TESTING_DECLINE_RESP) {
+ wpa_printf(MSG_DEBUG, "TDLS: Testing - decline response");
+ status = WLAN_STATUS_REQUEST_DECLINED;
+ goto error;
+ }
+#endif /* CONFIG_TDLS_TESTING */
+
+ if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) {
+ wpa_printf(MSG_INFO, "TDLS: No valid Link Identifier IE in "
+ "TPK M2");
+ goto error;
+ }
+ wpa_hexdump(MSG_DEBUG, "TDLS: Link ID Received from TPK M2",
+ kde.lnkid, kde.lnkid_len);
+ lnkid = (struct wpa_tdls_lnkid *) kde.lnkid;
+
+ if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) {
+ wpa_printf(MSG_INFO, "TDLS: TPK M2 from different BSS");
+ status = WLAN_STATUS_NOT_IN_SAME_BSS;
+ goto error;
+ }
+
+ if (copy_supp_rates(&kde, peer) < 0)
+ goto error;
+
+ if (!wpa_tdls_get_privacy(sm)) {
+ peer->rsnie_p_len = 0;
+ peer->cipher = WPA_CIPHER_NONE;
+ goto skip_rsn;
+ }
+
+ if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie) ||
+ kde.rsn_ie == NULL) {
+ wpa_printf(MSG_INFO, "TDLS: No FTIE or RSN IE in TPK M2");
+ status = WLAN_STATUS_INVALID_PARAMETERS;
+ goto error;
+ }
+ wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M2",
+ kde.rsn_ie, kde.rsn_ie_len);
+
+ /*
+ * FIX: bitwise comparison of RSN IE is not the correct way of
+ * validation this. It can be different, but certain fields must
+ * match. Since we list only a single pairwise cipher in TPK M1, the
+ * memcmp is likely to work in most cases, though.
+ */
+ if (kde.rsn_ie_len != peer->rsnie_i_len ||
+ os_memcmp(peer->rsnie_i, kde.rsn_ie, peer->rsnie_i_len) != 0) {
+ wpa_printf(MSG_INFO, "TDLS: RSN IE in TPK M2 does "
+ "not match with RSN IE used in TPK M1");
+ wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Sent in TPK M1",
+ peer->rsnie_i, peer->rsnie_i_len);
+ wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M2",
+ kde.rsn_ie, kde.rsn_ie_len);
+ status = WLAN_STATUS_INVALID_RSNIE;
+ goto error;
+ }
+
+ if (wpa_parse_wpa_ie_rsn(kde.rsn_ie, kde.rsn_ie_len, &ie) < 0) {
+ wpa_printf(MSG_INFO, "TDLS: Failed to parse RSN IE in TPK M2");
+ status = WLAN_STATUS_INVALID_RSNIE;
+ goto error;
+ }
+
+ cipher = ie.pairwise_cipher;
+ if (cipher == WPA_CIPHER_CCMP) {
+ wpa_printf(MSG_DEBUG, "TDLS: Using CCMP for direct link");
+ cipher = WPA_CIPHER_CCMP;
+ } else {
+ wpa_printf(MSG_INFO, "TDLS: No acceptable cipher in TPK M2");
+ status = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
+ goto error;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "TDLS: FTIE Received from TPK M2",
+ kde.ftie, sizeof(*ftie));
+ ftie = (struct wpa_tdls_ftie *) kde.ftie;
+
+ if (!os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) == 0) {
+ wpa_printf(MSG_INFO, "TDLS: FTIE SNonce in TPK M2 does "
+ "not match with FTIE SNonce used in TPK M1");
+ /* Silently discard the frame */
+ return -1;
+ }
+
+ /* Responder Nonce and RSN IE */
+ os_memcpy(peer->rnonce, ftie->Anonce, WPA_NONCE_LEN);
+ os_memcpy(peer->rsnie_p, kde.rsn_ie, kde.rsn_ie_len);
+ peer->rsnie_p_len = kde.rsn_ie_len;
+ peer->cipher = cipher;
+
+ /* Lifetime */
+ if (kde.key_lifetime == NULL) {
+ wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M2");
+ status = WLAN_STATUS_UNACCEPTABLE_LIFETIME;
+ goto error;
+ }
+ timeoutie = (struct wpa_tdls_timeoutie *) kde.key_lifetime;
+ lifetime = WPA_GET_LE32(timeoutie->value);
+ wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds in TPK M2",
+ lifetime);
+ if (lifetime != peer->lifetime) {
+ wpa_printf(MSG_INFO, "TDLS: Unexpected TPK lifetime %u in "
+ "TPK M2 (expected %u)", lifetime, peer->lifetime);
+ status = WLAN_STATUS_UNACCEPTABLE_LIFETIME;
+ goto error;
+ }
+
+ wpa_tdls_generate_tpk(peer, sm->own_addr, sm->bssid);
+
+ /* Process MIC check to see if TPK M2 is right */
+ if (wpa_supplicant_verify_tdls_mic(2, peer, (u8 *) lnkid,
+ (u8 *) timeoutie, ftie) < 0) {
+ /* Discard the frame */
+ wpa_tdls_del_key(sm, peer);
+ wpa_tdls_peer_free(sm, peer);
+ if (sm->tdls_external_setup)
+ wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr);
+ return -1;
+ }
+
+ wpa_tdls_set_key(sm, peer);
+
+skip_rsn:
+ peer->dtoken = dtoken;
+
+ wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Confirm / "
+ "TPK Handshake Message 3");
+ wpa_tdls_send_tpk_m3(sm, src_addr, dtoken, lnkid, peer);
+
+ wpa_tdls_enable_link(sm, peer);
+
+ return 0;
+
+error:
+ wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM, dtoken,
+ status);
+ if (sm->tdls_external_setup)
+ wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr);
+ return -1;
+}
+
+
+static int wpa_tdls_process_tpk_m3(struct wpa_sm *sm, const u8 *src_addr,
+ const u8 *buf, size_t len)
+{
+ struct wpa_tdls_peer *peer;
+ struct wpa_eapol_ie_parse kde;
+ struct wpa_tdls_ftie *ftie;
+ struct wpa_tdls_timeoutie *timeoutie;
+ struct wpa_tdls_lnkid *lnkid;
+ int ielen;
+ u16 status;
+ const u8 *pos;
+ u32 lifetime;
+
+ wpa_printf(MSG_DEBUG, "TDLS: Received TDLS Setup Confirm / TPK M3 "
+ "(Peer " MACSTR ")", MAC2STR(src_addr));
+ for (peer = sm->tdls; peer; peer = peer->next) {
+ if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0)
+ break;
+ }
+ if (peer == NULL) {
+ wpa_printf(MSG_INFO, "TDLS: No matching peer found for "
+ "TPK M3: " MACSTR, MAC2STR(src_addr));
+ return -1;
+ }
+ wpa_tdls_tpk_retry_timeout_cancel(sm, peer, WLAN_TDLS_SETUP_RESPONSE);
+
+ if (len < 3 + 3)
+ return -1;
+ pos = buf;
+ pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */;
+
+ status = WPA_GET_LE16(pos);
+
+ if (status != 0) {
+ wpa_printf(MSG_INFO, "TDLS: Status code in TPK M3: %u",
+ status);
+ if (sm->tdls_external_setup)
+ wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr);
+ return -1;
+ }
+ pos += 2 /* status code */ + 1 /* dialog token */;
+
+ ielen = len - (pos - buf); /* start of IE in buf */
+ if (wpa_supplicant_parse_ies((const u8 *) pos, ielen, &kde) < 0) {
+ wpa_printf(MSG_INFO, "TDLS: Failed to parse KDEs in TPK M3");
+ return -1;
+ }
+
+ if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) {
+ wpa_printf(MSG_INFO, "TDLS: No Link Identifier IE in TPK M3");
+ return -1;
+ }
+ wpa_hexdump(MSG_DEBUG, "TDLS: Link ID Received from TPK M3",
+ (u8 *) kde.lnkid, kde.lnkid_len);
+ lnkid = (struct wpa_tdls_lnkid *) kde.lnkid;
+
+ if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) {
+ wpa_printf(MSG_INFO, "TDLS: TPK M3 from diff BSS");
+ return -1;
+ }
+
+ if (!wpa_tdls_get_privacy(sm))
+ goto skip_rsn;
+
+ if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie)) {
+ wpa_printf(MSG_INFO, "TDLS: No FTIE in TPK M3");
+ return -1;
+ }
+ wpa_hexdump(MSG_DEBUG, "TDLS: FTIE Received from TPK M3",
+ kde.ftie, sizeof(*ftie));
+ ftie = (struct wpa_tdls_ftie *) kde.ftie;
+
+ if (kde.rsn_ie == NULL) {
+ wpa_printf(MSG_INFO, "TDLS: No RSN IE in TPK M3");
+ return -1;
+ }
+ wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M3",
+ kde.rsn_ie, kde.rsn_ie_len);
+ if (kde.rsn_ie_len != peer->rsnie_p_len ||
+ os_memcmp(kde.rsn_ie, peer->rsnie_p, peer->rsnie_p_len) != 0) {
+ wpa_printf(MSG_INFO, "TDLS: RSN IE in TPK M3 does not match "
+ "with the one sent in TPK M2");
+ return -1;
+ }
+
+ if (!os_memcmp(peer->rnonce, ftie->Anonce, WPA_NONCE_LEN) == 0) {
+ wpa_printf(MSG_INFO, "TDLS: FTIE ANonce in TPK M3 does "
+ "not match with FTIE ANonce used in TPK M2");
+ return -1;
+ }
+
+ if (!os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) == 0) {
+ wpa_printf(MSG_INFO, "TDLS: FTIE SNonce in TPK M3 does not "
+ "match with FTIE SNonce used in TPK M1");
+ return -1;
+ }
+
+ if (kde.key_lifetime == NULL) {
+ wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M3");
+ return -1;
+ }
+ timeoutie = (struct wpa_tdls_timeoutie *) kde.key_lifetime;
+ wpa_hexdump(MSG_DEBUG, "TDLS: Timeout IE Received from TPK M3",
+ (u8 *) timeoutie, sizeof(*timeoutie));
+ lifetime = WPA_GET_LE32(timeoutie->value);
+ wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds in TPK M3",
+ lifetime);
+ if (lifetime != peer->lifetime) {
+ wpa_printf(MSG_INFO, "TDLS: Unexpected TPK lifetime %u in "
+ "TPK M3 (expected %u)", lifetime, peer->lifetime);
+ if (sm->tdls_external_setup)
+ wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr);
+ return -1;
+ }
+
+ if (wpa_supplicant_verify_tdls_mic(3, peer, (u8 *) lnkid,
+ (u8 *) timeoutie, ftie) < 0) {
+ wpa_tdls_del_key(sm, peer);
+ wpa_tdls_peer_free(sm, peer);
+ return -1;
+ }
+
+ if (wpa_tdls_set_key(sm, peer) < 0)
+ return -1;
+
+skip_rsn:
+ wpa_tdls_enable_link(sm, peer);
+
+ return 0;
+}
+
+
+static u8 * wpa_add_tdls_timeoutie(u8 *pos, u8 *ie, size_t ie_len, u32 tsecs)
+{
+ struct wpa_tdls_timeoutie *lifetime = (struct wpa_tdls_timeoutie *) ie;
+
+ os_memset(lifetime, 0, ie_len);
+ lifetime->ie_type = WLAN_EID_TIMEOUT_INTERVAL;
+ lifetime->ie_len = sizeof(struct wpa_tdls_timeoutie) - 2;
+ lifetime->interval_type = WLAN_TIMEOUT_KEY_LIFETIME;
+ WPA_PUT_LE32(lifetime->value, tsecs);
+ os_memcpy(pos, ie, ie_len);
+ return pos + ie_len;
+}
+
+
+/**
+ * wpa_tdls_start - Initiate TDLS handshake (send TPK Handshake Message 1)
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @peer: MAC address of the peer STA
+ * Returns: 0 on success, or -1 on failure
+ *
+ * Send TPK Handshake Message 1 info to driver to start TDLS
+ * handshake with the peer.
+ */
+int wpa_tdls_start(struct wpa_sm *sm, const u8 *addr)
+{
+ struct wpa_tdls_peer *peer;
+ int tdls_prohibited = sm->tdls_prohibited;
+
+ if (sm->tdls_disabled || !sm->tdls_supported)
+ return -1;
+
+#ifdef CONFIG_TDLS_TESTING
+ if ((tdls_testing & TDLS_TESTING_IGNORE_AP_PROHIBIT) &&
+ tdls_prohibited) {
+ wpa_printf(MSG_DEBUG, "TDLS: Testing - ignore AP prohibition "
+ "on TDLS");
+ tdls_prohibited = 0;
+ }
+#endif /* CONFIG_TDLS_TESTING */
+
+ if (tdls_prohibited) {
+ wpa_printf(MSG_DEBUG, "TDLS: TDLS is prohibited in this BSS - "
+ "reject request to start setup");
+ return -1;
+ }
+
+ /* Find existing entry and if found, use that instead of adding
+ * a new one */
+ for (peer = sm->tdls; peer; peer = peer->next) {
+ if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
+ break;
+ }
+
+ if (peer == NULL) {
+ peer = wpa_tdls_add_peer(sm, addr);
+ if (peer == NULL)
+ return -1;
+ }
+
+ peer->initiator = 1;
+
+ /* add the peer to the driver as a "setup in progress" peer */
+ wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, NULL, 0);
+
+ if (wpa_tdls_send_tpk_m1(sm, peer) < 0) {
+ wpa_tdls_disable_link(sm, peer->addr);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int wpa_tdls_reneg(struct wpa_sm *sm, const u8 *addr)
+{
+ struct wpa_tdls_peer *peer;
+
+ if (sm->tdls_disabled || !sm->tdls_supported)
+ return -1;
+
+ for (peer = sm->tdls; peer; peer = peer->next) {
+ if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
+ break;
+ }
+
+ if (peer == NULL || !peer->tpk_success)
+ return -1;
+
+ if (sm->tdls_external_setup) {
+ /*
+ * Disable previous link to allow renegotiation to be completed
+ * on AP path.
+ */
+ wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr);
+ }
+
+ return wpa_tdls_start(sm, addr);
+}
+
+
+/**
+ * wpa_supplicant_rx_tdls - Receive TDLS data frame
+ *
+ * This function is called to receive TDLS (ethertype = 0x890d) data frames.
+ */
+static void wpa_supplicant_rx_tdls(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len)
+{
+ struct wpa_sm *sm = ctx;
+ struct wpa_tdls_frame *tf;
+
+ wpa_hexdump(MSG_DEBUG, "TDLS: Received Data frame encapsulation",
+ buf, len);
+
+ if (sm->tdls_disabled || !sm->tdls_supported) {
+ wpa_printf(MSG_DEBUG, "TDLS: Discard message - TDLS disabled "
+ "or unsupported by driver");
+ return;
+ }
+
+ if (os_memcmp(src_addr, sm->own_addr, ETH_ALEN) == 0) {
+ wpa_printf(MSG_DEBUG, "TDLS: Discard copy of own message");
+ return;
+ }
+
+ if (len < sizeof(*tf)) {
+ wpa_printf(MSG_INFO, "TDLS: Drop too short frame");
+ return;
+ }
+
+ /* Check to make sure its a valid encapsulated TDLS frame */
+ tf = (struct wpa_tdls_frame *) buf;
+ if (tf->payloadtype != 2 /* TDLS_RFTYPE */ ||
+ tf->category != WLAN_ACTION_TDLS) {
+ wpa_printf(MSG_INFO, "TDLS: Invalid frame - payloadtype=%u "
+ "category=%u action=%u",
+ tf->payloadtype, tf->category, tf->action);
+ return;
+ }
+
+ switch (tf->action) {
+ case WLAN_TDLS_SETUP_REQUEST:
+ wpa_tdls_process_tpk_m1(sm, src_addr, buf, len);
+ break;
+ case WLAN_TDLS_SETUP_RESPONSE:
+ wpa_tdls_process_tpk_m2(sm, src_addr, buf, len);
+ break;
+ case WLAN_TDLS_SETUP_CONFIRM:
+ wpa_tdls_process_tpk_m3(sm, src_addr, buf, len);
+ break;
+ case WLAN_TDLS_TEARDOWN:
+ wpa_tdls_recv_teardown(sm, src_addr, buf, len);
+ break;
+ case WLAN_TDLS_DISCOVERY_REQUEST:
+ wpa_tdls_process_discovery_request(sm, src_addr, buf, len);
+ break;
+ default:
+ /* Kernel code will process remaining frames */
+ wpa_printf(MSG_DEBUG, "TDLS: Ignore TDLS frame action code %u",
+ tf->action);
+ break;
+ }
+}
+
+
+/**
+ * wpa_tdls_init - Initialize driver interface parameters for TDLS
+ * @wpa_s: Pointer to wpa_supplicant data
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is called to initialize driver interface parameters for TDLS.
+ * wpa_drv_init() must have been called before this function to initialize the
+ * driver interface.
+ */
+int wpa_tdls_init(struct wpa_sm *sm)
+{
+ if (sm == NULL)
+ return -1;
+
+ sm->l2_tdls = l2_packet_init(sm->ifname, sm->own_addr,
+ ETH_P_80211_ENCAP, wpa_supplicant_rx_tdls,
+ sm, 0);
+ if (sm->l2_tdls == NULL) {
+ wpa_printf(MSG_ERROR, "TDLS: Failed to open l2_packet "
+ "connection");
+ return -1;
+ }
+
+ /*
+ * Drivers that support TDLS but don't implement the get_capa callback
+ * are assumed to perform everything internally
+ */
+ if (wpa_sm_tdls_get_capa(sm, &sm->tdls_supported,
+ &sm->tdls_external_setup) < 0) {
+ sm->tdls_supported = 1;
+ sm->tdls_external_setup = 0;
+ }
+
+ wpa_printf(MSG_DEBUG, "TDLS: TDLS operation%s supported by "
+ "driver", sm->tdls_supported ? "" : " not");
+ wpa_printf(MSG_DEBUG, "TDLS: Driver uses %s link setup",
+ sm->tdls_external_setup ? "external" : "internal");
+
+ return 0;
+}
+
+
+static void wpa_tdls_remove_peers(struct wpa_sm *sm)
+{
+ struct wpa_tdls_peer *peer, *tmp;
+
+ peer = sm->tdls;
+ sm->tdls = NULL;
+
+ while (peer) {
+ int res;
+ tmp = peer->next;
+ res = wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr);
+ wpa_printf(MSG_DEBUG, "TDLS: Remove peer " MACSTR " (res=%d)",
+ MAC2STR(peer->addr), res);
+ wpa_tdls_peer_free(sm, peer);
+ os_free(peer);
+ peer = tmp;
+ }
+}
+
+
+/**
+ * wpa_tdls_deinit - Deinitialize driver interface parameters for TDLS
+ *
+ * This function is called to recover driver interface parameters for TDLS
+ * and frees resources allocated for it.
+ */
+void wpa_tdls_deinit(struct wpa_sm *sm)
+{
+ if (sm == NULL)
+ return;
+
+ if (sm->l2_tdls)
+ l2_packet_deinit(sm->l2_tdls);
+ sm->l2_tdls = NULL;
+
+ wpa_tdls_remove_peers(sm);
+}
+
+
+void wpa_tdls_assoc(struct wpa_sm *sm)
+{
+ wpa_printf(MSG_DEBUG, "TDLS: Remove peers on association");
+ wpa_tdls_remove_peers(sm);
+}
+
+
+void wpa_tdls_disassoc(struct wpa_sm *sm)
+{
+ wpa_printf(MSG_DEBUG, "TDLS: Remove peers on disassociation");
+ wpa_tdls_remove_peers(sm);
+}
+
+
+static int wpa_tdls_prohibited(const u8 *ies, size_t len)
+{
+ struct wpa_eapol_ie_parse elems;
+
+ if (ies == NULL)
+ return 0;
+
+ if (wpa_supplicant_parse_ies(ies, len, &elems) < 0)
+ return 0;
+
+ if (elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5)
+ return 0;
+
+ /* bit 38 - TDLS Prohibited */
+ return !!(elems.ext_capab[2 + 4] & 0x40);
+}
+
+
+void wpa_tdls_ap_ies(struct wpa_sm *sm, const u8 *ies, size_t len)
+{
+ sm->tdls_prohibited = wpa_tdls_prohibited(ies, len);
+ wpa_printf(MSG_DEBUG, "TDLS: TDLS is %s in the target BSS",
+ sm->tdls_prohibited ? "prohibited" : "allowed");
+}
+
+
+void wpa_tdls_assoc_resp_ies(struct wpa_sm *sm, const u8 *ies, size_t len)
+{
+ if (!sm->tdls_prohibited && wpa_tdls_prohibited(ies, len)) {
+ wpa_printf(MSG_DEBUG, "TDLS: TDLS prohibited based on "
+ "(Re)Association Response IEs");
+ sm->tdls_prohibited = 1;
+ }
+}
+
+
+void wpa_tdls_enable(struct wpa_sm *sm, int enabled)
+{
+ wpa_printf(MSG_DEBUG, "TDLS: %s", enabled ? "enabled" : "disabled");
+ sm->tdls_disabled = !enabled;
+}
+
+
+int wpa_tdls_is_external_setup(struct wpa_sm *sm)
+{
+ return sm->tdls_external_setup;
+}
#include "common.h"
#include "crypto/aes_wrap.h"
#include "crypto/crypto.h"
+#include "crypto/random.h"
#include "common/ieee802_11_defs.h"
#include "eapol_supp/eapol_supp_sm.h"
#include "wpa.h"
* BSSID from the driver.
*/
if (wpa_sm_get_bssid(sm, sm->bssid) < 0) {
- wpa_printf(MSG_DEBUG, "WPA: Failed to read BSSID for "
- "EAPOL-Key destination address");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: Failed to read BSSID for "
+ "EAPOL-Key destination address");
} else {
dest = sm->bssid;
- wpa_printf(MSG_DEBUG, "WPA: Use BSSID (" MACSTR
- ") as the destination for EAPOL-Key",
- MAC2STR(dest));
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: Use BSSID (" MACSTR
+ ") as the destination for EAPOL-Key",
+ MAC2STR(dest));
}
}
if (key_mic &&
wpa_eapol_key_mic(kck, ver, msg, msg_len, key_mic)) {
- wpa_printf(MSG_ERROR, "WPA: Failed to generate EAPOL-Key "
- "version %d MIC", ver);
+ wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
+ "WPA: Failed to generate EAPOL-Key "
+ "version %d MIC", ver);
goto out;
}
+ wpa_hexdump_key(MSG_DEBUG, "WPA: KCK", kck, 16);
+ wpa_hexdump(MSG_DEBUG, "WPA: Derived Key MIC", key_mic, 16);
wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key", msg, msg_len);
wpa_sm_ether_send(sm, dest, proto, msg, msg_len);
eapol_sm_notify_tx_eapol_key(sm->eapol);
ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
if (wpa_sm_get_bssid(sm, bssid) < 0) {
- wpa_printf(MSG_WARNING, "Failed to read BSSID for EAPOL-Key "
- "request");
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "Failed to read BSSID for EAPOL-Key request");
return;
}
WPA_PUT_BE16(reply->key_data_length, 0);
- wpa_printf(MSG_INFO, "WPA: Sending EAPOL-Key Request (error=%d "
- "pairwise=%d ptk_set=%d len=%lu)",
- error, pairwise, sm->ptk_set, (unsigned long) rlen);
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: Sending EAPOL-Key Request (error=%d "
+ "pairwise=%d ptk_set=%d len=%lu)",
+ error, pairwise, sm->ptk_set, (unsigned long) rlen);
wpa_eapol_key_send(sm, sm->ptk.kck, ver, bssid, ETH_P_EAPOL,
rbuf, rlen, key_info & WPA_KEY_INFO_MIC ?
reply->key_mic : NULL);
* matching PMKSA cache entry here. */
sm->cur_pmksa = pmksa_cache_get(sm->pmksa, src_addr, pmkid);
if (sm->cur_pmksa) {
- wpa_printf(MSG_DEBUG, "RSN: found matching PMKID from "
- "PMKSA cache");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: found matching PMKID from PMKSA cache");
} else {
- wpa_printf(MSG_DEBUG, "RSN: no matching PMKID found");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: no matching PMKID found");
abort_cached = 1;
}
}
wpa_hexdump_key(MSG_DEBUG, "WPA: PMK from EAPOL state "
"machines", sm->pmk, pmk_len);
sm->pmk_len = pmk_len;
- if (sm->proto == WPA_PROTO_RSN) {
+ if (sm->proto == WPA_PROTO_RSN &&
+ !wpa_key_mgmt_ft(sm->key_mgmt)) {
pmksa_cache_add(sm->pmksa, sm->pmk, pmk_len,
src_addr, sm->own_addr,
sm->network_ctx, sm->key_mgmt);
}
if (!sm->cur_pmksa && pmkid &&
pmksa_cache_get(sm->pmksa, src_addr, pmkid)) {
- wpa_printf(MSG_DEBUG, "RSN: the new PMK "
- "matches with the PMKID");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: the new PMK matches with the "
+ "PMKID");
abort_cached = 0;
}
} else {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Failed to get master session key from "
- "EAPOL state machines");
- wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
- "WPA: Key handshake aborted");
+ "EAPOL state machines - key handshake "
+ "aborted");
if (sm->cur_pmksa) {
- wpa_printf(MSG_DEBUG, "RSN: Cancelled PMKSA "
- "caching attempt");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: Cancelled PMKSA caching "
+ "attempt");
sm->cur_pmksa = NULL;
abort_cached = 1;
} else if (!abort_cached) {
}
}
- if (abort_cached && wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt)) {
+ if (abort_cached && wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt) &&
+ !wpa_key_mgmt_ft(sm->key_mgmt)) {
/* Send EAPOL-Start to trigger full EAP authentication. */
u8 *buf;
size_t buflen;
- wpa_printf(MSG_DEBUG, "RSN: no PMKSA entry found - trigger "
- "full EAP authentication");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: no PMKSA entry found - trigger "
+ "full EAP authentication");
buf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_START,
NULL, 0, &buflen, NULL);
if (buf) {
u8 *rsn_ie_buf = NULL;
if (wpa_ie == NULL) {
- wpa_printf(MSG_WARNING, "WPA: No wpa_ie set - cannot "
- "generate msg 2/4");
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: No wpa_ie set - "
+ "cannot generate msg 2/4");
return -1;
}
os_memcpy(reply->key_length, key->key_length, 2);
os_memcpy(reply->replay_counter, key->replay_counter,
WPA_REPLAY_COUNTER_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPA: Replay Counter", reply->replay_counter,
+ WPA_REPLAY_COUNTER_LEN);
WPA_PUT_BE16(reply->key_data_length, wpa_ie_len);
os_memcpy(reply + 1, wpa_ie, wpa_ie_len);
os_memcpy(reply->key_nonce, nonce, WPA_NONCE_LEN);
- wpa_printf(MSG_DEBUG, "WPA: Sending EAPOL-Key 2/4");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/4");
wpa_eapol_key_send(sm, ptk->kck, ver, dst, ETH_P_EAPOL,
rbuf, rlen, reply->key_mic);
int res;
if (wpa_sm_get_network_ctx(sm) == NULL) {
- wpa_printf(MSG_WARNING, "WPA: No SSID info found (msg 1 of "
- "4).");
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: No SSID info "
+ "found (msg 1 of 4)");
return;
}
wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE);
- wpa_printf(MSG_DEBUG, "WPA: RX message 1 of 4-Way Handshake from "
- MACSTR " (ver=%d)", MAC2STR(src_addr), ver);
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: RX message 1 of 4-Way "
+ "Handshake from " MACSTR " (ver=%d)", MAC2STR(src_addr), ver);
os_memset(&ie, 0, sizeof(ie));
const u8 *_buf = (const u8 *) (key + 1);
size_t len = WPA_GET_BE16(key->key_data_length);
wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data", _buf, len);
- wpa_supplicant_parse_ies(_buf, len, &ie);
+ if (wpa_supplicant_parse_ies(_buf, len, &ie) < 0)
+ goto failed;
if (ie.pmkid) {
wpa_hexdump(MSG_DEBUG, "RSN: PMKID from "
"Authenticator", ie.pmkid, PMKID_LEN);
res = wpa_supplicant_get_pmk(sm, src_addr, ie.pmkid);
if (res == -2) {
- wpa_printf(MSG_DEBUG, "RSN: Do not reply to msg 1/4 - "
- "requesting full EAP authentication");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: Do not reply to "
+ "msg 1/4 - requesting full EAP authentication");
return;
}
if (res)
goto failed;
if (sm->renew_snonce) {
- if (os_get_random(sm->snonce, WPA_NONCE_LEN)) {
+ if (random_get_bytes(sm->snonce, WPA_NONCE_LEN)) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Failed to get random data for SNonce");
goto failed;
* Start preauthentication after a short wait to avoid a
* possible race condition between the data receive and key
* configuration after the 4-Way Handshake. This increases the
- * likelyhood of the first preauth EAPOL-Start frame getting to
+ * likelihood of the first preauth EAPOL-Start frame getting to
* the target AP.
*/
eloop_register_timeout(1, 0, wpa_sm_start_preauth, sm, NULL);
}
if (sm->cur_pmksa && sm->cur_pmksa->opportunistic) {
- wpa_printf(MSG_DEBUG, "RSN: Authenticator accepted "
- "opportunistic PMKSA entry - marking it valid");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: Authenticator accepted "
+ "opportunistic PMKSA entry - marking it valid");
sm->cur_pmksa->opportunistic = 0;
}
static void wpa_sm_rekey_ptk(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_sm *sm = eloop_ctx;
- wpa_printf(MSG_DEBUG, "WPA: Request PTK rekeying");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Request PTK rekeying");
wpa_sm_key_request(sm, 0, 1);
}
const u8 *key_rsc;
u8 null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
- wpa_printf(MSG_DEBUG, "WPA: Installing PTK to the driver.");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: Installing PTK to the driver");
switch (sm->pairwise_cipher) {
case WPA_CIPHER_CCMP:
rsclen = 6;
break;
case WPA_CIPHER_NONE:
- wpa_printf(MSG_DEBUG, "WPA: Pairwise Cipher Suite: "
- "NONE - do not use pairwise keys");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Pairwise Cipher "
+ "Suite: NONE - do not use pairwise keys");
return 0;
default:
- wpa_printf(MSG_WARNING, "WPA: Unsupported pairwise cipher %d",
- sm->pairwise_cipher);
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Unsupported pairwise cipher %d",
+ sm->pairwise_cipher);
return -1;
}
if (wpa_sm_set_key(sm, alg, sm->bssid, 0, 1, key_rsc, rsclen,
(u8 *) sm->ptk.tk1, keylen) < 0) {
- wpa_printf(MSG_WARNING, "WPA: Failed to set PTK to the "
- "driver (alg=%d keylen=%d bssid=" MACSTR ")",
- alg, keylen, MAC2STR(sm->bssid));
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Failed to set PTK to the "
+ "driver (alg=%d keylen=%d bssid=" MACSTR ")",
+ alg, keylen, MAC2STR(sm->bssid));
return -1;
}
}
-static int wpa_supplicant_check_group_cipher(int group_cipher,
+static int wpa_supplicant_check_group_cipher(struct wpa_sm *sm,
+ int group_cipher,
int keylen, int maxkeylen,
int *key_rsc_len,
enum wpa_alg *alg)
*alg = WPA_ALG_WEP;
break;
default:
- wpa_printf(MSG_WARNING, "WPA: Unsupported Group Cipher %d",
- group_cipher);
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Unsupported Group Cipher %d",
+ group_cipher);
return -1;
}
if (ret < 0 ) {
- wpa_printf(MSG_WARNING, "WPA: Unsupported %s Group Cipher key "
- "length %d (%d).",
- wpa_cipher_txt(group_cipher), keylen, maxkeylen);
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Unsupported %s Group Cipher key length %d (%d)",
+ wpa_cipher_txt(group_cipher), keylen, maxkeylen);
}
return ret;
u8 gtk_buf[32];
wpa_hexdump_key(MSG_DEBUG, "WPA: Group Key", gd->gtk, gd->gtk_len);
- wpa_printf(MSG_DEBUG, "WPA: Installing GTK to the driver "
- "(keyidx=%d tx=%d len=%d).", gd->keyidx, gd->tx,
- gd->gtk_len);
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: Installing GTK to the driver (keyidx=%d tx=%d len=%d)",
+ gd->keyidx, gd->tx, gd->gtk_len);
wpa_hexdump(MSG_DEBUG, "WPA: RSC", key_rsc, gd->key_rsc_len);
if (sm->group_cipher == WPA_CIPHER_TKIP) {
/* Swap Tx/Rx keys for Michael MIC */
_gtk = gtk_buf;
}
if (sm->pairwise_cipher == WPA_CIPHER_NONE) {
- if (wpa_sm_set_key(sm, gd->alg,
- (u8 *) "\xff\xff\xff\xff\xff\xff",
+ if (wpa_sm_set_key(sm, gd->alg, NULL,
gd->keyidx, 1, key_rsc, gd->key_rsc_len,
_gtk, gd->gtk_len) < 0) {
- wpa_printf(MSG_WARNING, "WPA: Failed to set "
- "GTK to the driver (Group only).");
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Failed to set GTK to the driver "
+ "(Group only)");
return -1;
}
- } else if (wpa_sm_set_key(sm, gd->alg,
- (u8 *) "\xff\xff\xff\xff\xff\xff",
+ } else if (wpa_sm_set_key(sm, gd->alg, broadcast_ether_addr,
gd->keyidx, gd->tx, key_rsc, gd->key_rsc_len,
_gtk, gd->gtk_len) < 0) {
- wpa_printf(MSG_WARNING, "WPA: Failed to set GTK to "
- "the driver (alg=%d keylen=%d keyidx=%d)",
- gd->alg, gd->gtk_len, gd->keyidx);
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Failed to set GTK to "
+ "the driver (alg=%d keylen=%d keyidx=%d)",
+ gd->alg, gd->gtk_len, gd->keyidx);
return -1;
}
* doing Group Key only APs) and without this workaround, the
* data connection does not work because wpa_supplicant
* configured non-zero keyidx to be used for unicast. */
- wpa_printf(MSG_INFO, "WPA: Tx bit set for GTK, but pairwise "
- "keys are used - ignore Tx bit");
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: Tx bit set for GTK, but pairwise "
+ "keys are used - ignore Tx bit");
return 0;
}
return tx;
os_memcpy(gd.gtk, gtk, gtk_len);
gd.gtk_len = gtk_len;
- if (wpa_supplicant_check_group_cipher(sm->group_cipher,
+ if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher,
gtk_len, gtk_len,
&gd.key_rsc_len, &gd.alg) ||
wpa_supplicant_install_gtk(sm, &gd, key->key_rsc)) {
- wpa_printf(MSG_DEBUG, "RSN: Failed to install GTK");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: Failed to install GTK");
return -1;
}
return -1;
igtk = (const struct wpa_igtk_kde *) ie->igtk;
keyidx = WPA_GET_LE16(igtk->keyid);
- wpa_printf(MSG_DEBUG, "WPA: IGTK keyid %d "
- "pn %02x%02x%02x%02x%02x%02x",
- keyidx, MAC2STR(igtk->pn));
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: IGTK keyid %d "
+ "pn %02x%02x%02x%02x%02x%02x",
+ keyidx, MAC2STR(igtk->pn));
wpa_hexdump_key(MSG_DEBUG, "WPA: IGTK",
igtk->igtk, WPA_IGTK_LEN);
if (keyidx > 4095) {
- wpa_printf(MSG_WARNING, "WPA: Invalid IGTK KeyID %d",
- keyidx);
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Invalid IGTK KeyID %d", keyidx);
return -1;
}
- if (wpa_sm_set_key(sm, WPA_ALG_IGTK,
- (u8 *) "\xff\xff\xff\xff\xff\xff",
+ if (wpa_sm_set_key(sm, WPA_ALG_IGTK, broadcast_ether_addr,
keyidx, 0, igtk->pn, sizeof(igtk->pn),
igtk->igtk, WPA_IGTK_LEN) < 0) {
- wpa_printf(MSG_WARNING, "WPA: Failed to configure IGTK"
- " to the driver");
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Failed to configure IGTK to the driver");
return -1;
}
}
}
if (wpa_ie) {
if (!sm->ap_wpa_ie) {
- wpa_printf(MSG_INFO, "WPA: No WPA IE in "
- "Beacon/ProbeResp");
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: No WPA IE in Beacon/ProbeResp");
}
wpa_hexdump(MSG_INFO, "WPA: WPA IE in 3/4 msg",
wpa_ie, wpa_ie_len);
}
if (rsn_ie) {
if (!sm->ap_rsn_ie) {
- wpa_printf(MSG_INFO, "WPA: No RSN IE in "
- "Beacon/ProbeResp");
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: No RSN IE in Beacon/ProbeResp");
}
wpa_hexdump(MSG_INFO, "WPA: RSN IE in 3/4 msg",
rsn_ie, rsn_ie_len);
if (ie->mdie == NULL || ie->mdie_len < 2 + sizeof(*mdie) ||
os_memcmp(mdie->mobility_domain, sm->mobility_domain,
MOBILITY_DOMAIN_ID_LEN) != 0) {
- wpa_printf(MSG_DEBUG, "FT: MDIE in msg 3/4 did not "
- "match with the current mobility domain");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: MDIE in msg 3/4 did "
+ "not match with the current mobility domain");
return -1;
}
if (assoc_resp_mdie &&
(assoc_resp_mdie[1] != ie->mdie[1] ||
os_memcmp(assoc_resp_mdie, ie->mdie, 2 + ie->mdie[1]) != 0)) {
- wpa_printf(MSG_DEBUG, "FT: MDIE mismatch");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: MDIE mismatch");
wpa_hexdump(MSG_DEBUG, "FT: MDIE in EAPOL-Key msg 3/4",
ie->mdie, 2 + ie->mdie[1]);
wpa_hexdump(MSG_DEBUG, "FT: MDIE in (Re)Association Response",
const u8 *assoc_resp_ftie)
{
if (ie->ftie == NULL) {
- wpa_printf(MSG_DEBUG, "FT: No FTIE in EAPOL-Key msg 3/4");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "FT: No FTIE in EAPOL-Key msg 3/4");
return -1;
}
if (assoc_resp_ftie[1] != ie->ftie[1] ||
os_memcmp(assoc_resp_ftie, ie->ftie, 2 + ie->ftie[1]) != 0) {
- wpa_printf(MSG_DEBUG, "FT: FTIE mismatch");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: FTIE mismatch");
wpa_hexdump(MSG_DEBUG, "FT: FTIE in EAPOL-Key msg 3/4",
ie->ftie, 2 + ie->ftie[1]);
wpa_hexdump(MSG_DEBUG, "FT: FTIE in (Re)Association Response",
*/
if (wpa_parse_wpa_ie_rsn(ie->rsn_ie, ie->rsn_ie_len, &rsn) < 0 ||
rsn.num_pmkid != 1 || rsn.pmkid == NULL) {
- wpa_printf(MSG_DEBUG, "FT: No PMKR1Name in "
- "FT 4-way handshake message 3/4");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: No PMKR1Name in "
+ "FT 4-way handshake message 3/4");
return -1;
}
if (os_memcmp(rsn.pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN) != 0) {
- wpa_printf(MSG_DEBUG, "FT: PMKR1Name mismatch in "
- "FT 4-way handshake message 3/4");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "FT: PMKR1Name mismatch in "
+ "FT 4-way handshake message 3/4");
wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name from Authenticator",
rsn.pmkid, WPA_PMK_NAME_LEN);
wpa_hexdump(MSG_DEBUG, "FT: Derived PMKR1Name",
struct wpa_eapol_ie_parse *ie)
{
if (sm->ap_wpa_ie == NULL && sm->ap_rsn_ie == NULL) {
- wpa_printf(MSG_DEBUG, "WPA: No WPA/RSN IE for this AP known. "
- "Trying to get from scan results");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: No WPA/RSN IE for this AP known. "
+ "Trying to get from scan results");
if (wpa_sm_get_beacon_ie(sm) < 0) {
- wpa_printf(MSG_WARNING, "WPA: Could not find AP from "
- "the scan results");
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Could not find AP from "
+ "the scan results");
} else {
- wpa_printf(MSG_DEBUG, "WPA: Found the current AP from "
- "updated scan results");
+ wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: Found the current AP from "
+ "updated scan results");
}
}
if (kde)
os_memcpy(reply + 1, kde, kde_len);
- wpa_printf(MSG_DEBUG, "WPA: Sending EAPOL-Key 4/4");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 4/4");
wpa_eapol_key_send(sm, ptk->kck, ver, dst, ETH_P_EAPOL,
rbuf, rlen, reply->key_mic);
struct wpa_eapol_ie_parse ie;
wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE);
- wpa_printf(MSG_DEBUG, "WPA: RX message 3 of 4-Way Handshake from "
- MACSTR " (ver=%d)", MAC2STR(sm->bssid), ver);
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: RX message 3 of 4-Way "
+ "Handshake from " MACSTR " (ver=%d)", MAC2STR(sm->bssid), ver);
key_info = WPA_GET_BE16(key->key_info);
pos = (const u8 *) (key + 1);
len = WPA_GET_BE16(key->key_data_length);
wpa_hexdump(MSG_DEBUG, "WPA: IE KeyData", pos, len);
- wpa_supplicant_parse_ies(pos, len, &ie);
+ if (wpa_supplicant_parse_ies(pos, len, &ie) < 0)
+ goto failed;
if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
- wpa_printf(MSG_WARNING, "WPA: GTK IE in unencrypted key data");
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: GTK IE in unencrypted key data");
goto failed;
}
#ifdef CONFIG_IEEE80211W
if (ie.igtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
- wpa_printf(MSG_WARNING, "WPA: IGTK KDE in unencrypted key "
- "data");
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: IGTK KDE in unencrypted key data");
goto failed;
}
if (ie.igtk && ie.igtk_len != sizeof(struct wpa_igtk_kde)) {
- wpa_printf(MSG_WARNING, "WPA: Invalid IGTK KDE length %lu",
- (unsigned long) ie.igtk_len);
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Invalid IGTK KDE length %lu",
+ (unsigned long) ie.igtk_len);
goto failed;
}
#endif /* CONFIG_IEEE80211W */
goto failed;
if (os_memcmp(sm->anonce, key->key_nonce, WPA_NONCE_LEN) != 0) {
- wpa_printf(MSG_WARNING, "WPA: ANonce from message 1 of 4-Way "
- "Handshake differs from 3 of 4-Way Handshake - drop"
- " packet (src=" MACSTR ")", MAC2STR(sm->bssid));
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: ANonce from message 1 of 4-Way Handshake "
+ "differs from 3 of 4-Way Handshake - drop packet (src="
+ MACSTR ")", MAC2STR(sm->bssid));
goto failed;
}
switch (sm->pairwise_cipher) {
case WPA_CIPHER_CCMP:
if (keylen != 16) {
- wpa_printf(MSG_WARNING, "WPA: Invalid CCMP key length "
- "%d (src=" MACSTR ")",
- keylen, MAC2STR(sm->bssid));
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Invalid CCMP key length %d (src=" MACSTR
+ ")", keylen, MAC2STR(sm->bssid));
goto failed;
}
break;
case WPA_CIPHER_TKIP:
if (keylen != 32) {
- wpa_printf(MSG_WARNING, "WPA: Invalid TKIP key length "
- "%d (src=" MACSTR ")",
- keylen, MAC2STR(sm->bssid));
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Invalid TKIP key length %d (src=" MACSTR
+ ")", keylen, MAC2STR(sm->bssid));
goto failed;
}
break;
if (ie.gtk &&
wpa_supplicant_pairwise_gtk(sm, key,
ie.gtk, ie.gtk_len, key_info) < 0) {
- wpa_printf(MSG_INFO, "RSN: Failed to configure GTK");
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: Failed to configure GTK");
goto failed;
}
if (ieee80211w_set_keys(sm, &ie) < 0) {
- wpa_printf(MSG_INFO, "RSN: Failed to configure IGTK");
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: Failed to configure IGTK");
goto failed;
}
+ wpa_sm_set_rekey_offload(sm);
+
return;
failed:
struct wpa_eapol_ie_parse ie;
wpa_hexdump(MSG_DEBUG, "RSN: msg 1/2 key data", keydata, keydatalen);
- wpa_supplicant_parse_ies(keydata, keydatalen, &ie);
+ if (wpa_supplicant_parse_ies(keydata, keydatalen, &ie) < 0)
+ return -1;
if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
- wpa_printf(MSG_WARNING, "WPA: GTK IE in unencrypted key data");
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: GTK IE in unencrypted key data");
return -1;
}
if (ie.gtk == NULL) {
- wpa_printf(MSG_INFO, "WPA: No GTK IE in Group Key msg 1/2");
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: No GTK IE in Group Key msg 1/2");
return -1;
}
maxkeylen = gd->gtk_len = ie.gtk_len - 2;
- if (wpa_supplicant_check_group_cipher(sm->group_cipher,
+ if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher,
gd->gtk_len, maxkeylen,
&gd->key_rsc_len, &gd->alg))
return -1;
gd->tx = wpa_supplicant_gtk_tx_bit_workaround(sm,
!!(ie.gtk[0] & BIT(2)));
if (ie.gtk_len - 2 > sizeof(gd->gtk)) {
- wpa_printf(MSG_INFO, "RSN: Too long GTK in GTK IE "
- "(len=%lu)", (unsigned long) ie.gtk_len - 2);
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: Too long GTK in GTK IE (len=%lu)",
+ (unsigned long) ie.gtk_len - 2);
return -1;
}
os_memcpy(gd->gtk, ie.gtk + 2, ie.gtk_len - 2);
if (ieee80211w_set_keys(sm, &ie) < 0)
- wpa_printf(MSG_INFO, "RSN: Failed to configure IGTK");
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: Failed to configure IGTK");
return 0;
}
gd->gtk_len = WPA_GET_BE16(key->key_length);
maxkeylen = keydatalen;
if (keydatalen > extra_len) {
- wpa_printf(MSG_INFO, "WPA: Truncated EAPOL-Key packet:"
- " key_data_length=%lu > extra_len=%lu",
- (unsigned long) keydatalen,
- (unsigned long) extra_len);
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: Truncated EAPOL-Key packet: "
+ "key_data_length=%lu > extra_len=%lu",
+ (unsigned long) keydatalen, (unsigned long) extra_len);
return -1;
}
if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
if (maxkeylen < 8) {
- wpa_printf(MSG_INFO, "WPA: Too short maxkeylen (%lu)",
- (unsigned long) maxkeylen);
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: Too short maxkeylen (%lu)",
+ (unsigned long) maxkeylen);
return -1;
}
maxkeylen -= 8;
}
- if (wpa_supplicant_check_group_cipher(sm->group_cipher,
+ if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher,
gd->gtk_len, maxkeylen,
&gd->key_rsc_len, &gd->alg))
return -1;
os_memcpy(ek, key->key_iv, 16);
os_memcpy(ek + 16, sm->ptk.kek, 16);
if (keydatalen > sizeof(gd->gtk)) {
- wpa_printf(MSG_WARNING, "WPA: RC4 key data "
- "too long (%lu)",
- (unsigned long) keydatalen);
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: RC4 key data too long (%lu)",
+ (unsigned long) keydatalen);
return -1;
}
os_memcpy(gd->gtk, key + 1, keydatalen);
if (rc4_skip(ek, 32, 256, gd->gtk, keydatalen)) {
- wpa_printf(MSG_ERROR, "WPA: RC4 failed");
+ wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
+ "WPA: RC4 failed");
return -1;
}
} else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
if (keydatalen % 8) {
- wpa_printf(MSG_WARNING, "WPA: Unsupported AES-WRAP "
- "len %lu", (unsigned long) keydatalen);
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Unsupported AES-WRAP len %lu",
+ (unsigned long) keydatalen);
return -1;
}
if (maxkeylen > sizeof(gd->gtk)) {
- wpa_printf(MSG_WARNING, "WPA: AES-WRAP key data "
- "too long (keydatalen=%lu maxkeylen=%lu)",
- (unsigned long) keydatalen,
- (unsigned long) maxkeylen);
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: AES-WRAP key data "
+ "too long (keydatalen=%lu maxkeylen=%lu)",
+ (unsigned long) keydatalen,
+ (unsigned long) maxkeylen);
return -1;
}
if (aes_unwrap(sm->ptk.kek, maxkeylen / 8,
(const u8 *) (key + 1), gd->gtk)) {
- wpa_printf(MSG_WARNING, "WPA: AES unwrap "
- "failed - could not decrypt GTK");
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: AES unwrap failed - could not decrypt "
+ "GTK");
return -1;
}
} else {
- wpa_printf(MSG_WARNING, "WPA: Unsupported key_info type %d",
- ver);
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Unsupported key_info type %d", ver);
return -1;
}
gd->tx = wpa_supplicant_gtk_tx_bit_workaround(
WPA_PUT_BE16(reply->key_data_length, 0);
- wpa_printf(MSG_DEBUG, "WPA: Sending EAPOL-Key 2/2");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/2");
wpa_eapol_key_send(sm, sm->ptk.kck, ver, sm->bssid, ETH_P_EAPOL,
rbuf, rlen, reply->key_mic);
os_memset(&gd, 0, sizeof(gd));
rekey = wpa_sm_get_state(sm) == WPA_COMPLETED;
- wpa_printf(MSG_DEBUG, "WPA: RX message 1 of Group Key Handshake from "
- MACSTR " (ver=%d)", MAC2STR(src_addr), ver);
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: RX message 1 of Group Key "
+ "Handshake from " MACSTR " (ver=%d)", MAC2STR(src_addr), ver);
key_info = WPA_GET_BE16(key->key_info);
keydatalen = WPA_GET_BE16(key->key_data_length);
MAC2STR(sm->bssid), wpa_cipher_txt(sm->group_cipher));
wpa_sm_cancel_auth_timeout(sm);
wpa_sm_set_state(sm, WPA_COMPLETED);
+
+ wpa_sm_set_rekey_offload(sm);
} else {
wpa_supplicant_key_neg_complete(sm, sm->bssid,
key_info &
wpa_eapol_key_mic(sm->tptk.kck, ver, buf, len,
key->key_mic);
if (os_memcmp(mic, key->key_mic, 16) != 0) {
- wpa_printf(MSG_WARNING, "WPA: Invalid EAPOL-Key MIC "
- "when using TPTK - ignoring TPTK");
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Invalid EAPOL-Key MIC "
+ "when using TPTK - ignoring TPTK");
} else {
ok = 1;
sm->tptk_set = 0;
wpa_eapol_key_mic(sm->ptk.kck, ver, buf, len,
key->key_mic);
if (os_memcmp(mic, key->key_mic, 16) != 0) {
- wpa_printf(MSG_WARNING, "WPA: Invalid EAPOL-Key MIC "
- "- dropping packet");
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Invalid EAPOL-Key MIC - "
+ "dropping packet");
return -1;
}
ok = 1;
}
if (!ok) {
- wpa_printf(MSG_WARNING, "WPA: Could not verify EAPOL-Key MIC "
- "- dropping packet");
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Could not verify EAPOL-Key MIC - "
+ "dropping packet");
return -1;
}
wpa_hexdump(MSG_DEBUG, "RSN: encrypted key data",
(u8 *) (key + 1), keydatalen);
if (!sm->ptk_set) {
- wpa_printf(MSG_WARNING, "WPA: PTK not available, "
- "cannot decrypt EAPOL-Key key data.");
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: PTK not available, cannot decrypt EAPOL-Key Key "
+ "Data");
return -1;
}
os_memcpy(ek, key->key_iv, 16);
os_memcpy(ek + 16, sm->ptk.kek, 16);
if (rc4_skip(ek, 32, 256, (u8 *) (key + 1), keydatalen)) {
- wpa_printf(MSG_ERROR, "WPA: RC4 failed");
+ wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
+ "WPA: RC4 failed");
return -1;
}
} else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
ver == WPA_KEY_INFO_TYPE_AES_128_CMAC) {
u8 *buf;
if (keydatalen % 8) {
- wpa_printf(MSG_WARNING, "WPA: Unsupported "
- "AES-WRAP len %d", keydatalen);
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Unsupported AES-WRAP len %d",
+ keydatalen);
return -1;
}
keydatalen -= 8; /* AES-WRAP adds 8 bytes */
buf = os_malloc(keydatalen);
if (buf == NULL) {
- wpa_printf(MSG_WARNING, "WPA: No memory for "
- "AES-UNWRAP buffer");
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: No memory for AES-UNWRAP buffer");
return -1;
}
if (aes_unwrap(sm->ptk.kek, keydatalen / 8,
(u8 *) (key + 1), buf)) {
os_free(buf);
- wpa_printf(MSG_WARNING, "WPA: AES unwrap failed - "
- "could not decrypt EAPOL-Key key data");
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: AES unwrap failed - "
+ "could not decrypt EAPOL-Key key data");
return -1;
}
os_memcpy(key + 1, buf, keydatalen);
os_free(buf);
WPA_PUT_BE16(key->key_data_length, keydatalen);
} else {
- wpa_printf(MSG_WARNING, "WPA: Unsupported key_info type %d",
- ver);
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Unsupported key_info type %d", ver);
return -1;
}
wpa_hexdump_key(MSG_DEBUG, "WPA: decrypted EAPOL-Key key data",
void wpa_sm_aborted_cached(struct wpa_sm *sm)
{
if (sm && sm->cur_pmksa) {
- wpa_printf(MSG_DEBUG, "RSN: Cancelling PMKSA caching attempt");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: Cancelling PMKSA caching attempt");
sm->cur_pmksa = NULL;
}
}
-static void wpa_eapol_key_dump(const struct wpa_eapol_key *key)
+static void wpa_eapol_key_dump(struct wpa_sm *sm,
+ const struct wpa_eapol_key *key)
{
#ifndef CONFIG_NO_STDOUT_DEBUG
u16 key_info = WPA_GET_BE16(key->key_info);
- wpa_printf(MSG_DEBUG, " EAPOL-Key type=%d", key->type);
- wpa_printf(MSG_DEBUG, " key_info 0x%x (ver=%d keyidx=%d rsvd=%d %s"
- "%s%s%s%s%s%s%s)",
- key_info, key_info & WPA_KEY_INFO_TYPE_MASK,
- (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >>
- WPA_KEY_INFO_KEY_INDEX_SHIFT,
- (key_info & (BIT(13) | BIT(14) | BIT(15))) >> 13,
- key_info & WPA_KEY_INFO_KEY_TYPE ? "Pairwise" : "Group",
- key_info & WPA_KEY_INFO_INSTALL ? " Install" : "",
- key_info & WPA_KEY_INFO_ACK ? " Ack" : "",
- key_info & WPA_KEY_INFO_MIC ? " MIC" : "",
- key_info & WPA_KEY_INFO_SECURE ? " Secure" : "",
- key_info & WPA_KEY_INFO_ERROR ? " Error" : "",
- key_info & WPA_KEY_INFO_REQUEST ? " Request" : "",
- key_info & WPA_KEY_INFO_ENCR_KEY_DATA ? " Encr" : "");
- wpa_printf(MSG_DEBUG, " key_length=%u key_data_length=%u",
- WPA_GET_BE16(key->key_length),
- WPA_GET_BE16(key->key_data_length));
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, " EAPOL-Key type=%d", key->type);
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ " key_info 0x%x (ver=%d keyidx=%d rsvd=%d %s%s%s%s%s%s%s%s)",
+ key_info, key_info & WPA_KEY_INFO_TYPE_MASK,
+ (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >>
+ WPA_KEY_INFO_KEY_INDEX_SHIFT,
+ (key_info & (BIT(13) | BIT(14) | BIT(15))) >> 13,
+ key_info & WPA_KEY_INFO_KEY_TYPE ? "Pairwise" : "Group",
+ key_info & WPA_KEY_INFO_INSTALL ? " Install" : "",
+ key_info & WPA_KEY_INFO_ACK ? " Ack" : "",
+ key_info & WPA_KEY_INFO_MIC ? " MIC" : "",
+ key_info & WPA_KEY_INFO_SECURE ? " Secure" : "",
+ key_info & WPA_KEY_INFO_ERROR ? " Error" : "",
+ key_info & WPA_KEY_INFO_REQUEST ? " Request" : "",
+ key_info & WPA_KEY_INFO_ENCR_KEY_DATA ? " Encr" : "");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ " key_length=%u key_data_length=%u",
+ WPA_GET_BE16(key->key_length),
+ WPA_GET_BE16(key->key_data_length));
wpa_hexdump(MSG_DEBUG, " replay_counter",
key->replay_counter, WPA_REPLAY_COUNTER_LEN);
wpa_hexdump(MSG_DEBUG, " key_nonce", key->key_nonce, WPA_NONCE_LEN);
#endif /* CONFIG_IEEE80211R */
if (len < sizeof(*hdr) + sizeof(*key)) {
- wpa_printf(MSG_DEBUG, "WPA: EAPOL frame too short to be a WPA "
- "EAPOL-Key (len %lu, expecting at least %lu)",
- (unsigned long) len,
- (unsigned long) sizeof(*hdr) + sizeof(*key));
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: EAPOL frame too short to be a WPA "
+ "EAPOL-Key (len %lu, expecting at least %lu)",
+ (unsigned long) len,
+ (unsigned long) sizeof(*hdr) + sizeof(*key));
return 0;
}
key = (struct wpa_eapol_key *) (hdr + 1);
plen = be_to_host16(hdr->length);
data_len = plen + sizeof(*hdr);
- wpa_printf(MSG_DEBUG, "IEEE 802.1X RX: version=%d type=%d length=%lu",
- hdr->version, hdr->type, (unsigned long) plen);
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "IEEE 802.1X RX: version=%d type=%d length=%lu",
+ hdr->version, hdr->type, (unsigned long) plen);
if (hdr->version < EAPOL_VERSION) {
/* TODO: backwards compatibility */
}
if (hdr->type != IEEE802_1X_TYPE_EAPOL_KEY) {
- wpa_printf(MSG_DEBUG, "WPA: EAPOL frame (type %u) discarded, "
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: EAPOL frame (type %u) discarded, "
"not a Key frame", hdr->type);
ret = 0;
goto out;
}
if (plen > len - sizeof(*hdr) || plen < sizeof(*key)) {
- wpa_printf(MSG_DEBUG, "WPA: EAPOL frame payload size %lu "
- "invalid (frame size %lu)",
- (unsigned long) plen, (unsigned long) len);
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: EAPOL frame payload size %lu "
+ "invalid (frame size %lu)",
+ (unsigned long) plen, (unsigned long) len);
ret = 0;
goto out;
}
if (key->type != EAPOL_KEY_TYPE_WPA && key->type != EAPOL_KEY_TYPE_RSN)
{
- wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key type (%d) unknown, "
- "discarded", key->type);
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: EAPOL-Key type (%d) unknown, discarded",
+ key->type);
ret = 0;
goto out;
}
- wpa_eapol_key_dump(key);
+ wpa_eapol_key_dump(sm, key);
eapol_sm_notify_lower_layer_success(sm->eapol, 0);
wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL-Key", tmp, len);
if (data_len < len) {
- wpa_printf(MSG_DEBUG, "WPA: ignoring %lu bytes after the IEEE "
- "802.1X data", (unsigned long) len - data_len);
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: ignoring %lu bytes after the IEEE 802.1X data",
+ (unsigned long) len - data_len);
}
key_info = WPA_GET_BE16(key->key_info);
ver = key_info & WPA_KEY_INFO_TYPE_MASK;
ver != WPA_KEY_INFO_TYPE_AES_128_CMAC &&
#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
- wpa_printf(MSG_INFO, "WPA: Unsupported EAPOL-Key descriptor "
- "version %d.", ver);
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: Unsupported EAPOL-Key descriptor version %d",
+ ver);
goto out;
}
if (wpa_key_mgmt_ft(sm->key_mgmt)) {
/* IEEE 802.11r uses a new key_info type (AES-128-CMAC). */
if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) {
- wpa_printf(MSG_INFO, "FT: AP did not use "
- "AES-128-CMAC.");
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "FT: AP did not use AES-128-CMAC");
goto out;
}
} else
#ifdef CONFIG_IEEE80211W
if (wpa_key_mgmt_sha256(sm->key_mgmt)) {
if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) {
- wpa_printf(MSG_INFO, "WPA: AP did not use the "
- "negotiated AES-128-CMAC.");
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: AP did not use the "
+ "negotiated AES-128-CMAC");
goto out;
}
} else
#endif /* CONFIG_IEEE80211W */
if (sm->pairwise_cipher == WPA_CIPHER_CCMP &&
ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
- wpa_printf(MSG_INFO, "WPA: CCMP is used, but EAPOL-Key "
- "descriptor version (%d) is not 2.", ver);
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: CCMP is used, but EAPOL-Key "
+ "descriptor version (%d) is not 2", ver);
if (sm->group_cipher != WPA_CIPHER_CCMP &&
!(key_info & WPA_KEY_INFO_KEY_TYPE)) {
/* Earlier versions of IEEE 802.11i did not explicitly
* require version 2 descriptor for all EAPOL-Key
* packets, so allow group keys to use version 1 if
* CCMP is not used for them. */
- wpa_printf(MSG_INFO, "WPA: Backwards compatibility: "
- "allow invalid version for non-CCMP group "
- "keys");
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: Backwards compatibility: allow invalid "
+ "version for non-CCMP group keys");
} else
goto out;
}
if (!peerkey->initiator && peerkey->replay_counter_set &&
os_memcmp(key->replay_counter, peerkey->replay_counter,
WPA_REPLAY_COUNTER_LEN) <= 0) {
- wpa_printf(MSG_WARNING, "RSN: EAPOL-Key Replay "
- "Counter did not increase (STK) - dropping "
- "packet");
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "RSN: EAPOL-Key Replay Counter did not "
+ "increase (STK) - dropping packet");
goto out;
} else if (peerkey->initiator) {
u8 _tmp[WPA_REPLAY_COUNTER_LEN];
inc_byte_array(_tmp, WPA_REPLAY_COUNTER_LEN);
if (os_memcmp(_tmp, peerkey->replay_counter,
WPA_REPLAY_COUNTER_LEN) != 0) {
- wpa_printf(MSG_DEBUG, "RSN: EAPOL-Key Replay "
- "Counter did not match (STK) - "
- "dropping packet");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: EAPOL-Key Replay "
+ "Counter did not match (STK) - "
+ "dropping packet");
goto out;
}
}
}
if (peerkey && peerkey->initiator && (key_info & WPA_KEY_INFO_ACK)) {
- wpa_printf(MSG_INFO, "RSN: Ack bit in key_info from STK peer");
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: Ack bit in key_info from STK peer");
goto out;
}
#endif /* CONFIG_PEERKEY */
if (!peerkey && sm->rx_replay_counter_set &&
os_memcmp(key->replay_counter, sm->rx_replay_counter,
WPA_REPLAY_COUNTER_LEN) <= 0) {
- wpa_printf(MSG_WARNING, "WPA: EAPOL-Key Replay Counter did not"
- " increase - dropping packet");
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: EAPOL-Key Replay Counter did not increase - "
+ "dropping packet");
goto out;
}
&& (peerkey == NULL || !peerkey->initiator)
#endif /* CONFIG_PEERKEY */
) {
- wpa_printf(MSG_INFO, "WPA: No Ack bit in key_info");
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: No Ack bit in key_info");
goto out;
}
if (key_info & WPA_KEY_INFO_REQUEST) {
- wpa_printf(MSG_INFO, "WPA: EAPOL-Key with Request bit - "
- "dropped");
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: EAPOL-Key with Request bit - dropped");
goto out;
}
if (key_info & WPA_KEY_INFO_KEY_TYPE) {
if (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) {
- wpa_printf(MSG_WARNING, "WPA: Ignored EAPOL-Key "
- "(Pairwise) with non-zero key index");
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Ignored EAPOL-Key (Pairwise) with "
+ "non-zero key index");
goto out;
}
if (peerkey) {
wpa_supplicant_process_1_of_2(sm, src_addr, key,
extra_len, ver);
} else {
- wpa_printf(MSG_WARNING, "WPA: EAPOL-Key (Group) "
- "without Mic bit - dropped");
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: EAPOL-Key (Group) without Mic bit - "
+ "dropped");
}
}
if (sm->cur_pmksa == entry ||
(sm->pmk_len == entry->pmk_len &&
os_memcmp(sm->pmk, entry->pmk, sm->pmk_len) == 0)) {
- wpa_printf(MSG_DEBUG, "RSN: removed current PMKSA entry");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: removed current PMKSA entry");
sm->cur_pmksa = NULL;
if (replace) {
sm->pmksa = pmksa_cache_init(wpa_sm_pmksa_free_cb, sm, sm);
if (sm->pmksa == NULL) {
- wpa_printf(MSG_ERROR, "RSN: PMKSA cache initialization "
- "failed");
+ wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
+ "RSN: PMKSA cache initialization failed");
os_free(sm);
return NULL;
}
if (sm == NULL)
return;
- wpa_printf(MSG_DEBUG, "WPA: Association event - clear replay counter");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: Association event - clear replay counter");
os_memcpy(sm->bssid, bssid, ETH_ALEN);
os_memset(sm->rx_replay_counter, 0, WPA_REPLAY_COUNTER_LEN);
sm->rx_replay_counter_set = 0;
* IEEE 802.11, 8.4.10: Delete PTK SA on (re)association if
* this is not part of a Fast BSS Transition.
*/
- wpa_printf(MSG_DEBUG, "WPA: Clear old PTK");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Clear old PTK");
sm->ptk_set = 0;
sm->tptk_set = 0;
}
+
+#ifdef CONFIG_TDLS
+ wpa_tdls_assoc(sm);
+#endif /* CONFIG_TDLS */
}
rsn_preauth_deinit(sm);
if (wpa_sm_get_state(sm) == WPA_4WAY_HANDSHAKE)
sm->dot11RSNA4WayHandshakeFailures++;
+#ifdef CONFIG_TDLS
+ wpa_tdls_disassoc(sm);
+#endif /* CONFIG_TDLS */
}
sm->ssid_len = 0;
sm->wpa_ptk_rekey = 0;
}
- if (config == NULL || config->network_ctx != sm->network_ctx)
- pmksa_cache_notify_reconfig(sm->pmksa);
}
os_free(sm->assoc_wpa_ie);
if (ie == NULL || len == 0) {
- wpa_printf(MSG_DEBUG, "WPA: clearing own WPA/RSN IE");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: clearing own WPA/RSN IE");
sm->assoc_wpa_ie = NULL;
sm->assoc_wpa_ie_len = 0;
} else {
os_free(sm->ap_wpa_ie);
if (ie == NULL || len == 0) {
- wpa_printf(MSG_DEBUG, "WPA: clearing AP WPA IE");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: clearing AP WPA IE");
sm->ap_wpa_ie = NULL;
sm->ap_wpa_ie_len = 0;
} else {
os_free(sm->ap_rsn_ie);
if (ie == NULL || len == 0) {
- wpa_printf(MSG_DEBUG, "WPA: clearing AP RSN IE");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: clearing AP RSN IE");
sm->ap_rsn_ie = NULL;
sm->ap_rsn_ie_len = 0;
} else {
*/
int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm, struct wpa_ie_data *data)
{
- if (sm == NULL || sm->assoc_wpa_ie == NULL) {
- wpa_printf(MSG_DEBUG, "WPA: No WPA/RSN IE available from "
- "association info");
+ if (sm == NULL)
+ return -1;
+
+ if (sm->assoc_wpa_ie == NULL) {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: No WPA/RSN IE available from association info");
return -1;
}
if (wpa_parse_wpa_ie(sm->assoc_wpa_ie, sm->assoc_wpa_ie_len, data))
void wpa_sm_drop_sa(struct wpa_sm *sm)
{
- wpa_printf(MSG_DEBUG, "WPA: Clear old PMK and PTK");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Clear old PMK and PTK");
sm->ptk_set = 0;
sm->tptk_set = 0;
os_memset(sm->pmk, 0, sizeof(sm->pmk));
return 0;
return sm->ptk_set;
}
+
+
+void wpa_sm_update_replay_ctr(struct wpa_sm *sm, const u8 *replay_ctr)
+{
+ os_memcpy(sm->rx_replay_counter, replay_ctr, WPA_REPLAY_COUNTER_LEN);
+}
+
+
+void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx)
+{
+#ifndef CONFIG_NO_WPA2
+ pmksa_cache_flush(sm->pmksa, network_ctx);
+#endif /* CONFIG_NO_WPA2 */
+}
int (*send_ft_action)(void *ctx, u8 action, const u8 *target_ap,
const u8 *ies, size_t ies_len);
int (*mark_authenticated)(void *ctx, const u8 *target_ap);
+#ifdef CONFIG_TDLS
+ int (*tdls_get_capa)(void *ctx, int *tdls_supported,
+ int *tdls_ext_setup);
+ int (*send_tdls_mgmt)(void *ctx, const u8 *dst,
+ u8 action_code, u8 dialog_token,
+ u16 status_code, const u8 *buf, size_t len);
+ int (*tdls_oper)(void *ctx, int oper, const u8 *peer);
+ int (*tdls_peer_addset)(void *ctx, const u8 *addr, int add,
+ u16 capability, const u8 *supp_rates,
+ size_t supp_rates_len);
+#endif /* CONFIG_TDLS */
+ void (*set_rekey_offload)(void *ctx, const u8 *kek, const u8 *kck,
+ const u8 *replay_ctr);
};
void wpa_sm_drop_sa(struct wpa_sm *sm);
int wpa_sm_has_ptk(struct wpa_sm *sm);
+void wpa_sm_update_replay_ctr(struct wpa_sm *sm, const u8 *replay_ctr);
+
+void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx);
+
#else /* CONFIG_NO_WPA */
static inline struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx)
return 0;
}
+static inline void wpa_sm_update_replay_ctr(struct wpa_sm *sm,
+ const u8 *replay_ctr)
+{
+}
+
+static inline void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm,
+ void *network_ctx)
+{
+}
+
#endif /* CONFIG_NO_WPA */
#ifdef CONFIG_PEERKEY
#endif /* CONFIG_IEEE80211R */
+
+/* tdls.c */
+void wpa_tdls_ap_ies(struct wpa_sm *sm, const u8 *ies, size_t len);
+void wpa_tdls_assoc_resp_ies(struct wpa_sm *sm, const u8 *ies, size_t len);
+int wpa_tdls_start(struct wpa_sm *sm, const u8 *addr);
+int wpa_tdls_reneg(struct wpa_sm *sm, const u8 *addr);
+int wpa_tdls_send_teardown(struct wpa_sm *sm, const u8 *addr, u16 reason_code);
+int wpa_tdls_teardown_link(struct wpa_sm *sm, const u8 *addr, u16 reason_code);
+int wpa_tdls_send_discovery_request(struct wpa_sm *sm, const u8 *addr);
+int wpa_tdls_init(struct wpa_sm *sm);
+void wpa_tdls_deinit(struct wpa_sm *sm);
+void wpa_tdls_enable(struct wpa_sm *sm, int enabled);
+void wpa_tdls_disable_link(struct wpa_sm *sm, const u8 *addr);
+int wpa_tdls_is_external_setup(struct wpa_sm *sm);
+
#endif /* WPA_H */
#include "common.h"
#include "crypto/aes_wrap.h"
+#include "crypto/random.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "wpa.h"
#ifdef CONFIG_IEEE80211R
-struct wpa_ft_ies {
- const u8 *mdie;
- size_t mdie_len;
- const u8 *ftie;
- size_t ftie_len;
- const u8 *r1kh_id;
- const u8 *gtk;
- size_t gtk_len;
- const u8 *r0kh_id;
- size_t r0kh_id_len;
- const u8 *rsn;
- size_t rsn_len;
- const u8 *rsn_pmkid;
- const u8 *tie;
- size_t tie_len;
- const u8 *igtk;
- size_t igtk_len;
- const u8 *ric;
- size_t ric_len;
-};
-
-static int wpa_ft_parse_ies(const u8 *ies, size_t ies_len,
- struct wpa_ft_ies *parse);
-
-
int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr,
const struct wpa_eapol_key *key,
struct wpa_ptk *ptk, size_t ptk_len)
}
-static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len,
- struct wpa_ft_ies *parse)
-{
- const u8 *end, *pos;
-
- parse->ftie = ie;
- parse->ftie_len = ie_len;
-
- pos = ie + sizeof(struct rsn_ftie);
- end = ie + ie_len;
-
- while (pos + 2 <= end && pos + 2 + pos[1] <= end) {
- switch (pos[0]) {
- case FTIE_SUBELEM_R1KH_ID:
- if (pos[1] != FT_R1KH_ID_LEN) {
- wpa_printf(MSG_DEBUG, "FT: Invalid R1KH-ID "
- "length in FTIE: %d", pos[1]);
- return -1;
- }
- parse->r1kh_id = pos + 2;
- break;
- case FTIE_SUBELEM_GTK:
- parse->gtk = pos + 2;
- parse->gtk_len = pos[1];
- break;
- case FTIE_SUBELEM_R0KH_ID:
- if (pos[1] < 1 || pos[1] > FT_R0KH_ID_MAX_LEN) {
- wpa_printf(MSG_DEBUG, "FT: Invalid R0KH-ID "
- "length in FTIE: %d", pos[1]);
- return -1;
- }
- parse->r0kh_id = pos + 2;
- parse->r0kh_id_len = pos[1];
- break;
-#ifdef CONFIG_IEEE80211W
- case FTIE_SUBELEM_IGTK:
- parse->igtk = pos + 2;
- parse->igtk_len = pos[1];
- break;
-#endif /* CONFIG_IEEE80211W */
- }
-
- pos += 2 + pos[1];
- }
-
- return 0;
-}
-
-
-static int wpa_ft_parse_ies(const u8 *ies, size_t ies_len,
- struct wpa_ft_ies *parse)
-{
- const u8 *end, *pos;
- struct wpa_ie_data data;
- int ret;
- const struct rsn_ftie *ftie;
- int prot_ie_count = 0;
-
- os_memset(parse, 0, sizeof(*parse));
- if (ies == NULL)
- return 0;
-
- pos = ies;
- end = ies + ies_len;
- while (pos + 2 <= end && pos + 2 + pos[1] <= end) {
- switch (pos[0]) {
- case WLAN_EID_RSN:
- parse->rsn = pos + 2;
- parse->rsn_len = pos[1];
- ret = wpa_parse_wpa_ie_rsn(parse->rsn - 2,
- parse->rsn_len + 2,
- &data);
- if (ret < 0) {
- wpa_printf(MSG_DEBUG, "FT: Failed to parse "
- "RSN IE: %d", ret);
- return -1;
- }
- if (data.num_pmkid == 1 && data.pmkid)
- parse->rsn_pmkid = data.pmkid;
- break;
- case WLAN_EID_MOBILITY_DOMAIN:
- parse->mdie = pos + 2;
- parse->mdie_len = pos[1];
- break;
- case WLAN_EID_FAST_BSS_TRANSITION:
- if (pos[1] < sizeof(*ftie))
- return -1;
- ftie = (const struct rsn_ftie *) (pos + 2);
- prot_ie_count = ftie->mic_control[1];
- if (wpa_ft_parse_ftie(pos + 2, pos[1], parse) < 0)
- return -1;
- break;
- case WLAN_EID_TIMEOUT_INTERVAL:
- parse->tie = pos + 2;
- parse->tie_len = pos[1];
- break;
- case WLAN_EID_RIC_DATA:
- if (parse->ric == NULL)
- parse->ric = pos;
- }
-
- pos += 2 + pos[1];
- }
-
- if (prot_ie_count == 0)
- return 0; /* no MIC */
-
- /*
- * Check that the protected IE count matches with IEs included in the
- * frame.
- */
- if (parse->rsn)
- prot_ie_count--;
- if (parse->mdie)
- prot_ie_count--;
- if (parse->ftie)
- prot_ie_count--;
- if (parse->tie)
- prot_ie_count--;
- if (prot_ie_count < 0) {
- wpa_printf(MSG_DEBUG, "FT: Some required IEs not included in "
- "the protected IE count");
- return -1;
- }
-
- if (prot_ie_count == 0 && parse->ric) {
- wpa_printf(MSG_DEBUG, "FT: RIC IE(s) in the frame, but not "
- "included in protected IE count");
- return -1;
- }
-
- /* Determine the end of the RIC IE(s) */
- pos = parse->ric;
- while (pos && pos + 2 <= end && pos + 2 + pos[1] <= end &&
- prot_ie_count) {
- prot_ie_count--;
- pos += 2 + pos[1];
- }
- parse->ric_len = pos - parse->ric;
- if (prot_ie_count) {
- wpa_printf(MSG_DEBUG, "FT: %d protected IEs missing from "
- "frame", (int) prot_ie_count);
- return -1;
- }
-
- return 0;
-}
-
-
static int wpa_ft_install_ptk(struct wpa_sm *sm, const u8 *bssid)
{
int keylen;
size_t ft_ies_len;
/* Generate a new SNonce */
- if (os_get_random(sm->snonce, WPA_NONCE_LEN)) {
+ if (random_get_bytes(sm->snonce, WPA_NONCE_LEN)) {
wpa_printf(MSG_INFO, "FT: Failed to generate a new SNonce");
return -1;
}
}
wpa_hexdump_key(MSG_DEBUG, "FT: GTK from Reassoc Resp", gtk, keylen);
- if (wpa_sm_set_key(sm, alg, (u8 *) "\xff\xff\xff\xff\xff\xff",
- keyidx, 0, gtk_elem + 3, rsc_len, gtk, keylen) <
- 0) {
+ if (wpa_sm_set_key(sm, alg, broadcast_ether_addr, keyidx, 0,
+ gtk_elem + 3, rsc_len, gtk, keylen) < 0) {
wpa_printf(MSG_WARNING, "WPA: Failed to set GTK to the "
"driver.");
return -1;
wpa_hexdump_key(MSG_DEBUG, "FT: IGTK from Reassoc Resp", igtk,
WPA_IGTK_LEN);
- if (wpa_sm_set_key(sm, WPA_ALG_IGTK, (u8 *) "\xff\xff\xff\xff\xff\xff",
- keyidx, 0, igtk_elem + 2, 6, igtk, WPA_IGTK_LEN) <
- 0) {
+ if (wpa_sm_set_key(sm, WPA_ALG_IGTK, broadcast_ether_addr, keyidx, 0,
+ igtk_elem + 2, 6, igtk, WPA_IGTK_LEN) < 0) {
wpa_printf(MSG_WARNING, "WPA: Failed to set IGTK to the "
"driver.");
return -1;
}
count = 3;
- if (parse.tie)
- count++;
+ if (parse.ric)
+ count += ieee802_11_ie_count(parse.ric, parse.ric_len);
if (ftie->mic_control[1] != count) {
wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC "
"Control: received %u expected %u",
MAC2STR(target_ap));
/* Generate a new SNonce */
- if (os_get_random(sm->snonce, WPA_NONCE_LEN)) {
+ if (random_get_bytes(sm->snonce, WPA_NONCE_LEN)) {
wpa_printf(MSG_INFO, "FT: Failed to generate a new SNonce");
return -1;
}
#include "utils/list.h"
struct wpa_peerkey;
+struct wpa_tdls_peer;
struct wpa_eapol_key;
/**
struct l2_packet_data *l2_preauth;
struct l2_packet_data *l2_preauth_br;
+ struct l2_packet_data *l2_tdls;
u8 preauth_bssid[ETH_ALEN]; /* current RSN pre-auth peer or
* 00:00:00:00:00:00 if no pre-auth is
* in progress */
#ifdef CONFIG_PEERKEY
struct wpa_peerkey *peerkey;
#endif /* CONFIG_PEERKEY */
+#ifdef CONFIG_TDLS
+ struct wpa_tdls_peer *tdls;
+ int tdls_prohibited;
+ int tdls_disabled;
+
+ /* The driver supports TDLS */
+ int tdls_supported;
+
+ /*
+ * The driver requires explicit discovery/setup/teardown frames sent
+ * to it via tdls_mgmt.
+ */
+ int tdls_external_setup;
+#endif /* CONFIG_TDLS */
#ifdef CONFIG_IEEE80211R
u8 xxkey[PMK_LEN]; /* PSK or the second 256 bits of MSK */
return -1;
}
+static inline void wpa_sm_set_rekey_offload(struct wpa_sm *sm)
+{
+ if (!sm->ctx->set_rekey_offload)
+ return;
+ sm->ctx->set_rekey_offload(sm->ctx->ctx, sm->ptk.kek,
+ sm->ptk.kck, sm->rx_replay_counter);
+}
+
+#ifdef CONFIG_TDLS
+static inline int wpa_sm_tdls_get_capa(struct wpa_sm *sm,
+ int *tdls_supported,
+ int *tdls_ext_setup)
+{
+ if (sm->ctx->tdls_get_capa)
+ return sm->ctx->tdls_get_capa(sm->ctx->ctx, tdls_supported,
+ tdls_ext_setup);
+ return -1;
+}
+
+static inline int wpa_sm_send_tdls_mgmt(struct wpa_sm *sm, const u8 *dst,
+ u8 action_code, u8 dialog_token,
+ u16 status_code, const u8 *buf,
+ size_t len)
+{
+ if (sm->ctx->send_tdls_mgmt)
+ return sm->ctx->send_tdls_mgmt(sm->ctx->ctx, dst, action_code,
+ dialog_token, status_code,
+ buf, len);
+ return -1;
+}
+
+static inline int wpa_sm_tdls_oper(struct wpa_sm *sm, int oper,
+ const u8 *peer)
+{
+ if (sm->ctx->tdls_oper)
+ return sm->ctx->tdls_oper(sm->ctx->ctx, oper, peer);
+ return -1;
+}
+
+static inline int
+wpa_sm_tdls_peer_addset(struct wpa_sm *sm, const u8 *addr, int add,
+ u16 capability, const u8 *supp_rates,
+ size_t supp_rates_len)
+{
+ if (sm->ctx->tdls_peer_addset)
+ return sm->ctx->tdls_peer_addset(sm->ctx->ctx, addr, add,
+ capability, supp_rates,
+ supp_rates_len);
+ return -1;
+}
+#endif /* CONFIG_TDLS */
void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck,
int ver, const u8 *dest, u16 proto,
const struct wpa_eapol_key *key,
struct wpa_ptk *ptk, size_t ptk_len);
+void wpa_tdls_assoc(struct wpa_sm *sm);
+void wpa_tdls_disassoc(struct wpa_sm *sm);
+
#endif /* WPA_I_H */
#include "wpa_ie.h"
-static int wpa_selector_to_bitfield(const u8 *s)
-{
- if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_NONE)
- return WPA_CIPHER_NONE;
- if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP40)
- return WPA_CIPHER_WEP40;
- if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_TKIP)
- return WPA_CIPHER_TKIP;
- if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_CCMP)
- return WPA_CIPHER_CCMP;
- if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP104)
- return WPA_CIPHER_WEP104;
- return 0;
-}
-
-
-static int wpa_key_mgmt_to_bitfield(const u8 *s)
-{
- if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_UNSPEC_802_1X)
- return WPA_KEY_MGMT_IEEE8021X;
- if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X)
- return WPA_KEY_MGMT_PSK;
- if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_NONE)
- return WPA_KEY_MGMT_WPA_NONE;
- return 0;
-}
-
-
-static int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len,
- struct wpa_ie_data *data)
-{
- const struct wpa_ie_hdr *hdr;
- const u8 *pos;
- int left;
- int i, count;
-
- os_memset(data, 0, sizeof(*data));
- data->proto = WPA_PROTO_WPA;
- data->pairwise_cipher = WPA_CIPHER_TKIP;
- data->group_cipher = WPA_CIPHER_TKIP;
- data->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
- data->capabilities = 0;
- data->pmkid = NULL;
- data->num_pmkid = 0;
- data->mgmt_group_cipher = 0;
-
- if (wpa_ie_len == 0) {
- /* No WPA IE - fail silently */
- return -1;
- }
-
- if (wpa_ie_len < sizeof(struct wpa_ie_hdr)) {
- wpa_printf(MSG_DEBUG, "%s: ie len too short %lu",
- __func__, (unsigned long) wpa_ie_len);
- return -1;
- }
-
- hdr = (const struct wpa_ie_hdr *) wpa_ie;
-
- if (hdr->elem_id != WLAN_EID_VENDOR_SPECIFIC ||
- hdr->len != wpa_ie_len - 2 ||
- RSN_SELECTOR_GET(hdr->oui) != WPA_OUI_TYPE ||
- WPA_GET_LE16(hdr->version) != WPA_VERSION) {
- wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version",
- __func__);
- return -1;
- }
-
- pos = (const u8 *) (hdr + 1);
- left = wpa_ie_len - sizeof(*hdr);
-
- if (left >= WPA_SELECTOR_LEN) {
- data->group_cipher = wpa_selector_to_bitfield(pos);
- pos += WPA_SELECTOR_LEN;
- left -= WPA_SELECTOR_LEN;
- } else if (left > 0) {
- wpa_printf(MSG_DEBUG, "%s: ie length mismatch, %u too much",
- __func__, left);
- return -1;
- }
-
- if (left >= 2) {
- data->pairwise_cipher = 0;
- count = WPA_GET_LE16(pos);
- pos += 2;
- left -= 2;
- if (count == 0 || left < count * WPA_SELECTOR_LEN) {
- wpa_printf(MSG_DEBUG, "%s: ie count botch (pairwise), "
- "count %u left %u", __func__, count, left);
- return -1;
- }
- for (i = 0; i < count; i++) {
- data->pairwise_cipher |= wpa_selector_to_bitfield(pos);
- pos += WPA_SELECTOR_LEN;
- left -= WPA_SELECTOR_LEN;
- }
- } else if (left == 1) {
- wpa_printf(MSG_DEBUG, "%s: ie too short (for key mgmt)",
- __func__);
- return -1;
- }
-
- if (left >= 2) {
- data->key_mgmt = 0;
- count = WPA_GET_LE16(pos);
- pos += 2;
- left -= 2;
- if (count == 0 || left < count * WPA_SELECTOR_LEN) {
- wpa_printf(MSG_DEBUG, "%s: ie count botch (key mgmt), "
- "count %u left %u", __func__, count, left);
- return -1;
- }
- for (i = 0; i < count; i++) {
- data->key_mgmt |= wpa_key_mgmt_to_bitfield(pos);
- pos += WPA_SELECTOR_LEN;
- left -= WPA_SELECTOR_LEN;
- }
- } else if (left == 1) {
- wpa_printf(MSG_DEBUG, "%s: ie too short (for capabilities)",
- __func__);
- return -1;
- }
-
- if (left >= 2) {
- data->capabilities = WPA_GET_LE16(pos);
- pos += 2;
- left -= 2;
- }
-
- if (left > 0) {
- wpa_printf(MSG_DEBUG, "%s: ie has %u trailing bytes - ignored",
- __func__, left);
- }
-
- return 0;
-}
-
-
/**
* wpa_parse_wpa_ie - Parse WPA/RSN IE
* @wpa_ie: Pointer to WPA or RSN IE
ie->rsn_ie_len = pos[1] + 2;
wpa_hexdump(MSG_DEBUG, "WPA: RSN IE in EAPOL-Key",
ie->rsn_ie, ie->rsn_ie_len);
-#ifdef CONFIG_IEEE80211R
} else if (*pos == WLAN_EID_MOBILITY_DOMAIN) {
ie->mdie = pos;
ie->mdie_len = pos[1] + 2;
"EAPOL-Key Key Data IE",
pos, 2 + pos[1]);
}
-#endif /* CONFIG_IEEE80211R */
+ } else if (*pos == WLAN_EID_LINK_ID) {
+ if (pos[1] >= 18) {
+ ie->lnkid = pos;
+ ie->lnkid_len = pos[1] + 2;
+ }
+ } else if (*pos == WLAN_EID_EXT_CAPAB) {
+ ie->ext_capab = pos;
+ ie->ext_capab_len = pos[1] + 2;
+ } else if (*pos == WLAN_EID_SUPP_RATES) {
+ ie->supp_rates = pos;
+ ie->supp_rates_len = pos[1] + 2;
+ } else if (*pos == WLAN_EID_EXT_SUPP_RATES) {
+ ie->ext_supp_rates = pos;
+ ie->ext_supp_rates_len = pos[1] + 2;
} else if (*pos == WLAN_EID_VENDOR_SPECIFIC) {
ret = wpa_parse_generic(pos, end, ie);
if (ret < 0)
#ifndef WPA_IE_H
#define WPA_IE_H
+struct wpa_sm;
+
struct wpa_eapol_ie_parse {
const u8 *wpa_ie;
size_t wpa_ie_len;
const u8 *igtk;
size_t igtk_len;
#endif /* CONFIG_IEEE80211W */
-#ifdef CONFIG_IEEE80211R
const u8 *mdie;
size_t mdie_len;
const u8 *ftie;
size_t ftie_len;
const u8 *reassoc_deadline;
const u8 *key_lifetime;
-#endif /* CONFIG_IEEE80211R */
+ const u8 *lnkid;
+ size_t lnkid_len;
+ const u8 *ext_capab;
+ size_t ext_capab_len;
+ const u8 *supp_rates;
+ size_t supp_rates_len;
+ const u8 *ext_supp_rates;
+ size_t ext_supp_rates_len;
};
int wpa_supplicant_parse_ies(const u8 *buf, size_t len,
/* this is a shell function that calls either the normal or Montgomery
* exptmod functions. Originally the call to the montgomery code was
- * embedded in the normal function but that wasted alot of stack space
+ * embedded in the normal function but that wasted a lot of stack space
* for nothing (since 99% of the time the Montgomery code would be called)
*/
static int mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y)
/* zero a as per default */
mp_zero (a);
- /* grow a to accomodate the single bit */
+ /* grow a to accommodate the single bit */
if ((res = mp_grow (a, b / DIGIT_BIT + 1)) != MP_OKAY) {
return res;
}
}
-/* multiplies |a| * |b| and only computes upto digs digits of result
+/* multiplies |a| * |b| and only computes up to digs digits of result
* HAC pp. 595, Algorithm 14.12 Modified so you can control how
* many digits of output are created.
*/
{
int x, res, oldused;
- /* grow to accomodate result */
+ /* grow to accommodate result */
if (b->alloc < a->used + 1) {
if ((res = mp_grow (b, a->used + 1)) != MP_OKAY) {
return res;
/*
* shifts with subtractions when the result is greater than b.
*
- * The method is slightly modified to shift B unconditionally upto just under
- * the leading bit of b. This saves alot of multiple precision shifting.
+ * The method is slightly modified to shift B unconditionally up to just under
+ * the leading bit of b. This saves a lot of multiple precision shifting.
*/
static int mp_montgomery_calc_normalization (mp_int * a, mp_int * b)
{
/*
- * TLSv1 client (RFC 2246)
- * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ * TLS v1.0 (RFC 2246) and v1.1 (RFC 4346) client
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN);
os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random, TLS_RANDOM_LEN);
- key_block_len = 2 * (conn->rl.hash_size + conn->rl.key_material_len +
- conn->rl.iv_size);
+ key_block_len = 2 * (conn->rl.hash_size + conn->rl.key_material_len);
+ if (conn->rl.tls_version == TLS_VERSION_1)
+ key_block_len += 2 * conn->rl.iv_size;
if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN,
"key expansion", seed, 2 * TLS_RANDOM_LEN,
key_block, key_block_len)) {
os_memcpy(conn->rl.read_key, pos, conn->rl.key_material_len);
pos += conn->rl.key_material_len;
- /* client_write_IV */
- os_memcpy(conn->rl.write_iv, pos, conn->rl.iv_size);
- pos += conn->rl.iv_size;
- /* server_write_IV */
- os_memcpy(conn->rl.read_iv, pos, conn->rl.iv_size);
- pos += conn->rl.iv_size;
+ if (conn->rl.tls_version == TLS_VERSION_1) {
+ /* client_write_IV */
+ os_memcpy(conn->rl.write_iv, pos, conn->rl.iv_size);
+ pos += conn->rl.iv_size;
+ /* server_write_IV */
+ os_memcpy(conn->rl.read_iv, pos, conn->rl.iv_size);
+ pos += conn->rl.iv_size;
+ } else {
+ /*
+ * Use IV field to set the mask value for TLS v1.1. A fixed
+ * mask of zero is used per the RFC 4346, 6.2.3.2 CBC Block
+ * Cipher option 2a.
+ */
+ os_memset(conn->rl.write_iv, 0, conn->rl.iv_size);
+ }
return 0;
}
wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Plaintext AppData",
in_data, in_len);
- os_memcpy(out_data + TLS_RECORD_HEADER_LEN, in_data, in_len);
-
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_APPLICATION_DATA,
- out_data, out_len, in_len, &rlen) < 0) {
+ out_data, out_len, in_data, in_len, &rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
suites[count++] = TLS_RSA_WITH_RC4_128_MD5;
conn->num_cipher_suites = count;
+ conn->rl.tls_version = TLS_VERSION;
+
return conn;
}
}
+void tlsv1_client_set_time_checks(struct tlsv1_client *conn, int enabled)
+{
+ conn->disable_time_checks = !enabled;
+}
+
+
void tlsv1_client_set_session_ticket_cb(struct tlsv1_client *conn,
tlsv1_client_session_ticket_cb cb,
void *ctx)
/*
* TLSv1 client (RFC 2246)
- * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
int tlsv1_client_set_cipher_list(struct tlsv1_client *conn, u8 *ciphers);
int tlsv1_client_set_cred(struct tlsv1_client *conn,
struct tlsv1_credentials *cred);
+void tlsv1_client_set_time_checks(struct tlsv1_client *conn, int enabled);
typedef int (*tlsv1_client_session_ticket_cb)
(void *ctx, const u8 *ticket, size_t len, const u8 *client_random,
/*
* TLSv1 client - internal structures
- * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
unsigned int session_resumed:1;
unsigned int session_ticket_included:1;
unsigned int use_session_ticket:1;
+ unsigned int disable_time_checks:1;
struct crypto_public_key *server_rsa_key;
/*
- * TLSv1 client - read handshake message
- * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ * TLS v1.0 (RFC 2246) and v1.1 (RFC 4346) client - read handshake message
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
const u8 *pos, *end;
size_t left, len, i;
u16 cipher_suite;
+ u16 tls_version;
if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
/* ProtocolVersion server_version */
if (end - pos < 2)
goto decode_error;
- if (WPA_GET_BE16(pos) != TLS_VERSION) {
+ tls_version = WPA_GET_BE16(pos);
+ if (tls_version != TLS_VERSION_1 &&
+ (tls_version != TLS_VERSION_1_1 ||
+ TLS_VERSION == TLS_VERSION_1)) {
wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version in "
- "ServerHello");
+ "ServerHello %u.%u", pos[0], pos[1]);
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_PROTOCOL_VERSION);
return -1;
}
pos += 2;
+ wpa_printf(MSG_DEBUG, "TLSv1: Using TLS v%s",
+ tls_version == TLS_VERSION_1_1 ? "1.1" : "1.0");
+ conn->rl.tls_version = tls_version;
+
/* Random random */
if (end - pos < TLS_RANDOM_LEN)
goto decode_error;
if (conn->cred &&
x509_certificate_chain_validate(conn->cred->trusted_certs, chain,
- &reason) < 0) {
+ &reason, conn->disable_time_checks)
+ < 0) {
int tls_reason;
wpa_printf(MSG_DEBUG, "TLSv1: Server certificate chain "
"validation failed (reason=%d)", reason);
/*
- * TLSv1 client - write handshake message
- * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ * TLS v1.0 (RFC 2246) and v1.1 (RFC 4346) client - write handshake message
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
#include "crypto/md5.h"
#include "crypto/sha1.h"
#include "crypto/tls.h"
+#include "crypto/random.h"
#include "x509v3.h"
#include "tlsv1_common.h"
#include "tlsv1_record.h"
os_get_time(&now);
WPA_PUT_BE32(conn->client_random, now.sec);
- if (os_get_random(conn->client_random + 4, TLS_RANDOM_LEN - 4)) {
+ if (random_get_bytes(conn->client_random + 4, TLS_RANDOM_LEN - 4)) {
wpa_printf(MSG_ERROR, "TLSv1: Could not generate "
"client_random");
return NULL;
tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
- rhdr, end - rhdr, pos - hs_start, out_len) < 0) {
+ rhdr, end - rhdr, hs_start, pos - hs_start,
+ out_len) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to create TLS record");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
WPA_PUT_BE24(hs_length, pos - hs_length - 3);
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
- rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+ rhdr, end - rhdr, hs_start, pos - hs_start,
+ &rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
- if (os_get_random(csecret, csecret_len)) {
+ if (random_get_bytes(csecret, csecret_len)) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to get random "
"data for Diffie-Hellman");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
WPA_PUT_BE24(hs_length, pos - hs_length - 3);
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
- rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+ rhdr, end - rhdr, hs_start, pos - hs_start,
+ &rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
WPA_PUT_BE24(hs_length, pos - hs_length - 3);
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
- rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+ rhdr, end - rhdr, hs_start, pos - hs_start,
+ &rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
static int tls_write_client_change_cipher_spec(struct tlsv1_client *conn,
u8 **msgpos, u8 *end)
{
- u8 *pos, *rhdr;
size_t rlen;
-
- pos = *msgpos;
+ u8 payload[1];
wpa_printf(MSG_DEBUG, "TLSv1: Send ChangeCipherSpec");
- rhdr = pos;
- pos += TLS_RECORD_HEADER_LEN;
- *pos = TLS_CHANGE_CIPHER_SPEC;
+
+ payload[0] = TLS_CHANGE_CIPHER_SPEC;
+
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC,
- rhdr, end - rhdr, 1, &rlen) < 0) {
+ *msgpos, end - *msgpos, payload, sizeof(payload),
+ &rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
- *msgpos = rhdr + rlen;
+ *msgpos += rlen;
return 0;
}
static int tls_write_client_finished(struct tlsv1_client *conn,
u8 **msgpos, u8 *end)
{
- u8 *pos, *rhdr, *hs_start, *hs_length;
+ u8 *pos, *hs_start;
size_t rlen, hlen;
- u8 verify_data[TLS_VERIFY_DATA_LEN];
+ u8 verify_data[1 + 3 + TLS_VERIFY_DATA_LEN];
u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN];
- pos = *msgpos;
-
wpa_printf(MSG_DEBUG, "TLSv1: Send Finished");
/* Encrypted Handshake Message: Finished */
if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN,
"client finished", hash, MD5_MAC_LEN + SHA1_MAC_LEN,
- verify_data, TLS_VERIFY_DATA_LEN)) {
+ verify_data + 1 + 3, TLS_VERIFY_DATA_LEN)) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate verify_data");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (client)",
- verify_data, TLS_VERIFY_DATA_LEN);
+ verify_data + 1 + 3, TLS_VERIFY_DATA_LEN);
- rhdr = pos;
- pos += TLS_RECORD_HEADER_LEN;
/* Handshake */
- hs_start = pos;
+ pos = hs_start = verify_data;
/* HandshakeType msg_type */
*pos++ = TLS_HANDSHAKE_TYPE_FINISHED;
- /* uint24 length (to be filled) */
- hs_length = pos;
+ /* uint24 length */
+ WPA_PUT_BE24(pos, TLS_VERIFY_DATA_LEN);
pos += 3;
- os_memcpy(pos, verify_data, TLS_VERIFY_DATA_LEN);
- pos += TLS_VERIFY_DATA_LEN;
- WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+ pos += TLS_VERIFY_DATA_LEN; /* verify_data already in place */
tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
- rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+ *msgpos, end - *msgpos, hs_start, pos - hs_start,
+ &rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
- pos = rhdr + rlen;
-
- *msgpos = pos;
+ *msgpos += rlen;
return 0;
}
*out_len = 0;
- msglen = 1000;
+ msglen = 2000;
if (conn->certificate_requested)
msglen += tls_client_cert_chain_der_len(conn);
/* ContentType type */
*pos++ = TLS_CONTENT_TYPE_ALERT;
/* ProtocolVersion version */
- WPA_PUT_BE16(pos, TLS_VERSION);
+ WPA_PUT_BE16(pos, conn->rl.tls_version ? conn->rl.tls_version :
+ TLS_VERSION);
pos += 2;
/* uint16 length (to be filled) */
length = pos;
/*
- * TLSv1 common definitions
- * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ * TLS v1.0 (RFC 2246) and v1.1 (RFC 4346) common definitions
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
#include "crypto/crypto.h"
-#define TLS_VERSION 0x0301 /* TLSv1 */
+#define TLS_VERSION_1 0x0301 /* TLSv1 */
+#define TLS_VERSION_1_1 0x0302 /* TLSv1.1 */
+#ifdef CONFIG_TLSV11
+#define TLS_VERSION TLS_VERSION_1_1
+#else /* CONFIG_TLSV11 */
+#define TLS_VERSION TLS_VERSION_1
+#endif /* CONFIG_TLSV11 */
#define TLS_RANDOM_LEN 32
#define TLS_PRE_MASTER_SECRET_LEN 48
#define TLS_MASTER_SECRET_LEN 48
static int tlsv1_add_cert_der(struct x509_certificate **chain,
const u8 *buf, size_t len)
{
- struct x509_certificate *cert;
+ struct x509_certificate *cert, *p;
char name[128];
cert = x509_certificate_parse(buf, len);
return -1;
}
- cert->next = *chain;
- *chain = cert;
+ p = *chain;
+ while (p && p->next)
+ p = p->next;
+ if (p && x509_name_compare(&cert->subject, &p->issuer) == 0) {
+ /*
+ * The new certificate is the issuer of the last certificate in
+ * the chain - add the new certificate to the end.
+ */
+ p->next = cert;
+ } else {
+ /* Add to the beginning of the chain */
+ cert->next = *chain;
+ *chain = cert;
+ }
x509_name_string(&cert->subject, name, sizeof(name));
wpa_printf(MSG_DEBUG, "TLSv1: Added certificate: %s", name);
/*
- * TLSv1 Record Protocol
- * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ * TLS v1.0 (RFC 2246) and v1.1 (RFC 4346) Record Protocol
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* tlsv1_record_send - TLS record layer: Send a message
* @rl: Pointer to TLS record layer data
* @content_type: Content type (TLS_CONTENT_TYPE_*)
- * @buf: Buffer to send (with TLS_RECORD_HEADER_LEN octets reserved in the
- * beginning for record layer to fill in; payload filled in after this and
- * extra space in the end for HMAC).
+ * @buf: Buffer for the generated TLS message (needs to have extra space for
+ * header, IV (TLS v1.1), and HMAC)
* @buf_size: Maximum buf size
+ * @payload: Payload to be sent
* @payload_len: Length of the payload
* @out_len: Buffer for returning the used buf length
* Returns: 0 on success, -1 on failure
* the data using the current write cipher.
*/
int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf,
- size_t buf_size, size_t payload_len, size_t *out_len)
+ size_t buf_size, const u8 *payload, size_t payload_len,
+ size_t *out_len)
{
- u8 *pos, *ct_start, *length, *payload;
+ u8 *pos, *ct_start, *length, *cpayload;
struct crypto_hash *hmac;
size_t clen;
+ int explicit_iv;
pos = buf;
+ if (pos + TLS_RECORD_HEADER_LEN > buf + buf_size)
+ return -1;
+
/* ContentType type */
ct_start = pos;
*pos++ = content_type;
/* ProtocolVersion version */
- WPA_PUT_BE16(pos, TLS_VERSION);
+ WPA_PUT_BE16(pos, rl->tls_version);
pos += 2;
/* uint16 length */
length = pos;
WPA_PUT_BE16(length, payload_len);
pos += 2;
- /* opaque fragment[TLSPlaintext.length] */
- payload = pos;
+ cpayload = pos;
+ explicit_iv = rl->write_cipher_suite != TLS_NULL_WITH_NULL_NULL &&
+ rl->iv_size && rl->tls_version == TLS_VERSION_1_1;
+ if (explicit_iv) {
+ /* opaque IV[Cipherspec.block_length] */
+ if (pos + rl->iv_size > buf + buf_size)
+ return -1;
+
+ /*
+ * Use random number R per the RFC 4346, 6.2.3.2 CBC Block
+ * Cipher option 2a.
+ */
+
+ if (os_get_random(pos, rl->iv_size))
+ return -1;
+ pos += rl->iv_size;
+ }
+
+ /*
+ * opaque fragment[TLSPlaintext.length]
+ * (opaque content[TLSCompressed.length] in GenericBlockCipher)
+ */
+ if (pos + payload_len > buf + buf_size)
+ return -1;
+ os_memmove(pos, payload, payload_len);
pos += payload_len;
if (rl->write_cipher_suite != TLS_NULL_WITH_NULL_NULL) {
+ /*
+ * MAC calculated over seq_num + TLSCompressed.type +
+ * TLSCompressed.version + TLSCompressed.length +
+ * TLSCompressed.fragment
+ */
hmac = crypto_hash_init(rl->hash_alg, rl->write_mac_secret,
rl->hash_size);
if (hmac == NULL) {
}
crypto_hash_update(hmac, rl->write_seq_num, TLS_SEQ_NUM_LEN);
/* type + version + length + fragment */
- crypto_hash_update(hmac, ct_start, pos - ct_start);
+ crypto_hash_update(hmac, ct_start, TLS_RECORD_HEADER_LEN);
+ crypto_hash_update(hmac, payload, payload_len);
clen = buf + buf_size - pos;
if (clen < rl->hash_size) {
wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Not "
pos, clen);
pos += clen;
if (rl->iv_size) {
- size_t len = pos - payload;
+ size_t len = pos - cpayload;
size_t pad;
pad = (len + 1) % rl->iv_size;
if (pad)
pos += pad + 1;
}
- if (crypto_cipher_encrypt(rl->write_cbc, payload,
- payload, pos - payload) < 0)
+ if (crypto_cipher_encrypt(rl->write_cbc, cpayload,
+ cpayload, pos - cpayload) < 0)
return -1;
}
u8 padlen;
struct crypto_hash *hmac;
u8 len[2], hash[100];
+ int force_mac_error = 0;
wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Received",
in_data, in_len);
return -1;
}
- if (WPA_GET_BE16(in_data + 1) != TLS_VERSION) {
+ /*
+ * TLS v1.0 and v1.1 RFCs were not exactly clear on the use of the
+ * protocol version in record layer. As such, accept any {03,xx} value
+ * to remain compatible with existing implementations.
+ */
+ if (in_data[1] != 0x03) {
wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version "
- "%d.%d", in_data[1], in_data[2]);
+ "%u.%u", in_data[1], in_data[2]);
*alert = TLS_ALERT_PROTOCOL_VERSION;
return -1;
}
return -1;
}
- os_memcpy(out_data, in_data, in_len);
- *out_len = in_len;
-
if (rl->read_cipher_suite != TLS_NULL_WITH_NULL_NULL) {
- if (crypto_cipher_decrypt(rl->read_cbc, out_data,
+ size_t plen;
+ if (crypto_cipher_decrypt(rl->read_cbc, in_data,
out_data, in_len) < 0) {
*alert = TLS_ALERT_DECRYPTION_FAILED;
return -1;
}
+ plen = in_len;
+ wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Record Layer - Decrypted "
+ "data", out_data, plen);
+
if (rl->iv_size) {
- if (in_len == 0) {
+ /*
+ * TLS v1.0 defines different alert values for various
+ * failures. That may information to aid in attacks, so
+ * use the same bad_record_mac alert regardless of the
+ * issues.
+ *
+ * In addition, instead of returning immediately on
+ * error, run through the MAC check to make timing
+ * attacks more difficult.
+ */
+
+ if (rl->tls_version == TLS_VERSION_1_1) {
+ /* Remove opaque IV[Cipherspec.block_length] */
+ if (plen < rl->iv_size) {
+ wpa_printf(MSG_DEBUG, "TLSv1.1: Not "
+ "enough room for IV");
+ force_mac_error = 1;
+ goto check_mac;
+ }
+ os_memmove(out_data, out_data + rl->iv_size,
+ plen - rl->iv_size);
+ plen -= rl->iv_size;
+ }
+
+ /* Verify and remove padding */
+ if (plen == 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Too short record"
" (no pad)");
- *alert = TLS_ALERT_DECODE_ERROR;
- return -1;
+ force_mac_error = 1;
+ goto check_mac;
}
- padlen = out_data[in_len - 1];
- if (padlen >= in_len) {
+ padlen = out_data[plen - 1];
+ if (padlen >= plen) {
wpa_printf(MSG_DEBUG, "TLSv1: Incorrect pad "
- "length (%u, in_len=%lu) in "
+ "length (%u, plen=%lu) in "
"received record",
- padlen, (unsigned long) in_len);
- *alert = TLS_ALERT_DECRYPTION_FAILED;
- return -1;
+ padlen, (unsigned long) plen);
+ force_mac_error = 1;
+ goto check_mac;
}
- for (i = in_len - padlen; i < in_len; i++) {
+ for (i = plen - padlen - 1; i < plen - 1; i++) {
if (out_data[i] != padlen) {
wpa_hexdump(MSG_DEBUG,
"TLSv1: Invalid pad in "
"received record",
- out_data + in_len - padlen,
- padlen);
- *alert = TLS_ALERT_DECRYPTION_FAILED;
- return -1;
+ out_data + plen - padlen -
+ 1, padlen + 1);
+ force_mac_error = 1;
+ goto check_mac;
}
}
- *out_len -= padlen + 1;
+ plen -= padlen + 1;
}
- wpa_hexdump(MSG_MSGDUMP,
- "TLSv1: Record Layer - Decrypted data",
- out_data, in_len);
+ check_mac:
+ wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Record Layer - Decrypted "
+ "data with IV and padding removed",
+ out_data, plen);
- if (*out_len < rl->hash_size) {
+ if (plen < rl->hash_size) {
wpa_printf(MSG_DEBUG, "TLSv1: Too short record; no "
"hash value");
- *alert = TLS_ALERT_INTERNAL_ERROR;
+ *alert = TLS_ALERT_BAD_RECORD_MAC;
return -1;
}
- *out_len -= rl->hash_size;
+ plen -= rl->hash_size;
hmac = crypto_hash_init(rl->hash_alg, rl->read_mac_secret,
rl->hash_size);
crypto_hash_update(hmac, rl->read_seq_num, TLS_SEQ_NUM_LEN);
/* type + version + length + fragment */
crypto_hash_update(hmac, in_data - TLS_RECORD_HEADER_LEN, 3);
- WPA_PUT_BE16(len, *out_len);
+ WPA_PUT_BE16(len, plen);
crypto_hash_update(hmac, len, 2);
- crypto_hash_update(hmac, out_data, *out_len);
+ crypto_hash_update(hmac, out_data, plen);
hlen = sizeof(hash);
if (crypto_hash_finish(hmac, hash, &hlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed "
"to calculate HMAC");
+ *alert = TLS_ALERT_INTERNAL_ERROR;
return -1;
}
if (hlen != rl->hash_size ||
- os_memcmp(hash, out_data + *out_len, hlen) != 0) {
+ os_memcmp(hash, out_data + plen, hlen) != 0 ||
+ force_mac_error) {
wpa_printf(MSG_DEBUG, "TLSv1: Invalid HMAC value in "
- "received message");
+ "received message (force_mac_error=%d)",
+ force_mac_error);
*alert = TLS_ALERT_BAD_RECORD_MAC;
return -1;
}
+
+ *out_len = plen;
+ } else {
+ os_memcpy(out_data, in_data, in_len);
+ *out_len = in_len;
}
/* TLSCompressed must not be more than 2^14+1024 bytes */
/*
- * TLSv1 Record Protocol
- * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ * TLS v1.0 (RFC 2246) and v1.1 (RFC 4346) Record Protocol
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
};
struct tlsv1_record_layer {
+ u16 tls_version;
+
u8 write_mac_secret[TLS_MAX_WRITE_MAC_SECRET_LEN];
u8 read_mac_secret[TLS_MAX_WRITE_MAC_SECRET_LEN];
u8 write_key[TLS_MAX_WRITE_KEY_LEN];
int tlsv1_record_change_write_cipher(struct tlsv1_record_layer *rl);
int tlsv1_record_change_read_cipher(struct tlsv1_record_layer *rl);
int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf,
- size_t buf_size, size_t payload_len, size_t *out_len);
+ size_t buf_size, const u8 *payload, size_t payload_len,
+ size_t *out_len);
int tlsv1_record_receive(struct tlsv1_record_layer *rl,
const u8 *in_data, size_t in_len,
u8 *out_data, size_t *out_len, u8 *alert);
/*
- * TLSv1 server (RFC 2246)
- * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ * TLS v1.0 (RFC 2246) and v1.1 (RFC 4346) server
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Plaintext AppData",
in_data, in_len);
- os_memcpy(out_data + TLS_RECORD_HEADER_LEN, in_data, in_len);
-
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_APPLICATION_DATA,
- out_data, out_len, in_len, &rlen) < 0) {
+ out_data, out_len, in_data, in_len, &rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
conn->client_version = WPA_GET_BE16(pos);
wpa_printf(MSG_DEBUG, "TLSv1: Client version %d.%d",
conn->client_version >> 8, conn->client_version & 0xff);
- if (conn->client_version < TLS_VERSION) {
+ if (conn->client_version < TLS_VERSION_1) {
wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version in "
- "ClientHello");
+ "ClientHello %u.%u",
+ conn->client_version >> 8,
+ conn->client_version & 0xff);
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_PROTOCOL_VERSION);
return -1;
}
pos += 2;
+ if (TLS_VERSION == TLS_VERSION_1)
+ conn->rl.tls_version = TLS_VERSION_1;
+ else if (conn->client_version > TLS_VERSION_1_1)
+ conn->rl.tls_version = TLS_VERSION_1_1;
+ else
+ conn->rl.tls_version = conn->client_version;
+ wpa_printf(MSG_DEBUG, "TLSv1: Using TLS v%s",
+ conn->rl.tls_version == TLS_VERSION_1_1 ? "1.1" : "1.0");
+
/* Random random */
if (end - pos < TLS_RANDOM_LEN)
goto decode_error;
}
if (x509_certificate_chain_validate(conn->cred->trusted_certs, chain,
- &reason) < 0) {
+ &reason, 0) < 0) {
int tls_reason;
wpa_printf(MSG_DEBUG, "TLSv1: Server certificate chain "
"validation failed (reason=%d)", reason);
encr_len = WPA_GET_BE16(pos);
pos += 2;
+ if (pos + encr_len > end) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Invalid ClientKeyExchange "
+ "format: encr_len=%u left=%u",
+ encr_len, (unsigned int) (end - pos));
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
outbuflen = outlen = end - pos;
out = os_malloc(outlen >= TLS_PRE_MASTER_SECRET_LEN ?
*/
if (crypto_private_key_decrypt_pkcs1_v15(conn->cred->key,
- pos, end - pos,
+ pos, encr_len,
out, &outlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to decrypt "
- "PreMasterSecret (encr_len=%d outlen=%lu)",
- (int) (end - pos), (unsigned long) outlen);
+ "PreMasterSecret (encr_len=%u outlen=%lu)",
+ encr_len, (unsigned long) outlen);
use_random = 1;
}
- if (outlen != TLS_PRE_MASTER_SECRET_LEN) {
+ if (!use_random && outlen != TLS_PRE_MASTER_SECRET_LEN) {
wpa_printf(MSG_DEBUG, "TLSv1: Unexpected PreMasterSecret "
"length %lu", (unsigned long) outlen);
use_random = 1;
}
- if (WPA_GET_BE16(out) != conn->client_version) {
+ if (!use_random && WPA_GET_BE16(out) != conn->client_version) {
wpa_printf(MSG_DEBUG, "TLSv1: Client version in "
"ClientKeyExchange does not match with version in "
"ClientHello");
/*
- * TLSv1 server - write handshake message
- * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ * TLS v1.0 (RFC 2246) and v1.1 (RFC 4346) server - write handshake message
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
#include "crypto/md5.h"
#include "crypto/sha1.h"
#include "crypto/tls.h"
+#include "crypto/random.h"
#include "x509v3.h"
#include "tlsv1_common.h"
#include "tlsv1_record.h"
os_get_time(&now);
WPA_PUT_BE32(conn->server_random, now.sec);
- if (os_get_random(conn->server_random + 4, TLS_RANDOM_LEN - 4)) {
+ if (random_get_bytes(conn->server_random + 4, TLS_RANDOM_LEN - 4)) {
wpa_printf(MSG_ERROR, "TLSv1: Could not generate "
"server_random");
return -1;
conn->server_random, TLS_RANDOM_LEN);
conn->session_id_len = TLS_SESSION_ID_MAX_LEN;
- if (os_get_random(conn->session_id, conn->session_id_len)) {
+ if (random_get_bytes(conn->session_id, conn->session_id_len)) {
wpa_printf(MSG_ERROR, "TLSv1: Could not generate "
"session_id");
return -1;
pos += 3;
/* body - ServerHello */
/* ProtocolVersion server_version */
- WPA_PUT_BE16(pos, TLS_VERSION);
+ WPA_PUT_BE16(pos, conn->rl.tls_version);
pos += 2;
/* Random random: uint32 gmt_unix_time, opaque random_bytes */
os_memcpy(pos, conn->server_random, TLS_RANDOM_LEN);
tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
- rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+ rhdr, end - rhdr, hs_start, pos - hs_start,
+ &rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to create TLS record");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
WPA_PUT_BE24(hs_length, pos - hs_length - 3);
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
- rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+ rhdr, end - rhdr, hs_start, pos - hs_start,
+ &rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
- if (os_get_random(conn->dh_secret, conn->dh_secret_len)) {
+ if (random_get_bytes(conn->dh_secret, conn->dh_secret_len)) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to get random "
"data for Diffie-Hellman");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
WPA_PUT_BE24(hs_length, pos - hs_length - 3);
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
- rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+ rhdr, end - rhdr, hs_start, pos - hs_start,
+ &rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
WPA_PUT_BE24(hs_length, pos - hs_length - 3);
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
- rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+ rhdr, end - rhdr, hs_start, pos - hs_start,
+ &rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
static int tls_write_server_hello_done(struct tlsv1_server *conn,
u8 **msgpos, u8 *end)
{
- u8 *pos, *rhdr, *hs_start, *hs_length;
+ u8 *pos;
size_t rlen;
-
- pos = *msgpos;
+ u8 payload[4];
wpa_printf(MSG_DEBUG, "TLSv1: Send ServerHelloDone");
- rhdr = pos;
- pos += TLS_RECORD_HEADER_LEN;
/* opaque fragment[TLSPlaintext.length] */
/* Handshake */
- hs_start = pos;
+ pos = payload;
/* HandshakeType msg_type */
*pos++ = TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE;
- /* uint24 length (to be filled) */
- hs_length = pos;
+ /* uint24 length */
+ WPA_PUT_BE24(pos, 0);
pos += 3;
/* body - ServerHelloDone (empty) */
- WPA_PUT_BE24(hs_length, pos - hs_length - 3);
-
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
- rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+ *msgpos, end - *msgpos, payload, pos - payload,
+ &rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
- pos = rhdr + rlen;
- tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+ tls_verify_hash_add(&conn->verify, payload, pos - payload);
- *msgpos = pos;
+ *msgpos += rlen;
return 0;
}
static int tls_write_server_change_cipher_spec(struct tlsv1_server *conn,
u8 **msgpos, u8 *end)
{
- u8 *pos, *rhdr;
size_t rlen;
-
- pos = *msgpos;
+ u8 payload[1];
wpa_printf(MSG_DEBUG, "TLSv1: Send ChangeCipherSpec");
- rhdr = pos;
- pos += TLS_RECORD_HEADER_LEN;
- *pos = TLS_CHANGE_CIPHER_SPEC;
+
+ payload[0] = TLS_CHANGE_CIPHER_SPEC;
+
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC,
- rhdr, end - rhdr, 1, &rlen) < 0) {
+ *msgpos, end - *msgpos, payload, sizeof(payload),
+ &rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
- *msgpos = rhdr + rlen;
+ *msgpos += rlen;
return 0;
}
static int tls_write_server_finished(struct tlsv1_server *conn,
u8 **msgpos, u8 *end)
{
- u8 *pos, *rhdr, *hs_start, *hs_length;
+ u8 *pos, *hs_start;
size_t rlen, hlen;
- u8 verify_data[TLS_VERIFY_DATA_LEN];
+ u8 verify_data[1 + 3 + TLS_VERIFY_DATA_LEN];
u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN];
pos = *msgpos;
if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN,
"server finished", hash, MD5_MAC_LEN + SHA1_MAC_LEN,
- verify_data, TLS_VERIFY_DATA_LEN)) {
+ verify_data + 1 + 3, TLS_VERIFY_DATA_LEN)) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate verify_data");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (server)",
- verify_data, TLS_VERIFY_DATA_LEN);
+ verify_data + 1 + 3, TLS_VERIFY_DATA_LEN);
- rhdr = pos;
- pos += TLS_RECORD_HEADER_LEN;
/* Handshake */
- hs_start = pos;
+ pos = hs_start = verify_data;
/* HandshakeType msg_type */
*pos++ = TLS_HANDSHAKE_TYPE_FINISHED;
- /* uint24 length (to be filled) */
- hs_length = pos;
+ /* uint24 length */
+ WPA_PUT_BE24(pos, TLS_VERIFY_DATA_LEN);
pos += 3;
- os_memcpy(pos, verify_data, TLS_VERIFY_DATA_LEN);
pos += TLS_VERIFY_DATA_LEN;
- WPA_PUT_BE24(hs_length, pos - hs_length - 3);
tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
- rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+ *msgpos, end - *msgpos, hs_start, pos - hs_start,
+ &rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
- pos = rhdr + rlen;
-
- *msgpos = pos;
+ *msgpos += rlen;
return 0;
}
/* ContentType type */
*pos++ = TLS_CONTENT_TYPE_ALERT;
/* ProtocolVersion version */
- WPA_PUT_BE16(pos, TLS_VERSION);
+ WPA_PUT_BE16(pos, conn->rl.tls_version ? conn->rl.tls_version :
+ TLS_VERSION);
pos += 2;
/* uint16 length (to be filled) */
length = pos;
/*
* X.509v3 certificate parsing and processing (RFC 3280 profile)
- * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
*/
int x509_certificate_chain_validate(struct x509_certificate *trusted,
struct x509_certificate *chain,
- int *reason)
+ int *reason, int disable_time_checks)
{
long unsigned idx;
int chain_trusted = 0;
if (chain_trusted)
continue;
- if ((unsigned long) now.sec <
- (unsigned long) cert->not_before ||
- (unsigned long) now.sec >
- (unsigned long) cert->not_after) {
+ if (!disable_time_checks &&
+ ((unsigned long) now.sec <
+ (unsigned long) cert->not_before ||
+ (unsigned long) now.sec >
+ (unsigned long) cert->not_after)) {
wpa_printf(MSG_INFO, "X509: Certificate not valid "
"(now=%lu not_before=%lu not_after=%lu)",
now.sec, cert->not_before, cert->not_after);
/*
* X.509v3 certificate parsing and processing
- * Copyright (c) 2006, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
struct x509_certificate *cert);
int x509_certificate_chain_validate(struct x509_certificate *trusted,
struct x509_certificate *chain,
- int *reason);
+ int *reason, int disable_time_checks);
struct x509_certificate *
x509_certificate_get_subject(struct x509_certificate *chain,
struct x509_name *name);
# Pick correct event loop implementation
LIB_OBJS += eloop.o
+# Pick correct edit implementation
+LIB_OBJS += edit.o
+
#LIB_OBJS += pcsc_funcs.o
libutils.a: $(LIB_OBJS)
/*
* Base64 encoding/decoding (RFC1341)
- * Copyright (c) 2005, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2005-2011, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
unsigned char * base64_decode(const unsigned char *src, size_t len,
size_t *out_len)
{
- unsigned char dtable[256], *out, *pos, in[4], block[4], tmp;
+ unsigned char dtable[256], *out, *pos, block[4], tmp;
size_t i, count, olen;
+ int pad = 0;
os_memset(dtable, 0x80, 256);
for (i = 0; i < sizeof(base64_table) - 1; i++)
if (tmp == 0x80)
continue;
- in[count] = src[i];
+ if (src[i] == '=')
+ pad++;
block[count] = tmp;
count++;
if (count == 4) {
*pos++ = (block[1] << 4) | (block[2] >> 2);
*pos++ = (block[2] << 6) | block[3];
count = 0;
+ if (pad) {
+ if (pad == 1)
+ pos--;
+ else if (pad == 2)
+ pos -= 2;
+ else {
+ /* Invalid padding */
+ os_free(out);
+ return NULL;
+ }
+ break;
+ }
}
}
- if (pos > out) {
- if (in[2] == '=')
- pos -= 2;
- else if (in[3] == '=')
- pos--;
- }
-
*out_len = pos - out;
return out;
}
*/
#ifndef BASE64_H
-#define BASE64_h
+#define BASE64_H
unsigned char * base64_encode(const unsigned char *src, size_t len,
size_t *out_len);
}
-static int hex2byte(const char *hex)
+int hex2byte(const char *hex)
{
int a, b;
a = hex2num(*hex++);
return 0;
}
+/**
+ * hwaddr_compact_aton - Convert ASCII string to MAC address (no colon delimitors format)
+ * @txt: MAC address as a string (e.g., "001122334455")
+ * @addr: Buffer for the MAC address (ETH_ALEN = 6 bytes)
+ * Returns: 0 on success, -1 on failure (e.g., string not a MAC address)
+ */
+int hwaddr_compact_aton(const char *txt, u8 *addr)
+{
+ int i;
+
+ for (i = 0; i < 6; i++) {
+ int a, b;
+
+ a = hex2num(*txt++);
+ if (a < 0)
+ return -1;
+ b = hex2num(*txt++);
+ if (b < 0)
+ return -1;
+ *addr++ = (a << 4) | b;
+ }
+
+ return 0;
+}
/**
* hwaddr_aton2 - Convert ASCII string to MAC address (in any known format)
#ifndef ETH_P_ALL
#define ETH_P_ALL 0x0003
#endif
+#ifndef ETH_P_80211_ENCAP
+#define ETH_P_80211_ENCAP 0x890d /* TDLS comes under this category */
+#endif
#ifndef ETH_P_PAE
#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */
#endif /* ETH_P_PAE */
#ifndef MAC2STR
#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x"
+
+/*
+ * Compact form for string representation of MAC address
+ * To be used, e.g., for constructing dbus paths for P2P Devices
+ */
+#define COMPACT_MACSTR "%02x%02x%02x%02x%02x%02x"
#endif
#ifndef BIT
#endif /* __must_check */
int hwaddr_aton(const char *txt, u8 *addr);
+int hwaddr_compact_aton(const char *txt, u8 *addr);
int hwaddr_aton2(const char *txt, u8 *addr);
+int hex2byte(const char *hex);
int hexstr2bin(const char *hex, u8 *buf, size_t len);
void inc_byte_array(u8 *counter, size_t len);
void wpa_get_ntp_timestamp(u8 *buf);
return !(a[0] | a[1] | a[2] | a[3] | a[4] | a[5]);
}
+static inline int is_broadcast_ether_addr(const u8 *a)
+{
+ return (a[0] & a[1] & a[2] & a[3] & a[4] & a[5]) == 0xff;
+}
+
+#define broadcast_ether_addr (const u8 *) "\xff\xff\xff\xff\xff\xff"
+
#include "wpa_debug.h"
void * __hide_aliasing_typecast(void *foo);
#define aliasing_hide_typecast(a,t) (t *) __hide_aliasing_typecast((a))
+#ifdef CONFIG_VALGRIND
+#include <valgrind/memcheck.h>
+#define WPA_MEM_DEFINED(ptr, len) VALGRIND_MAKE_MEM_DEFINED((ptr), (len))
+#else /* CONFIG_VALGRIND */
+#define WPA_MEM_DEFINED(ptr, len) do { } while (0)
+#endif /* CONFIG_VALGRIND */
+
#endif /* COMMON_H */
--- /dev/null
+/*
+ * Command line editing and history
+ * Copyright (c) 2010-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#include <termios.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "list.h"
+#include "edit.h"
+
+#define CMD_BUF_LEN 256
+static char cmdbuf[CMD_BUF_LEN];
+static int cmdbuf_pos = 0;
+static int cmdbuf_len = 0;
+static char currbuf[CMD_BUF_LEN];
+static int currbuf_valid = 0;
+
+#define HISTORY_MAX 100
+
+struct edit_history {
+ struct dl_list list;
+ char str[1];
+};
+
+static struct dl_list history_list;
+static struct edit_history *history_curr;
+
+static void *edit_cb_ctx;
+static void (*edit_cmd_cb)(void *ctx, char *cmd);
+static void (*edit_eof_cb)(void *ctx);
+static char ** (*edit_completion_cb)(void *ctx, const char *cmd, int pos) =
+ NULL;
+
+static struct termios prevt, newt;
+
+
+#define CLEAR_END_LINE "\e[K"
+
+
+void edit_clear_line(void)
+{
+ int i;
+ putchar('\r');
+ for (i = 0; i < cmdbuf_len + 2; i++)
+ putchar(' ');
+}
+
+
+static void move_start(void)
+{
+ cmdbuf_pos = 0;
+ edit_redraw();
+}
+
+
+static void move_end(void)
+{
+ cmdbuf_pos = cmdbuf_len;
+ edit_redraw();
+}
+
+
+static void move_left(void)
+{
+ if (cmdbuf_pos > 0) {
+ cmdbuf_pos--;
+ edit_redraw();
+ }
+}
+
+
+static void move_right(void)
+{
+ if (cmdbuf_pos < cmdbuf_len) {
+ cmdbuf_pos++;
+ edit_redraw();
+ }
+}
+
+
+static void move_word_left(void)
+{
+ while (cmdbuf_pos > 0 && cmdbuf[cmdbuf_pos - 1] == ' ')
+ cmdbuf_pos--;
+ while (cmdbuf_pos > 0 && cmdbuf[cmdbuf_pos - 1] != ' ')
+ cmdbuf_pos--;
+ edit_redraw();
+}
+
+
+static void move_word_right(void)
+{
+ while (cmdbuf_pos < cmdbuf_len && cmdbuf[cmdbuf_pos] == ' ')
+ cmdbuf_pos++;
+ while (cmdbuf_pos < cmdbuf_len && cmdbuf[cmdbuf_pos] != ' ')
+ cmdbuf_pos++;
+ edit_redraw();
+}
+
+
+static void delete_left(void)
+{
+ if (cmdbuf_pos == 0)
+ return;
+
+ edit_clear_line();
+ os_memmove(cmdbuf + cmdbuf_pos - 1, cmdbuf + cmdbuf_pos,
+ cmdbuf_len - cmdbuf_pos);
+ cmdbuf_pos--;
+ cmdbuf_len--;
+ edit_redraw();
+}
+
+
+static void delete_current(void)
+{
+ if (cmdbuf_pos == cmdbuf_len)
+ return;
+
+ edit_clear_line();
+ os_memmove(cmdbuf + cmdbuf_pos, cmdbuf + cmdbuf_pos + 1,
+ cmdbuf_len - cmdbuf_pos);
+ cmdbuf_len--;
+ edit_redraw();
+}
+
+
+static void delete_word(void)
+{
+ int pos;
+
+ edit_clear_line();
+ pos = cmdbuf_pos;
+ while (pos > 0 && cmdbuf[pos - 1] == ' ')
+ pos--;
+ while (pos > 0 && cmdbuf[pos - 1] != ' ')
+ pos--;
+ os_memmove(cmdbuf + pos, cmdbuf + cmdbuf_pos, cmdbuf_len - cmdbuf_pos);
+ cmdbuf_len -= cmdbuf_pos - pos;
+ cmdbuf_pos = pos;
+ edit_redraw();
+}
+
+
+static void clear_left(void)
+{
+ if (cmdbuf_pos == 0)
+ return;
+
+ edit_clear_line();
+ os_memmove(cmdbuf, cmdbuf + cmdbuf_pos, cmdbuf_len - cmdbuf_pos);
+ cmdbuf_len -= cmdbuf_pos;
+ cmdbuf_pos = 0;
+ edit_redraw();
+}
+
+
+static void clear_right(void)
+{
+ if (cmdbuf_pos == cmdbuf_len)
+ return;
+
+ edit_clear_line();
+ cmdbuf_len = cmdbuf_pos;
+ edit_redraw();
+}
+
+
+static void history_add(const char *str)
+{
+ struct edit_history *h, *match = NULL, *last = NULL;
+ size_t len, count = 0;
+
+ if (str[0] == '\0')
+ return;
+
+ dl_list_for_each(h, &history_list, struct edit_history, list) {
+ if (os_strcmp(str, h->str) == 0) {
+ match = h;
+ break;
+ }
+ last = h;
+ count++;
+ }
+
+ if (match) {
+ dl_list_del(&h->list);
+ dl_list_add(&history_list, &h->list);
+ history_curr = h;
+ return;
+ }
+
+ if (count >= HISTORY_MAX && last) {
+ dl_list_del(&last->list);
+ os_free(last);
+ }
+
+ len = os_strlen(str);
+ h = os_zalloc(sizeof(*h) + len);
+ if (h == NULL)
+ return;
+ dl_list_add(&history_list, &h->list);
+ os_strlcpy(h->str, str, len + 1);
+ history_curr = h;
+}
+
+
+static void history_use(void)
+{
+ edit_clear_line();
+ cmdbuf_len = cmdbuf_pos = os_strlen(history_curr->str);
+ os_memcpy(cmdbuf, history_curr->str, cmdbuf_len);
+ edit_redraw();
+}
+
+
+static void history_prev(void)
+{
+ if (history_curr == NULL)
+ return;
+
+ if (history_curr ==
+ dl_list_first(&history_list, struct edit_history, list)) {
+ if (!currbuf_valid) {
+ cmdbuf[cmdbuf_len] = '\0';
+ os_memcpy(currbuf, cmdbuf, cmdbuf_len + 1);
+ currbuf_valid = 1;
+ history_use();
+ return;
+ }
+ }
+
+ if (history_curr ==
+ dl_list_last(&history_list, struct edit_history, list))
+ return;
+
+ history_curr = dl_list_entry(history_curr->list.next,
+ struct edit_history, list);
+ history_use();
+}
+
+
+static void history_next(void)
+{
+ if (history_curr == NULL ||
+ history_curr ==
+ dl_list_first(&history_list, struct edit_history, list)) {
+ if (currbuf_valid) {
+ currbuf_valid = 0;
+ edit_clear_line();
+ cmdbuf_len = cmdbuf_pos = os_strlen(currbuf);
+ os_memcpy(cmdbuf, currbuf, cmdbuf_len);
+ edit_redraw();
+ }
+ return;
+ }
+
+ history_curr = dl_list_entry(history_curr->list.prev,
+ struct edit_history, list);
+ history_use();
+}
+
+
+static void history_read(const char *fname)
+{
+ FILE *f;
+ char buf[CMD_BUF_LEN], *pos;
+
+ f = fopen(fname, "r");
+ if (f == NULL)
+ return;
+
+ while (fgets(buf, CMD_BUF_LEN, f)) {
+ for (pos = buf; *pos; pos++) {
+ if (*pos == '\r' || *pos == '\n') {
+ *pos = '\0';
+ break;
+ }
+ }
+ history_add(buf);
+ }
+
+ fclose(f);
+}
+
+
+static void history_write(const char *fname,
+ int (*filter_cb)(void *ctx, const char *cmd))
+{
+ FILE *f;
+ struct edit_history *h;
+
+ f = fopen(fname, "w");
+ if (f == NULL)
+ return;
+
+ dl_list_for_each_reverse(h, &history_list, struct edit_history, list) {
+ if (filter_cb && filter_cb(edit_cb_ctx, h->str))
+ continue;
+ fprintf(f, "%s\n", h->str);
+ }
+
+ fclose(f);
+}
+
+
+static void history_debug_dump(void)
+{
+ struct edit_history *h;
+ edit_clear_line();
+ printf("\r");
+ dl_list_for_each_reverse(h, &history_list, struct edit_history, list)
+ printf("%s%s\n", h == history_curr ? "[C]" : "", h->str);
+ if (currbuf_valid)
+ printf("{%s}\n", currbuf);
+ edit_redraw();
+}
+
+
+static void insert_char(int c)
+{
+ if (cmdbuf_len >= (int) sizeof(cmdbuf) - 1)
+ return;
+ if (cmdbuf_len == cmdbuf_pos) {
+ cmdbuf[cmdbuf_pos++] = c;
+ cmdbuf_len++;
+ putchar(c);
+ fflush(stdout);
+ } else {
+ os_memmove(cmdbuf + cmdbuf_pos + 1, cmdbuf + cmdbuf_pos,
+ cmdbuf_len - cmdbuf_pos);
+ cmdbuf[cmdbuf_pos++] = c;
+ cmdbuf_len++;
+ edit_redraw();
+ }
+}
+
+
+static void process_cmd(void)
+{
+
+ if (cmdbuf_len == 0) {
+ printf("\n> ");
+ fflush(stdout);
+ return;
+ }
+ printf("\n");
+ cmdbuf[cmdbuf_len] = '\0';
+ history_add(cmdbuf);
+ cmdbuf_pos = 0;
+ cmdbuf_len = 0;
+ edit_cmd_cb(edit_cb_ctx, cmdbuf);
+ printf("> ");
+ fflush(stdout);
+}
+
+
+static void free_completions(char **c)
+{
+ int i;
+ if (c == NULL)
+ return;
+ for (i = 0; c[i]; i++)
+ os_free(c[i]);
+ os_free(c);
+}
+
+
+static int filter_strings(char **c, char *str, size_t len)
+{
+ int i, j;
+
+ for (i = 0, j = 0; c[j]; j++) {
+ if (os_strncasecmp(c[j], str, len) == 0) {
+ if (i != j) {
+ c[i] = c[j];
+ c[j] = NULL;
+ }
+ i++;
+ } else {
+ os_free(c[j]);
+ c[j] = NULL;
+ }
+ }
+ c[i] = NULL;
+ return i;
+}
+
+
+static int common_len(const char *a, const char *b)
+{
+ int len = 0;
+ while (a[len] && a[len] == b[len])
+ len++;
+ return len;
+}
+
+
+static int max_common_length(char **c)
+{
+ int len, i;
+
+ len = os_strlen(c[0]);
+ for (i = 1; c[i]; i++) {
+ int same = common_len(c[0], c[i]);
+ if (same < len)
+ len = same;
+ }
+
+ return len;
+}
+
+
+static int cmp_str(const void *a, const void *b)
+{
+ return os_strcmp(* (const char **) a, * (const char **) b);
+}
+
+static void complete(int list)
+{
+ char **c;
+ int i, len, count;
+ int start, end;
+ int room, plen, add_space;
+
+ if (edit_completion_cb == NULL)
+ return;
+
+ cmdbuf[cmdbuf_len] = '\0';
+ c = edit_completion_cb(edit_cb_ctx, cmdbuf, cmdbuf_pos);
+ if (c == NULL)
+ return;
+
+ end = cmdbuf_pos;
+ start = end;
+ while (start > 0 && cmdbuf[start - 1] != ' ')
+ start--;
+ plen = end - start;
+
+ count = filter_strings(c, &cmdbuf[start], plen);
+ if (count == 0) {
+ free_completions(c);
+ return;
+ }
+
+ len = max_common_length(c);
+ if (len <= plen && count > 1) {
+ if (list) {
+ qsort(c, count, sizeof(char *), cmp_str);
+ edit_clear_line();
+ printf("\r");
+ for (i = 0; c[i]; i++)
+ printf("%s%s", i > 0 ? " " : "", c[i]);
+ printf("\n");
+ edit_redraw();
+ }
+ free_completions(c);
+ return;
+ }
+ len -= plen;
+
+ room = sizeof(cmdbuf) - 1 - cmdbuf_len;
+ if (room < len)
+ len = room;
+ add_space = count == 1 && len < room;
+
+ os_memmove(cmdbuf + cmdbuf_pos + len + add_space, cmdbuf + cmdbuf_pos,
+ cmdbuf_len - cmdbuf_pos);
+ os_memcpy(&cmdbuf[cmdbuf_pos - plen], c[0], plen + len);
+ if (add_space)
+ cmdbuf[cmdbuf_pos + len] = ' ';
+
+ cmdbuf_pos += len + add_space;
+ cmdbuf_len += len + add_space;
+
+ edit_redraw();
+
+ free_completions(c);
+}
+
+
+enum edit_key_code {
+ EDIT_KEY_NONE = 256,
+ EDIT_KEY_TAB,
+ EDIT_KEY_UP,
+ EDIT_KEY_DOWN,
+ EDIT_KEY_RIGHT,
+ EDIT_KEY_LEFT,
+ EDIT_KEY_ENTER,
+ EDIT_KEY_BACKSPACE,
+ EDIT_KEY_INSERT,
+ EDIT_KEY_DELETE,
+ EDIT_KEY_HOME,
+ EDIT_KEY_END,
+ EDIT_KEY_PAGE_UP,
+ EDIT_KEY_PAGE_DOWN,
+ EDIT_KEY_F1,
+ EDIT_KEY_F2,
+ EDIT_KEY_F3,
+ EDIT_KEY_F4,
+ EDIT_KEY_F5,
+ EDIT_KEY_F6,
+ EDIT_KEY_F7,
+ EDIT_KEY_F8,
+ EDIT_KEY_F9,
+ EDIT_KEY_F10,
+ EDIT_KEY_F11,
+ EDIT_KEY_F12,
+ EDIT_KEY_CTRL_UP,
+ EDIT_KEY_CTRL_DOWN,
+ EDIT_KEY_CTRL_RIGHT,
+ EDIT_KEY_CTRL_LEFT,
+ EDIT_KEY_CTRL_A,
+ EDIT_KEY_CTRL_B,
+ EDIT_KEY_CTRL_D,
+ EDIT_KEY_CTRL_E,
+ EDIT_KEY_CTRL_F,
+ EDIT_KEY_CTRL_G,
+ EDIT_KEY_CTRL_H,
+ EDIT_KEY_CTRL_J,
+ EDIT_KEY_CTRL_K,
+ EDIT_KEY_CTRL_L,
+ EDIT_KEY_CTRL_N,
+ EDIT_KEY_CTRL_O,
+ EDIT_KEY_CTRL_P,
+ EDIT_KEY_CTRL_R,
+ EDIT_KEY_CTRL_T,
+ EDIT_KEY_CTRL_U,
+ EDIT_KEY_CTRL_V,
+ EDIT_KEY_CTRL_W,
+ EDIT_KEY_ALT_UP,
+ EDIT_KEY_ALT_DOWN,
+ EDIT_KEY_ALT_RIGHT,
+ EDIT_KEY_ALT_LEFT,
+ EDIT_KEY_SHIFT_UP,
+ EDIT_KEY_SHIFT_DOWN,
+ EDIT_KEY_SHIFT_RIGHT,
+ EDIT_KEY_SHIFT_LEFT,
+ EDIT_KEY_ALT_SHIFT_UP,
+ EDIT_KEY_ALT_SHIFT_DOWN,
+ EDIT_KEY_ALT_SHIFT_RIGHT,
+ EDIT_KEY_ALT_SHIFT_LEFT,
+ EDIT_KEY_EOF
+};
+
+static void show_esc_buf(const char *esc_buf, char c, int i)
+{
+ edit_clear_line();
+ printf("\rESC buffer '%s' c='%c' [%d]\n", esc_buf, c, i);
+ edit_redraw();
+}
+
+
+static enum edit_key_code esc_seq_to_key1_no(char last)
+{
+ switch (last) {
+ case 'A':
+ return EDIT_KEY_UP;
+ case 'B':
+ return EDIT_KEY_DOWN;
+ case 'C':
+ return EDIT_KEY_RIGHT;
+ case 'D':
+ return EDIT_KEY_LEFT;
+ default:
+ return EDIT_KEY_NONE;
+ }
+}
+
+
+static enum edit_key_code esc_seq_to_key1_shift(char last)
+{
+ switch (last) {
+ case 'A':
+ return EDIT_KEY_SHIFT_UP;
+ case 'B':
+ return EDIT_KEY_SHIFT_DOWN;
+ case 'C':
+ return EDIT_KEY_SHIFT_RIGHT;
+ case 'D':
+ return EDIT_KEY_SHIFT_LEFT;
+ default:
+ return EDIT_KEY_NONE;
+ }
+}
+
+
+static enum edit_key_code esc_seq_to_key1_alt(char last)
+{
+ switch (last) {
+ case 'A':
+ return EDIT_KEY_ALT_UP;
+ case 'B':
+ return EDIT_KEY_ALT_DOWN;
+ case 'C':
+ return EDIT_KEY_ALT_RIGHT;
+ case 'D':
+ return EDIT_KEY_ALT_LEFT;
+ default:
+ return EDIT_KEY_NONE;
+ }
+}
+
+
+static enum edit_key_code esc_seq_to_key1_alt_shift(char last)
+{
+ switch (last) {
+ case 'A':
+ return EDIT_KEY_ALT_SHIFT_UP;
+ case 'B':
+ return EDIT_KEY_ALT_SHIFT_DOWN;
+ case 'C':
+ return EDIT_KEY_ALT_SHIFT_RIGHT;
+ case 'D':
+ return EDIT_KEY_ALT_SHIFT_LEFT;
+ default:
+ return EDIT_KEY_NONE;
+ }
+}
+
+
+static enum edit_key_code esc_seq_to_key1_ctrl(char last)
+{
+ switch (last) {
+ case 'A':
+ return EDIT_KEY_CTRL_UP;
+ case 'B':
+ return EDIT_KEY_CTRL_DOWN;
+ case 'C':
+ return EDIT_KEY_CTRL_RIGHT;
+ case 'D':
+ return EDIT_KEY_CTRL_LEFT;
+ default:
+ return EDIT_KEY_NONE;
+ }
+}
+
+
+static enum edit_key_code esc_seq_to_key1(int param1, int param2, char last)
+{
+ /* ESC-[<param1>;<param2><last> */
+
+ if (param1 < 0 && param2 < 0)
+ return esc_seq_to_key1_no(last);
+
+ if (param1 == 1 && param2 == 2)
+ return esc_seq_to_key1_shift(last);
+
+ if (param1 == 1 && param2 == 3)
+ return esc_seq_to_key1_alt(last);
+
+ if (param1 == 1 && param2 == 4)
+ return esc_seq_to_key1_alt_shift(last);
+
+ if (param1 == 1 && param2 == 5)
+ return esc_seq_to_key1_ctrl(last);
+
+ if (param2 < 0) {
+ if (last != '~')
+ return EDIT_KEY_NONE;
+ switch (param1) {
+ case 2:
+ return EDIT_KEY_INSERT;
+ case 3:
+ return EDIT_KEY_DELETE;
+ case 5:
+ return EDIT_KEY_PAGE_UP;
+ case 6:
+ return EDIT_KEY_PAGE_DOWN;
+ case 15:
+ return EDIT_KEY_F5;
+ case 17:
+ return EDIT_KEY_F6;
+ case 18:
+ return EDIT_KEY_F7;
+ case 19:
+ return EDIT_KEY_F8;
+ case 20:
+ return EDIT_KEY_F9;
+ case 21:
+ return EDIT_KEY_F10;
+ case 23:
+ return EDIT_KEY_F11;
+ case 24:
+ return EDIT_KEY_F12;
+ }
+ }
+
+ return EDIT_KEY_NONE;
+}
+
+
+static enum edit_key_code esc_seq_to_key2(int param1, int param2, char last)
+{
+ /* ESC-O<param1>;<param2><last> */
+
+ if (param1 >= 0 || param2 >= 0)
+ return EDIT_KEY_NONE;
+
+ switch (last) {
+ case 'F':
+ return EDIT_KEY_END;
+ case 'H':
+ return EDIT_KEY_HOME;
+ case 'P':
+ return EDIT_KEY_F1;
+ case 'Q':
+ return EDIT_KEY_F2;
+ case 'R':
+ return EDIT_KEY_F3;
+ case 'S':
+ return EDIT_KEY_F4;
+ default:
+ return EDIT_KEY_NONE;
+ }
+}
+
+
+static enum edit_key_code esc_seq_to_key(char *seq)
+{
+ char last, *pos;
+ int param1 = -1, param2 = -1;
+ enum edit_key_code ret = EDIT_KEY_NONE;
+
+ last = '\0';
+ for (pos = seq; *pos; pos++)
+ last = *pos;
+
+ if (seq[1] >= '0' && seq[1] <= '9') {
+ param1 = atoi(&seq[1]);
+ pos = os_strchr(seq, ';');
+ if (pos)
+ param2 = atoi(pos + 1);
+ }
+
+ if (seq[0] == '[')
+ ret = esc_seq_to_key1(param1, param2, last);
+ else if (seq[0] == 'O')
+ ret = esc_seq_to_key2(param1, param2, last);
+
+ if (ret != EDIT_KEY_NONE)
+ return ret;
+
+ edit_clear_line();
+ printf("\rUnknown escape sequence '%s'\n", seq);
+ edit_redraw();
+ return EDIT_KEY_NONE;
+}
+
+
+static enum edit_key_code edit_read_key(int sock)
+{
+ int c;
+ unsigned char buf[1];
+ int res;
+ static int esc = -1;
+ static char esc_buf[7];
+
+ res = read(sock, buf, 1);
+ if (res < 0)
+ perror("read");
+ if (res <= 0)
+ return EDIT_KEY_EOF;
+
+ c = buf[0];
+
+ if (esc >= 0) {
+ if (c == 27 /* ESC */) {
+ esc = 0;
+ return EDIT_KEY_NONE;
+ }
+
+ if (esc == 6) {
+ show_esc_buf(esc_buf, c, 0);
+ esc = -1;
+ } else {
+ esc_buf[esc++] = c;
+ esc_buf[esc] = '\0';
+ }
+ }
+
+ if (esc == 1) {
+ if (esc_buf[0] != '[' && esc_buf[0] != 'O') {
+ show_esc_buf(esc_buf, c, 1);
+ esc = -1;
+ return EDIT_KEY_NONE;
+ } else
+ return EDIT_KEY_NONE; /* Escape sequence continues */
+ }
+
+ if (esc > 1) {
+ if ((c >= '0' && c <= '9') || c == ';')
+ return EDIT_KEY_NONE; /* Escape sequence continues */
+
+ if (c == '~' || (c >= 'A' && c <= 'Z')) {
+ esc = -1;
+ return esc_seq_to_key(esc_buf);
+ }
+
+ show_esc_buf(esc_buf, c, 2);
+ esc = -1;
+ return EDIT_KEY_NONE;
+ }
+
+ switch (c) {
+ case 1:
+ return EDIT_KEY_CTRL_A;
+ case 2:
+ return EDIT_KEY_CTRL_B;
+ case 4:
+ return EDIT_KEY_CTRL_D;
+ case 5:
+ return EDIT_KEY_CTRL_E;
+ case 6:
+ return EDIT_KEY_CTRL_F;
+ case 7:
+ return EDIT_KEY_CTRL_G;
+ case 8:
+ return EDIT_KEY_CTRL_H;
+ case 9:
+ return EDIT_KEY_TAB;
+ case 10:
+ return EDIT_KEY_CTRL_J;
+ case 13: /* CR */
+ return EDIT_KEY_ENTER;
+ case 11:
+ return EDIT_KEY_CTRL_K;
+ case 12:
+ return EDIT_KEY_CTRL_L;
+ case 14:
+ return EDIT_KEY_CTRL_N;
+ case 15:
+ return EDIT_KEY_CTRL_O;
+ case 16:
+ return EDIT_KEY_CTRL_P;
+ case 18:
+ return EDIT_KEY_CTRL_R;
+ case 20:
+ return EDIT_KEY_CTRL_T;
+ case 21:
+ return EDIT_KEY_CTRL_U;
+ case 22:
+ return EDIT_KEY_CTRL_V;
+ case 23:
+ return EDIT_KEY_CTRL_W;
+ case 27: /* ESC */
+ esc = 0;
+ return EDIT_KEY_NONE;
+ case 127:
+ return EDIT_KEY_BACKSPACE;
+ default:
+ return c;
+ }
+}
+
+
+static char search_buf[21];
+static int search_skip;
+
+static char * search_find(void)
+{
+ struct edit_history *h;
+ size_t len = os_strlen(search_buf);
+ int skip = search_skip;
+
+ if (len == 0)
+ return NULL;
+
+ dl_list_for_each(h, &history_list, struct edit_history, list) {
+ if (os_strstr(h->str, search_buf)) {
+ if (skip == 0)
+ return h->str;
+ skip--;
+ }
+ }
+
+ search_skip = 0;
+ return NULL;
+}
+
+
+static void search_redraw(void)
+{
+ char *match = search_find();
+ printf("\rsearch '%s': %s" CLEAR_END_LINE,
+ search_buf, match ? match : "");
+ printf("\rsearch '%s", search_buf);
+ fflush(stdout);
+}
+
+
+static void search_start(void)
+{
+ edit_clear_line();
+ search_buf[0] = '\0';
+ search_skip = 0;
+ search_redraw();
+}
+
+
+static void search_clear(void)
+{
+ search_redraw();
+ printf("\r" CLEAR_END_LINE);
+}
+
+
+static void search_stop(void)
+{
+ char *match = search_find();
+ search_buf[0] = '\0';
+ search_clear();
+ if (match) {
+ os_strlcpy(cmdbuf, match, CMD_BUF_LEN);
+ cmdbuf_len = os_strlen(cmdbuf);
+ cmdbuf_pos = cmdbuf_len;
+ }
+ edit_redraw();
+}
+
+
+static void search_cancel(void)
+{
+ search_buf[0] = '\0';
+ search_clear();
+ edit_redraw();
+}
+
+
+static void search_backspace(void)
+{
+ size_t len;
+ len = os_strlen(search_buf);
+ if (len == 0)
+ return;
+ search_buf[len - 1] = '\0';
+ search_skip = 0;
+ search_redraw();
+}
+
+
+static void search_next(void)
+{
+ search_skip++;
+ search_find();
+ search_redraw();
+}
+
+
+static void search_char(char c)
+{
+ size_t len;
+ len = os_strlen(search_buf);
+ if (len == sizeof(search_buf) - 1)
+ return;
+ search_buf[len] = c;
+ search_buf[len + 1] = '\0';
+ search_skip = 0;
+ search_redraw();
+}
+
+
+static enum edit_key_code search_key(enum edit_key_code c)
+{
+ switch (c) {
+ case EDIT_KEY_ENTER:
+ case EDIT_KEY_CTRL_J:
+ case EDIT_KEY_LEFT:
+ case EDIT_KEY_RIGHT:
+ case EDIT_KEY_HOME:
+ case EDIT_KEY_END:
+ case EDIT_KEY_CTRL_A:
+ case EDIT_KEY_CTRL_E:
+ search_stop();
+ return c;
+ case EDIT_KEY_DOWN:
+ case EDIT_KEY_UP:
+ search_cancel();
+ return EDIT_KEY_EOF;
+ case EDIT_KEY_CTRL_H:
+ case EDIT_KEY_BACKSPACE:
+ search_backspace();
+ break;
+ case EDIT_KEY_CTRL_R:
+ search_next();
+ break;
+ default:
+ if (c >= 32 && c <= 255)
+ search_char(c);
+ break;
+ }
+
+ return EDIT_KEY_NONE;
+}
+
+
+static void edit_read_char(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ static int last_tab = 0;
+ static int search = 0;
+ enum edit_key_code c;
+
+ c = edit_read_key(sock);
+
+ if (search) {
+ c = search_key(c);
+ if (c == EDIT_KEY_NONE)
+ return;
+ search = 0;
+ if (c == EDIT_KEY_EOF)
+ return;
+ }
+
+ if (c != EDIT_KEY_TAB && c != EDIT_KEY_NONE)
+ last_tab = 0;
+
+ switch (c) {
+ case EDIT_KEY_NONE:
+ break;
+ case EDIT_KEY_EOF:
+ edit_eof_cb(edit_cb_ctx);
+ break;
+ case EDIT_KEY_TAB:
+ complete(last_tab);
+ last_tab = 1;
+ break;
+ case EDIT_KEY_UP:
+ case EDIT_KEY_CTRL_P:
+ history_prev();
+ break;
+ case EDIT_KEY_DOWN:
+ case EDIT_KEY_CTRL_N:
+ history_next();
+ break;
+ case EDIT_KEY_RIGHT:
+ case EDIT_KEY_CTRL_F:
+ move_right();
+ break;
+ case EDIT_KEY_LEFT:
+ case EDIT_KEY_CTRL_B:
+ move_left();
+ break;
+ case EDIT_KEY_CTRL_RIGHT:
+ move_word_right();
+ break;
+ case EDIT_KEY_CTRL_LEFT:
+ move_word_left();
+ break;
+ case EDIT_KEY_DELETE:
+ delete_current();
+ break;
+ case EDIT_KEY_END:
+ move_end();
+ break;
+ case EDIT_KEY_HOME:
+ case EDIT_KEY_CTRL_A:
+ move_start();
+ break;
+ case EDIT_KEY_F2:
+ history_debug_dump();
+ break;
+ case EDIT_KEY_CTRL_D:
+ if (cmdbuf_len > 0) {
+ delete_current();
+ return;
+ }
+ printf("\n");
+ edit_eof_cb(edit_cb_ctx);
+ break;
+ case EDIT_KEY_CTRL_E:
+ move_end();
+ break;
+ case EDIT_KEY_CTRL_H:
+ case EDIT_KEY_BACKSPACE:
+ delete_left();
+ break;
+ case EDIT_KEY_ENTER:
+ case EDIT_KEY_CTRL_J:
+ process_cmd();
+ break;
+ case EDIT_KEY_CTRL_K:
+ clear_right();
+ break;
+ case EDIT_KEY_CTRL_L:
+ edit_clear_line();
+ edit_redraw();
+ break;
+ case EDIT_KEY_CTRL_R:
+ search = 1;
+ search_start();
+ break;
+ case EDIT_KEY_CTRL_U:
+ clear_left();
+ break;
+ case EDIT_KEY_CTRL_W:
+ delete_word();
+ break;
+ default:
+ if (c >= 32 && c <= 255)
+ insert_char(c);
+ break;
+ }
+}
+
+
+int edit_init(void (*cmd_cb)(void *ctx, char *cmd),
+ void (*eof_cb)(void *ctx),
+ char ** (*completion_cb)(void *ctx, const char *cmd, int pos),
+ void *ctx, const char *history_file)
+{
+ currbuf[0] = '\0';
+ dl_list_init(&history_list);
+ history_curr = NULL;
+ if (history_file)
+ history_read(history_file);
+
+ edit_cb_ctx = ctx;
+ edit_cmd_cb = cmd_cb;
+ edit_eof_cb = eof_cb;
+ edit_completion_cb = completion_cb;
+
+ tcgetattr(STDIN_FILENO, &prevt);
+ newt = prevt;
+ newt.c_lflag &= ~(ICANON | ECHO);
+ tcsetattr(STDIN_FILENO, TCSANOW, &newt);
+
+ eloop_register_read_sock(STDIN_FILENO, edit_read_char, NULL, NULL);
+
+ printf("> ");
+ fflush(stdout);
+
+ return 0;
+}
+
+
+void edit_deinit(const char *history_file,
+ int (*filter_cb)(void *ctx, const char *cmd))
+{
+ struct edit_history *h;
+ if (history_file)
+ history_write(history_file, filter_cb);
+ while ((h = dl_list_first(&history_list, struct edit_history, list))) {
+ dl_list_del(&h->list);
+ os_free(h);
+ }
+ edit_clear_line();
+ putchar('\r');
+ fflush(stdout);
+ eloop_unregister_read_sock(STDIN_FILENO);
+ tcsetattr(STDIN_FILENO, TCSANOW, &prevt);
+}
+
+
+void edit_redraw(void)
+{
+ char tmp;
+ cmdbuf[cmdbuf_len] = '\0';
+ printf("\r> %s", cmdbuf);
+ if (cmdbuf_pos != cmdbuf_len) {
+ tmp = cmdbuf[cmdbuf_pos];
+ cmdbuf[cmdbuf_pos] = '\0';
+ printf("\r> %s", cmdbuf);
+ cmdbuf[cmdbuf_pos] = tmp;
+ }
+ fflush(stdout);
+}
--- /dev/null
+/*
+ * Command line editing and history
+ * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef EDIT_H
+#define EDIT_H
+
+int edit_init(void (*cmd_cb)(void *ctx, char *cmd),
+ void (*eof_cb)(void *ctx),
+ char ** (*completion_cb)(void *ctx, const char *cmd, int pos),
+ void *ctx, const char *history_file);
+void edit_deinit(const char *history_file,
+ int (*filter_cb)(void *ctx, const char *cmd));
+void edit_clear_line(void);
+void edit_redraw(void);
+
+#endif /* EDIT_H */
--- /dev/null
+/*
+ * Command line editing and history wrapper for readline
+ * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#include <readline/readline.h>
+#include <readline/history.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "edit.h"
+
+
+static void *edit_cb_ctx;
+static void (*edit_cmd_cb)(void *ctx, char *cmd);
+static void (*edit_eof_cb)(void *ctx);
+static char ** (*edit_completion_cb)(void *ctx, const char *cmd, int pos) =
+ NULL;
+
+static char **pending_completions = NULL;
+
+
+static void readline_free_completions(void)
+{
+ int i;
+ if (pending_completions == NULL)
+ return;
+ for (i = 0; pending_completions[i]; i++)
+ os_free(pending_completions[i]);
+ os_free(pending_completions);
+ pending_completions = NULL;
+}
+
+
+static char * readline_completion_func(const char *text, int state)
+{
+ static int pos = 0;
+ static size_t len = 0;
+
+ if (pending_completions == NULL) {
+ rl_attempted_completion_over = 1;
+ return NULL;
+ }
+
+ if (state == 0) {
+ pos = 0;
+ len = os_strlen(text);
+ }
+ for (; pending_completions[pos]; pos++) {
+ if (strncmp(pending_completions[pos], text, len) == 0)
+ return strdup(pending_completions[pos++]);
+ }
+
+ rl_attempted_completion_over = 1;
+ return NULL;
+}
+
+
+static char ** readline_completion(const char *text, int start, int end)
+{
+ readline_free_completions();
+ if (edit_completion_cb)
+ pending_completions = edit_completion_cb(edit_cb_ctx,
+ rl_line_buffer, end);
+ return rl_completion_matches(text, readline_completion_func);
+}
+
+
+static void edit_read_char(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ rl_callback_read_char();
+}
+
+
+static void trunc_nl(char *str)
+{
+ char *pos = str;
+ while (*pos != '\0') {
+ if (*pos == '\n') {
+ *pos = '\0';
+ break;
+ }
+ pos++;
+ }
+}
+
+
+static void readline_cmd_handler(char *cmd)
+{
+ if (cmd && *cmd) {
+ HIST_ENTRY *h;
+ while (next_history())
+ ;
+ h = previous_history();
+ if (h == NULL || os_strcmp(cmd, h->line) != 0)
+ add_history(cmd);
+ next_history();
+ }
+ if (cmd == NULL) {
+ edit_eof_cb(edit_cb_ctx);
+ return;
+ }
+ trunc_nl(cmd);
+ edit_cmd_cb(edit_cb_ctx, cmd);
+}
+
+
+int edit_init(void (*cmd_cb)(void *ctx, char *cmd),
+ void (*eof_cb)(void *ctx),
+ char ** (*completion_cb)(void *ctx, const char *cmd, int pos),
+ void *ctx, const char *history_file)
+{
+ edit_cb_ctx = ctx;
+ edit_cmd_cb = cmd_cb;
+ edit_eof_cb = eof_cb;
+ edit_completion_cb = completion_cb;
+
+ rl_attempted_completion_function = readline_completion;
+ if (history_file) {
+ read_history(history_file);
+ stifle_history(100);
+ }
+
+ eloop_register_read_sock(STDIN_FILENO, edit_read_char, NULL, NULL);
+
+ rl_callback_handler_install("> ", readline_cmd_handler);
+
+ return 0;
+}
+
+
+void edit_deinit(const char *history_file,
+ int (*filter_cb)(void *ctx, const char *cmd))
+{
+ rl_callback_handler_remove();
+ readline_free_completions();
+
+ eloop_unregister_read_sock(STDIN_FILENO);
+
+ if (history_file) {
+ /* Save command history, excluding lines that may contain
+ * passwords. */
+ HIST_ENTRY *h;
+ history_set_pos(0);
+ while ((h = current_history())) {
+ char *p = h->line;
+ while (*p == ' ' || *p == '\t')
+ p++;
+ if (filter_cb && filter_cb(edit_cb_ctx, p)) {
+ h = remove_history(where_history());
+ if (h) {
+ os_free(h->line);
+ free(h->data);
+ os_free(h);
+ } else
+ next_history();
+ } else
+ next_history();
+ }
+ write_history(history_file);
+ }
+}
+
+
+void edit_clear_line(void)
+{
+}
+
+
+void edit_redraw(void)
+{
+ rl_on_new_line();
+ rl_redisplay();
+}
--- /dev/null
+/*
+ * Minimal command line editing
+ * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eloop.h"
+#include "edit.h"
+
+
+#define CMD_BUF_LEN 256
+static char cmdbuf[CMD_BUF_LEN];
+static int cmdbuf_pos = 0;
+
+static void *edit_cb_ctx;
+static void (*edit_cmd_cb)(void *ctx, char *cmd);
+static void (*edit_eof_cb)(void *ctx);
+
+
+static void edit_read_char(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ int c;
+ unsigned char buf[1];
+ int res;
+
+ res = read(sock, buf, 1);
+ if (res < 0)
+ perror("read");
+ if (res <= 0) {
+ edit_eof_cb(edit_cb_ctx);
+ return;
+ }
+ c = buf[0];
+
+ if (c == '\r' || c == '\n') {
+ cmdbuf[cmdbuf_pos] = '\0';
+ cmdbuf_pos = 0;
+ edit_cmd_cb(edit_cb_ctx, cmdbuf);
+ printf("> ");
+ fflush(stdout);
+ return;
+ }
+
+ if (c >= 32 && c <= 255) {
+ if (cmdbuf_pos < (int) sizeof(cmdbuf) - 1) {
+ cmdbuf[cmdbuf_pos++] = c;
+ }
+ }
+}
+
+
+int edit_init(void (*cmd_cb)(void *ctx, char *cmd),
+ void (*eof_cb)(void *ctx),
+ char ** (*completion_cb)(void *ctx, const char *cmd, int pos),
+ void *ctx, const char *history_file)
+{
+ edit_cb_ctx = ctx;
+ edit_cmd_cb = cmd_cb;
+ edit_eof_cb = eof_cb;
+ eloop_register_read_sock(STDIN_FILENO, edit_read_char, NULL, NULL);
+
+ printf("> ");
+ fflush(stdout);
+
+ return 0;
+}
+
+
+void edit_deinit(const char *history_file,
+ int (*filter_cb)(void *ctx, const char *cmd))
+{
+ eloop_unregister_read_sock(STDIN_FILENO);
+}
+
+
+void edit_clear_line(void)
+{
+}
+
+
+void edit_redraw(void)
+{
+ cmdbuf[cmdbuf_pos] = '\0';
+ printf("\r> %s", cmdbuf);
+}
void *eloop_data, void *user_data)
{
struct eloop_timeout *timeout, *tmp;
+ os_time_t now_sec;
timeout = os_zalloc(sizeof(*timeout));
if (timeout == NULL)
os_free(timeout);
return -1;
}
+ now_sec = timeout->time.sec;
timeout->time.sec += secs;
+ if (timeout->time.sec < now_sec) {
+ /*
+ * Integer overflow - assume long enough timeout to be assumed
+ * to be infinite, i.e., the timeout would never happen.
+ */
+ wpa_printf(MSG_DEBUG, "ELOOP: Too long timeout (secs=%u) to "
+ "ever happen - ignore it", secs);
+ os_free(timeout);
+ return 0;
+ }
timeout->time.usec += usecs;
while (timeout->time.usec >= 1000000) {
timeout->time.sec++;
* Returns: 0 on success, -1 on failure
*
* Register an event handler for the given event. This function is used to
- * register eloop implementation specific events which are mainly targetted for
+ * register eloop implementation specific events which are mainly targeted for
* operating system specific code (driver interface and l2_packet) since the
* portable code will not be able to use such an OS-specific call. The handler
* function will be called whenever the event is triggered. The handler
void *eloop_data, void *user_data)
{
struct eloop_timeout *timeout, *tmp, *prev;
+ os_time_t now_sec;
timeout = os_malloc(sizeof(*timeout));
if (timeout == NULL)
return -1;
os_get_time(&timeout->time);
+ now_sec = timeout->time.sec;
timeout->time.sec += secs;
+ if (timeout->time.sec < now_sec) {
+ /*
+ * Integer overflow - assume long enough timeout to be assumed
+ * to be infinite, i.e., the timeout would never happen.
+ */
+ wpa_printf(MSG_DEBUG, "ELOOP: Too long timeout (secs=%u) to "
+ "ever happen - ignore it", secs);
+ os_free(timeout);
+ return 0;
+ }
timeout->time.usec += usecs;
while (timeout->time.usec >= 1000000) {
timeout->time.sec++;
#include <errno.h>
#endif /* _WIN32_WCE */
#include <ctype.h>
-#include <time.h>
#ifndef CONFIG_TI_COMPILER
#ifndef _MSC_VER
(dl_list_empty((list)) ? NULL : \
dl_list_entry((list)->next, type, member))
+#define dl_list_last(list, type, member) \
+ (dl_list_empty((list)) ? NULL : \
+ dl_list_entry((list)->prev, type, member))
+
#define dl_list_for_each(item, list, type, member) \
for (item = dl_list_entry((list)->next, type, member); \
&item->member != (list); \
&item->member != (list); \
item = n, n = dl_list_entry(n->member.next, type, member))
+#define dl_list_for_each_reverse(item, list, type, member) \
+ for (item = dl_list_entry((list)->prev, type, member); \
+ &item->member != (list); \
+ item = dl_list_entry(item->member.prev, type, member))
+
+#define DEFINE_DL_LIST(name) \
+ struct dl_list name = { &(name), &(name) }
+
#endif /* LIST_H */
int os_mktime(int year, int month, int day, int hour, int min, int sec,
os_time_t *t);
+struct os_tm {
+ int sec; /* 0..59 or 60 for leap seconds */
+ int min; /* 0..59 */
+ int hour; /* 0..23 */
+ int day; /* 1..31 */
+ int month; /* 1..12 */
+ int year; /* Four digit year */
+};
+
+int os_gmtime(os_time_t t, struct os_tm *tm);
/**
* os_daemonize - Run in the background (detach from the controlling terminal)
}
+int os_gmtime(os_time_t t, struct os_tm *tm)
+{
+ struct tm *tm2;
+ time_t t2 = t;
+
+ tm2 = gmtime(&t2);
+ if (tm2 == NULL)
+ return -1;
+ tm->sec = tm2->tm_sec;
+ tm->min = tm2->tm_min;
+ tm->hour = tm2->tm_hour;
+ tm->day = tm2->tm_mday;
+ tm->month = tm2->tm_mon + 1;
+ tm->year = tm2->tm_year + 1900;
+ return 0;
+}
+
+
int os_daemonize(const char *pid_file)
{
if (daemon(0, 0)) {
return -1;
}
+int os_gmtime(os_time_t t, struct os_tm *tm)
+{
+ return -1;
+}
+
int os_daemonize(const char *pid_file)
{
#include "includes.h"
+#include <time.h>
+
+#ifdef ANDROID
+#include <linux/capability.h>
+#include <linux/prctl.h>
+#include <private/android_filesystem_config.h>
+#endif /* ANDROID */
+
#include "os.h"
#ifdef WPA_TRACE
}
+int os_gmtime(os_time_t t, struct os_tm *tm)
+{
+ struct tm *tm2;
+ time_t t2 = t;
+
+ tm2 = gmtime(&t2);
+ if (tm2 == NULL)
+ return -1;
+ tm->sec = tm2->tm_sec;
+ tm->min = tm2->tm_min;
+ tm->hour = tm2->tm_hour;
+ tm->day = tm2->tm_mday;
+ tm->month = tm2->tm_mon + 1;
+ tm->year = tm2->tm_year + 1900;
+ return 0;
+}
+
+
#ifdef __APPLE__
#include <fcntl.h>
static int os_daemon(int nochdir, int noclose)
int os_daemonize(const char *pid_file)
{
-#ifdef __uClinux__
+#if defined(__uClinux__) || defined(__sun__)
return -1;
-#else /* __uClinux__ */
+#else /* defined(__uClinux__) || defined(__sun__) */
if (os_daemon(0, 0)) {
perror("daemon");
return -1;
}
return -0;
-#endif /* __uClinux__ */
+#endif /* defined(__uClinux__) || defined(__sun__) */
}
int os_program_init(void)
{
+#ifdef ANDROID
+ /*
+ * We ignore errors here since errors are normal if we
+ * are already running as non-root.
+ */
+ gid_t groups[] = { AID_INET, AID_WIFI, AID_KEYSTORE };
+ struct __user_cap_header_struct header;
+ struct __user_cap_data_struct cap;
+
+ setgroups(sizeof(groups)/sizeof(groups[0]), groups);
+
+ prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
+
+ setgid(AID_WIFI);
+ setuid(AID_WIFI);
+
+ header.version = _LINUX_CAPABILITY_VERSION;
+ header.pid = 0;
+ cap.effective = cap.permitted =
+ (1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW);
+ cap.inheritable = 0;
+ capset(&header, &cap);
+#endif /* ANDROID */
+
#ifdef WPA_TRACE
dl_list_init(&alloc_list);
#endif /* WPA_TRACE */
{
FILE *f;
char *buf;
+ long pos;
f = fopen(name, "rb");
if (f == NULL)
return NULL;
- fseek(f, 0, SEEK_END);
- *len = ftell(f);
- fseek(f, 0, SEEK_SET);
+ if (fseek(f, 0, SEEK_END) < 0 || (pos = ftell(f)) < 0) {
+ fclose(f);
+ return NULL;
+ }
+ *len = pos;
+ if (fseek(f, 0, SEEK_SET) < 0) {
+ fclose(f);
+ return NULL;
+ }
buf = os_malloc(*len);
if (buf == NULL) {
*/
#include "includes.h"
+#include <time.h>
#include <winsock2.h>
#include <wincrypt.h>
}
+int os_gmtime(os_time_t t, struct os_tm *tm)
+{
+ struct tm *tm2;
+ time_t t2 = t;
+
+ tm2 = gmtime(&t2);
+ if (tm2 == NULL)
+ return -1;
+ tm->sec = tm2->tm_sec;
+ tm->min = tm2->tm_min;
+ tm->hour = tm2->tm_hour;
+ tm->day = tm2->tm_mday;
+ tm->month = tm2->tm_mon + 1;
+ tm->year = tm2->tm_year + 1900;
+ return 0;
+}
+
+
int os_daemonize(const char *pid_file)
{
/* TODO */
wpa_hexdump(MSG_DEBUG, "SCARD: file length determination response",
buf, blen);
- if (blen < 2 || buf[0] != 0x6c) {
+ if (blen < 2 || (buf[0] != 0x6c && buf[0] != 0x67)) {
wpa_printf(MSG_DEBUG, "SCARD: unexpected response to file "
"length determination");
return -1;
+/*
+ * Radiotap parser
+ *
+ * Copyright 2007 Andy Green <andy@warmcat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
#ifndef __RADIOTAP_ITER_H
#define __RADIOTAP_ITER_H
#endif /* CONFIG_DEBUG_SYSLOG */
-#ifdef CONFIG_DEBUG_FILE
-static FILE *out_file = NULL;
-#endif /* CONFIG_DEBUG_FILE */
int wpa_debug_level = MSG_INFO;
int wpa_debug_show_keys = 0;
int wpa_debug_timestamp = 0;
+#ifdef CONFIG_ANDROID_LOG
+
+#include <android/log.h>
+
+#ifndef ANDROID_LOG_NAME
+#define ANDROID_LOG_NAME "wpa_supplicant"
+#endif /* ANDROID_LOG_NAME */
+
+void android_printf(int level, char *format, ...)
+{
+ if (level >= wpa_debug_level) {
+ va_list ap;
+ if (level == MSG_ERROR)
+ level = ANDROID_LOG_ERROR;
+ else if (level == MSG_WARNING)
+ level = ANDROID_LOG_WARN;
+ else if (level == MSG_INFO)
+ level = ANDROID_LOG_INFO;
+ else
+ level = ANDROID_LOG_DEBUG;
+ va_start(ap, format);
+ __android_log_vprint(level, ANDROID_LOG_NAME, format, ap);
+ va_end(ap);
+ }
+}
+
+#else /* CONFIG_ANDROID_LOG */
+
#ifndef CONFIG_NO_STDOUT_DEBUG
+#ifdef CONFIG_DEBUG_FILE
+static FILE *out_file = NULL;
+#endif /* CONFIG_DEBUG_FILE */
+
+
void wpa_debug_print_timestamp(void)
{
struct os_time tv;
#ifdef CONFIG_DEBUG_SYSLOG
+#ifndef LOG_HOSTAPD
+#define LOG_HOSTAPD LOG_DAEMON
+#endif /* LOG_HOSTAPD */
+
void wpa_debug_open_syslog(void)
{
- openlog("wpa_supplicant", LOG_PID | LOG_NDELAY, LOG_DAEMON);
+ openlog("wpa_supplicant", LOG_PID | LOG_NDELAY, LOG_HOSTAPD);
wpa_debug_syslog++;
}
size_t i;
if (level < wpa_debug_level)
return;
+#ifdef CONFIG_DEBUG_SYSLOG
+ if (wpa_debug_syslog) {
+ const char *display;
+ char *strbuf = NULL;
+
+ if (buf == NULL) {
+ display = " [NULL]";
+ } else if (len == 0) {
+ display = "";
+ } else if (show && len) {
+ strbuf = os_malloc(1 + 3 * len);
+ if (strbuf == NULL) {
+ wpa_printf(MSG_ERROR, "wpa_hexdump: Failed to "
+ "allocate message buffer");
+ return;
+ }
+
+ for (i = 0; i < len; i++)
+ os_snprintf(&strbuf[i * 3], 4, " %02x",
+ buf[i]);
+
+ display = strbuf;
+ } else {
+ display = " [REMOVED]";
+ }
+
+ syslog(syslog_priority(level), "%s - hexdump(len=%lu):%s",
+ title, len, display);
+ os_free(strbuf);
+ return;
+ }
+#endif /* CONFIG_DEBUG_SYSLOG */
wpa_debug_print_timestamp();
#ifdef CONFIG_DEBUG_FILE
if (out_file) {
}
+#ifdef CONFIG_DEBUG_FILE
+static char *last_path = NULL;
+#endif /* CONFIG_DEBUG_FILE */
+
+int wpa_debug_reopen_file(void)
+{
+#ifdef CONFIG_DEBUG_FILE
+ int rv;
+ if (last_path) {
+ char *tmp = os_strdup(last_path);
+ wpa_debug_close_file();
+ rv = wpa_debug_open_file(tmp);
+ os_free(tmp);
+ } else {
+ wpa_printf(MSG_ERROR, "Last-path was not set, cannot "
+ "re-open log file.");
+ rv = -1;
+ }
+ return rv;
+#else /* CONFIG_DEBUG_FILE */
+ return 0;
+#endif /* CONFIG_DEBUG_FILE */
+}
+
+
int wpa_debug_open_file(const char *path)
{
#ifdef CONFIG_DEBUG_FILE
if (!path)
return 0;
+
+ if (last_path == NULL || os_strcmp(last_path, path) != 0) {
+ /* Save our path to enable re-open */
+ os_free(last_path);
+ last_path = os_strdup(path);
+ }
+
out_file = fopen(path, "a");
if (out_file == NULL) {
wpa_printf(MSG_ERROR, "wpa_debug_open_file: Failed to open "
return;
fclose(out_file);
out_file = NULL;
+ os_free(last_path);
+ last_path = NULL;
#endif /* CONFIG_DEBUG_FILE */
}
#endif /* CONFIG_NO_STDOUT_DEBUG */
+#endif /* CONFIG_ANDROID_LOG */
#ifndef CONFIG_NO_WPA_MSG
static wpa_msg_cb_func wpa_msg_cb = NULL;
}
+static wpa_msg_get_ifname_func wpa_msg_ifname_cb = NULL;
+
+void wpa_msg_register_ifname_cb(wpa_msg_get_ifname_func func)
+{
+ wpa_msg_ifname_cb = func;
+}
+
+
void wpa_msg(void *ctx, int level, const char *fmt, ...)
{
va_list ap;
char *buf;
const int buflen = 2048;
int len;
+ char prefix[130];
buf = os_malloc(buflen);
if (buf == NULL) {
return;
}
va_start(ap, fmt);
+ prefix[0] = '\0';
+ if (wpa_msg_ifname_cb) {
+ const char *ifname = wpa_msg_ifname_cb(ctx);
+ if (ifname) {
+ int res = os_snprintf(prefix, sizeof(prefix), "%s: ",
+ ifname);
+ if (res < 0 || res >= (int) sizeof(prefix))
+ prefix[0] = '\0';
+ }
+ }
len = vsnprintf(buf, buflen, fmt, ap);
va_end(ap);
- wpa_printf(level, "%s", buf);
+ wpa_printf(level, "%s%s", prefix, buf);
if (wpa_msg_cb)
wpa_msg_cb(ctx, level, buf, len);
os_free(buf);
/* Debugging function - conditional printf and hex dump. Driver wrappers can
* use these for debugging purposes. */
-enum { MSG_MSGDUMP, MSG_DEBUG, MSG_INFO, MSG_WARNING, MSG_ERROR };
+enum {
+ MSG_EXCESSIVE, MSG_MSGDUMP, MSG_DEBUG, MSG_INFO, MSG_WARNING, MSG_ERROR
+};
+
+#ifdef CONFIG_ANDROID_LOG
+
+#define wpa_debug_print_timestamp() do {} while (0)
+#define wpa_hexdump(...) do {} while (0)
+#define wpa_hexdump_key(...) do {} while (0)
+#define wpa_hexdump_buf(l,t,b) do {} while (0)
+#define wpa_hexdump_buf_key(l,t,b) do {} while (0)
+#define wpa_hexdump_ascii(...) do {} while (0)
+#define wpa_hexdump_ascii_key(...) do {} while (0)
+#define wpa_debug_open_file(...) do {} while (0)
+#define wpa_debug_close_file() do {} while (0)
+#define wpa_dbg(...) do {} while (0)
+
+static inline int wpa_debug_reopen_file(void)
+{
+ return 0;
+}
+
+
+void android_printf(int level, char *format, ...)
+PRINTF_FORMAT(2, 3);
+
+#define wpa_printf android_printf
+
+#else /* CONFIG_ANDROID_LOG */
#ifdef CONFIG_NO_STDOUT_DEBUG
#define wpa_hexdump_ascii_key(l,t,b,le) do { } while (0)
#define wpa_debug_open_file(p) do { } while (0)
#define wpa_debug_close_file() do { } while (0)
+#define wpa_dbg(args...) do { } while (0)
+
+static inline int wpa_debug_reopen_file(void)
+{
+ return 0;
+}
#else /* CONFIG_NO_STDOUT_DEBUG */
int wpa_debug_open_file(const char *path);
+int wpa_debug_reopen_file(void);
void wpa_debug_close_file(void);
/**
static inline void wpa_hexdump_buf(int level, const char *title,
const struct wpabuf *buf)
{
- wpa_hexdump(level, title, wpabuf_head(buf), wpabuf_len(buf));
+ wpa_hexdump(level, title, buf ? wpabuf_head(buf) : NULL,
+ buf ? wpabuf_len(buf) : 0);
}
/**
static inline void wpa_hexdump_buf_key(int level, const char *title,
const struct wpabuf *buf)
{
- wpa_hexdump_key(level, title, wpabuf_head(buf), wpabuf_len(buf));
+ wpa_hexdump_key(level, title, buf ? wpabuf_head(buf) : 0,
+ buf ? wpabuf_len(buf) : 0);
}
/**
void wpa_hexdump_ascii_key(int level, const char *title, const u8 *buf,
size_t len);
+/*
+ * wpa_dbg() behaves like wpa_msg(), but it can be removed from build to reduce
+ * binary size. As such, it should be used with debugging messages that are not
+ * needed in the control interface while wpa_msg() has to be used for anything
+ * that needs to shown to control interface monitors.
+ */
+#define wpa_dbg(args...) wpa_msg(args)
+
#endif /* CONFIG_NO_STDOUT_DEBUG */
+#endif /* CONFIG_ANDROID_LOG */
+
#ifdef CONFIG_NO_WPA_MSG
#define wpa_msg(args...) do { } while (0)
#define wpa_msg_ctrl(args...) do { } while (0)
#define wpa_msg_register_cb(f) do { } while (0)
+#define wpa_msg_register_ifname_cb(f) do { } while (0)
#else /* CONFIG_NO_WPA_MSG */
/**
* wpa_msg - Conditional printf for default target and ctrl_iface monitors
* @func: Callback function (%NULL to unregister)
*/
void wpa_msg_register_cb(wpa_msg_cb_func func);
-#endif /* CONFIG_NO_WPA_MSG */
+typedef const char * (*wpa_msg_get_ifname_func)(void *ctx);
+void wpa_msg_register_ifname_cb(wpa_msg_get_ifname_func func);
+
+#endif /* CONFIG_NO_WPA_MSG */
#ifdef CONFIG_NO_HOSTAPD_LOGGER
#define hostapd_logger(args...) do { } while (0)
WPA_PUT_LE16(pos, data);
}
+static inline void wpabuf_put_le32(struct wpabuf *buf, u32 data)
+{
+ u8 *pos = wpabuf_put(buf, 4);
+ WPA_PUT_LE32(pos, data);
+}
+
static inline void wpabuf_put_be16(struct wpabuf *buf, u16 data)
{
u8 *pos = wpabuf_put(buf, 2);
#include "http_client.h"
-#define HTTP_CLIENT_TIMEOUT 30
+#define HTTP_CLIENT_TIMEOUT_SEC 30
struct http_client {
static void http_client_timeout(void *eloop_data, void *user_ctx)
{
struct http_client *c = eloop_data;
- wpa_printf(MSG_DEBUG, "HTTP: Timeout");
+ wpa_printf(MSG_DEBUG, "HTTP: Timeout (c=%p)", c);
c->cb(c->cb_ctx, c, HTTP_CLIENT_TIMEOUT);
}
{
struct http_client *c = cookie;
+ wpa_printf(MSG_DEBUG, "HTTP: httpread callback: handle=%p cookie=%p "
+ "e=%d", handle, cookie, e);
+
eloop_cancel_timeout(http_client_timeout, c, NULL);
switch (e) {
case HTTPREAD_EVENT_FILE_READY:
c->req = NULL;
c->hread = httpread_create(c->sd, http_client_got_response, c,
- c->max_response, HTTP_CLIENT_TIMEOUT);
+ c->max_response, HTTP_CLIENT_TIMEOUT_SEC);
if (c->hread == NULL) {
c->cb(c->cb_ctx, c, HTTP_CLIENT_FAILED);
return;
return NULL;
}
- if (eloop_register_timeout(HTTP_CLIENT_TIMEOUT, 0, http_client_timeout,
- c, NULL)) {
+ if (eloop_register_timeout(HTTP_CLIENT_TIMEOUT_SEC, 0,
+ http_client_timeout, c, NULL)) {
http_client_free(c);
return NULL;
}
* Note that angle brackets present in the original data must have been encoded
* as < and > so they will not trouble us.
*/
-static int xml_next_tag(const char *in, const char **out,
- const char **out_tagname, const char **end)
+int xml_next_tag(const char *in, const char **out,
+ const char **out_tagname, const char **end)
{
while (*in && *in != '<')
in++;
void xml_data_encode(struct wpabuf *buf, const char *data, int len);
void xml_add_tagged_data(struct wpabuf *buf, const char *tag,
const char *data);
+int xml_next_tag(const char *in, const char **out,
+ const char **out_tagname, const char **end);
char * xml_get_first_item(const char *doc, const char *item);
struct wpabuf * xml_get_base64_item(const char *data, const char *name,
enum http_reply_code *ret);
#include "wps_dev_attr.h"
+#ifdef CONFIG_WPS_TESTING
+int wps_version_number = 0x20;
+int wps_testing_dummy_cred = 0;
+#endif /* CONFIG_WPS_TESTING */
+
+
/**
* wps_init - Initialize WPS Registration protocol data
* @cfg: WPS configuration
}
if (cfg->pin) {
data->dev_pw_id = data->wps->oob_dev_pw_id == 0 ?
- DEV_PW_DEFAULT : data->wps->oob_dev_pw_id;
+ cfg->dev_pw_id : data->wps->oob_dev_pw_id;
data->dev_password = os_malloc(cfg->pin_len);
if (data->dev_password == NULL) {
os_free(data);
/* Use special PIN '00000000' for PBC */
data->dev_pw_id = DEV_PW_PUSHBUTTON;
os_free(data->dev_password);
- data->dev_password = os_malloc(8);
+ data->dev_password = (u8 *) os_strdup("00000000");
if (data->dev_password == NULL) {
os_free(data);
return NULL;
}
- os_memset(data->dev_password, '0', 8);
data->dev_password_len = 8;
}
if (cfg->peer_addr)
os_memcpy(data->peer_dev.mac_addr, cfg->peer_addr, ETH_ALEN);
+ if (cfg->p2p_dev_addr)
+ os_memcpy(data->p2p_dev_addr, cfg->p2p_dev_addr, ETH_ALEN);
data->use_psk_key = cfg->use_psk_key;
+ data->pbc_in_m1 = cfg->pbc_in_m1;
return data;
}
WPA_GET_BE16(attr.dev_password_id) != DEV_PW_PUSHBUTTON)
return 0;
+#ifdef CONFIG_WPS_STRICT
+ if (!attr.sel_reg_config_methods ||
+ !(WPA_GET_BE16(attr.sel_reg_config_methods) &
+ WPS_CONFIG_PUSHBUTTON))
+ return 0;
+#endif /* CONFIG_WPS_STRICT */
+
return 1;
}
-/**
- * wps_is_selected_pin_registrar - Check whether WPS IE indicates active PIN
- * @msg: WPS IE contents from Beacon or Probe Response frame
- * Returns: 1 if PIN Registrar is active, 0 if not
- */
-int wps_is_selected_pin_registrar(const struct wpabuf *msg)
+static int is_selected_pin_registrar(struct wps_parse_attr *attr)
{
- struct wps_parse_attr attr;
-
/*
* In theory, this could also verify that attr.sel_reg_config_methods
* includes WPS_CONFIG_LABEL, WPS_CONFIG_DISPLAY, or WPS_CONFIG_KEYPAD,
* Device Password ID here.
*/
- if (wps_parse_msg(msg, &attr) < 0)
+ if (!attr->selected_registrar || *attr->selected_registrar == 0)
return 0;
- if (!attr.selected_registrar || *attr.selected_registrar == 0)
+ if (attr->dev_password_id != NULL &&
+ WPA_GET_BE16(attr->dev_password_id) == DEV_PW_PUSHBUTTON)
return 0;
- if (attr.dev_password_id != NULL &&
- WPA_GET_BE16(attr.dev_password_id) == DEV_PW_PUSHBUTTON)
+#ifdef CONFIG_WPS_STRICT
+ if (!attr->sel_reg_config_methods ||
+ !(WPA_GET_BE16(attr->sel_reg_config_methods) &
+ (WPS_CONFIG_LABEL | WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD)))
return 0;
+#endif /* CONFIG_WPS_STRICT */
return 1;
}
/**
+ * wps_is_selected_pin_registrar - Check whether WPS IE indicates active PIN
+ * @msg: WPS IE contents from Beacon or Probe Response frame
+ * Returns: 1 if PIN Registrar is active, 0 if not
+ */
+int wps_is_selected_pin_registrar(const struct wpabuf *msg)
+{
+ struct wps_parse_attr attr;
+
+ if (wps_parse_msg(msg, &attr) < 0)
+ return 0;
+
+ return is_selected_pin_registrar(&attr);
+}
+
+
+/**
+ * wps_is_addr_authorized - Check whether WPS IE authorizes MAC address
+ * @msg: WPS IE contents from Beacon or Probe Response frame
+ * @addr: MAC address to search for
+ * @ver1_compat: Whether to use version 1 compatibility mode
+ * Returns: 1 if address is authorized, 0 if not
+ */
+int wps_is_addr_authorized(const struct wpabuf *msg, const u8 *addr,
+ int ver1_compat)
+{
+ struct wps_parse_attr attr;
+ unsigned int i;
+ const u8 *pos;
+ const u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+ if (wps_parse_msg(msg, &attr) < 0)
+ return 0;
+
+ if (!attr.version2 && ver1_compat) {
+ /*
+ * Version 1.0 AP - AuthorizedMACs not used, so revert back to
+ * old mechanism of using SelectedRegistrar.
+ */
+ return is_selected_pin_registrar(&attr);
+ }
+
+ if (!attr.authorized_macs)
+ return 0;
+
+ pos = attr.authorized_macs;
+ for (i = 0; i < attr.authorized_macs_len / ETH_ALEN; i++) {
+ if (os_memcmp(pos, addr, ETH_ALEN) == 0 ||
+ os_memcmp(pos, bcast, ETH_ALEN) == 0)
+ return 1;
+ pos += ETH_ALEN;
+ }
+
+ return 0;
+}
+
+
+/**
+ * wps_ap_priority_compar - Prioritize WPS IE from two APs
+ * @wps_a: WPS IE contents from Beacon or Probe Response frame
+ * @wps_b: WPS IE contents from Beacon or Probe Response frame
+ * Returns: 1 if wps_b is considered more likely selection for WPS
+ * provisioning, -1 if wps_a is considered more like, or 0 if no preference
+ */
+int wps_ap_priority_compar(const struct wpabuf *wps_a,
+ const struct wpabuf *wps_b)
+{
+ struct wps_parse_attr attr_a, attr_b;
+ int sel_a, sel_b;
+
+ if (wps_a == NULL || wps_parse_msg(wps_a, &attr_a) < 0)
+ return 1;
+ if (wps_b == NULL || wps_parse_msg(wps_b, &attr_b) < 0)
+ return -1;
+
+ sel_a = attr_a.selected_registrar && *attr_a.selected_registrar != 0;
+ sel_b = attr_b.selected_registrar && *attr_b.selected_registrar != 0;
+
+ if (sel_a && !sel_b)
+ return -1;
+ if (!sel_a && sel_b)
+ return 1;
+
+ return 0;
+}
+
+
+/**
* wps_get_uuid_e - Get UUID-E from WPS IE
* @msg: WPS IE contents from Beacon or Probe Response frame
* Returns: Pointer to UUID-E or %NULL if not included
/**
+ * wps_is_20 - Check whether WPS attributes claim support for WPS 2.0
+ */
+int wps_is_20(const struct wpabuf *msg)
+{
+ struct wps_parse_attr attr;
+
+ if (msg == NULL || wps_parse_msg(msg, &attr) < 0)
+ return 0;
+ return attr.version2 != NULL;
+}
+
+
+/**
* wps_build_assoc_req_ie - Build WPS IE for (Re)Association Request
* @req_type: Value for Request Type attribute
* Returns: WPS IE or %NULL on failure
wpabuf_put_be32(ie, WPS_DEV_OUI_WFA);
if (wps_build_version(ie) ||
- wps_build_req_type(ie, req_type)) {
+ wps_build_req_type(ie, req_type) ||
+ wps_build_wfa_ext(ie, 0, NULL, 0)) {
wpabuf_free(ie);
return NULL;
}
wpabuf_put_be32(ie, WPS_DEV_OUI_WFA);
if (wps_build_version(ie) ||
- wps_build_resp_type(ie, WPS_RESP_AP)) {
+ wps_build_resp_type(ie, WPS_RESP_AP) ||
+ wps_build_wfa_ext(ie, 0, NULL, 0)) {
wpabuf_free(ie);
return NULL;
}
* @dev: Device attributes
* @uuid: Own UUID
* @req_type: Value for Request Type attribute
+ * @num_req_dev_types: Number of requested device types
+ * @req_dev_types: Requested device types (8 * num_req_dev_types octets) or
+ * %NULL if none
* Returns: WPS IE or %NULL on failure
*
* The caller is responsible for freeing the buffer.
*/
struct wpabuf * wps_build_probe_req_ie(int pbc, struct wps_device_data *dev,
const u8 *uuid,
- enum wps_request_type req_type)
+ enum wps_request_type req_type,
+ unsigned int num_req_dev_types,
+ const u8 *req_dev_types)
{
struct wpabuf *ie;
- u8 *len;
- u16 methods;
wpa_printf(MSG_DEBUG, "WPS: Building WPS IE for Probe Request");
- ie = wpabuf_alloc(200);
+ ie = wpabuf_alloc(500);
if (ie == NULL)
return NULL;
- wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
- len = wpabuf_put(ie, 1);
- wpabuf_put_be32(ie, WPS_DEV_OUI_WFA);
-
- if (pbc)
- methods = WPS_CONFIG_PUSHBUTTON;
- else {
- methods = WPS_CONFIG_LABEL | WPS_CONFIG_DISPLAY |
- WPS_CONFIG_KEYPAD;
-#ifdef CONFIG_WPS_UFD
- methods |= WPS_CONFIG_USBA;
-#endif /* CONFIG_WPS_UFD */
-#ifdef CONFIG_WPS_NFC
- methods |= WPS_CONFIG_NFC_INTERFACE;
-#endif /* CONFIG_WPS_NFC */
- }
-
if (wps_build_version(ie) ||
wps_build_req_type(ie, req_type) ||
- wps_build_config_methods(ie, methods) ||
+ wps_build_config_methods(ie, dev->config_methods) ||
wps_build_uuid_e(ie, uuid) ||
wps_build_primary_dev_type(dev, ie) ||
wps_build_rf_bands(dev, ie) ||
wps_build_assoc_state(NULL, ie) ||
wps_build_config_error(ie, WPS_CFG_NO_ERROR) ||
wps_build_dev_password_id(ie, pbc ? DEV_PW_PUSHBUTTON :
- DEV_PW_DEFAULT)) {
+ DEV_PW_DEFAULT) ||
+#ifdef CONFIG_WPS2
+ wps_build_manufacturer(dev, ie) ||
+ wps_build_model_name(dev, ie) ||
+ wps_build_model_number(dev, ie) ||
+ wps_build_dev_name(dev, ie) ||
+ wps_build_wfa_ext(ie, req_type == WPS_REQ_ENROLLEE, NULL, 0) ||
+#endif /* CONFIG_WPS2 */
+ wps_build_req_dev_type(dev, ie, num_req_dev_types, req_dev_types)
+ ||
+ wps_build_secondary_dev_type(dev, ie)
+ ) {
wpabuf_free(ie);
return NULL;
}
- *len = wpabuf_len(ie) - 2;
+#ifndef CONFIG_WPS2
+ if (dev->p2p && wps_build_dev_name(dev, ie)) {
+ wpabuf_free(ie);
+ return NULL;
+ }
+#endif /* CONFIG_WPS2 */
- return ie;
+ return wps_ie_encapsulate(ie);
}
#define WPS_DEV_TYPE_LEN 8
#define WPS_DEV_TYPE_BUFSIZE 21
+#define WPS_SEC_DEV_TYPE_MAX_LEN 128
+/* maximum number of advertised WPS vendor extension attributes */
+#define MAX_WPS_VENDOR_EXTENSIONS 10
+/* maximum size of WPS Vendor extension attribute */
+#define WPS_MAX_VENDOR_EXT_LEN 1024
+/* maximum number of parsed WPS vendor extension attributes */
+#define MAX_WPS_PARSE_VENDOR_EXT 10
/**
* struct wps_device_data - WPS Device Data
* @model_number: Model Number (0..32 octets encoded in UTF-8)
* @serial_number: Serial Number (0..32 octets encoded in UTF-8)
* @pri_dev_type: Primary Device Type
+ * @sec_dev_type: Array of secondary device types
+ * @num_sec_dev_type: Number of secondary device types
* @os_version: OS Version
* @rf_bands: RF bands (WPS_RF_24GHZ, WPS_RF_50GHZ flags)
+ * @p2p: Whether the device is a P2P device
*/
struct wps_device_data {
u8 mac_addr[ETH_ALEN];
char *model_number;
char *serial_number;
u8 pri_dev_type[WPS_DEV_TYPE_LEN];
+#define WPS_SEC_DEVICE_TYPES 5
+ u8 sec_dev_type[WPS_SEC_DEVICE_TYPES][WPS_DEV_TYPE_LEN];
+ u8 num_sec_dev_types;
u32 os_version;
u8 rf_bands;
+ u16 config_methods;
+ struct wpabuf *vendor_ext[MAX_WPS_VENDOR_EXTENSIONS];
+
+ int p2p;
};
struct oob_conf_data {
* struct wpa_context::psk.
*/
int use_psk_key;
+
+ /**
+ * dev_pw_id - Device Password ID for Enrollee when PIN is used
+ */
+ u16 dev_pw_id;
+
+ /**
+ * p2p_dev_addr - P2P Device Address from (Re)Association Request
+ *
+ * On AP/GO, this is set to the P2P Device Address of the associating
+ * P2P client if a P2P IE is included in the (Re)Association Request
+ * frame and the P2P Device Address is included. Otherwise, this is set
+ * to %NULL to indicate the station does not have a P2P Device Address.
+ */
+ const u8 *p2p_dev_addr;
+
+ /**
+ * pbc_in_m1 - Do not remove PushButton config method in M1 (AP)
+ *
+ * This can be used to enable a workaround to allow Windows 7 to use
+ * PBC with the AP.
+ */
+ int pbc_in_m1;
};
struct wps_data * wps_init(const struct wps_config *cfg);
int wps_is_selected_pbc_registrar(const struct wpabuf *msg);
int wps_is_selected_pin_registrar(const struct wpabuf *msg);
+int wps_ap_priority_compar(const struct wpabuf *wps_a,
+ const struct wpabuf *wps_b);
+int wps_is_addr_authorized(const struct wpabuf *msg, const u8 *addr,
+ int ver1_compat);
const u8 * wps_get_uuid_e(const struct wpabuf *msg);
+int wps_is_20(const struct wpabuf *msg);
struct wpabuf * wps_build_assoc_req_ie(enum wps_request_type req_type);
struct wpabuf * wps_build_assoc_resp_ie(void);
struct wpabuf * wps_build_probe_req_ie(int pbc, struct wps_device_data *dev,
const u8 *uuid,
- enum wps_request_type req_type);
+ enum wps_request_type req_type,
+ unsigned int num_req_dev_types,
+ const u8 *req_dev_types);
/**
* static_wep_only - Whether the BSS supports only static WEP
*/
int static_wep_only;
+
+ /**
+ * dualband - Whether this is a concurrent dualband AP
+ */
+ int dualband;
};
/**
* WPS_EV_ER_ENROLLEE_REMOVE - ER: Enrollee removed
*/
- WPS_EV_ER_ENROLLEE_REMOVE
+ WPS_EV_ER_ENROLLEE_REMOVE,
+
+ /**
+ * WPS_EV_ER_AP_SETTINGS - ER: AP Settings learned
+ */
+ WPS_EV_ER_AP_SETTINGS,
+
+ /**
+ * WPS_EV_ER_SET_SELECTED_REGISTRAR - ER: SetSelectedRegistrar event
+ */
+ WPS_EV_ER_SET_SELECTED_REGISTRAR,
+
+ /**
+ * WPS_EV_AP_PIN_SUCCESS - External Registrar used correct AP PIN
+ */
+ WPS_EV_AP_PIN_SUCCESS
};
/**
*/
struct wps_event_fail {
int msg;
+ u16 config_error;
+ u16 error_indication;
} fail;
struct wps_event_pwd_auth_fail {
const char *model_number;
const char *serial_number;
} enrollee;
+
+ struct wps_event_er_ap_settings {
+ const u8 *uuid;
+ const struct wps_credential *cred;
+ } ap_settings;
+
+ struct wps_event_er_set_selected_registrar {
+ const u8 *uuid;
+ int sel_reg;
+ u16 dev_passwd_id;
+ u16 sel_reg_config_methods;
+ enum {
+ WPS_ER_SET_SEL_REG_START,
+ WPS_ER_SET_SEL_REG_DONE,
+ WPS_ER_SET_SEL_REG_FAILED
+ } state;
+ } set_sel_reg;
};
/**
wps_registrar_init(struct wps_context *wps,
const struct wps_registrar_config *cfg);
void wps_registrar_deinit(struct wps_registrar *reg);
-int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *uuid,
- const u8 *pin, size_t pin_len, int timeout);
+int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *addr,
+ const u8 *uuid, const u8 *pin, size_t pin_len,
+ int timeout);
int wps_registrar_invalidate_pin(struct wps_registrar *reg, const u8 *uuid);
+int wps_registrar_wps_cancel(struct wps_registrar *reg);
int wps_registrar_unlock_pin(struct wps_registrar *reg, const u8 *uuid);
-int wps_registrar_button_pushed(struct wps_registrar *reg);
+int wps_registrar_button_pushed(struct wps_registrar *reg,
+ const u8 *p2p_dev_addr);
+void wps_registrar_complete(struct wps_registrar *registrar, const u8 *uuid_e);
void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr,
- const struct wpabuf *wps_data);
+ const struct wpabuf *wps_data,
+ int p2p_wildcard);
int wps_registrar_update_ie(struct wps_registrar *reg);
int wps_registrar_get_info(struct wps_registrar *reg, const u8 *addr,
char *buf, size_t buflen);
+int wps_registrar_config_ap(struct wps_registrar *reg,
+ struct wps_credential *cred);
+
+int wps_build_credential_wrap(struct wpabuf *msg,
+ const struct wps_credential *cred);
unsigned int wps_pin_checksum(unsigned int pin);
unsigned int wps_pin_valid(unsigned int pin);
int registrar);
int wps_attr_text(struct wpabuf *data, char *buf, char *end);
-struct wps_er * wps_er_init(struct wps_context *wps, const char *ifname);
+struct wps_er * wps_er_init(struct wps_context *wps, const char *ifname,
+ const char *filter);
void wps_er_refresh(struct wps_er *er);
void wps_er_deinit(struct wps_er *er, void (*cb)(void *ctx), void *ctx);
void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id,
int wps_er_pbc(struct wps_er *er, const u8 *uuid);
int wps_er_learn(struct wps_er *er, const u8 *uuid, const u8 *pin,
size_t pin_len);
+int wps_er_set_config(struct wps_er *er, const u8 *uuid,
+ const struct wps_credential *cred);
+int wps_er_config(struct wps_er *er, const u8 *uuid, const u8 *pin,
+ size_t pin_len, const struct wps_credential *cred);
int wps_dev_type_str2bin(const char *str, u8 dev_type[WPS_DEV_TYPE_LEN]);
char * wps_dev_type_bin2str(const u8 dev_type[WPS_DEV_TYPE_LEN], char *buf,
void uuid_gen_mac_addr(const u8 *mac_addr, u8 *uuid);
u16 wps_config_methods_str2bin(const char *str);
+#ifdef CONFIG_WPS_STRICT
+int wps_validate_beacon(const struct wpabuf *wps_ie);
+int wps_validate_beacon_probe_resp(const struct wpabuf *wps_ie, int probe,
+ const u8 *addr);
+int wps_validate_probe_req(const struct wpabuf *wps_ie, const u8 *addr);
+int wps_validate_assoc_req(const struct wpabuf *wps_ie);
+int wps_validate_assoc_resp(const struct wpabuf *wps_ie);
+int wps_validate_m1(const struct wpabuf *tlvs);
+int wps_validate_m2(const struct wpabuf *tlvs);
+int wps_validate_m2d(const struct wpabuf *tlvs);
+int wps_validate_m3(const struct wpabuf *tlvs);
+int wps_validate_m4(const struct wpabuf *tlvs);
+int wps_validate_m4_encr(const struct wpabuf *tlvs, int wps2);
+int wps_validate_m5(const struct wpabuf *tlvs);
+int wps_validate_m5_encr(const struct wpabuf *tlvs, int wps2);
+int wps_validate_m6(const struct wpabuf *tlvs);
+int wps_validate_m6_encr(const struct wpabuf *tlvs, int wps2);
+int wps_validate_m7(const struct wpabuf *tlvs);
+int wps_validate_m7_encr(const struct wpabuf *tlvs, int ap, int wps2);
+int wps_validate_m8(const struct wpabuf *tlvs);
+int wps_validate_m8_encr(const struct wpabuf *tlvs, int ap, int wps2);
+int wps_validate_wsc_ack(const struct wpabuf *tlvs);
+int wps_validate_wsc_nack(const struct wpabuf *tlvs);
+int wps_validate_wsc_done(const struct wpabuf *tlvs);
+int wps_validate_upnp_set_selected_registrar(const struct wpabuf *tlvs);
+#else /* CONFIG_WPS_STRICT */
+static inline int wps_validate_beacon(const struct wpabuf *wps_ie){
+ return 0;
+}
+
+static inline int wps_validate_beacon_probe_resp(const struct wpabuf *wps_ie,
+ int probe, const u8 *addr)
+{
+ return 0;
+}
+
+static inline int wps_validate_probe_req(const struct wpabuf *wps_ie,
+ const u8 *addr)
+{
+ return 0;
+}
+
+static inline int wps_validate_assoc_req(const struct wpabuf *wps_ie)
+{
+ return 0;
+}
+
+static inline int wps_validate_assoc_resp(const struct wpabuf *wps_ie)
+{
+ return 0;
+}
+
+static inline int wps_validate_m1(const struct wpabuf *tlvs)
+{
+ return 0;
+}
+
+static inline int wps_validate_m2(const struct wpabuf *tlvs)
+{
+ return 0;
+}
+
+static inline int wps_validate_m2d(const struct wpabuf *tlvs)
+{
+ return 0;
+}
+
+static inline int wps_validate_m3(const struct wpabuf *tlvs)
+{
+ return 0;
+}
+
+static inline int wps_validate_m4(const struct wpabuf *tlvs)
+{
+ return 0;
+}
+
+static inline int wps_validate_m4_encr(const struct wpabuf *tlvs, int wps2)
+{
+ return 0;
+}
+
+static inline int wps_validate_m5(const struct wpabuf *tlvs)
+{
+ return 0;
+}
+
+static inline int wps_validate_m5_encr(const struct wpabuf *tlvs, int wps2)
+{
+ return 0;
+}
+
+static inline int wps_validate_m6(const struct wpabuf *tlvs)
+{
+ return 0;
+}
+
+static inline int wps_validate_m6_encr(const struct wpabuf *tlvs, int wps2)
+{
+ return 0;
+}
+
+static inline int wps_validate_m7(const struct wpabuf *tlvs)
+{
+ return 0;
+}
+
+static inline int wps_validate_m7_encr(const struct wpabuf *tlvs, int ap,
+ int wps2)
+{
+ return 0;
+}
+
+static inline int wps_validate_m8(const struct wpabuf *tlvs)
+{
+ return 0;
+}
+
+static inline int wps_validate_m8_encr(const struct wpabuf *tlvs, int ap,
+ int wps2)
+{
+ return 0;
+}
+
+static inline int wps_validate_wsc_ack(const struct wpabuf *tlvs)
+{
+ return 0;
+}
+
+static inline int wps_validate_wsc_nack(const struct wpabuf *tlvs)
+{
+ return 0;
+}
+
+static inline int wps_validate_wsc_done(const struct wpabuf *tlvs)
+{
+ return 0;
+}
+
+static inline int wps_validate_upnp_set_selected_registrar(
+ const struct wpabuf *tlvs)
+{
+ return 0;
+}
+#endif /* CONFIG_WPS_STRICT */
+
#endif /* WPS_H */
#include "crypto/crypto.h"
#include "crypto/dh_group5.h"
#include "crypto/sha256.h"
+#include "crypto/random.h"
+#include "common/ieee802_11_defs.h"
#include "wps_i.h"
wpabuf_free(pubkey);
return -1;
}
+ wpa_hexdump_buf_key(MSG_DEBUG, "WPS: DH Private Key", wps->dh_privkey);
+ wpa_hexdump_buf(MSG_DEBUG, "WPS: DH own Public Key", pubkey);
wpabuf_put_be16(msg, ATTR_PUBLIC_KEY);
wpabuf_put_be16(msg, wpabuf_len(pubkey));
int wps_build_version(struct wpabuf *msg)
{
- wpa_printf(MSG_DEBUG, "WPS: * Version");
+ /*
+ * Note: This attribute is deprecated and set to hardcoded 0x10 for
+ * backwards compatibility reasons. The real version negotiation is
+ * done with Version2.
+ */
+ wpa_printf(MSG_DEBUG, "WPS: * Version (hardcoded 0x10)");
wpabuf_put_be16(msg, ATTR_VERSION);
wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, 0x10);
+ return 0;
+}
+
+
+int wps_build_wfa_ext(struct wpabuf *msg, int req_to_enroll,
+ const u8 *auth_macs, size_t auth_macs_count)
+{
+#ifdef CONFIG_WPS2
+ u8 *len;
+
+ wpabuf_put_be16(msg, ATTR_VENDOR_EXT);
+ len = wpabuf_put(msg, 2); /* to be filled */
+ wpabuf_put_be24(msg, WPS_VENDOR_ID_WFA);
+
+ wpa_printf(MSG_DEBUG, "WPS: * Version2 (0x%x)", WPS_VERSION);
+ wpabuf_put_u8(msg, WFA_ELEM_VERSION2);
+ wpabuf_put_u8(msg, 1);
wpabuf_put_u8(msg, WPS_VERSION);
+
+ if (req_to_enroll) {
+ wpa_printf(MSG_DEBUG, "WPS: * Request to Enroll (1)");
+ wpabuf_put_u8(msg, WFA_ELEM_REQUEST_TO_ENROLL);
+ wpabuf_put_u8(msg, 1);
+ wpabuf_put_u8(msg, 1);
+ }
+
+ if (auth_macs && auth_macs_count) {
+ size_t i;
+ wpa_printf(MSG_DEBUG, "WPS: * AuthorizedMACs (count=%d)",
+ (int) auth_macs_count);
+ wpabuf_put_u8(msg, WFA_ELEM_AUTHORIZEDMACS);
+ wpabuf_put_u8(msg, auth_macs_count * ETH_ALEN);
+ wpabuf_put_data(msg, auth_macs, auth_macs_count * ETH_ALEN);
+ for (i = 0; i < auth_macs_count; i++)
+ wpa_printf(MSG_DEBUG, "WPS: AuthorizedMAC: " MACSTR,
+ MAC2STR(&auth_macs[i * ETH_ALEN]));
+ }
+
+ WPA_PUT_BE16(len, (u8 *) wpabuf_put(msg, 0) - len - 2);
+#endif /* CONFIG_WPS2 */
+
+#ifdef CONFIG_WPS_TESTING
+ if (WPS_VERSION > 0x20) {
+ wpa_printf(MSG_DEBUG, "WPS: * Extensibility Testing - extra "
+ "attribute");
+ wpabuf_put_be16(msg, ATTR_EXTENSIBILITY_TEST);
+ wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, 42);
+ }
+#endif /* CONFIG_WPS_TESTING */
return 0;
}
int wps_build_auth_type_flags(struct wps_data *wps, struct wpabuf *msg)
{
+ u16 auth_types = WPS_AUTH_TYPES;
+#ifdef CONFIG_WPS2
+ auth_types &= ~WPS_AUTH_SHARED;
+#endif /* CONFIG_WPS2 */
wpa_printf(MSG_DEBUG, "WPS: * Authentication Type Flags");
wpabuf_put_be16(msg, ATTR_AUTH_TYPE_FLAGS);
wpabuf_put_be16(msg, 2);
- wpabuf_put_be16(msg, WPS_AUTH_TYPES);
+ wpabuf_put_be16(msg, auth_types);
return 0;
}
int wps_build_encr_type_flags(struct wps_data *wps, struct wpabuf *msg)
{
+ u16 encr_types = WPS_ENCR_TYPES;
+#ifdef CONFIG_WPS2
+ encr_types &= ~WPS_ENCR_WEP;
+#endif /* CONFIG_WPS2 */
wpa_printf(MSG_DEBUG, "WPS: * Encryption Type Flags");
wpabuf_put_be16(msg, ATTR_ENCR_TYPE_FLAGS);
wpabuf_put_be16(msg, 2);
- wpabuf_put_be16(msg, WPS_ENCR_TYPES);
+ wpabuf_put_be16(msg, encr_types);
return 0;
}
wpabuf_put_be16(msg, block_size + wpabuf_len(plain));
iv = wpabuf_put(msg, block_size);
- if (os_get_random(iv, block_size) < 0)
+ if (random_get_bytes(iv, block_size) < 0)
return -1;
data = wpabuf_put(msg, 0);
}
wps->oob_dev_pw_id |= 0x0010;
- if (os_get_random(dev_password_bin, WPS_OOB_DEVICE_PASSWORD_LEN) < 0) {
+ if (random_get_bytes(dev_password_bin, WPS_OOB_DEVICE_PASSWORD_LEN) <
+ 0) {
wpa_printf(MSG_ERROR, "WPS: OOB device password "
"generation error");
return -1;
return 0;
}
#endif /* CONFIG_WPS_OOB */
+
+
+/* Encapsulate WPS IE data with one (or more, if needed) IE headers */
+struct wpabuf * wps_ie_encapsulate(struct wpabuf *data)
+{
+ struct wpabuf *ie;
+ const u8 *pos, *end;
+
+ ie = wpabuf_alloc(wpabuf_len(data) + 100);
+ if (ie == NULL) {
+ wpabuf_free(data);
+ return NULL;
+ }
+
+ pos = wpabuf_head(data);
+ end = pos + wpabuf_len(data);
+
+ while (end > pos) {
+ size_t frag_len = end - pos;
+ if (frag_len > 251)
+ frag_len = 251;
+ wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
+ wpabuf_put_u8(ie, 4 + frag_len);
+ wpabuf_put_be32(ie, WPS_DEV_OUI_WFA);
+ wpabuf_put_data(ie, pos, frag_len);
+ pos += frag_len;
+ }
+
+ wpabuf_free(data);
+
+ return ie;
+}
#include "common.h"
#include "wps_i.h"
+#ifndef CONFIG_WPS_STRICT
#define WPS_WORKAROUNDS
+#endif /* CONFIG_WPS_STRICT */
+
+
+static int wps_set_vendor_ext_wfa_subelem(struct wps_parse_attr *attr,
+ u8 id, u8 len, const u8 *pos)
+{
+ wpa_printf(MSG_EXCESSIVE, "WPS: WFA subelement id=%u len=%u",
+ id, len);
+ switch (id) {
+ case WFA_ELEM_VERSION2:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Version2 length "
+ "%u", len);
+ return -1;
+ }
+ attr->version2 = pos;
+ break;
+ case WFA_ELEM_AUTHORIZEDMACS:
+ attr->authorized_macs = pos;
+ attr->authorized_macs_len = len;
+ break;
+ case WFA_ELEM_NETWORK_KEY_SHAREABLE:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Network Key "
+ "Shareable length %u", len);
+ return -1;
+ }
+ attr->network_key_shareable = pos;
+ break;
+ case WFA_ELEM_REQUEST_TO_ENROLL:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Request to Enroll "
+ "length %u", len);
+ return -1;
+ }
+ attr->request_to_enroll = pos;
+ break;
+ case WFA_ELEM_SETTINGS_DELAY_TIME:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Settings Delay "
+ "Time length %u", len);
+ return -1;
+ }
+ attr->settings_delay_time = pos;
+ break;
+ default:
+ wpa_printf(MSG_MSGDUMP, "WPS: Skipped unknown WFA Vendor "
+ "Extension subelement %u", id);
+ break;
+ }
+
+ return 0;
+}
+
+
+static int wps_parse_vendor_ext_wfa(struct wps_parse_attr *attr, const u8 *pos,
+ u16 len)
+{
+ const u8 *end = pos + len;
+ u8 id, elen;
+
+ while (pos + 2 < end) {
+ id = *pos++;
+ elen = *pos++;
+ if (pos + elen > end)
+ break;
+ if (wps_set_vendor_ext_wfa_subelem(attr, id, elen, pos) < 0)
+ return -1;
+ pos += elen;
+ }
+
+ return 0;
+}
+
+
+static int wps_parse_vendor_ext(struct wps_parse_attr *attr, const u8 *pos,
+ u16 len)
+{
+ u32 vendor_id;
+
+ if (len < 3) {
+ wpa_printf(MSG_DEBUG, "WPS: Skip invalid Vendor Extension");
+ return 0;
+ }
+
+ vendor_id = WPA_GET_BE24(pos);
+ switch (vendor_id) {
+ case WPS_VENDOR_ID_WFA:
+ return wps_parse_vendor_ext_wfa(attr, pos + 3, len - 3);
+ }
+
+ /* Handle unknown vendor extensions */
+
+ wpa_printf(MSG_MSGDUMP, "WPS: Unknown Vendor Extension (Vendor ID %u)",
+ vendor_id);
+
+ if (len > WPS_MAX_VENDOR_EXT_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Too long Vendor Extension (%u)",
+ len);
+ return -1;
+ }
+
+ if (attr->num_vendor_ext >= MAX_WPS_PARSE_VENDOR_EXT) {
+ wpa_printf(MSG_DEBUG, "WPS: Skipped Vendor Extension "
+ "attribute (max %d vendor extensions)",
+ MAX_WPS_PARSE_VENDOR_EXT);
+ return -1;
+ }
+ attr->vendor_ext[attr->num_vendor_ext] = pos;
+ attr->vendor_ext_len[attr->num_vendor_ext] = len;
+ attr->num_vendor_ext++;
+
+ return 0;
+}
static int wps_set_attr(struct wps_parse_attr *attr, u16 type,
}
attr->ap_setup_locked = pos;
break;
+ case ATTR_REQUESTED_DEV_TYPE:
+ if (len != WPS_DEV_TYPE_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Requested Device "
+ "Type length %u", len);
+ return -1;
+ }
+ if (attr->num_req_dev_type >= MAX_REQ_DEV_TYPE_COUNT) {
+ wpa_printf(MSG_DEBUG, "WPS: Skipped Requested Device "
+ "Type attribute (max %u types)",
+ MAX_REQ_DEV_TYPE_COUNT);
+ break;
+ }
+ attr->req_dev_type[attr->num_req_dev_type] = pos;
+ attr->num_req_dev_type++;
+ break;
+ case ATTR_SECONDARY_DEV_TYPE_LIST:
+ if (len > WPS_SEC_DEV_TYPE_MAX_LEN ||
+ (len % WPS_DEV_TYPE_LEN) > 0) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Secondary Device "
+ "Type length %u", len);
+ return -1;
+ }
+ attr->sec_dev_type_list = pos;
+ attr->sec_dev_type_list_len = len;
+ break;
+ case ATTR_VENDOR_EXT:
+ if (wps_parse_vendor_ext(attr, pos, len) < 0)
+ return -1;
+ break;
default:
wpa_printf(MSG_DEBUG, "WPS: Unsupported attribute type 0x%x "
"len=%u", type, len);
{
const u8 *pos, *end;
u16 type, len;
+#ifdef WPS_WORKAROUNDS
+ u16 prev_type = 0;
+#endif /* WPS_WORKAROUNDS */
os_memset(attr, 0, sizeof(*attr));
pos = wpabuf_head(msg);
pos += 2;
len = WPA_GET_BE16(pos);
pos += 2;
- wpa_printf(MSG_MSGDUMP, "WPS: attr type=0x%x len=%u",
+ wpa_printf(MSG_EXCESSIVE, "WPS: attr type=0x%x len=%u",
type, len);
if (len > end - pos) {
wpa_printf(MSG_DEBUG, "WPS: Attribute overflow");
+ wpa_hexdump_buf(MSG_MSGDUMP, "WPS: Message data", msg);
+#ifdef WPS_WORKAROUNDS
+ /*
+ * Some deployed APs seem to have a bug in encoding of
+ * Network Key attribute in the Credential attribute
+ * where they add an extra octet after the Network Key
+ * attribute at least when open network is being
+ * provisioned.
+ */
+ if ((type & 0xff00) != 0x1000 &&
+ prev_type == ATTR_NETWORK_KEY) {
+ wpa_printf(MSG_DEBUG, "WPS: Workaround - try "
+ "to skip unexpected octet after "
+ "Network Key");
+ pos -= 3;
+ continue;
+ }
+#endif /* WPS_WORKAROUNDS */
return -1;
}
if (wps_set_attr(attr, type, pos, len) < 0)
return -1;
+#ifdef WPS_WORKAROUNDS
+ prev_type = type;
+#endif /* WPS_WORKAROUNDS */
pos += len;
}
}
-static void wps_workaround_cred_key(struct wps_credential *cred)
+static int wps_workaround_cred_key(struct wps_credential *cred)
{
if (cred->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK) &&
cred->key_len > 8 && cred->key_len < 64 &&
cred->key[cred->key_len - 1] == 0) {
+#ifdef CONFIG_WPS_STRICT
+ wpa_printf(MSG_INFO, "WPS: WPA/WPA2-Personal passphrase uses "
+ "forbidden NULL termination");
+ wpa_hexdump_ascii_key(MSG_INFO, "WPS: Network Key",
+ cred->key, cred->key_len);
+ return -1;
+#else /* CONFIG_WPS_STRICT */
/*
* A deployed external registrar is known to encode ASCII
* passphrases incorrectly. Remove the extra NULL termination
wpa_printf(MSG_DEBUG, "WPS: Workaround - remove NULL "
"termination from ASCII passphrase");
cred->key_len--;
+#endif /* CONFIG_WPS_STRICT */
}
+ return 0;
}
wps_process_cred_802_1x_enabled(cred, attr->dot1x_enabled))
return -1;
- wps_workaround_cred_key(cred);
-
- return 0;
+ return wps_workaround_cred_key(cred);
}
wps_process_cred_mac_addr(cred, attr->mac_addr))
return -1;
- wps_workaround_cred_key(cred);
-
- return 0;
+ return wps_workaround_cred_key(cred);
}
#include "crypto/dh_group5.h"
#include "crypto/sha1.h"
#include "crypto/sha256.h"
+#include "crypto/random.h"
#include "wps_i.h"
#include "wps_dev_attr.h"
return -1;
}
+ wpa_hexdump_buf_key(MSG_DEBUG, "WPS: DH Private Key", wps->dh_privkey);
+ wpa_hexdump_buf(MSG_DEBUG, "WPS: DH peer Public Key", pubkey);
dh_shared = dh5_derive_shared(wps->dh_ctx, pubkey, wps->dh_privkey);
dh5_free(wps->dh_ctx);
wps->dh_ctx = NULL;
unsigned int val;
/* Generate seven random digits for the PIN */
- if (os_get_random((unsigned char *) &val, sizeof(val)) < 0) {
+ if (random_get_bytes((unsigned char *) &val, sizeof(val)) < 0) {
struct os_time now;
os_get_time(&now);
val = os_random() ^ now.sec ^ now.usec;
}
-void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg)
+void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg,
+ u16 config_error, u16 error_indication)
{
union wps_event_data data;
os_memset(&data, 0, sizeof(data));
data.fail.msg = msg;
+ data.fail.config_error = config_error;
+ data.fail.error_indication = error_indication;
wps->event_cb(wps->cb_ctx, WPS_EV_FAIL, &data);
}
data.wps = wps;
data.auth_type = wps->auth_types;
data.encr_type = wps->encr_types;
- if (wps_build_version(plain) || wps_build_cred(&data, plain)) {
+ if (wps_build_version(plain) ||
+ wps_build_cred(&data, plain) ||
+ wps_build_wfa_ext(plain, 0, NULL, 0)) {
wpabuf_free(plain);
return NULL;
}
}
if (wps_build_version(data) ||
- wps_build_oob_dev_password(data, wps)) {
+ wps_build_oob_dev_password(data, wps) ||
+ wps_build_wfa_ext(data, 0, NULL, 0)) {
wpa_printf(MSG_ERROR, "WPS: Build OOB device password "
"attribute error");
wpabuf_free(data);
if (str == NULL) {
/* Default to enabling methods based on build configuration */
methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
+#ifdef CONFIG_WPS2
+ methods |= WPS_CONFIG_VIRT_DISPLAY;
+#endif /* CONFIG_WPS2 */
#ifdef CONFIG_WPS_UFD
methods |= WPS_CONFIG_USBA;
#endif /* CONFIG_WPS_UFD */
methods |= WPS_CONFIG_PUSHBUTTON;
if (os_strstr(str, "keypad"))
methods |= WPS_CONFIG_KEYPAD;
+#ifdef CONFIG_WPS2
+ if (os_strstr(str, "virtual_display"))
+ methods |= WPS_CONFIG_VIRT_DISPLAY;
+ if (os_strstr(str, "physical_display"))
+ methods |= WPS_CONFIG_PHY_DISPLAY;
+ if (os_strstr(str, "virtual_push_button"))
+ methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
+ if (os_strstr(str, "physical_push_button"))
+ methods |= WPS_CONFIG_PHY_PUSHBUTTON;
+#endif /* CONFIG_WPS2 */
}
return methods;
}
+
+
+struct wpabuf * wps_build_wsc_ack(struct wps_data *wps)
+{
+ struct wpabuf *msg;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_ACK");
+
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL)
+ return NULL;
+
+ if (wps_build_version(msg) ||
+ wps_build_msg_type(msg, WPS_WSC_ACK) ||
+ wps_build_enrollee_nonce(wps, msg) ||
+ wps_build_registrar_nonce(wps, msg) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0)) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+
+ return msg;
+}
+
+
+struct wpabuf * wps_build_wsc_nack(struct wps_data *wps)
+{
+ struct wpabuf *msg;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_NACK");
+
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL)
+ return NULL;
+
+ if (wps_build_version(msg) ||
+ wps_build_msg_type(msg, WPS_WSC_NACK) ||
+ wps_build_enrollee_nonce(wps, msg) ||
+ wps_build_registrar_nonce(wps, msg) ||
+ wps_build_config_error(msg, wps->config_error) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0)) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+
+ return msg;
+}
#ifndef WPS_DEFS_H
#define WPS_DEFS_H
+#ifdef CONFIG_WPS_TESTING
+
+extern int wps_version_number;
+extern int wps_testing_dummy_cred;
+#define WPS_VERSION wps_version_number
+
+#else /* CONFIG_WPS_TESTING */
+
+#ifdef CONFIG_WPS2
+#define WPS_VERSION 0x20
+#else /* CONFIG_WPS2 */
#define WPS_VERSION 0x10
+#endif /* CONFIG_WPS2 */
+
+#endif /* CONFIG_WPS_TESTING */
/* Diffie-Hellman 1536-bit MODP Group; RFC 3526, Group 5 */
#define WPS_DH_GROUP 5
ATTR_KEY_PROVIDED_AUTO = 0x1061,
ATTR_802_1X_ENABLED = 0x1062,
ATTR_APPSESSIONKEY = 0x1063,
- ATTR_WEPTRANSMITKEY = 0x1064
+ ATTR_WEPTRANSMITKEY = 0x1064,
+ ATTR_REQUESTED_DEV_TYPE = 0x106a,
+ ATTR_EXTENSIBILITY_TEST = 0x10fa /* _NOT_ defined in the spec */
+};
+
+#define WPS_VENDOR_ID_WFA 14122
+
+/* WFA Vendor Extension subelements */
+enum {
+ WFA_ELEM_VERSION2 = 0x00,
+ WFA_ELEM_AUTHORIZEDMACS = 0x01,
+ WFA_ELEM_NETWORK_KEY_SHAREABLE = 0x02,
+ WFA_ELEM_REQUEST_TO_ENROLL = 0x03,
+ WFA_ELEM_SETTINGS_DELAY_TIME = 0x04
};
/* Device Password ID */
WPS_CFG_DEV_PASSWORD_AUTH_FAILURE = 18
};
+/* Vendor specific Error Indication for WPS event messages */
+enum wps_error_indication {
+ WPS_EI_NO_ERROR,
+ WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED,
+ WPS_EI_SECURITY_WEP_PROHIBITED,
+ NUM_WPS_EI_VALUES
+};
+
/* RF Bands */
#define WPS_RF_24GHZ 0x01
#define WPS_RF_50GHZ 0x02
#define WPS_CONFIG_NFC_INTERFACE 0x0040
#define WPS_CONFIG_PUSHBUTTON 0x0080
#define WPS_CONFIG_KEYPAD 0x0100
+#ifdef CONFIG_WPS2
+#define WPS_CONFIG_VIRT_PUSHBUTTON 0x0280
+#define WPS_CONFIG_PHY_PUSHBUTTON 0x0480
+#define WPS_CONFIG_VIRT_DISPLAY 0x2008
+#define WPS_CONFIG_PHY_DISPLAY 0x4008
+#endif /* CONFIG_WPS2 */
/* Connection Type Flags */
#define WPS_CONN_ESS 0x01
/* Walk Time for push button configuration (in seconds) */
#define WPS_PBC_WALK_TIME 120
+#define WPS_MAX_AUTHORIZED_MACS 5
+
#endif /* WPS_DEFS_H */
#include "wps_dev_attr.h"
-static int wps_build_manufacturer(struct wps_device_data *dev,
- struct wpabuf *msg)
+int wps_build_manufacturer(struct wps_device_data *dev, struct wpabuf *msg)
{
size_t len;
wpa_printf(MSG_DEBUG, "WPS: * Manufacturer");
wpabuf_put_be16(msg, ATTR_MANUFACTURER);
len = dev->manufacturer ? os_strlen(dev->manufacturer) : 0;
+#ifndef CONFIG_WPS_STRICT
if (len == 0) {
/*
* Some deployed WPS implementations fail to parse zero-length
- * attributes. As a workaround, send a null character if the
+ * attributes. As a workaround, send a space character if the
* device attribute string is empty.
*/
wpabuf_put_be16(msg, 1);
- wpabuf_put_u8(msg, '\0');
- } else {
- wpabuf_put_be16(msg, len);
- wpabuf_put_data(msg, dev->manufacturer, len);
+ wpabuf_put_u8(msg, ' ');
+ return 0;
}
+#endif /* CONFIG_WPS_STRICT */
+ wpabuf_put_be16(msg, len);
+ wpabuf_put_data(msg, dev->manufacturer, len);
return 0;
}
-static int wps_build_model_name(struct wps_device_data *dev,
- struct wpabuf *msg)
+int wps_build_model_name(struct wps_device_data *dev, struct wpabuf *msg)
{
size_t len;
wpa_printf(MSG_DEBUG, "WPS: * Model Name");
wpabuf_put_be16(msg, ATTR_MODEL_NAME);
len = dev->model_name ? os_strlen(dev->model_name) : 0;
+#ifndef CONFIG_WPS_STRICT
if (len == 0) {
/*
* Some deployed WPS implementations fail to parse zero-length
- * attributes. As a workaround, send a null character if the
+ * attributes. As a workaround, send a space character if the
* device attribute string is empty.
*/
wpabuf_put_be16(msg, 1);
- wpabuf_put_u8(msg, '\0');
- } else {
- wpabuf_put_be16(msg, len);
- wpabuf_put_data(msg, dev->model_name, len);
+ wpabuf_put_u8(msg, ' ');
+ return 0;
}
+#endif /* CONFIG_WPS_STRICT */
+ wpabuf_put_be16(msg, len);
+ wpabuf_put_data(msg, dev->model_name, len);
return 0;
}
-static int wps_build_model_number(struct wps_device_data *dev,
- struct wpabuf *msg)
+int wps_build_model_number(struct wps_device_data *dev, struct wpabuf *msg)
{
size_t len;
wpa_printf(MSG_DEBUG, "WPS: * Model Number");
wpabuf_put_be16(msg, ATTR_MODEL_NUMBER);
len = dev->model_number ? os_strlen(dev->model_number) : 0;
+#ifndef CONFIG_WPS_STRICT
if (len == 0) {
/*
* Some deployed WPS implementations fail to parse zero-length
- * attributes. As a workaround, send a null character if the
+ * attributes. As a workaround, send a space character if the
* device attribute string is empty.
*/
wpabuf_put_be16(msg, 1);
- wpabuf_put_u8(msg, '\0');
- } else {
- wpabuf_put_be16(msg, len);
- wpabuf_put_data(msg, dev->model_number, len);
+ wpabuf_put_u8(msg, ' ');
+ return 0;
}
+#endif /* CONFIG_WPS_STRICT */
+ wpabuf_put_be16(msg, len);
+ wpabuf_put_data(msg, dev->model_number, len);
return 0;
}
wpa_printf(MSG_DEBUG, "WPS: * Serial Number");
wpabuf_put_be16(msg, ATTR_SERIAL_NUMBER);
len = dev->serial_number ? os_strlen(dev->serial_number) : 0;
+#ifndef CONFIG_WPS_STRICT
if (len == 0) {
/*
* Some deployed WPS implementations fail to parse zero-length
- * attributes. As a workaround, send a null character if the
+ * attributes. As a workaround, send a space character if the
* device attribute string is empty.
*/
wpabuf_put_be16(msg, 1);
- wpabuf_put_u8(msg, '\0');
- } else {
- wpabuf_put_be16(msg, len);
- wpabuf_put_data(msg, dev->serial_number, len);
+ wpabuf_put_u8(msg, ' ');
+ return 0;
}
+#endif /* CONFIG_WPS_STRICT */
+ wpabuf_put_be16(msg, len);
+ wpabuf_put_data(msg, dev->serial_number, len);
return 0;
}
}
-static int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg)
+int wps_build_secondary_dev_type(struct wps_device_data *dev,
+ struct wpabuf *msg)
+{
+ if (!dev->num_sec_dev_types)
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "WPS: * Secondary Device Type");
+ wpabuf_put_be16(msg, ATTR_SECONDARY_DEV_TYPE_LIST);
+ wpabuf_put_be16(msg, WPS_DEV_TYPE_LEN * dev->num_sec_dev_types);
+ wpabuf_put_data(msg, dev->sec_dev_type,
+ WPS_DEV_TYPE_LEN * dev->num_sec_dev_types);
+
+ return 0;
+}
+
+
+int wps_build_req_dev_type(struct wps_device_data *dev, struct wpabuf *msg,
+ unsigned int num_req_dev_types,
+ const u8 *req_dev_types)
+{
+ unsigned int i;
+
+ for (i = 0; i < num_req_dev_types; i++) {
+ wpa_hexdump(MSG_DEBUG, "WPS: * Requested Device Type",
+ req_dev_types + i * WPS_DEV_TYPE_LEN,
+ WPS_DEV_TYPE_LEN);
+ wpabuf_put_be16(msg, ATTR_REQUESTED_DEV_TYPE);
+ wpabuf_put_be16(msg, WPS_DEV_TYPE_LEN);
+ wpabuf_put_data(msg, req_dev_types + i * WPS_DEV_TYPE_LEN,
+ WPS_DEV_TYPE_LEN);
+ }
+
+ return 0;
+}
+
+
+int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg)
{
size_t len;
wpa_printf(MSG_DEBUG, "WPS: * Device Name");
wpabuf_put_be16(msg, ATTR_DEV_NAME);
len = dev->device_name ? os_strlen(dev->device_name) : 0;
+#ifndef CONFIG_WPS_STRICT
if (len == 0) {
/*
* Some deployed WPS implementations fail to parse zero-length
- * attributes. As a workaround, send a null character if the
+ * attributes. As a workaround, send a space character if the
* device attribute string is empty.
*/
wpabuf_put_be16(msg, 1);
- wpabuf_put_u8(msg, '\0');
- } else {
- wpabuf_put_be16(msg, len);
- wpabuf_put_data(msg, dev->device_name, len);
+ wpabuf_put_u8(msg, ' ');
+ return 0;
}
+#endif /* CONFIG_WPS_STRICT */
+ wpabuf_put_be16(msg, len);
+ wpabuf_put_data(msg, dev->device_name, len);
return 0;
}
}
+int wps_build_vendor_ext(struct wps_device_data *dev, struct wpabuf *msg)
+{
+ int i;
+
+ for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) {
+ if (dev->vendor_ext[i] == NULL)
+ continue;
+ wpa_hexdump(MSG_DEBUG, "WPS: * Vendor Extension",
+ wpabuf_head_u8(dev->vendor_ext[i]),
+ wpabuf_len(dev->vendor_ext[i]));
+ wpabuf_put_be16(msg, ATTR_VENDOR_EXT);
+ wpabuf_put_be16(msg, wpabuf_len(dev->vendor_ext[i]));
+ wpabuf_put_buf(msg, dev->vendor_ext[i]);
+ }
+
+ return 0;
+}
+
+
static int wps_process_manufacturer(struct wps_device_data *dev, const u8 *str,
size_t str_len)
{
struct wps_parse_attr;
+int wps_build_manufacturer(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_model_name(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_model_number(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg);
int wps_build_device_attrs(struct wps_device_data *dev, struct wpabuf *msg);
int wps_build_os_version(struct wps_device_data *dev, struct wpabuf *msg);
int wps_build_rf_bands(struct wps_device_data *dev, struct wpabuf *msg);
int wps_build_primary_dev_type(struct wps_device_data *dev,
struct wpabuf *msg);
+int wps_build_secondary_dev_type(struct wps_device_data *dev,
+ struct wpabuf *msg);
+int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg);
int wps_process_device_attrs(struct wps_device_data *dev,
struct wps_parse_attr *attr);
int wps_process_os_version(struct wps_device_data *dev, const u8 *ver);
void wps_device_data_dup(struct wps_device_data *dst,
const struct wps_device_data *src);
void wps_device_data_free(struct wps_device_data *dev);
+int wps_build_vendor_ext(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_req_dev_type(struct wps_device_data *dev, struct wpabuf *msg,
+ unsigned int num_req_dev_types,
+ const u8 *req_dev_types);
#endif /* WPS_DEV_ATTR_H */
#include "common.h"
#include "crypto/crypto.h"
#include "crypto/sha256.h"
+#include "crypto/random.h"
#include "wps_i.h"
#include "wps_dev_attr.h"
const u8 *addr[4];
size_t len[4];
- if (os_get_random(wps->snonce, 2 * WPS_SECRET_NONCE_LEN) < 0)
+ if (random_get_bytes(wps->snonce, 2 * WPS_SECRET_NONCE_LEN) < 0)
return -1;
wpa_hexdump(MSG_DEBUG, "WPS: E-S1", wps->snonce, WPS_SECRET_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "WPS: E-S2",
static struct wpabuf * wps_build_m1(struct wps_data *wps)
{
struct wpabuf *msg;
+ u16 config_methods;
- if (os_get_random(wps->nonce_e, WPS_NONCE_LEN) < 0)
+ if (random_get_bytes(wps->nonce_e, WPS_NONCE_LEN) < 0)
return NULL;
wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Nonce",
wps->nonce_e, WPS_NONCE_LEN);
if (msg == NULL)
return NULL;
+ config_methods = wps->wps->config_methods;
+ if (wps->wps->ap && !wps->pbc_in_m1 &&
+ (wps->dev_password_len != 0 ||
+ (config_methods & WPS_CONFIG_DISPLAY))) {
+ /*
+ * These are the methods that the AP supports as an Enrollee
+ * for adding external Registrars, so remove PushButton.
+ *
+ * As a workaround for Windows 7 mechanism for probing WPS
+ * capabilities from M1, leave PushButton option if no PIN
+ * method is available or if WPS configuration enables PBC
+ * workaround.
+ */
+ config_methods &= ~WPS_CONFIG_PUSHBUTTON;
+#ifdef CONFIG_WPS2
+ config_methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
+ WPS_CONFIG_PHY_PUSHBUTTON);
+#endif /* CONFIG_WPS2 */
+ }
+
if (wps_build_version(msg) ||
wps_build_msg_type(msg, WPS_M1) ||
wps_build_uuid_e(msg, wps->uuid_e) ||
wps_build_auth_type_flags(wps, msg) ||
wps_build_encr_type_flags(wps, msg) ||
wps_build_conn_type_flags(wps, msg) ||
- wps_build_config_methods(msg, wps->wps->config_methods) ||
+ wps_build_config_methods(msg, config_methods) ||
wps_build_wps_state(wps, msg) ||
wps_build_device_attrs(&wps->wps->dev, msg) ||
wps_build_rf_bands(&wps->wps->dev, msg) ||
wps_build_assoc_state(wps, msg) ||
wps_build_dev_password_id(msg, wps->dev_pw_id) ||
wps_build_config_error(msg, WPS_CFG_NO_ERROR) ||
- wps_build_os_version(&wps->wps->dev, msg)) {
+ wps_build_os_version(&wps->wps->dev, msg) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0)) {
wpabuf_free(msg);
return NULL;
}
wps_build_msg_type(msg, WPS_M3) ||
wps_build_registrar_nonce(wps, msg) ||
wps_build_e_hash(wps, msg) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0) ||
wps_build_authenticator(wps, msg)) {
wpabuf_free(msg);
return NULL;
wps_build_e_snonce1(wps, plain) ||
wps_build_key_wrap_auth(wps, plain) ||
wps_build_encr_settings(wps, msg, plain) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0) ||
wps_build_authenticator(wps, msg)) {
wpabuf_free(plain);
wpabuf_free(msg);
static int wps_build_cred_auth_type(struct wps_data *wps, struct wpabuf *msg)
{
- wpa_printf(MSG_DEBUG, "WPS: * Authentication Type");
+ u16 auth_type = wps->wps->auth_types;
+
+ /* Select the best authentication type */
+ if (auth_type & WPS_AUTH_WPA2PSK)
+ auth_type = WPS_AUTH_WPA2PSK;
+ else if (auth_type & WPS_AUTH_WPAPSK)
+ auth_type = WPS_AUTH_WPAPSK;
+ else if (auth_type & WPS_AUTH_OPEN)
+ auth_type = WPS_AUTH_OPEN;
+ else if (auth_type & WPS_AUTH_SHARED)
+ auth_type = WPS_AUTH_SHARED;
+
+ wpa_printf(MSG_DEBUG, "WPS: * Authentication Type (0x%x)", auth_type);
wpabuf_put_be16(msg, ATTR_AUTH_TYPE);
wpabuf_put_be16(msg, 2);
- wpabuf_put_be16(msg, wps->wps->auth_types);
+ wpabuf_put_be16(msg, auth_type);
return 0;
}
static int wps_build_cred_encr_type(struct wps_data *wps, struct wpabuf *msg)
{
- wpa_printf(MSG_DEBUG, "WPS: * Encryption Type");
+ u16 encr_type = wps->wps->encr_types;
+
+ /* Select the best encryption type */
+ if (wps->wps->auth_types & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) {
+ if (encr_type & WPS_ENCR_AES)
+ encr_type = WPS_ENCR_AES;
+ else if (encr_type & WPS_ENCR_TKIP)
+ encr_type = WPS_ENCR_TKIP;
+ } else {
+ if (encr_type & WPS_ENCR_WEP)
+ encr_type = WPS_ENCR_WEP;
+ else if (encr_type & WPS_ENCR_NONE)
+ encr_type = WPS_ENCR_NONE;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: * Encryption Type (0x%x)", encr_type);
wpabuf_put_be16(msg, ATTR_ENCR_TYPE);
wpabuf_put_be16(msg, 2);
- wpabuf_put_be16(msg, wps->wps->encr_types);
+ wpabuf_put_be16(msg, encr_type);
return 0;
}
(wps->wps->ap && wps_build_ap_settings(wps, plain)) ||
wps_build_key_wrap_auth(wps, plain) ||
wps_build_encr_settings(wps, msg, plain) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0) ||
wps_build_authenticator(wps, msg)) {
wpabuf_free(plain);
wpabuf_free(msg);
if (wps_build_version(msg) ||
wps_build_msg_type(msg, WPS_WSC_DONE) ||
wps_build_enrollee_nonce(wps, msg) ||
- wps_build_registrar_nonce(wps, msg)) {
+ wps_build_registrar_nonce(wps, msg) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0)) {
wpabuf_free(msg);
return NULL;
}
}
-static struct wpabuf * wps_build_wsc_ack(struct wps_data *wps)
-{
- struct wpabuf *msg;
-
- wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_ACK");
-
- msg = wpabuf_alloc(1000);
- if (msg == NULL)
- return NULL;
-
- if (wps_build_version(msg) ||
- wps_build_msg_type(msg, WPS_WSC_ACK) ||
- wps_build_enrollee_nonce(wps, msg) ||
- wps_build_registrar_nonce(wps, msg)) {
- wpabuf_free(msg);
- return NULL;
- }
-
- return msg;
-}
-
-
-static struct wpabuf * wps_build_wsc_nack(struct wps_data *wps)
-{
- struct wpabuf *msg;
-
- wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_NACK");
-
- msg = wpabuf_alloc(1000);
- if (msg == NULL)
- return NULL;
-
- if (wps_build_version(msg) ||
- wps_build_msg_type(msg, WPS_WSC_NACK) ||
- wps_build_enrollee_nonce(wps, msg) ||
- wps_build_registrar_nonce(wps, msg) ||
- wps_build_config_error(msg, wps->config_error)) {
- wpabuf_free(msg);
- return NULL;
- }
-
- return msg;
-}
-
-
struct wpabuf * wps_enrollee_get_msg(struct wps_data *wps,
enum wsc_op_code *op_code)
{
static int wps_process_cred_e(struct wps_data *wps, const u8 *cred,
- size_t cred_len)
+ size_t cred_len, int wps2)
{
struct wps_parse_attr attr;
struct wpabuf msg;
* reasons, allow this to be processed since we do not really
* use the MAC Address information for anything.
*/
+#ifdef CONFIG_WPS_STRICT
+ if (wps2) {
+ wpa_printf(MSG_INFO, "WPS: Do not accept incorrect "
+ "MAC Address in AP Settings");
+ return -1;
+ }
+#endif /* CONFIG_WPS_STRICT */
+ }
+
+#ifdef CONFIG_WPS2
+ if (!(wps->cred.encr_type &
+ (WPS_ENCR_NONE | WPS_ENCR_TKIP | WPS_ENCR_AES))) {
+ if (wps->cred.encr_type & WPS_ENCR_WEP) {
+ wpa_printf(MSG_INFO, "WPS: Reject Credential "
+ "due to WEP configuration");
+ wps->error_indication = WPS_EI_SECURITY_WEP_PROHIBITED;
+ return -2;
+ }
+
+ wpa_printf(MSG_INFO, "WPS: Reject Credential due to "
+ "invalid encr_type 0x%x", wps->cred.encr_type);
+ return -1;
}
+#endif /* CONFIG_WPS2 */
if (wps->wps->cred_cb) {
wps->cred.cred_attr = cred - 4;
static int wps_process_creds(struct wps_data *wps, const u8 *cred[],
- size_t cred_len[], size_t num_cred)
+ size_t cred_len[], size_t num_cred, int wps2)
{
size_t i;
+ int ok = 0;
if (wps->wps->ap)
return 0;
}
for (i = 0; i < num_cred; i++) {
- if (wps_process_cred_e(wps, cred[i], cred_len[i]))
+ int res;
+ res = wps_process_cred_e(wps, cred[i], cred_len[i], wps2);
+ if (res == 0)
+ ok++;
+ else if (res == -2)
+ wpa_printf(MSG_DEBUG, "WPS: WEP credential skipped");
+ else
return -1;
}
+ if (ok == 0) {
+ wpa_printf(MSG_DEBUG, "WPS: No valid Credential attribute "
+ "received");
+ return -1;
+ }
+
return 0;
}
static int wps_process_ap_settings_e(struct wps_data *wps,
struct wps_parse_attr *attr,
- struct wpabuf *attrs)
+ struct wpabuf *attrs, int wps2)
{
struct wps_credential cred;
* reasons, allow this to be processed since we do not really
* use the MAC Address information for anything.
*/
+#ifdef CONFIG_WPS_STRICT
+ if (wps2) {
+ wpa_printf(MSG_INFO, "WPS: Do not accept incorrect "
+ "MAC Address in AP Settings");
+ return -1;
+ }
+#endif /* CONFIG_WPS_STRICT */
}
+#ifdef CONFIG_WPS2
+ if (!(cred.encr_type & (WPS_ENCR_NONE | WPS_ENCR_TKIP | WPS_ENCR_AES)))
+ {
+ if (cred.encr_type & WPS_ENCR_WEP) {
+ wpa_printf(MSG_INFO, "WPS: Reject new AP settings "
+ "due to WEP configuration");
+ wps->error_indication = WPS_EI_SECURITY_WEP_PROHIBITED;
+ return -1;
+ }
+
+ wpa_printf(MSG_INFO, "WPS: Reject new AP settings due to "
+ "invalid encr_type 0x%x", cred.encr_type);
+ return -1;
+ }
+#endif /* CONFIG_WPS2 */
+
+#ifdef CONFIG_WPS_STRICT
+ if (wps2) {
+ if ((cred.encr_type & (WPS_ENCR_TKIP | WPS_ENCR_AES)) ==
+ WPS_ENCR_TKIP ||
+ (cred.auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) ==
+ WPS_AUTH_WPAPSK) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid WSC 2.0 "
+ "AP Settings: WPA-Personal/TKIP only");
+ wps->error_indication =
+ WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED;
+ return -1;
+ }
+ }
+#endif /* CONFIG_WPS_STRICT */
+
+#ifdef CONFIG_WPS2
+ if ((cred.encr_type & (WPS_ENCR_TKIP | WPS_ENCR_AES)) == WPS_ENCR_TKIP)
+ {
+ wpa_printf(MSG_DEBUG, "WPS: Upgrade encr_type TKIP -> "
+ "TKIP+AES");
+ cred.encr_type |= WPS_ENCR_AES;
+ }
+
+ if ((cred.auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) ==
+ WPS_AUTH_WPAPSK) {
+ wpa_printf(MSG_DEBUG, "WPS: Upgrade auth_type WPAPSK -> "
+ "WPAPSK+WPA2PSK");
+ cred.auth_type |= WPS_AUTH_WPA2PSK;
+ }
+#endif /* CONFIG_WPS2 */
+
if (wps->wps->cred_cb) {
cred.cred_attr = wpabuf_head(attrs);
cred.cred_attr_len = wpabuf_len(attrs);
return WPS_CONTINUE;
}
+ /*
+ * Stop here on an AP as an Enrollee if AP Setup is locked unless the
+ * special locked mode is used to allow protocol run up to M7 in order
+ * to support external Registrars that only learn the current AP
+ * configuration without changing it.
+ */
if (wps->wps->ap &&
- (wps->wps->ap_setup_locked || wps->dev_password == NULL)) {
+ ((wps->wps->ap_setup_locked && wps->wps->ap_setup_locked != 2) ||
+ wps->dev_password == NULL)) {
wpa_printf(MSG_DEBUG, "WPS: AP Setup is locked - refuse "
"registration of a new Registrar");
wps->config_error = WPS_CFG_SETUP_LOCKED;
return WPS_CONTINUE;
}
+ if (wps_validate_m4_encr(decrypted, attr->version2 != 0) < 0) {
+ wpabuf_free(decrypted);
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
"attribute");
if (wps_parse_msg(decrypted, &eattr) < 0 ||
return WPS_CONTINUE;
}
+ if (wps_validate_m6_encr(decrypted, attr->version2 != 0) < 0) {
+ wpabuf_free(decrypted);
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
"attribute");
if (wps_parse_msg(decrypted, &eattr) < 0 ||
}
wpabuf_free(decrypted);
+ if (wps->wps->ap)
+ wps->wps->event_cb(wps->wps->cb_ctx, WPS_EV_AP_PIN_SUCCESS,
+ NULL);
+
wps->state = SEND_M7;
return WPS_CONTINUE;
}
return WPS_CONTINUE;
}
+ if (wps->wps->ap && wps->wps->ap_setup_locked) {
+ /*
+ * Stop here if special ap_setup_locked == 2 mode allowed the
+ * protocol to continue beyond M2. This allows ER to learn the
+ * current AP settings without changing them.
+ */
+ wpa_printf(MSG_DEBUG, "WPS: AP Setup is locked - refuse "
+ "registration of a new Registrar");
+ wps->config_error = WPS_CFG_SETUP_LOCKED;
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
attr->encr_settings_len);
if (decrypted == NULL) {
return WPS_CONTINUE;
}
+ if (wps_validate_m8_encr(decrypted, wps->wps->ap, attr->version2 != 0)
+ < 0) {
+ wpabuf_free(decrypted);
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
"attribute");
if (wps_parse_msg(decrypted, &eattr) < 0 ||
wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
wps_process_creds(wps, eattr.cred, eattr.cred_len,
- eattr.num_cred) ||
- wps_process_ap_settings_e(wps, &eattr, decrypted)) {
+ eattr.num_cred, attr->version2 != NULL) ||
+ wps_process_ap_settings_e(wps, &eattr, decrypted,
+ attr->version2 != NULL)) {
wpabuf_free(decrypted);
wps->state = SEND_WSC_NACK;
return WPS_CONTINUE;
if (wps_parse_msg(msg, &attr) < 0)
return WPS_FAILURE;
- if (!wps_version_supported(attr.version)) {
- wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x",
- attr.version ? *attr.version : 0);
- return WPS_FAILURE;
- }
-
if (attr.enrollee_nonce == NULL ||
os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) {
wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
if (attr.msg_type == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
- return WPS_FAILURE;
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
}
switch (*attr.msg_type) {
case WPS_M2:
+ if (wps_validate_m2(msg) < 0)
+ return WPS_FAILURE;
ret = wps_process_m2(wps, msg, &attr);
break;
case WPS_M2D:
+ if (wps_validate_m2d(msg) < 0)
+ return WPS_FAILURE;
ret = wps_process_m2d(wps, &attr);
break;
case WPS_M4:
+ if (wps_validate_m4(msg) < 0)
+ return WPS_FAILURE;
ret = wps_process_m4(wps, msg, &attr);
if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
- wps_fail_event(wps->wps, WPS_M4);
+ wps_fail_event(wps->wps, WPS_M4, wps->config_error,
+ wps->error_indication);
break;
case WPS_M6:
+ if (wps_validate_m6(msg) < 0)
+ return WPS_FAILURE;
ret = wps_process_m6(wps, msg, &attr);
if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
- wps_fail_event(wps->wps, WPS_M6);
+ wps_fail_event(wps->wps, WPS_M6, wps->config_error,
+ wps->error_indication);
break;
case WPS_M8:
+ if (wps_validate_m8(msg) < 0)
+ return WPS_FAILURE;
ret = wps_process_m8(wps, msg, &attr);
if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
- wps_fail_event(wps->wps, WPS_M8);
+ wps_fail_event(wps->wps, WPS_M8, wps->config_error,
+ wps->error_indication);
break;
default:
wpa_printf(MSG_DEBUG, "WPS: Unsupported Message Type %d",
if (wps_parse_msg(msg, &attr) < 0)
return WPS_FAILURE;
- if (!wps_version_supported(attr.version)) {
- wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x",
- attr.version ? *attr.version : 0);
- return WPS_FAILURE;
- }
-
if (attr.msg_type == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
return WPS_FAILURE;
const struct wpabuf *msg)
{
struct wps_parse_attr attr;
+ u16 config_error;
wpa_printf(MSG_DEBUG, "WPS: Received WSC_NACK");
if (wps_parse_msg(msg, &attr) < 0)
return WPS_FAILURE;
- if (!wps_version_supported(attr.version)) {
- wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x",
- attr.version ? *attr.version : 0);
- return WPS_FAILURE;
- }
-
if (attr.msg_type == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
return WPS_FAILURE;
return WPS_FAILURE;
}
+ config_error = WPA_GET_BE16(attr.config_error);
wpa_printf(MSG_DEBUG, "WPS: Registrar terminated negotiation with "
- "Configuration Error %d", WPA_GET_BE16(attr.config_error));
+ "Configuration Error %d", config_error);
switch (wps->state) {
case RECV_M4:
- wps_fail_event(wps->wps, WPS_M3);
+ wps_fail_event(wps->wps, WPS_M3, config_error,
+ wps->error_indication);
break;
case RECV_M6:
- wps_fail_event(wps->wps, WPS_M5);
+ wps_fail_event(wps->wps, WPS_M5, config_error,
+ wps->error_indication);
break;
case RECV_M8:
- wps_fail_event(wps->wps, WPS_M7);
+ wps_fail_event(wps->wps, WPS_M7, config_error,
+ wps->error_indication);
break;
default:
break;
case WSC_UPnP:
return wps_process_wsc_msg(wps, msg);
case WSC_ACK:
+ if (wps_validate_wsc_ack(msg) < 0)
+ return WPS_FAILURE;
return wps_process_wsc_ack(wps, msg);
case WSC_NACK:
+ if (wps_validate_wsc_nack(msg) < 0)
+ return WPS_FAILURE;
return wps_process_wsc_nack(wps, msg);
default:
wpa_printf(MSG_DEBUG, "WPS: Unsupported op_code %d", op_code);
}
-static struct wps_er_sta * wps_er_sta_get(struct wps_er_ap *ap, const u8 *addr)
+static struct wps_er_sta * wps_er_sta_get(struct wps_er_ap *ap, const u8 *addr,
+ const u8 *uuid)
{
struct wps_er_sta *sta;
dl_list_for_each(sta, &ap->sta, struct wps_er_sta, list) {
- if (os_memcmp(sta->addr, addr, ETH_ALEN) == 0)
+ if ((addr == NULL ||
+ os_memcmp(sta->addr, addr, ETH_ALEN) == 0) &&
+ (uuid == NULL ||
+ os_memcmp(uuid, sta->uuid, WPS_UUID_LEN) == 0))
return sta;
}
return NULL;
wps_er_ap_unsubscribed(ap->er, ap);
}
+
+static struct wps_er_ap_settings * wps_er_ap_get_settings(struct wps_er *er,
+ const u8 *uuid)
+{
+ struct wps_er_ap_settings *s;
+ dl_list_for_each(s, &er->ap_settings, struct wps_er_ap_settings, list)
+ if (os_memcmp(uuid, s->uuid, WPS_UUID_LEN) == 0)
+ return s;
+ return NULL;
+}
+
+
+int wps_er_ap_cache_settings(struct wps_er *er, struct in_addr *addr)
+{
+ struct wps_er_ap *ap;
+ struct wps_er_ap_settings *settings;
+
+ ap = wps_er_ap_get(er, addr, NULL);
+ if (ap == NULL || ap->ap_settings == NULL)
+ return -1;
+
+ settings = wps_er_ap_get_settings(er, ap->uuid);
+ if (!settings) {
+ settings = os_zalloc(sizeof(*settings));
+ if (settings == NULL)
+ return -1;
+ os_memcpy(settings->uuid, ap->uuid, WPS_UUID_LEN);
+ dl_list_add(&er->ap_settings, &settings->list);
+ }
+ os_memcpy(&settings->ap_settings, ap->ap_settings,
+ sizeof(struct wps_credential));
+
+ return 0;
+}
+
+
+static int wps_er_ap_use_cached_settings(struct wps_er *er,
+ struct wps_er_ap *ap)
+{
+ struct wps_er_ap_settings *s;
+
+ if (ap->ap_settings)
+ return 0;
+
+ s = wps_er_ap_get_settings(ap->er, ap->uuid);
+ if (!s)
+ return -1;
+
+ ap->ap_settings = os_malloc(sizeof(*ap->ap_settings));
+ if (ap->ap_settings == NULL)
+ return -1;
+
+ os_memcpy(ap->ap_settings, &s->ap_settings, sizeof(*ap->ap_settings));
+ wpa_printf(MSG_DEBUG, "WPS ER: Use cached AP settings");
+ return 0;
+}
+
+
static void wps_er_ap_remove_entry(struct wps_er *er, struct wps_er_ap *ap)
{
wpa_printf(MSG_DEBUG, "WPS ER: Removing AP entry for %s (%s)",
wpa_printf(MSG_DEBUG, "WPS ER: Subscribed to events");
ap->subscribed = 1;
wps_er_get_sid(ap, http_client_get_hdr_line(c, "SID"));
+ wps_er_ap_use_cached_settings(ap->er, ap);
wps_er_ap_event(ap->er->wps, ap, WPS_EV_ER_AP_ADD);
break;
case HTTP_CLIENT_FAILED:
}
+static const char * wps_er_find_wfadevice(const char *data)
+{
+ const char *tag, *tagname, *end;
+ char *val;
+ int found = 0;
+
+ while (!found) {
+ /* Find next <device> */
+ for (;;) {
+ if (xml_next_tag(data, &tag, &tagname, &end))
+ return NULL;
+ data = end;
+ if (!os_strncasecmp(tagname, "device", 6) &&
+ *tag != '/' &&
+ (tagname[6] == '>' || !isgraph(tagname[6]))) {
+ break;
+ }
+ }
+
+ /* Check whether deviceType is WFADevice */
+ val = xml_get_first_item(data, "deviceType");
+ if (val == NULL)
+ return NULL;
+ wpa_printf(MSG_DEBUG, "WPS ER: Found deviceType '%s'", val);
+ found = os_strcasecmp(val, "urn:schemas-wifialliance-org:"
+ "device:WFADevice:1") == 0;
+ os_free(val);
+ }
+
+ return data;
+}
+
+
static void wps_er_parse_device_description(struct wps_er_ap *ap,
struct wpabuf *reply)
{
/* Note: reply includes null termination after the buffer data */
- const char *data = wpabuf_head(reply);
+ const char *tmp, *data = wpabuf_head(reply);
char *pos;
wpa_hexdump_ascii(MSG_MSGDUMP, "WPS ER: Device info",
wpabuf_head(reply), wpabuf_len(reply));
+ /*
+ * The root device description may include multiple devices, so first
+ * find the beginning of the WFADevice description to allow the
+ * simplistic parser to pick the correct entries.
+ */
+ tmp = wps_er_find_wfadevice(data);
+ if (tmp == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS ER: WFADevice:1 device not found - "
+ "trying to parse invalid data");
+ } else
+ data = tmp;
+
ap->friendly_name = xml_get_first_item(data, "friendlyName");
wpa_printf(MSG_DEBUG, "WPS ER: friendlyName='%s'", ap->friendly_name);
static void wps_er_ap_remove_all(struct wps_er *er)
{
struct wps_er_ap *prev, *ap;
+ struct wps_er_ap_settings *prev_s, *s;
dl_list_for_each_safe(ap, prev, &er->ap, struct wps_er_ap, list)
wps_er_ap_remove_entry(er, ap);
+ dl_list_for_each_safe(s, prev_s, &er->ap_settings,
+ struct wps_er_ap_settings, list)
+ os_free(s);
}
struct wps_parse_attr *attr,
int probe_req)
{
- struct wps_er_sta *sta = wps_er_sta_get(ap, addr);
+ struct wps_er_sta *sta = wps_er_sta_get(ap, addr, NULL);
int new_sta = 0;
int m1;
wpa_hexdump_buf(MSG_MSGDUMP, "WPS ER: WLANEvent - Enrollee's message "
"(TLVs from Probe Request)", msg);
+ if (wps_validate_probe_req(msg, addr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: ER: Ignore invalid proxied "
+ "Probe Request frame from " MACSTR, MAC2STR(addr));
+ return;
+ }
+
if (wps_parse_msg(msg, &attr) < 0) {
wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse TLVs in "
"WLANEvent message");
}
wps_er_add_sta_data(ap, addr, &attr, 1);
+ wps_registrar_probe_req_rx(ap->er->wps->registrar, addr, msg, 0);
}
struct wps_er *
-wps_er_init(struct wps_context *wps, const char *ifname)
+wps_er_init(struct wps_context *wps, const char *ifname, const char *filter)
{
struct wps_er *er;
struct in_addr addr;
return NULL;
dl_list_init(&er->ap);
dl_list_init(&er->ap_unsubscribing);
+ dl_list_init(&er->ap_settings);
er->multicast_sd = -1;
er->ssdp_sd = -1;
/* Limit event_id to < 32 bits to avoid issues with atoi() */
er->event_id &= 0x0fffffff;
+ if (filter) {
+ if (inet_aton(filter, &er->filter_addr) == 0) {
+ wpa_printf(MSG_INFO, "WPS UPnP: Invalid filter "
+ "address %s", filter);
+ wps_er_deinit(er, NULL, NULL);
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Only accepting connections "
+ "with %s", filter);
+ }
if (get_netif_info(ifname, &er->ip_addr, &er->ip_addr_text,
er->mac_addr)) {
wpa_printf(MSG_INFO, "WPS UPnP: Could not get IP/MAC address "
}
if (wps_er_ssdp_init(er) < 0) {
+ wpa_printf(MSG_INFO, "WPS UPnP: SSDP initialization failed");
wps_er_deinit(er, NULL, NULL);
return NULL;
}
addr.s_addr = er->ip_addr;
er->http_srv = http_server_init(&addr, -1, wps_er_http_req, er);
if (er->http_srv == NULL) {
+ wpa_printf(MSG_INFO, "WPS UPnP: HTTP initialization failed");
wps_er_deinit(er, NULL, NULL);
return NULL;
}
enum http_client_event event)
{
struct wps_er_ap *ap = ctx;
+ union wps_event_data data;
+
+ os_memset(&data, 0, sizeof(data));
switch (event) {
case HTTP_CLIENT_OK:
wpa_printf(MSG_DEBUG, "WPS ER: SetSelectedRegistrar OK");
+ data.set_sel_reg.state = WPS_ER_SET_SEL_REG_DONE;
+ data.set_sel_reg.uuid = ap->uuid;
break;
case HTTP_CLIENT_FAILED:
case HTTP_CLIENT_INVALID_REPLY:
case HTTP_CLIENT_TIMEOUT:
wpa_printf(MSG_DEBUG, "WPS ER: SetSelectedRegistrar failed");
+ data.set_sel_reg.state = WPS_ER_SET_SEL_REG_FAILED;
+ data.set_sel_reg.uuid = ap->uuid;
break;
}
http_client_free(ap->http);
ap->http = NULL;
+
+ if (data.set_sel_reg.uuid)
+ ap->er->wps->event_cb(ap->er->wps->cb_ctx,
+ WPS_EV_ER_SET_SELECTED_REGISTRAR, &data);
}
return;
}
+ if (ap->wps) {
+ wpa_printf(MSG_DEBUG, "WPS ER: Pending WPS operation for AP - "
+ "skip SetSelectedRegistrar");
+ return;
+ }
+
url = http_client_url_parse(ap->control_url, &dst, &path);
if (url == NULL) {
wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse controlURL");
}
+static int wps_er_build_uuid_r(struct wpabuf *msg, const u8 *uuid_r)
+{
+#ifdef CONFIG_WPS2
+ wpabuf_put_be16(msg, ATTR_UUID_R);
+ wpabuf_put_be16(msg, WPS_UUID_LEN);
+ wpabuf_put_data(msg, uuid_r, WPS_UUID_LEN);
+#endif /* CONFIG_WPS2 */
+ return 0;
+}
+
+
void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id,
u16 sel_reg_config_methods)
{
struct wpabuf *msg;
struct wps_er_ap *ap;
+ struct wps_registrar *reg = er->wps->registrar;
+ const u8 *auth_macs;
+#ifdef CONFIG_WPS2
+ u8 bcast[ETH_ALEN];
+#endif /* CONFIG_WPS2 */
+ size_t count;
+ union wps_event_data data;
+
+ if (er->skip_set_sel_reg) {
+ wpa_printf(MSG_DEBUG, "WPS ER: Skip SetSelectedRegistrar");
+ return;
+ }
msg = wpabuf_alloc(500);
if (msg == NULL)
return;
+ auth_macs = wps_authorized_macs(reg, &count);
+#ifdef CONFIG_WPS2
+ if (count == 0) {
+ os_memset(bcast, 0xff, ETH_ALEN);
+ auth_macs = bcast;
+ count = 1;
+ }
+#endif /* CONFIG_WPS2 */
+
if (wps_build_version(msg) ||
wps_er_build_selected_registrar(msg, sel_reg) ||
wps_er_build_dev_password_id(msg, dev_passwd_id) ||
- wps_er_build_sel_reg_config_methods(msg, sel_reg_config_methods)) {
+ wps_er_build_sel_reg_config_methods(msg, sel_reg_config_methods) ||
+ wps_build_wfa_ext(msg, 0, auth_macs, count) ||
+ wps_er_build_uuid_r(msg, er->wps->uuid)) {
wpabuf_free(msg);
return;
}
- dl_list_for_each(ap, &er->ap, struct wps_er_ap, list)
+ os_memset(&data, 0, sizeof(data));
+ data.set_sel_reg.sel_reg = sel_reg;
+ data.set_sel_reg.dev_passwd_id = dev_passwd_id;
+ data.set_sel_reg.sel_reg_config_methods = sel_reg_config_methods;
+ data.set_sel_reg.state = WPS_ER_SET_SEL_REG_START;
+
+ dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) {
+ if (er->set_sel_reg_uuid_filter &&
+ os_memcmp(ap->uuid, er->set_sel_reg_uuid_filter,
+ WPS_UUID_LEN) != 0)
+ continue;
+ data.set_sel_reg.uuid = ap->uuid;
+ er->wps->event_cb(er->wps->cb_ctx,
+ WPS_EV_ER_SET_SELECTED_REGISTRAR, &data);
wps_er_send_set_sel_reg(ap, msg);
+ }
wpabuf_free(msg);
}
int wps_er_pbc(struct wps_er *er, const u8 *uuid)
{
+ int res;
+ struct wps_er_ap *ap;
+
if (er == NULL || er->wps == NULL)
return -1;
- /*
- * TODO: Should enable PBC mode only in a single AP based on which AP
- * the Enrollee (uuid) is using. Now, we may end up enabling multiple
- * APs in PBC mode which could result in session overlap at the
- * Enrollee.
- */
- if (wps_registrar_button_pushed(er->wps->registrar))
+ if (wps_registrar_pbc_overlap(er->wps->registrar, NULL, NULL)) {
+ wpa_printf(MSG_DEBUG, "WPS ER: PBC overlap - do not start PBC "
+ "mode");
+ return -2;
+ }
+
+ ap = wps_er_ap_get(er, NULL, uuid);
+ if (ap == NULL) {
+ struct wps_er_sta *sta = NULL;
+ dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) {
+ sta = wps_er_sta_get(ap, NULL, uuid);
+ if (sta) {
+ uuid = ap->uuid;
+ break;
+ }
+ }
+ if (sta == NULL)
+ return -3; /* Unknown UUID */
+ }
+
+ if (ap->ap_settings == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS ER: AP settings not known");
+ return -4;
+ }
+
+ er->set_sel_reg_uuid_filter = uuid;
+ res = wps_registrar_button_pushed(er->wps->registrar, NULL);
+ er->set_sel_reg_uuid_filter = NULL;
+ if (res)
return -1;
return 0;
static void wps_er_ap_settings_cb(void *ctx, const struct wps_credential *cred)
{
struct wps_er_ap *ap = ctx;
+ union wps_event_data data;
+
wpa_printf(MSG_DEBUG, "WPS ER: AP Settings received");
os_free(ap->ap_settings);
ap->ap_settings = os_malloc(sizeof(*cred));
ap->ap_settings->cred_attr = NULL;
}
- /* TODO: send info through ctrl_iface */
+ os_memset(&data, 0, sizeof(data));
+ data.ap_settings.uuid = ap->uuid;
+ data.ap_settings.cred = cred;
+ ap->er->wps->event_cb(ap->er->wps->cb_ctx, WPS_EV_ER_AP_SETTINGS,
+ &data);
}
if (buf == NULL) {
wpa_printf(MSG_DEBUG, "WPS ER: Could not extract "
"NewOutMessage from PutMessage response");
+ wps_deinit(ap->wps);
+ ap->wps = NULL;
return;
}
wps_er_ap_process(ap, buf);
static void wps_er_ap_process(struct wps_er_ap *ap, struct wpabuf *msg)
{
enum wps_process_res res;
+ struct wps_parse_attr attr;
+ enum wsc_op_code op_code;
+
+ op_code = WSC_MSG;
+ if (wps_parse_msg(msg, &attr) == 0 && attr.msg_type) {
+ switch (*attr.msg_type) {
+ case WPS_WSC_ACK:
+ op_code = WSC_ACK;
+ break;
+ case WPS_WSC_NACK:
+ op_code = WSC_NACK;
+ break;
+ case WPS_WSC_DONE:
+ op_code = WSC_Done;
+ break;
+ }
+ }
- res = wps_process_msg(ap->wps, WSC_MSG, msg);
+ res = wps_process_msg(ap->wps, op_code, msg);
if (res == WPS_CONTINUE) {
- enum wsc_op_code op_code;
struct wpabuf *next = wps_get_msg(ap->wps, &op_code);
if (next) {
wps_er_ap_put_message(ap, next);
wps_deinit(ap->wps);
ap->wps = NULL;
}
+ } else if (res == WPS_DONE) {
+ wpa_printf(MSG_DEBUG, "WPS ER: Protocol run done");
+ wps_deinit(ap->wps);
+ ap->wps = NULL;
} else {
wpa_printf(MSG_DEBUG, "WPS ER: Failed to process message from "
"AP (res=%d)", res);
if (wps_er_send_get_device_info(ap, wps_er_ap_learn_m1) < 0)
return -1;
- /* TODO: add PIN without SetSelectedRegistrar trigger to all APs */
- wps_registrar_add_pin(er->wps->registrar, uuid, pin, pin_len, 0);
+ er->skip_set_sel_reg = 1;
+ wps_registrar_add_pin(er->wps->registrar, NULL, uuid, pin, pin_len, 0);
+ er->skip_set_sel_reg = 0;
+
+ return 0;
+}
+
+
+int wps_er_set_config(struct wps_er *er, const u8 *uuid,
+ const struct wps_credential *cred)
+{
+ struct wps_er_ap *ap;
+
+ if (er == NULL)
+ return -1;
+
+ ap = wps_er_ap_get(er, NULL, uuid);
+ if (ap == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS ER: AP not found for set config "
+ "request");
+ return -1;
+ }
+
+ os_free(ap->ap_settings);
+ ap->ap_settings = os_malloc(sizeof(*cred));
+ if (ap->ap_settings == NULL)
+ return -1;
+ os_memcpy(ap->ap_settings, cred, sizeof(*cred));
+ ap->ap_settings->cred_attr = NULL;
+ wpa_printf(MSG_DEBUG, "WPS ER: Updated local AP settings based set "
+ "config request");
+
+ return 0;
+}
+
+
+static void wps_er_ap_config_m1(struct wps_er_ap *ap, struct wpabuf *m1)
+{
+ struct wps_config cfg;
+
+ if (ap->wps) {
+ wpa_printf(MSG_DEBUG, "WPS ER: Protocol run already in "
+ "progress with this AP");
+ return;
+ }
+
+ os_memset(&cfg, 0, sizeof(cfg));
+ cfg.wps = ap->er->wps;
+ cfg.registrar = 1;
+ cfg.new_ap_settings = ap->ap_settings;
+ ap->wps = wps_init(&cfg);
+ if (ap->wps == NULL)
+ return;
+ ap->wps->ap_settings_cb = NULL;
+ ap->wps->ap_settings_cb_ctx = NULL;
+
+ wps_er_ap_process(ap, m1);
+}
+
+
+int wps_er_config(struct wps_er *er, const u8 *uuid, const u8 *pin,
+ size_t pin_len, const struct wps_credential *cred)
+{
+ struct wps_er_ap *ap;
+
+ if (er == NULL)
+ return -1;
+
+ ap = wps_er_ap_get(er, NULL, uuid);
+ if (ap == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS ER: AP not found for config "
+ "request");
+ return -1;
+ }
+ if (ap->wps) {
+ wpa_printf(MSG_DEBUG, "WPS ER: Pending operation ongoing "
+ "with the AP - cannot start config");
+ return -1;
+ }
+
+ os_free(ap->ap_settings);
+ ap->ap_settings = os_malloc(sizeof(*cred));
+ if (ap->ap_settings == NULL)
+ return -1;
+ os_memcpy(ap->ap_settings, cred, sizeof(*cred));
+ ap->ap_settings->cred_attr = NULL;
+
+ if (wps_er_send_get_device_info(ap, wps_er_ap_config_m1) < 0)
+ return -1;
+
+ er->skip_set_sel_reg = 1;
+ wps_registrar_add_pin(er->wps->registrar, NULL, uuid, pin, pin_len, 0);
+ er->skip_set_sel_reg = 0;
return 0;
}
void (*m1_handler)(struct wps_er_ap *ap, struct wpabuf *m1);
};
+struct wps_er_ap_settings {
+ struct dl_list list;
+ u8 uuid[WPS_UUID_LEN];
+ struct wps_credential ap_settings;
+};
+
struct wps_er {
struct wps_context *wps;
char ifname[17];
int ssdp_sd;
struct dl_list ap;
struct dl_list ap_unsubscribing;
+ struct dl_list ap_settings;
struct http_server *http_srv;
int http_port;
unsigned int next_ap_id;
int deinitializing;
void (*deinit_done_cb)(void *ctx);
void *deinit_done_ctx;
+ struct in_addr filter_addr;
+ int skip_set_sel_reg;
+ const u8 *set_sel_reg_uuid_filter;
};
void wps_er_ap_add(struct wps_er *er, const u8 *uuid, struct in_addr *addr,
const char *location, int max_age);
void wps_er_ap_remove(struct wps_er *er, struct in_addr *addr);
+int wps_er_ap_cache_settings(struct wps_er *er, struct in_addr *addr);
/* wps_er_ssdp.c */
int wps_er_ssdp_init(struct wps_er *er);
if (nread <= 0)
return;
buf[nread] = '\0';
+ if (er->filter_addr.s_addr &&
+ er->filter_addr.s_addr != addr.sin_addr.s_addr)
+ return;
wpa_printf(MSG_DEBUG, "WPS ER: Received SSDP from %s",
inet_ntoa(addr.sin_addr));
return; /* Not WPS advertisement/reply */
if (byebye) {
+ wps_er_ap_cache_settings(er, &addr.sin_addr);
wps_er_ap_remove(er, &addr.sin_addr);
return;
}
int wps_er_ssdp_init(struct wps_er *er)
{
- if (add_ssdp_network(er->ifname))
+ if (add_ssdp_network(er->ifname)) {
+ wpa_printf(MSG_INFO, "WPS ER: Failed to add routing entry for "
+ "SSDP");
return -1;
+ }
er->multicast_sd = ssdp_open_multicast_sock(er->ip_addr);
- if (er->multicast_sd < 0)
+ if (er->multicast_sd < 0) {
+ wpa_printf(MSG_INFO, "WPS ER: Failed to open multicast socket "
+ "for SSDP");
return -1;
+ }
er->ssdp_sd = ssdp_listener_open();
- if (er->ssdp_sd < 0)
+ if (er->ssdp_sd < 0) {
+ wpa_printf(MSG_INFO, "WPS ER: Failed to open SSDP listener "
+ "socket");
return -1;
+ }
if (eloop_register_sock(er->multicast_sd, EVENT_TYPE_READ,
wps_er_ssdp_rx, er, NULL) ||
* config_error - Configuration Error value to be used in NACK
*/
u16 config_error;
+ u16 error_indication;
int ext_reg;
int int_reg;
struct wps_credential *use_cred;
int use_psk_key;
+ u8 p2p_dev_addr[ETH_ALEN]; /* P2P Device Address of the client or
+ * 00:00:00:00:00:00 if not a P2p client */
+ int pbc_in_m1;
};
struct wps_parse_attr {
/* fixed length fields */
const u8 *version; /* 1 octet */
+ const u8 *version2; /* 1 octet */
const u8 *msg_type; /* 1 octet */
const u8 *enrollee_nonce; /* WPS_NONCE_LEN (16) octets */
const u8 *registrar_nonce; /* WPS_NONCE_LEN (16) octets */
const u8 *request_type; /* 1 octet */
const u8 *response_type; /* 1 octet */
const u8 *ap_setup_locked; /* 1 octet */
+ const u8 *settings_delay_time; /* 1 octet */
+ const u8 *network_key_shareable; /* 1 octet (Bool) */
+ const u8 *request_to_enroll; /* 1 octet (Bool) */
/* variable length fields */
const u8 *manufacturer;
size_t eap_type_len;
const u8 *eap_identity; /* <= 64 octets */
size_t eap_identity_len;
+ const u8 *authorized_macs; /* <= 30 octets */
+ size_t authorized_macs_len;
+ const u8 *sec_dev_type_list; /* <= 128 octets */
+ size_t sec_dev_type_list_len;
/* attributes that can occur multiple times */
#define MAX_CRED_COUNT 10
const u8 *cred[MAX_CRED_COUNT];
size_t cred_len[MAX_CRED_COUNT];
size_t num_cred;
+
+#define MAX_REQ_DEV_TYPE_COUNT 10
+ const u8 *req_dev_type[MAX_REQ_DEV_TYPE_COUNT];
+ size_t num_req_dev_type;
+
+ const u8 *vendor_ext[MAX_WPS_PARSE_VENDOR_EXT];
+ size_t vendor_ext_len[MAX_WPS_PARSE_VENDOR_EXT];
+ size_t num_vendor_ext;
};
/* wps_common.c */
size_t dev_passwd_len);
struct wpabuf * wps_decrypt_encr_settings(struct wps_data *wps, const u8 *encr,
size_t encr_len);
-void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg);
+void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg,
+ u16 config_error, u16 error_indication);
void wps_success_event(struct wps_context *wps);
void wps_pwd_auth_fail_event(struct wps_context *wps, int enrollee, int part);
void wps_pbc_overlap_event(struct wps_context *wps);
extern struct oob_device_data oob_nfc_device_data;
extern struct oob_nfc_device_data oob_nfc_pn531_device_data;
+struct wpabuf * wps_build_wsc_ack(struct wps_data *wps);
+struct wpabuf * wps_build_wsc_nack(struct wps_data *wps);
+
/* wps_attr_parse.c */
int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr);
int wps_build_encr_settings(struct wps_data *wps, struct wpabuf *msg,
struct wpabuf *plain);
int wps_build_version(struct wpabuf *msg);
+int wps_build_wfa_ext(struct wpabuf *msg, int req_to_enroll,
+ const u8 *auth_macs, size_t auth_macs_count);
int wps_build_msg_type(struct wpabuf *msg, enum wps_msg_type msg_type);
int wps_build_enrollee_nonce(struct wps_data *wps, struct wpabuf *msg);
int wps_build_registrar_nonce(struct wps_data *wps, struct wpabuf *msg);
int wps_build_conn_type_flags(struct wps_data *wps, struct wpabuf *msg);
int wps_build_assoc_state(struct wps_data *wps, struct wpabuf *msg);
int wps_build_oob_dev_password(struct wpabuf *msg, struct wps_context *wps);
+struct wpabuf * wps_ie_encapsulate(struct wpabuf *data);
/* wps_attr_process.c */
int wps_process_authenticator(struct wps_data *wps, const u8 *authenticator,
int wps_device_store(struct wps_registrar *reg,
struct wps_device_data *dev, const u8 *uuid);
void wps_registrar_selected_registrar_changed(struct wps_registrar *reg);
+const u8 * wps_authorized_macs(struct wps_registrar *reg, size_t *count);
+int wps_registrar_pbc_overlap(struct wps_registrar *reg,
+ const u8 *addr, const u8 *uuid_e);
/* ndef.c */
struct wpabuf * ndef_parse_wifi(struct wpabuf *buf);
struct wpabuf * ndef_build_wifi(struct wpabuf *buf);
-static inline int wps_version_supported(const u8 *version)
-{
- /* Require major version match, but allow minor version differences */
- return version && (*version & 0xf0) == (WPS_VERSION & 0xf0);
-}
-
#endif /* WPS_I_H */
#include "utils/list.h"
#include "crypto/crypto.h"
#include "crypto/sha256.h"
+#include "crypto/random.h"
#include "common/ieee802_11_defs.h"
#include "wps_i.h"
#include "wps_dev_attr.h"
#include "wps_upnp.h"
#include "wps_upnp_i.h"
+#ifndef CONFIG_WPS_STRICT
#define WPS_WORKAROUNDS
+#endif /* CONFIG_WPS_STRICT */
struct wps_uuid_pin {
struct dl_list list;
#define PIN_EXPIRES BIT(1)
int flags;
struct os_time expiration;
+ u8 enrollee_addr[ETH_ALEN];
};
int sel_reg_dev_password_id_override;
int sel_reg_config_methods_override;
int static_wep_only;
+ int dualband;
struct wps_registrar_device *devices;
int force_pbc_overlap;
+
+ u8 authorized_macs[WPS_MAX_AUTHORIZED_MACS][ETH_ALEN];
+ u8 authorized_macs_union[WPS_MAX_AUTHORIZED_MACS][ETH_ALEN];
+
+ u8 p2p_dev_addr[ETH_ALEN];
};
void *timeout_ctx);
+static void wps_registrar_add_authorized_mac(struct wps_registrar *reg,
+ const u8 *addr)
+{
+ int i;
+ wpa_printf(MSG_DEBUG, "WPS: Add authorized MAC " MACSTR,
+ MAC2STR(addr));
+ for (i = 0; i < WPS_MAX_AUTHORIZED_MACS; i++)
+ if (os_memcmp(reg->authorized_macs[i], addr, ETH_ALEN) == 0) {
+ wpa_printf(MSG_DEBUG, "WPS: Authorized MAC was "
+ "already in the list");
+ return; /* already in list */
+ }
+ for (i = WPS_MAX_AUTHORIZED_MACS - 1; i > 0; i--)
+ os_memcpy(reg->authorized_macs[i], reg->authorized_macs[i - 1],
+ ETH_ALEN);
+ os_memcpy(reg->authorized_macs[0], addr, ETH_ALEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: Authorized MACs",
+ (u8 *) reg->authorized_macs, sizeof(reg->authorized_macs));
+}
+
+
+static void wps_registrar_remove_authorized_mac(struct wps_registrar *reg,
+ const u8 *addr)
+{
+ int i;
+ wpa_printf(MSG_DEBUG, "WPS: Remove authorized MAC " MACSTR,
+ MAC2STR(addr));
+ for (i = 0; i < WPS_MAX_AUTHORIZED_MACS; i++) {
+ if (os_memcmp(reg->authorized_macs, addr, ETH_ALEN) == 0)
+ break;
+ }
+ if (i == WPS_MAX_AUTHORIZED_MACS) {
+ wpa_printf(MSG_DEBUG, "WPS: Authorized MAC was not in the "
+ "list");
+ return; /* not in the list */
+ }
+ for (; i + 1 < WPS_MAX_AUTHORIZED_MACS; i++)
+ os_memcpy(reg->authorized_macs[i], reg->authorized_macs[i + 1],
+ ETH_ALEN);
+ os_memset(reg->authorized_macs[WPS_MAX_AUTHORIZED_MACS - 1], 0,
+ ETH_ALEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: Authorized MACs",
+ (u8 *) reg->authorized_macs, sizeof(reg->authorized_macs));
+}
+
+
static void wps_free_devices(struct wps_registrar_device *dev)
{
struct wps_registrar_device *prev;
static void wps_registrar_remove_pbc_session(struct wps_registrar *reg,
- const u8 *addr, const u8 *uuid_e)
+ const u8 *uuid_e,
+ const u8 *p2p_dev_addr)
{
- struct wps_pbc_session *pbc, *prev = NULL;
+ struct wps_pbc_session *pbc, *prev = NULL, *tmp;
pbc = reg->pbc_sessions;
while (pbc) {
- if (os_memcmp(pbc->addr, addr, ETH_ALEN) == 0 &&
- os_memcmp(pbc->uuid_e, uuid_e, WPS_UUID_LEN) == 0) {
+ if (os_memcmp(pbc->uuid_e, uuid_e, WPS_UUID_LEN) == 0 ||
+ (p2p_dev_addr && !is_zero_ether_addr(reg->p2p_dev_addr) &&
+ os_memcmp(reg->p2p_dev_addr, p2p_dev_addr, ETH_ALEN) ==
+ 0)) {
if (prev)
prev->next = pbc->next;
else
reg->pbc_sessions = pbc->next;
- os_free(pbc);
- break;
+ tmp = pbc;
+ pbc = pbc->next;
+ wpa_printf(MSG_DEBUG, "WPS: Removing PBC session for "
+ "addr=" MACSTR, MAC2STR(tmp->addr));
+ wpa_hexdump(MSG_DEBUG, "WPS: Removed UUID-E",
+ tmp->uuid_e, WPS_UUID_LEN);
+ os_free(tmp);
+ continue;
}
prev = pbc;
pbc = pbc->next;
}
-static int wps_registrar_pbc_overlap(struct wps_registrar *reg,
- const u8 *addr, const u8 *uuid_e)
+int wps_registrar_pbc_overlap(struct wps_registrar *reg,
+ const u8 *addr, const u8 *uuid_e)
{
int count = 0;
struct wps_pbc_session *pbc;
+ struct wps_pbc_session *first = NULL;
struct os_time now;
os_get_time(&now);
+ wpa_printf(MSG_DEBUG, "WPS: Checking active PBC sessions for overlap");
+
+ if (uuid_e) {
+ wpa_printf(MSG_DEBUG, "WPS: Add one for the requested UUID");
+ wpa_hexdump(MSG_DEBUG, "WPS: Requested UUID",
+ uuid_e, WPS_UUID_LEN);
+ count++;
+ }
+
for (pbc = reg->pbc_sessions; pbc; pbc = pbc->next) {
- if (now.sec > pbc->timestamp.sec + WPS_PBC_WALK_TIME)
+ wpa_printf(MSG_DEBUG, "WPS: Consider PBC session with " MACSTR,
+ MAC2STR(pbc->addr));
+ wpa_hexdump(MSG_DEBUG, "WPS: UUID-E",
+ pbc->uuid_e, WPS_UUID_LEN);
+ if (now.sec > pbc->timestamp.sec + WPS_PBC_WALK_TIME) {
+ wpa_printf(MSG_DEBUG, "WPS: PBC walk time has "
+ "expired");
break;
- if (addr == NULL || os_memcmp(addr, pbc->addr, ETH_ALEN) ||
- uuid_e == NULL ||
- os_memcmp(uuid_e, pbc->uuid_e, WPS_UUID_LEN))
+ }
+ if (first &&
+ os_memcmp(pbc->uuid_e, first->uuid_e, WPS_UUID_LEN) == 0) {
+ wpa_printf(MSG_DEBUG, "WPS: Same Enrollee");
+ continue; /* same Enrollee */
+ }
+ if (uuid_e == NULL ||
+ os_memcmp(uuid_e, pbc->uuid_e, WPS_UUID_LEN)) {
+ wpa_printf(MSG_DEBUG, "WPS: New Enrollee");
count++;
+ }
+ if (first == NULL)
+ first = pbc;
}
- if (addr || uuid_e)
- count++;
+ wpa_printf(MSG_DEBUG, "WPS: %u active PBC session(s) found", count);
return count > 1 ? 1 : 0;
}
static int wps_build_ap_setup_locked(struct wps_context *wps,
struct wpabuf *msg)
{
- if (wps->ap_setup_locked) {
+ if (wps->ap_setup_locked && wps->ap_setup_locked != 2) {
wpa_printf(MSG_DEBUG, "WPS: * AP Setup Locked");
wpabuf_put_be16(msg, ATTR_AP_SETUP_LOCKED);
wpabuf_put_be16(msg, 1);
}
+static int wps_build_sel_pbc_reg_uuid_e(struct wps_registrar *reg,
+ struct wpabuf *msg)
+{
+ u16 id = reg->pbc ? DEV_PW_PUSHBUTTON : DEV_PW_DEFAULT;
+ if (!reg->sel_reg_union)
+ return 0;
+ if (reg->sel_reg_dev_password_id_override >= 0)
+ id = reg->sel_reg_dev_password_id_override;
+ if (id != DEV_PW_PUSHBUTTON || !reg->dualband)
+ return 0;
+ return wps_build_uuid_e(msg, reg->wps->uuid);
+}
+
+
+static void wps_set_pushbutton(u16 *methods, u16 conf_methods)
+{
+ *methods |= WPS_CONFIG_PUSHBUTTON;
+#ifdef CONFIG_WPS2
+ if (conf_methods & WPS_CONFIG_VIRT_PUSHBUTTON)
+ *methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
+ if (conf_methods & WPS_CONFIG_PHY_PUSHBUTTON)
+ *methods |= WPS_CONFIG_PHY_PUSHBUTTON;
+ if (!(*methods & (WPS_CONFIG_VIRT_PUSHBUTTON |
+ WPS_CONFIG_PHY_PUSHBUTTON))) {
+ /*
+ * Required to include virtual/physical flag, but we were not
+ * configured with push button type, so have to default to one
+ * of them.
+ */
+ *methods |= WPS_CONFIG_PHY_PUSHBUTTON;
+ }
+#endif /* CONFIG_WPS2 */
+}
+
+
static int wps_build_sel_reg_config_methods(struct wps_registrar *reg,
struct wpabuf *msg)
{
u16 methods;
if (!reg->sel_reg_union)
return 0;
- methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
+ methods = reg->wps->config_methods;
+ methods &= ~WPS_CONFIG_PUSHBUTTON;
+#ifdef CONFIG_WPS2
+ methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
+ WPS_CONFIG_PHY_PUSHBUTTON);
+#endif /* CONFIG_WPS2 */
if (reg->pbc)
- methods |= WPS_CONFIG_PUSHBUTTON;
+ wps_set_pushbutton(&methods, reg->wps->config_methods);
if (reg->sel_reg_config_methods_override >= 0)
methods = reg->sel_reg_config_methods_override;
wpa_printf(MSG_DEBUG, "WPS: * Selected Registrar Config Methods (%x)",
* external Registrars.
*/
methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
+#ifdef CONFIG_WPS2
+ methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
+ WPS_CONFIG_PHY_PUSHBUTTON);
+#endif /* CONFIG_WPS2 */
wpa_printf(MSG_DEBUG, "WPS: * Config Methods (%x)", methods);
wpabuf_put_be16(msg, ATTR_CONFIG_METHODS);
wpabuf_put_be16(msg, 2);
static int wps_build_config_methods_r(struct wps_registrar *reg,
struct wpabuf *msg)
{
- u16 methods;
- methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
- if (reg->pbc)
- methods |= WPS_CONFIG_PUSHBUTTON;
- return wps_build_config_methods(msg, methods);
+ return wps_build_config_methods(msg, reg->wps->config_methods);
+}
+
+
+const u8 * wps_authorized_macs(struct wps_registrar *reg, size_t *count)
+{
+ *count = 0;
+
+#ifdef CONFIG_WPS2
+ while (*count < WPS_MAX_AUTHORIZED_MACS) {
+ if (is_zero_ether_addr(reg->authorized_macs_union[*count]))
+ break;
+ (*count)++;
+ }
+#endif /* CONFIG_WPS2 */
+
+ return (const u8 *) reg->authorized_macs_union;
}
reg->sel_reg_dev_password_id_override = -1;
reg->sel_reg_config_methods_override = -1;
reg->static_wep_only = cfg->static_wep_only;
+ reg->dualband = cfg->dualband;
if (wps_set_ie(reg)) {
wps_registrar_deinit(reg);
/**
* wps_registrar_add_pin - Configure a new PIN for Registrar
* @reg: Registrar data from wps_registrar_init()
+ * @addr: Enrollee MAC address or %NULL if not known
* @uuid: UUID-E or %NULL for wildcard (any UUID)
* @pin: PIN (Device Password)
* @pin_len: Length of pin in octets
* @timeout: Time (in seconds) when the PIN will be invalidated; 0 = no timeout
* Returns: 0 on success, -1 on failure
*/
-int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *uuid,
- const u8 *pin, size_t pin_len, int timeout)
+int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *addr,
+ const u8 *uuid, const u8 *pin, size_t pin_len,
+ int timeout)
{
struct wps_uuid_pin *p;
p = os_zalloc(sizeof(*p));
if (p == NULL)
return -1;
+ if (addr)
+ os_memcpy(p->enrollee_addr, addr, ETH_ALEN);
if (uuid == NULL)
p->wildcard_uuid = 1;
else
wpa_hexdump_ascii_key(MSG_DEBUG, "WPS: PIN", pin, pin_len);
reg->selected_registrar = 1;
reg->pbc = 0;
+ if (addr)
+ wps_registrar_add_authorized_mac(reg, addr);
+ else
+ wps_registrar_add_authorized_mac(
+ reg, (u8 *) "\xff\xff\xff\xff\xff\xff");
wps_registrar_selected_registrar_changed(reg);
eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
eloop_register_timeout(WPS_PBC_WALK_TIME, 0,
}
+static void wps_registrar_remove_pin(struct wps_registrar *reg,
+ struct wps_uuid_pin *pin)
+{
+ u8 *addr;
+ u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+ if (is_zero_ether_addr(pin->enrollee_addr))
+ addr = bcast;
+ else
+ addr = pin->enrollee_addr;
+ wps_registrar_remove_authorized_mac(reg, addr);
+ wps_remove_pin(pin);
+ wps_registrar_selected_registrar_changed(reg);
+}
+
+
static void wps_registrar_expire_pins(struct wps_registrar *reg)
{
struct wps_uuid_pin *pin, *prev;
os_time_before(&pin->expiration, &now)) {
wpa_hexdump(MSG_DEBUG, "WPS: Expired PIN for UUID",
pin->uuid, WPS_UUID_LEN);
- wps_remove_pin(pin);
+ wps_registrar_remove_pin(reg, pin);
+ }
+ }
+}
+
+
+/**
+ * wps_registrar_invalidate_wildcard_pin - Invalidate a wildcard PIN
+ * @reg: Registrar data from wps_registrar_init()
+ * Returns: 0 on success, -1 if not wildcard PIN is enabled
+ */
+static int wps_registrar_invalidate_wildcard_pin(struct wps_registrar *reg)
+{
+ struct wps_uuid_pin *pin, *prev;
+
+ dl_list_for_each_safe(pin, prev, ®->pins, struct wps_uuid_pin, list)
+ {
+ if (pin->wildcard_uuid) {
+ wpa_hexdump(MSG_DEBUG, "WPS: Invalidated PIN for UUID",
+ pin->uuid, WPS_UUID_LEN);
+ wps_registrar_remove_pin(reg, pin);
+ return 0;
}
}
+
+ return -1;
}
if (os_memcmp(pin->uuid, uuid, WPS_UUID_LEN) == 0) {
wpa_hexdump(MSG_DEBUG, "WPS: Invalidated PIN for UUID",
pin->uuid, WPS_UUID_LEN);
- wps_remove_pin(pin);
+ wps_registrar_remove_pin(reg, pin);
return 0;
}
}
{
reg->selected_registrar = 0;
reg->pbc = 0;
+ os_memset(reg->p2p_dev_addr, 0, ETH_ALEN);
+ wps_registrar_remove_authorized_mac(reg,
+ (u8 *) "\xff\xff\xff\xff\xff\xff");
wps_registrar_selected_registrar_changed(reg);
}
/**
* wps_registrar_button_pushed - Notify Registrar that AP button was pushed
* @reg: Registrar data from wps_registrar_init()
- * Returns: 0 on success, -1 on failure
+ * @p2p_dev_addr: Limit allowed PBC devices to the specified P2P device, %NULL
+ * indicates no such filtering
+ * Returns: 0 on success, -1 on failure, -2 on session overlap
*
* This function is called on an AP when a push button is pushed to activate
* PBC mode. The PBC mode will be stopped after walk time (2 minutes) timeout
- * or when a PBC registration is completed.
+ * or when a PBC registration is completed. If more than one Enrollee in active
+ * PBC mode has been detected during the monitor time (previous 2 minutes), the
+ * PBC mode is not activated and -2 is returned to indicate session overlap.
+ * This is skipped if a specific Enrollee is selected.
*/
-int wps_registrar_button_pushed(struct wps_registrar *reg)
+int wps_registrar_button_pushed(struct wps_registrar *reg,
+ const u8 *p2p_dev_addr)
{
- if (wps_registrar_pbc_overlap(reg, NULL, NULL)) {
+ if (p2p_dev_addr == NULL &&
+ wps_registrar_pbc_overlap(reg, NULL, NULL)) {
wpa_printf(MSG_DEBUG, "WPS: PBC overlap - do not start PBC "
"mode");
wps_pbc_overlap_event(reg->wps);
- return -1;
+ return -2;
}
wpa_printf(MSG_DEBUG, "WPS: Button pushed - PBC mode started");
reg->force_pbc_overlap = 0;
reg->selected_registrar = 1;
reg->pbc = 1;
+ if (p2p_dev_addr)
+ os_memcpy(reg->p2p_dev_addr, p2p_dev_addr, ETH_ALEN);
+ else
+ os_memset(reg->p2p_dev_addr, 0, ETH_ALEN);
+ wps_registrar_add_authorized_mac(reg,
+ (u8 *) "\xff\xff\xff\xff\xff\xff");
wps_registrar_selected_registrar_changed(reg);
+ eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL);
eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wps_registrar_pbc_timeout,
reg, NULL);
}
+void wps_registrar_complete(struct wps_registrar *registrar, const u8 *uuid_e)
+{
+ if (registrar->pbc) {
+ wps_registrar_remove_pbc_session(registrar,
+ uuid_e, NULL);
+ wps_registrar_pbc_completed(registrar);
+ } else {
+ wps_registrar_pin_completed(registrar);
+ }
+}
+
+
+int wps_registrar_wps_cancel(struct wps_registrar *reg)
+{
+ if (reg->pbc) {
+ wpa_printf(MSG_DEBUG, "WPS: PBC is set - cancelling it");
+ wps_registrar_pbc_timeout(reg, NULL);
+ eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL);
+ return 1;
+ } else if (reg->selected_registrar) {
+ /* PIN Method */
+ wpa_printf(MSG_DEBUG, "WPS: PIN is set - cancelling it");
+ wps_registrar_pin_completed(reg);
+ wps_registrar_invalidate_wildcard_pin(reg);
+ return 1;
+ }
+ return 0;
+}
+
+
/**
* wps_registrar_probe_req_rx - Notify Registrar of Probe Request
* @reg: Registrar data from wps_registrar_init()
* situation with other WPS APs.
*/
void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr,
- const struct wpabuf *wps_data)
+ const struct wpabuf *wps_data,
+ int p2p_wildcard)
{
struct wps_parse_attr attr;
if (wps_parse_msg(wps_data, &attr) < 0)
return;
- if (!wps_version_supported(attr.version)) {
- wpa_printf(MSG_DEBUG, "WPS: Unsupported ProbeReq WPS IE "
- "version 0x%x", attr.version ? *attr.version : 0);
- return;
- }
if (attr.config_methods == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No Config Methods attribute in "
}
if (reg->enrollee_seen_cb && attr.uuid_e &&
- attr.primary_dev_type && attr.request_type) {
+ attr.primary_dev_type && attr.request_type && !p2p_wildcard) {
char *dev_name = NULL;
if (attr.dev_name) {
dev_name = os_zalloc(attr.dev_name_len + 1);
"UUID-E included");
return;
}
+ wpa_hexdump(MSG_DEBUG, "WPS: UUID-E from Probe Request", attr.uuid_e,
+ WPS_UUID_LEN);
wps_registrar_add_pbc_session(reg, addr, attr.uuid_e);
if (wps_registrar_pbc_overlap(reg, addr, attr.uuid_e)) {
if (reg->selected_registrar) {
methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
+#ifdef CONFIG_WPS2
+ methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
+ WPS_CONFIG_PHY_PUSHBUTTON);
+#endif /* CONFIG_WPS2 */
if (reg->pbc)
- methods |= WPS_CONFIG_PUSHBUTTON;
+ wps_set_pushbutton(&methods, reg->wps->config_methods);
}
+ wpa_printf(MSG_DEBUG, "WPS: wps_cb_set_sel_reg: sel_reg=%d "
+ "config_methods=0x%x pbc=%d methods=0x%x",
+ reg->selected_registrar, reg->wps->config_methods,
+ reg->pbc, methods);
+
reg->set_sel_reg_cb(reg->cb_ctx, reg->selected_registrar,
reg->pbc ? DEV_PW_PUSHBUTTON : DEV_PW_DEFAULT,
methods);
}
-/* Encapsulate WPS IE data with one (or more, if needed) IE headers */
-static struct wpabuf * wps_ie_encapsulate(struct wpabuf *data)
-{
- struct wpabuf *ie;
- const u8 *pos, *end;
-
- ie = wpabuf_alloc(wpabuf_len(data) + 100);
- if (ie == NULL) {
- wpabuf_free(data);
- return NULL;
- }
-
- pos = wpabuf_head(data);
- end = pos + wpabuf_len(data);
-
- while (end > pos) {
- size_t frag_len = end - pos;
- if (frag_len > 251)
- frag_len = 251;
- wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
- wpabuf_put_u8(ie, 4 + frag_len);
- wpabuf_put_be32(ie, WPS_DEV_OUI_WFA);
- wpabuf_put_data(ie, pos, frag_len);
- pos += frag_len;
- }
-
- wpabuf_free(data);
-
- return ie;
-}
-
-
static int wps_set_ie(struct wps_registrar *reg)
{
struct wpabuf *beacon;
struct wpabuf *probe;
+ const u8 *auth_macs;
+ size_t count;
+ size_t vendor_len = 0;
+ int i;
if (reg->set_ie_cb == NULL)
return 0;
- wpa_printf(MSG_DEBUG, "WPS: Build Beacon and Probe Response IEs");
+ for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) {
+ if (reg->wps->dev.vendor_ext[i]) {
+ vendor_len += 2 + 2;
+ vendor_len += wpabuf_len(reg->wps->dev.vendor_ext[i]);
+ }
+ }
- beacon = wpabuf_alloc(300);
+ beacon = wpabuf_alloc(400 + vendor_len);
if (beacon == NULL)
return -1;
- probe = wpabuf_alloc(400);
+ probe = wpabuf_alloc(500 + vendor_len);
if (probe == NULL) {
wpabuf_free(beacon);
return -1;
}
+ auth_macs = wps_authorized_macs(reg, &count);
+
+ wpa_printf(MSG_DEBUG, "WPS: Build Beacon IEs");
+
if (wps_build_version(beacon) ||
wps_build_wps_state(reg->wps, beacon) ||
wps_build_ap_setup_locked(reg->wps, beacon) ||
wps_build_selected_registrar(reg, beacon) ||
wps_build_sel_reg_dev_password_id(reg, beacon) ||
wps_build_sel_reg_config_methods(reg, beacon) ||
- wps_build_version(probe) ||
+ wps_build_sel_pbc_reg_uuid_e(reg, beacon) ||
+ (reg->dualband && wps_build_rf_bands(®->wps->dev, beacon)) ||
+ wps_build_wfa_ext(beacon, 0, auth_macs, count) ||
+ wps_build_vendor_ext(®->wps->dev, beacon)) {
+ wpabuf_free(beacon);
+ wpabuf_free(probe);
+ return -1;
+ }
+
+#ifdef CONFIG_P2P
+ if (wps_build_dev_name(®->wps->dev, beacon) ||
+ wps_build_primary_dev_type(®->wps->dev, beacon)) {
+ wpabuf_free(beacon);
+ wpabuf_free(probe);
+ return -1;
+ }
+#endif /* CONFIG_P2P */
+
+ wpa_printf(MSG_DEBUG, "WPS: Build Probe Response IEs");
+
+ if (wps_build_version(probe) ||
wps_build_wps_state(reg->wps, probe) ||
wps_build_ap_setup_locked(reg->wps, probe) ||
wps_build_selected_registrar(reg, probe) ||
wps_build_uuid_e(probe, reg->wps->uuid) ||
wps_build_device_attrs(®->wps->dev, probe) ||
wps_build_probe_config_methods(reg, probe) ||
- wps_build_rf_bands(®->wps->dev, probe)) {
+ wps_build_rf_bands(®->wps->dev, probe) ||
+ wps_build_wfa_ext(probe, 0, auth_macs, count) ||
+ wps_build_vendor_ext(®->wps->dev, probe)) {
wpabuf_free(beacon);
wpabuf_free(probe);
return -1;
const u8 *addr[4];
size_t len[4];
- if (os_get_random(wps->snonce, 2 * WPS_SECRET_NONCE_LEN) < 0)
+ if (random_get_bytes(wps->snonce, 2 * WPS_SECRET_NONCE_LEN) < 0)
return -1;
wpa_hexdump(MSG_DEBUG, "WPS: R-S1", wps->snonce, WPS_SECRET_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "WPS: R-S2",
static int wps_build_cred_network_idx(struct wpabuf *msg,
const struct wps_credential *cred)
{
- wpa_printf(MSG_DEBUG, "WPS: * Network Index");
+ wpa_printf(MSG_DEBUG, "WPS: * Network Index (1)");
wpabuf_put_be16(msg, ATTR_NETWORK_INDEX);
wpabuf_put_be16(msg, 1);
wpabuf_put_u8(msg, 1);
const struct wps_credential *cred)
{
wpa_printf(MSG_DEBUG, "WPS: * SSID");
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID for Credential",
+ cred->ssid, cred->ssid_len);
wpabuf_put_be16(msg, ATTR_SSID);
wpabuf_put_be16(msg, cred->ssid_len);
wpabuf_put_data(msg, cred->ssid, cred->ssid_len);
{
wpa_printf(MSG_DEBUG, "WPS: * Network Key (len=%d)",
(int) cred->key_len);
+ wpa_hexdump_key(MSG_DEBUG, "WPS: Network Key",
+ cred->key, cred->key_len);
wpabuf_put_be16(msg, ATTR_NETWORK_KEY);
wpabuf_put_be16(msg, cred->key_len);
wpabuf_put_data(msg, cred->key, cred->key_len);
}
+int wps_build_credential_wrap(struct wpabuf *msg,
+ const struct wps_credential *cred)
+{
+ struct wpabuf *wbuf;
+ wbuf = wpabuf_alloc(200);
+ if (wbuf == NULL)
+ return -1;
+ if (wps_build_credential(wbuf, cred)) {
+ wpabuf_free(wbuf);
+ return -1;
+ }
+ wpabuf_put_be16(msg, ATTR_CRED);
+ wpabuf_put_be16(msg, wpabuf_len(wbuf));
+ wpabuf_put_buf(msg, wbuf);
+ wpabuf_free(wbuf);
+ return 0;
+}
+
+
int wps_build_cred(struct wps_data *wps, struct wpabuf *msg)
{
struct wpabuf *cred;
!wps->wps->registrar->disable_auto_conf) {
u8 r[16];
/* Generate a random passphrase */
- if (os_get_random(r, sizeof(r)) < 0)
+ if (random_get_bytes(r, sizeof(r)) < 0)
return -1;
os_free(wps->new_psk);
wps->new_psk = base64_encode(r, sizeof(r), &wps->new_psk_len);
wps->new_psk = os_malloc(wps->new_psk_len);
if (wps->new_psk == NULL)
return -1;
- if (os_get_random(wps->new_psk, wps->new_psk_len) < 0) {
+ if (random_get_bytes(wps->new_psk, wps->new_psk_len) < 0) {
os_free(wps->new_psk);
wps->new_psk = NULL;
return -1;
}
use_provided:
+#ifdef CONFIG_WPS_TESTING
+ if (wps_testing_dummy_cred)
+ cred = wpabuf_alloc(200);
+ else
+ cred = NULL;
+ if (cred) {
+ struct wps_credential dummy;
+ wpa_printf(MSG_DEBUG, "WPS: Add dummy credential");
+ os_memset(&dummy, 0, sizeof(dummy));
+ os_memcpy(dummy.ssid, "dummy", 5);
+ dummy.ssid_len = 5;
+ dummy.auth_type = WPS_AUTH_WPA2PSK;
+ dummy.encr_type = WPS_ENCR_AES;
+ os_memcpy(dummy.key, "dummy psk", 9);
+ dummy.key_len = 9;
+ os_memcpy(dummy.mac_addr, wps->mac_addr_e, ETH_ALEN);
+ wps_build_credential(cred, &dummy);
+ wpa_hexdump_buf(MSG_DEBUG, "WPS: Dummy Credential", cred);
+
+ wpabuf_put_be16(msg, ATTR_CRED);
+ wpabuf_put_be16(msg, wpabuf_len(cred));
+ wpabuf_put_buf(msg, cred);
+
+ wpabuf_free(cred);
+ }
+#endif /* CONFIG_WPS_TESTING */
+
cred = wpabuf_alloc(200);
if (cred == NULL)
return -1;
}
+static struct wpabuf * wps_build_ap_cred(struct wps_data *wps)
+{
+ struct wpabuf *msg, *plain;
+
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL)
+ return NULL;
+
+ plain = wpabuf_alloc(200);
+ if (plain == NULL) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+
+ if (wps_build_ap_settings(wps, plain)) {
+ wpabuf_free(plain);
+ wpabuf_free(msg);
+ return NULL;
+ }
+
+ wpabuf_put_be16(msg, ATTR_CRED);
+ wpabuf_put_be16(msg, wpabuf_len(plain));
+ wpabuf_put_buf(msg, plain);
+ wpabuf_free(plain);
+
+ return msg;
+}
+
+
static struct wpabuf * wps_build_m2(struct wps_data *wps)
{
struct wpabuf *msg;
- if (os_get_random(wps->nonce_r, WPS_NONCE_LEN) < 0)
+ if (random_get_bytes(wps->nonce_r, WPS_NONCE_LEN) < 0)
return NULL;
wpa_hexdump(MSG_DEBUG, "WPS: Registrar Nonce",
wps->nonce_r, WPS_NONCE_LEN);
wps_build_config_error(msg, WPS_CFG_NO_ERROR) ||
wps_build_dev_password_id(msg, wps->dev_pw_id) ||
wps_build_os_version(&wps->wps->dev, msg) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0) ||
wps_build_authenticator(wps, msg)) {
wpabuf_free(msg);
return NULL;
wps_build_rf_bands(&wps->wps->dev, msg) ||
wps_build_assoc_state(wps, msg) ||
wps_build_config_error(msg, err) ||
- wps_build_os_version(&wps->wps->dev, msg)) {
+ wps_build_os_version(&wps->wps->dev, msg) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0)) {
wpabuf_free(msg);
return NULL;
}
wps_build_r_snonce1(wps, plain) ||
wps_build_key_wrap_auth(wps, plain) ||
wps_build_encr_settings(wps, msg, plain) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0) ||
wps_build_authenticator(wps, msg)) {
wpabuf_free(plain);
wpabuf_free(msg);
wps_build_r_snonce2(wps, plain) ||
wps_build_key_wrap_auth(wps, plain) ||
wps_build_encr_settings(wps, msg, plain) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0) ||
wps_build_authenticator(wps, msg)) {
wpabuf_free(plain);
wpabuf_free(msg);
(!wps->wps->ap && !wps->er && wps_build_ap_settings(wps, plain)) ||
wps_build_key_wrap_auth(wps, plain) ||
wps_build_encr_settings(wps, msg, plain) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0) ||
wps_build_authenticator(wps, msg)) {
wpabuf_free(plain);
wpabuf_free(msg);
}
-static struct wpabuf * wps_build_wsc_ack(struct wps_data *wps)
-{
- struct wpabuf *msg;
-
- wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_ACK");
-
- msg = wpabuf_alloc(1000);
- if (msg == NULL)
- return NULL;
-
- if (wps_build_version(msg) ||
- wps_build_msg_type(msg, WPS_WSC_ACK) ||
- wps_build_enrollee_nonce(wps, msg) ||
- wps_build_registrar_nonce(wps, msg)) {
- wpabuf_free(msg);
- return NULL;
- }
-
- return msg;
-}
-
-
-static struct wpabuf * wps_build_wsc_nack(struct wps_data *wps)
-{
- struct wpabuf *msg;
-
- wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_NACK");
-
- msg = wpabuf_alloc(1000);
- if (msg == NULL)
- return NULL;
-
- if (wps_build_version(msg) ||
- wps_build_msg_type(msg, WPS_WSC_NACK) ||
- wps_build_enrollee_nonce(wps, msg) ||
- wps_build_registrar_nonce(wps, msg) ||
- wps_build_config_error(msg, wps->config_error)) {
- wpabuf_free(msg);
- return NULL;
- }
-
- return msg;
-}
-
-
struct wpabuf * wps_registrar_get_msg(struct wps_data *wps,
enum wsc_op_code *op_code)
{
}
+static int wps_registrar_p2p_dev_addr_match(struct wps_data *wps)
+{
+#ifdef CONFIG_P2P
+ struct wps_registrar *reg = wps->wps->registrar;
+
+ if (is_zero_ether_addr(reg->p2p_dev_addr))
+ return 1; /* no filtering in use */
+
+ if (os_memcmp(reg->p2p_dev_addr, wps->p2p_dev_addr, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG, "WPS: No match on P2P Device Address "
+ "filtering for PBC: expected " MACSTR " was "
+ MACSTR " - indicate PBC session overlap",
+ MAC2STR(reg->p2p_dev_addr),
+ MAC2STR(wps->p2p_dev_addr));
+ return 0;
+ }
+#endif /* CONFIG_P2P */
+ return 1;
+}
+
+
+static int wps_registrar_skip_overlap(struct wps_data *wps)
+{
+#ifdef CONFIG_P2P
+ struct wps_registrar *reg = wps->wps->registrar;
+
+ if (is_zero_ether_addr(reg->p2p_dev_addr))
+ return 0; /* no specific Enrollee selected */
+
+ if (os_memcmp(reg->p2p_dev_addr, wps->p2p_dev_addr, ETH_ALEN) == 0) {
+ wpa_printf(MSG_DEBUG, "WPS: Skip PBC overlap due to selected "
+ "Enrollee match");
+ return 1;
+ }
+#endif /* CONFIG_P2P */
+ return 0;
+}
+
+
static enum wps_process_res wps_process_m1(struct wps_data *wps,
struct wps_parse_attr *attr)
{
#endif /* CONFIG_WPS_OOB */
if (wps->dev_pw_id == DEV_PW_PUSHBUTTON) {
- if (wps->wps->registrar->force_pbc_overlap ||
- wps_registrar_pbc_overlap(wps->wps->registrar,
- wps->mac_addr_e, wps->uuid_e)) {
+ if ((wps->wps->registrar->force_pbc_overlap ||
+ wps_registrar_pbc_overlap(wps->wps->registrar,
+ wps->mac_addr_e, wps->uuid_e) ||
+ !wps_registrar_p2p_dev_addr_match(wps)) &&
+ !wps_registrar_skip_overlap(wps)) {
wpa_printf(MSG_DEBUG, "WPS: PBC overlap - deny PBC "
"negotiation");
wps->state = SEND_M2D;
wps->config_error = WPS_CFG_MULTIPLE_PBC_DETECTED;
wps_pbc_overlap_event(wps->wps);
+ wps_fail_event(wps->wps, WPS_M1,
+ WPS_CFG_MULTIPLE_PBC_DETECTED,
+ WPS_EI_NO_ERROR);
wps->wps->registrar->force_pbc_overlap = 1;
return WPS_CONTINUE;
}
return WPS_CONTINUE;
}
- if (wps->pbc && wps->wps->registrar->force_pbc_overlap) {
+ if (wps->pbc && wps->wps->registrar->force_pbc_overlap &&
+ !wps_registrar_skip_overlap(wps)) {
wpa_printf(MSG_DEBUG, "WPS: Reject negotiation due to PBC "
"session overlap");
wps->state = SEND_WSC_NACK;
return WPS_CONTINUE;
}
- if (wps->pbc && wps->wps->registrar->force_pbc_overlap) {
+ if (wps->pbc && wps->wps->registrar->force_pbc_overlap &&
+ !wps_registrar_skip_overlap(wps)) {
wpa_printf(MSG_DEBUG, "WPS: Reject negotiation due to PBC "
"session overlap");
wps->state = SEND_WSC_NACK;
return WPS_CONTINUE;
}
+ if (wps_validate_m5_encr(decrypted, attr->version2 != NULL) < 0) {
+ wpabuf_free(decrypted);
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
"attribute");
if (wps_parse_msg(decrypted, &eattr) < 0 ||
static int wps_process_ap_settings_r(struct wps_data *wps,
struct wps_parse_attr *attr)
{
+ struct wpabuf *msg;
+
if (wps->wps->ap || wps->er)
return 0;
* Use the AP PIN only to receive the current AP settings, not
* to reconfigure the AP.
*/
+
+ /*
+ * Clear selected registrar here since we do not get to
+ * WSC_Done in this protocol run.
+ */
+ wps_registrar_pin_completed(wps->wps->registrar);
+
+ msg = wps_build_ap_cred(wps);
+ if (msg == NULL)
+ return -1;
+ wps->cred.cred_attr = wpabuf_head(msg);
+ wps->cred.cred_attr_len = wpabuf_len(msg);
+
if (wps->ap_settings_cb) {
wps->ap_settings_cb(wps->ap_settings_cb_ctx,
&wps->cred);
+ wpabuf_free(msg);
return 1;
}
wps_sta_cred_cb(wps);
+
+ wps->cred.cred_attr = NULL;
+ wps->cred.cred_attr_len = 0;
+ wpabuf_free(msg);
+
return 1;
}
}
return WPS_CONTINUE;
}
- if (wps->pbc && wps->wps->registrar->force_pbc_overlap) {
+ if (wps->pbc && wps->wps->registrar->force_pbc_overlap &&
+ !wps_registrar_skip_overlap(wps)) {
wpa_printf(MSG_DEBUG, "WPS: Reject negotiation due to PBC "
"session overlap");
wps->state = SEND_WSC_NACK;
return WPS_CONTINUE;
}
+ if (wps_validate_m7_encr(decrypted, wps->wps->ap || wps->er,
+ attr->version2 != NULL) < 0) {
+ wpabuf_free(decrypted);
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
"attribute");
if (wps_parse_msg(decrypted, &eattr) < 0 ||
if (wps_parse_msg(msg, &attr) < 0)
return WPS_FAILURE;
- if (!wps_version_supported(attr.version)) {
- wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x",
- attr.version ? *attr.version : 0);
- return WPS_FAILURE;
- }
-
if (attr.msg_type == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
- return WPS_FAILURE;
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
}
if (*attr.msg_type != WPS_M1 &&
switch (*attr.msg_type) {
case WPS_M1:
+ if (wps_validate_m1(msg) < 0)
+ return WPS_FAILURE;
#ifdef CONFIG_WPS_UPNP
if (wps->wps->wps_upnp && attr.mac_addr) {
/* Remove old pending messages when starting new run */
ret = wps_process_m1(wps, &attr);
break;
case WPS_M3:
+ if (wps_validate_m3(msg) < 0)
+ return WPS_FAILURE;
ret = wps_process_m3(wps, msg, &attr);
if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
- wps_fail_event(wps->wps, WPS_M3);
+ wps_fail_event(wps->wps, WPS_M3, wps->config_error,
+ wps->error_indication);
break;
case WPS_M5:
+ if (wps_validate_m5(msg) < 0)
+ return WPS_FAILURE;
ret = wps_process_m5(wps, msg, &attr);
if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
- wps_fail_event(wps->wps, WPS_M5);
+ wps_fail_event(wps->wps, WPS_M5, wps->config_error,
+ wps->error_indication);
break;
case WPS_M7:
+ if (wps_validate_m7(msg) < 0)
+ return WPS_FAILURE;
ret = wps_process_m7(wps, msg, &attr);
if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
- wps_fail_event(wps->wps, WPS_M7);
+ wps_fail_event(wps->wps, WPS_M7, wps->config_error,
+ wps->error_indication);
break;
default:
wpa_printf(MSG_DEBUG, "WPS: Unsupported Message Type %d",
if (wps_parse_msg(msg, &attr) < 0)
return WPS_FAILURE;
- if (!wps_version_supported(attr.version)) {
- wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x",
- attr.version ? *attr.version : 0);
- return WPS_FAILURE;
- }
-
if (attr.msg_type == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
return WPS_FAILURE;
{
struct wps_parse_attr attr;
int old_state;
+ u16 config_error;
wpa_printf(MSG_DEBUG, "WPS: Received WSC_NACK");
if (wps_parse_msg(msg, &attr) < 0)
return WPS_FAILURE;
- if (!wps_version_supported(attr.version)) {
- wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x",
- attr.version ? *attr.version : 0);
- return WPS_FAILURE;
- }
-
if (attr.msg_type == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
return WPS_FAILURE;
return WPS_FAILURE;
}
+ config_error = WPA_GET_BE16(attr.config_error);
wpa_printf(MSG_DEBUG, "WPS: Enrollee terminated negotiation with "
- "Configuration Error %d", WPA_GET_BE16(attr.config_error));
+ "Configuration Error %d", config_error);
switch (old_state) {
case RECV_M3:
- wps_fail_event(wps->wps, WPS_M2);
+ wps_fail_event(wps->wps, WPS_M2, config_error,
+ wps->error_indication);
break;
case RECV_M5:
- wps_fail_event(wps->wps, WPS_M4);
+ wps_fail_event(wps->wps, WPS_M4, config_error,
+ wps->error_indication);
break;
case RECV_M7:
- wps_fail_event(wps->wps, WPS_M6);
+ wps_fail_event(wps->wps, WPS_M6, config_error,
+ wps->error_indication);
break;
case RECV_DONE:
- wps_fail_event(wps->wps, WPS_M8);
+ wps_fail_event(wps->wps, WPS_M8, config_error,
+ wps->error_indication);
break;
default:
break;
if (wps_parse_msg(msg, &attr) < 0)
return WPS_FAILURE;
- if (!wps_version_supported(attr.version)) {
- wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x",
- attr.version ? *attr.version : 0);
- return WPS_FAILURE;
- }
-
if (attr.msg_type == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
return WPS_FAILURE;
if (wps->pbc) {
wps_registrar_remove_pbc_session(wps->wps->registrar,
- wps->mac_addr_e, wps->uuid_e);
+ wps->uuid_e,
+ wps->p2p_dev_addr);
wps_registrar_pbc_completed(wps->wps->registrar);
} else {
wps_registrar_pin_completed(wps->wps->registrar);
}
+ /* TODO: maintain AuthorizedMACs somewhere separately for each ER and
+ * merge them into APs own list.. */
wps_success_event(wps->wps);
case WSC_MSG:
return wps_process_wsc_msg(wps, msg);
case WSC_ACK:
+ if (wps_validate_wsc_ack(msg) < 0)
+ return WPS_FAILURE;
return wps_process_wsc_ack(wps, msg);
case WSC_NACK:
+ if (wps_validate_wsc_nack(msg) < 0)
+ return WPS_FAILURE;
return wps_process_wsc_nack(wps, msg);
case WSC_Done:
+ if (wps_validate_wsc_done(msg) < 0)
+ return WPS_FAILURE;
ret = wps_process_wsc_done(wps, msg);
if (ret == WPS_FAILURE) {
wps->state = SEND_WSC_NACK;
- wps_fail_event(wps->wps, WPS_WSC_DONE);
+ wps_fail_event(wps->wps, WPS_WSC_DONE,
+ wps->config_error,
+ wps->error_indication);
}
return ret;
default:
static void wps_registrar_sel_reg_add(struct wps_registrar *reg,
struct subscription *s)
{
+ int i, j;
wpa_printf(MSG_DEBUG, "WPS: External Registrar selected (dev_pw_id=%d "
"config_methods=0x%x)",
s->dev_password_id, s->config_methods);
if (reg->sel_reg_config_methods_override == -1)
reg->sel_reg_config_methods_override = 0;
reg->sel_reg_config_methods_override |= s->config_methods;
+ for (i = 0; i < WPS_MAX_AUTHORIZED_MACS; i++)
+ if (is_zero_ether_addr(reg->authorized_macs_union[i]))
+ break;
+ for (j = 0; i < WPS_MAX_AUTHORIZED_MACS && j < WPS_MAX_AUTHORIZED_MACS;
+ j++) {
+ if (is_zero_ether_addr(s->authorized_macs[j]))
+ break;
+ wpa_printf(MSG_DEBUG, "WPS: Add authorized MAC into union: "
+ MACSTR, MAC2STR(s->authorized_macs[j]));
+ os_memcpy(reg->authorized_macs_union[i],
+ s->authorized_macs[j], ETH_ALEN);
+ i++;
+ }
+ wpa_hexdump(MSG_DEBUG, "WPS: Authorized MACs union",
+ (u8 *) reg->authorized_macs_union,
+ sizeof(reg->authorized_macs_union));
}
#endif /* CONFIG_WPS_UPNP */
reg->sel_reg_union = reg->selected_registrar;
reg->sel_reg_dev_password_id_override = -1;
reg->sel_reg_config_methods_override = -1;
+ os_memcpy(reg->authorized_macs_union, reg->authorized_macs,
+ WPS_MAX_AUTHORIZED_MACS * ETH_ALEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: Authorized MACs union (start with own)",
+ (u8 *) reg->authorized_macs_union,
+ sizeof(reg->authorized_macs_union));
if (reg->selected_registrar) {
- reg->sel_reg_config_methods_override =
- reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
+ u16 methods;
+
+ methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
+#ifdef CONFIG_WPS2
+ methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
+ WPS_CONFIG_PHY_PUSHBUTTON);
+#endif /* CONFIG_WPS2 */
if (reg->pbc) {
reg->sel_reg_dev_password_id_override =
DEV_PW_PUSHBUTTON;
- reg->sel_reg_config_methods_override |=
- WPS_CONFIG_PUSHBUTTON;
+ wps_set_pushbutton(&methods, reg->wps->config_methods);
}
wpa_printf(MSG_DEBUG, "WPS: Internal Registrar selected "
"(pbc=%d)", reg->pbc);
+ reg->sel_reg_config_methods_override = methods;
} else
wpa_printf(MSG_DEBUG, "WPS: Internal Registrar not selected");
return len;
}
+
+
+int wps_registrar_config_ap(struct wps_registrar *reg,
+ struct wps_credential *cred)
+{
+#ifdef CONFIG_WPS2
+ printf("encr_type=0x%x\n", cred->encr_type);
+ if (!(cred->encr_type & (WPS_ENCR_NONE | WPS_ENCR_TKIP |
+ WPS_ENCR_AES))) {
+ if (cred->encr_type & WPS_ENCR_WEP) {
+ wpa_printf(MSG_INFO, "WPS: Reject new AP settings "
+ "due to WEP configuration");
+ return -1;
+ }
+
+ wpa_printf(MSG_INFO, "WPS: Reject new AP settings due to "
+ "invalid encr_type 0x%x", cred->encr_type);
+ return -1;
+ }
+
+ if ((cred->encr_type & (WPS_ENCR_TKIP | WPS_ENCR_AES)) ==
+ WPS_ENCR_TKIP) {
+ wpa_printf(MSG_DEBUG, "WPS: Upgrade encr_type TKIP -> "
+ "TKIP+AES");
+ cred->encr_type |= WPS_ENCR_AES;
+ }
+
+ if ((cred->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) ==
+ WPS_AUTH_WPAPSK) {
+ wpa_printf(MSG_DEBUG, "WPS: Upgrade auth_type WPAPSK -> "
+ "WPAPSK+WPA2PSK");
+ cred->auth_type |= WPS_AUTH_WPA2PSK;
+ }
+#endif /* CONFIG_WPS2 */
+
+ if (reg->wps->cred_cb)
+ return reg->wps->cred_cb(reg->wps->cb_ctx, cred);
+
+ return -1;
+}
* Copyright (c) 2000-2003 Intel Corporation
* Copyright (c) 2006-2007 Sony Corporation
* Copyright (c) 2008-2009 Atheros Communications
- * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
*
* See below for more details on licensing and code history.
*/
* -- Needs renaming with module prefix to avoid polluting the debugger
* namespace and causing possible collisions with other static fncs
* and structure declarations when using the debugger.
- * -- The http error code generation is pretty bogus, hopefully noone cares.
+ * -- The http error code generation is pretty bogus, hopefully no one cares.
*
* Author: Ted Merrill, Atheros Communications, based upon earlier work
* as explained above and below.
#include "includes.h"
-#include <assert.h>
+#include <time.h>
#include <net/if.h>
#include <netdb.h>
#include <sys/ioctl.h>
#define MAX_SUBSCRIPTIONS 4 /* how many subscribing clients we handle */
#define MAX_ADDR_PER_SUBSCRIPTION 8
+/* Maximum number of Probe Request events per second */
+#define MAX_EVENTS_PER_SEC 5
+
+
+static struct upnp_wps_device_sm *shared_upnp_device = NULL;
+
/* Write the current date/time per RFC */
void format_date(struct wpabuf *buf)
/* subscr_addr_delete -- delete single unlinked subscriber address
* (be sure to unlink first if need be)
*/
-static void subscr_addr_delete(struct subscr_addr *a)
+void subscr_addr_delete(struct subscr_addr *a)
{
/*
* Note: do NOT free domain_and_port or path because they point to
/* subscr_addr_add_url -- add address(es) for one url to subscription */
-static void subscr_addr_add_url(struct subscription *s, const char *url)
+static void subscr_addr_add_url(struct subscription *s, const char *url,
+ size_t url_len)
{
int alloc_len;
char *scratch_mem = NULL;
struct addrinfo *result = NULL;
struct addrinfo *rp;
int rerr;
- struct subscr_addr *a = NULL;
/* url MUST begin with http: */
- if (os_strncasecmp(url, "http://", 7))
+ if (url_len < 7 || os_strncasecmp(url, "http://", 7))
goto fail;
url += 7;
+ url_len -= 7;
/* allocate memory for the extra stuff we need */
- alloc_len = (2 * (os_strlen(url) + 1));
+ alloc_len = 2 * (url_len + 1);
scratch_mem = os_zalloc(alloc_len);
if (scratch_mem == NULL)
goto fail;
mem = scratch_mem;
- strcpy(mem, url);
+ os_strncpy(mem, url, url_len);
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Adding URL '%s'", mem);
domain_and_port = mem;
mem += 1 + os_strlen(mem);
delim = os_strchr(domain_and_port, '/');
}
domain = mem;
strcpy(domain, domain_and_port);
- delim = strchr(domain, ':');
+ delim = os_strchr(domain, ':');
if (delim) {
*delim++ = 0; /* null terminate domain */
if (isdigit(*delim))
goto fail;
}
for (rp = result; rp; rp = rp->ai_next) {
+ struct subscr_addr *a;
+
/* Limit no. of address to avoid denial of service attack */
if (dl_list_len(&s->addr_list) >= MAX_ADDR_PER_SUBSCRIPTION) {
wpa_printf(MSG_INFO, "WPS UPnP: subscr_addr_add_url: "
if (path[0] != '/')
*mem++ = '/';
strcpy(mem, path);
- mem += 1 + strlen(mem);
+ mem += 1 + os_strlen(mem);
os_memcpy(&a->saddr, rp->ai_addr, sizeof(a->saddr));
a->saddr.sin_port = htons(port);
dl_list_add(&s->addr_list, &a->list);
- a = NULL; /* don't free it below */
}
fail:
if (result)
freeaddrinfo(result);
os_free(scratch_mem);
- os_free(a);
}
static void subscr_addr_list_create(struct subscription *s,
const char *url_list)
{
- char *end;
+ const char *end;
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Parsing URL list '%s'", url_list);
for (;;) {
while (*url_list == ' ' || *url_list == '\t')
url_list++;
end = os_strchr(url_list, '>');
if (end == NULL)
break;
- *end++ = 0;
- subscr_addr_add_url(s, url_list);
- url_list = end;
+ subscr_addr_add_url(s, url_list, end - url_list);
+ url_list = end + 1;
}
}
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
"<e:propertyset xmlns:e=\"urn:schemas-upnp-org:event-1-0\">\n";
const char *format_tail = "</e:propertyset>\n";
+ struct os_time now;
if (dl_list_empty(&sm->subscriptions)) {
/* optimize */
return;
}
+ if (os_get_time(&now) == 0) {
+ if (now.sec != sm->last_event_sec) {
+ sm->last_event_sec = now.sec;
+ sm->num_events_in_sec = 1;
+ } else {
+ sm->num_events_in_sec++;
+ /*
+ * In theory, this should apply to all WLANEvent
+ * notifications, but EAP messages are of much higher
+ * priority and Probe Request notifications should not
+ * be allowed to drop EAP messages, so only throttle
+ * Probe Request notifications.
+ */
+ if (sm->num_events_in_sec > MAX_EVENTS_PER_SEC &&
+ sm->wlanevent_type ==
+ UPNP_WPS_WLANEVENT_TYPE_PROBE) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Throttle "
+ "event notifications (%u seen "
+ "during one second)",
+ sm->num_events_in_sec);
+ return;
+ }
+ }
+ }
+
/* Determine buffer size needed first */
buf_size += os_strlen(format_head);
buf_size += 50 + 2 * os_strlen("WLANEvent");
dl_list_for_each_safe(s, tmp, &sm->subscriptions, struct subscription,
list) {
- if (event_add(s, buf)) {
- wpa_printf(MSG_INFO, "WPS UPnP: Dropping "
- "subscriber due to event backlog");
- dl_list_del(&s->list);
- subscription_destroy(s);
- }
+ event_add(s, buf,
+ sm->wlanevent_type == UPNP_WPS_WLANEVENT_TYPE_PROBE);
}
wpabuf_free(buf);
wpabuf_put_be16(msg, ATTR_REGISTRAR_NONCE);
wpabuf_put_be16(msg, WPS_NONCE_LEN);
wpabuf_put(msg, WPS_NONCE_LEN);
+ wps_build_wfa_ext(msg, 0, NULL, 0);
return msg;
}
"<e:propertyset xmlns:e=\"urn:schemas-upnp-org:event-1-0\">\n";
const char *tail = "</e:propertyset>\n";
char txt[10];
+ int ret;
if (s->sm->wlanevent == NULL) {
/*
}
buf = wpabuf_alloc(500 + os_strlen(wlan_event));
if (buf == NULL)
- return 1;
+ return -1;
wpabuf_put_str(buf, head);
wpabuf_put_property(buf, "STAStatus", "1");
wpabuf_put_property(buf, "WLANEvent", wlan_event);
wpabuf_put_str(buf, tail);
- if (event_add(s, buf)) {
+ ret = event_add(s, buf, 0);
+ if (ret) {
wpabuf_free(buf);
- return 1;
+ return ret;
}
wpabuf_free(buf);
s->timeout_time = expire;
uuid_make(s->uuid);
subscr_addr_list_create(s, callback_urls);
+ if (dl_list_empty(&s->addr_list)) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: No valid callback URLs in "
+ "'%s' - drop subscription", callback_urls);
+ subscription_destroy(s);
+ return NULL;
+ }
+
/* Add to end of list, since it has the highest expiration time */
dl_list_add_tail(&sm->subscriptions, &s->list);
/* Queue up immediate event message (our last event)
os_free(sm->wlanevent);
sm->wlanevent = val;
+ sm->wlanevent_type = ev_type;
upnp_wps_device_send_event(sm);
ret = 0;
}
-static void upnp_wps_free_subscriptions(struct dl_list *head)
+static void upnp_wps_free_subscriptions(struct dl_list *head,
+ struct wps_registrar *reg)
{
struct subscription *s, *tmp;
dl_list_for_each_safe(s, tmp, head, struct subscription, list) {
+ if (reg && s->reg != reg)
+ continue;
dl_list_del(&s->list);
subscription_destroy(s);
}
* upnp_wps_device_stop - Stop WPS UPnP operations on an interface
* @sm: WPS UPnP state machine from upnp_wps_device_init()
*/
-void upnp_wps_device_stop(struct upnp_wps_device_sm *sm)
+static void upnp_wps_device_stop(struct upnp_wps_device_sm *sm)
{
if (!sm || !sm->started)
return;
wpa_printf(MSG_DEBUG, "WPS UPnP: Stop device");
web_listener_stop(sm);
upnp_wps_free_msearchreply(&sm->msearch_replies);
- upnp_wps_free_subscriptions(&sm->subscriptions);
+ upnp_wps_free_subscriptions(&sm->subscriptions, NULL);
advertisement_state_machine_stop(sm, 1);
* @net_if: Selected network interface name
* Returns: 0 on success, -1 on failure
*/
-int upnp_wps_device_start(struct upnp_wps_device_sm *sm, char *net_if)
+static int upnp_wps_device_start(struct upnp_wps_device_sm *sm, char *net_if)
{
if (!sm || !net_if)
return -1;
}
+static struct upnp_wps_device_interface *
+upnp_wps_get_iface(struct upnp_wps_device_sm *sm, void *priv)
+{
+ struct upnp_wps_device_interface *iface;
+ dl_list_for_each(iface, &sm->interfaces,
+ struct upnp_wps_device_interface, list) {
+ if (iface->priv == priv)
+ return iface;
+ }
+ return NULL;
+}
+
+
/**
* upnp_wps_device_deinit - Deinitialize WPS UPnP
* @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * @priv: External context data that was used in upnp_wps_device_init() call
*/
-void upnp_wps_device_deinit(struct upnp_wps_device_sm *sm)
+void upnp_wps_device_deinit(struct upnp_wps_device_sm *sm, void *priv)
{
+ struct upnp_wps_device_interface *iface;
+
if (!sm)
return;
- upnp_wps_device_stop(sm);
-
- if (sm->peer.wps)
- wps_deinit(sm->peer.wps);
- os_free(sm->root_dir);
- os_free(sm->desc_url);
- os_free(sm->ctx->ap_pin);
- os_free(sm->ctx);
- os_free(sm);
+ iface = upnp_wps_get_iface(sm, priv);
+ if (iface == NULL) {
+ wpa_printf(MSG_ERROR, "WPS UPnP: Could not find the interface "
+ "instance to deinit");
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Deinit interface instance %p", iface);
+ if (dl_list_len(&sm->interfaces) == 1) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Deinitializing last instance "
+ "- free global device instance");
+ upnp_wps_device_stop(sm);
+ } else
+ upnp_wps_free_subscriptions(&sm->subscriptions,
+ iface->wps->registrar);
+ dl_list_del(&iface->list);
+
+ if (iface->peer.wps)
+ wps_deinit(iface->peer.wps);
+ os_free(iface->ctx->ap_pin);
+ os_free(iface->ctx);
+ os_free(iface);
+
+ if (dl_list_empty(&sm->interfaces)) {
+ os_free(sm->root_dir);
+ os_free(sm->desc_url);
+ os_free(sm);
+ shared_upnp_device = NULL;
+ }
}
* @ctx: callback table; we must eventually free it
* @wps: Pointer to longterm WPS context
* @priv: External context data that will be used in callbacks
+ * @net_if: Selected network interface name
* Returns: WPS UPnP state or %NULL on failure
*/
struct upnp_wps_device_sm *
upnp_wps_device_init(struct upnp_wps_device_ctx *ctx, struct wps_context *wps,
- void *priv)
+ void *priv, char *net_if)
{
struct upnp_wps_device_sm *sm;
+ struct upnp_wps_device_interface *iface;
+ int start = 0;
- sm = os_zalloc(sizeof(*sm));
- if (!sm) {
- wpa_printf(MSG_ERROR, "WPS UPnP: upnp_wps_device_init failed");
+ iface = os_zalloc(sizeof(*iface));
+ if (iface == NULL) {
+ os_free(ctx->ap_pin);
+ os_free(ctx);
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Init interface instance %p", iface);
+
+ iface->ctx = ctx;
+ iface->wps = wps;
+ iface->priv = priv;
+
+ if (shared_upnp_device) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Share existing device "
+ "context");
+ sm = shared_upnp_device;
+ } else {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Initialize device context");
+ sm = os_zalloc(sizeof(*sm));
+ if (!sm) {
+ wpa_printf(MSG_ERROR, "WPS UPnP: upnp_wps_device_init "
+ "failed");
+ os_free(iface);
+ os_free(ctx->ap_pin);
+ os_free(ctx);
+ return NULL;
+ }
+ shared_upnp_device = sm;
+
+ dl_list_init(&sm->msearch_replies);
+ dl_list_init(&sm->subscriptions);
+ dl_list_init(&sm->interfaces);
+ start = 1;
+ }
+
+ dl_list_add(&sm->interfaces, &iface->list);
+
+ if (start && upnp_wps_device_start(sm, net_if)) {
+ upnp_wps_device_deinit(sm, priv);
return NULL;
}
- sm->ctx = ctx;
- sm->wps = wps;
- sm->priv = priv;
- dl_list_init(&sm->msearch_replies);
- dl_list_init(&sm->subscriptions);
return sm;
}
int upnp_wps_set_ap_pin(struct upnp_wps_device_sm *sm, const char *ap_pin)
{
+ struct upnp_wps_device_interface *iface;
if (sm == NULL)
return 0;
- os_free(sm->ctx->ap_pin);
- if (ap_pin) {
- sm->ctx->ap_pin = os_strdup(ap_pin);
- if (sm->ctx->ap_pin == NULL)
- return -1;
- } else
- sm->ctx->ap_pin = NULL;
+ dl_list_for_each(iface, &sm->interfaces,
+ struct upnp_wps_device_interface, list) {
+ os_free(iface->ctx->ap_pin);
+ if (ap_pin) {
+ iface->ctx->ap_pin = os_strdup(ap_pin);
+ if (iface->ctx->ap_pin == NULL)
+ return -1;
+ } else
+ iface->ctx->ap_pin = NULL;
+ }
return 0;
}
struct upnp_wps_device_sm *
upnp_wps_device_init(struct upnp_wps_device_ctx *ctx, struct wps_context *wps,
- void *priv);
-void upnp_wps_device_deinit(struct upnp_wps_device_sm *sm);
-
-int upnp_wps_device_start(struct upnp_wps_device_sm *sm, char *net_if);
-void upnp_wps_device_stop(struct upnp_wps_device_sm *sm);
+ void *priv, char *net_if);
+void upnp_wps_device_deinit(struct upnp_wps_device_sm *sm, void *priv);
int upnp_wps_device_send_wlan_event(struct upnp_wps_device_sm *sm,
const u8 from_mac_addr[ETH_ALEN],
wpa_hexdump_buf(MSG_MSGDUMP, "WPS: SetSelectedRegistrar attributes",
msg);
+ if (wps_validate_upnp_set_selected_registrar(msg) < 0)
+ return -1;
if (wps_parse_msg(msg, &attr) < 0)
return -1;
- if (!wps_version_supported(attr.version)) {
- wpa_printf(MSG_DEBUG, "WPS: Unsupported SetSelectedRegistrar "
- "version 0x%x", attr.version ? *attr.version : 0);
- return -1;
- }
s->reg = reg;
eloop_cancel_timeout(upnp_er_set_selected_timeout, s, NULL);
+ os_memset(s->authorized_macs, 0, sizeof(s->authorized_macs));
if (attr.selected_registrar == NULL || *attr.selected_registrar == 0) {
wpa_printf(MSG_DEBUG, "WPS: SetSelectedRegistrar: Disable "
"Selected Registrar");
WPA_GET_BE16(attr.dev_password_id) : DEV_PW_DEFAULT;
s->config_methods = attr.sel_reg_config_methods ?
WPA_GET_BE16(attr.sel_reg_config_methods) : -1;
+ if (attr.authorized_macs) {
+ int count = attr.authorized_macs_len / ETH_ALEN;
+ if (count > WPS_MAX_AUTHORIZED_MACS)
+ count = WPS_MAX_AUTHORIZED_MACS;
+ os_memcpy(s->authorized_macs, attr.authorized_macs,
+ count * ETH_ALEN);
+ } else if (!attr.version2) {
+#ifdef CONFIG_WPS2
+ wpa_printf(MSG_DEBUG, "WPS: Add broadcast "
+ "AuthorizedMACs for WPS 1.0 ER");
+ os_memset(s->authorized_macs, 0xff, ETH_ALEN);
+#endif /* CONFIG_WPS2 */
+ }
eloop_register_timeout(WPS_PBC_WALK_TIME, 0,
upnp_er_set_selected_timeout, s, NULL);
}
* Copyright (c) 2000-2003 Intel Corporation
* Copyright (c) 2006-2007 Sony Corporation
* Copyright (c) 2008-2009 Atheros Communications
- * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
*
* See wps_upnp.c for more details on licensing and code history.
*/
*/
#define MAX_EVENTS_QUEUED 20 /* How far behind queued events */
-#define EVENT_TIMEOUT_SEC 30 /* Drop sending event after timeout */
+#define MAX_FAILURES 10 /* Drop subscription after this many failures */
/* How long to wait before sending event */
#define EVENT_DELAY_SECONDS 0
*/
static void event_delete(struct wps_event_ *e)
{
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Delete event %p", e);
event_clean(e);
wpabuf_free(e->data);
os_free(e);
{
struct wps_event_ *e;
e = dl_list_first(&s->event_queue, struct wps_event_, list);
- if (e)
+ if (e) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Dequeue event %p for "
+ "subscription %p", e, s);
dl_list_del(&e->list);
+ }
return e;
}
struct subscription *s = e->s;
struct upnp_wps_device_sm *sm = s->sm;
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Retry event %p for subscription %p",
+ e, s);
event_clean(e);
/* will set: s->current_event = NULL; */
- if (do_next_address)
+ if (do_next_address) {
e->retry++;
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Try address %d", e->retry);
+ }
if (e->retry >= dl_list_len(&s->addr_list)) {
wpa_printf(MSG_DEBUG, "WPS UPnP: Giving up on sending event "
"for %s", e->addr->domain_and_port);
+ event_delete(e);
+ s->last_event_failed = 1;
+ if (!dl_list_empty(&s->event_queue))
+ event_send_all_later(s->sm);
return;
}
dl_list_add(&s->event_queue, &e->list);
}
+static void event_addr_failure(struct wps_event_ *e)
+{
+ struct subscription *s = e->s;
+
+ e->addr->num_failures++;
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Failed to send event %p to %s "
+ "(num_failures=%u)",
+ e, e->addr->domain_and_port, e->addr->num_failures);
+
+ if (e->addr->num_failures < MAX_FAILURES) {
+ /* Try other addresses, if available */
+ event_retry(e, 1);
+ return;
+ }
+
+ /*
+ * If other side doesn't like what we say, forget about them.
+ * (There is no way to tell other side that we are dropping them...).
+ */
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Deleting subscription %p "
+ "address %s due to errors", s, e->addr->domain_and_port);
+ dl_list_del(&e->addr->list);
+ subscr_addr_delete(e->addr);
+ e->addr = NULL;
+
+ if (dl_list_empty(&s->addr_list)) {
+ /* if we've given up on all addresses */
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Removing subscription %p "
+ "with no addresses", s);
+ dl_list_del(&s->list);
+ subscription_destroy(s);
+ return;
+ }
+
+ /* Try other addresses, if available */
+ event_retry(e, 0);
+}
+
+
static void event_http_cb(void *ctx, struct http_client *c,
enum http_client_event event)
{
struct wps_event_ *e = ctx;
struct subscription *s = e->s;
+ wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP client callback: e=%p c=%p "
+ "event=%d", e, c, event);
switch (event) {
case HTTP_CLIENT_OK:
wpa_printf(MSG_DEBUG,
- "WPS UPnP: Got event reply OK from "
- "%s", e->addr->domain_and_port);
+ "WPS UPnP: Got event %p reply OK from %s",
+ e, e->addr->domain_and_port);
+ e->addr->num_failures = 0;
+ s->last_event_failed = 0;
event_delete(e);
/* Schedule sending more if there is more to send */
event_send_all_later(s->sm);
break;
case HTTP_CLIENT_FAILED:
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Event send failure");
+ event_addr_failure(e);
+ break;
case HTTP_CLIENT_INVALID_REPLY:
- wpa_printf(MSG_DEBUG, "WPS UPnP: Failed to send event to %s",
- e->addr->domain_and_port);
-
- /*
- * If other side doesn't like what we say, forget about them.
- * (There is no way to tell other side that we are dropping
- * them...).
- * Alternately, we could just do event_delete(e)
- */
- wpa_printf(MSG_DEBUG, "WPS UPnP: Deleting subscription due to "
- "errors");
- dl_list_del(&s->list);
- subscription_destroy(s);
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Invalid reply");
+ event_addr_failure(e);
break;
case HTTP_CLIENT_TIMEOUT:
wpa_printf(MSG_DEBUG, "WPS UPnP: Event send timeout");
- event_retry(e, 1);
+ event_addr_failure(e);
+ break;
}
}
* Assume we are called ONLY with no current event and ONLY with
* nonempty event queue and ONLY with at least one address to send to.
*/
- assert(!dl_list_empty(&s->addr_list));
- assert(s->current_event == NULL);
- assert(!dl_list_empty(&s->event_queue));
+ if (dl_list_empty(&s->addr_list))
+ return -1;
+ if (s->current_event)
+ return -1;
+ if (dl_list_empty(&s->event_queue))
+ return -1;
s->current_event = e = event_dequeue(s);
sm->event_send_all_queued = 0;
dl_list_for_each_safe(s, tmp, &sm->subscriptions, struct subscription,
list) {
- if (dl_list_empty(&s->addr_list)) {
- /* if we've given up on all addresses */
- wpa_printf(MSG_DEBUG, "WPS UPnP: Removing "
- "subscription with no addresses");
- dl_list_del(&s->list);
- subscription_destroy(s);
- } else {
- if (s->current_event == NULL /* not busy */ &&
- !dl_list_empty(&s->event_queue) /* more to do */) {
- if (event_send_start(s))
- nerrors++;
- }
+ if (s->current_event == NULL /* not busy */ &&
+ !dl_list_empty(&s->event_queue) /* more to do */) {
+ if (event_send_start(s))
+ nerrors++;
}
}
* event_add - Add a new event to a queue
* @s: Subscription
* @data: Event data (is copied; caller retains ownership)
- * Returns: 0 on success, 1 on error
+ * @probereq: Whether this is a Probe Request event
+ * Returns: 0 on success, -1 on error, 1 on max event queue limit reached
*/
-int event_add(struct subscription *s, const struct wpabuf *data)
+int event_add(struct subscription *s, const struct wpabuf *data, int probereq)
{
struct wps_event_ *e;
+ unsigned int len;
- if (dl_list_len(&s->event_queue) >= MAX_EVENTS_QUEUED) {
+ len = dl_list_len(&s->event_queue);
+ if (len >= MAX_EVENTS_QUEUED) {
wpa_printf(MSG_DEBUG, "WPS UPnP: Too many events queued for "
- "subscriber");
- return 1;
+ "subscriber %p", s);
+ if (probereq)
+ return 1;
+
+ /* Drop oldest entry to allow EAP event to be stored. */
+ e = event_dequeue(s);
+ if (!e)
+ return 1;
+ event_delete(e);
+ }
+
+ if (s->last_event_failed && probereq && len > 0) {
+ /*
+ * Avoid queuing frames for subscribers that may have left
+ * without unsubscribing.
+ */
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Do not queue more Probe "
+ "Request frames for subscription %p since last "
+ "delivery failed", s);
+ return -1;
}
e = os_zalloc(sizeof(*e));
if (e == NULL)
- return 1;
+ return -1;
dl_list_init(&e->list);
e->s = s;
e->data = wpabuf_dup(data);
if (e->data == NULL) {
os_free(e);
- return 1;
+ return -1;
}
e->subscriber_sequence = s->next_subscriber_sequence++;
if (s->next_subscriber_sequence == 0)
s->next_subscriber_sequence++;
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Queue event %p for subscriber %p "
+ "(queue len %u)", e, s, len + 1);
dl_list_add_tail(&s->event_queue, &e->list);
event_send_all_later(s->sm);
return 0;
char *domain_and_port; /* domain and port part of url */
char *path; /* "filepath" part of url (from "mem") */
struct sockaddr_in saddr; /* address for doing connect */
+ unsigned num_failures;
};
struct dl_list event_queue; /* Queued event messages. */
struct wps_event_ *current_event; /* non-NULL if being sent (not in q)
*/
+ int last_event_failed; /* Whether delivery of last event failed */
/* Information from SetSelectedRegistrar action */
u8 selected_registrar;
u16 dev_password_id;
u16 config_methods;
+ u8 authorized_macs[WPS_MAX_AUTHORIZED_MACS][ETH_ALEN];
struct wps_registrar *reg;
};
+struct upnp_wps_device_interface {
+ struct dl_list list;
+ struct upnp_wps_device_ctx *ctx; /* callback table */
+ struct wps_context *wps;
+ void *priv;
+
+ /* FIX: maintain separate structures for each UPnP peer */
+ struct upnp_wps_peer peer;
+};
+
/*
- * Our instance data corresponding to one WiFi network interface
- * (multiple might share the same wired network interface!).
+ * Our instance data corresponding to the AP device. Note that there may be
+ * multiple wireless interfaces sharing the same UPnP device instance. Each
+ * such interface is stored in the list of struct upnp_wps_device_interface
+ * instances.
*
* This is known as an opaque struct declaration to users of the WPS UPnP code.
*/
struct upnp_wps_device_sm {
- struct upnp_wps_device_ctx *ctx; /* callback table */
- struct wps_context *wps;
- void *priv;
+ struct dl_list interfaces; /* struct upnp_wps_device_interface */
char *root_dir;
char *desc_url;
int started; /* nonzero if we are active */
*/
char *wlanevent; /* the last WLANEvent data */
-
- /* FIX: maintain separate structures for each UPnP peer */
- struct upnp_wps_peer peer;
+ enum upnp_wps_wlanevent_type wlanevent_type;
+ os_time_t last_event_sec;
+ unsigned int num_events_in_sec;
};
/* wps_upnp.c */
void subscription_destroy(struct subscription *s);
struct subscription * subscription_find(struct upnp_wps_device_sm *sm,
const u8 uuid[UUID_LEN]);
+void subscr_addr_delete(struct subscr_addr *a);
int send_wpabuf(int fd, struct wpabuf *buf);
int get_netif_info(const char *net_if, unsigned *ip_addr, char **ip_addr_text,
u8 mac[ETH_ALEN]);
void web_listener_stop(struct upnp_wps_device_sm *sm);
/* wps_upnp_event.c */
-int event_add(struct subscription *s, const struct wpabuf *data);
+int event_add(struct subscription *s, const struct wpabuf *data, int probereq);
void event_delete_all(struct subscription *s);
void event_send_all_later(struct upnp_wps_device_sm *sm);
void event_send_stop_all(struct upnp_wps_device_sm *sm);
}
-/* No. of chars excluding trailing whitespace */
-static int line_length_stripped(const char *l)
-{
- const char *lp = l + line_length(l);
- while (lp > l && !isgraph(lp[-1]))
- lp--;
- return lp - l;
-}
-
-
static int str_starts(const char *str, const char *start)
{
return os_strncmp(str, start, os_strlen(start)) == 0;
struct wpabuf *msg;
char *NTString = "";
char uuid_string[80];
+ struct upnp_wps_device_interface *iface;
*islast = 0;
- uuid_bin2str(sm->wps->uuid, uuid_string, sizeof(uuid_string));
+ iface = dl_list_first(&sm->interfaces,
+ struct upnp_wps_device_interface, list);
+ uuid_bin2str(iface->wps->uuid, uuid_string, sizeof(uuid_string));
msg = wpabuf_alloc(800); /* more than big enough */
if (msg == NULL)
goto fail;
#ifndef CONFIG_NO_STDOUT_DEBUG
const char *start = data;
#endif /* CONFIG_NO_STDOUT_DEBUG */
- const char *end;
int got_host = 0;
int got_st = 0, st_match = 0;
int got_man = 0;
/* Parse remaining lines */
for (; *data != '\0'; data += line_length(data)) {
- end = data + line_length_stripped(data);
if (token_eq(data, "host")) {
/* The host line indicates who the packet
* is addressed to... but do we really care?
}
if (str_starts(data, "uuid:")) {
char uuid_string[80];
+ struct upnp_wps_device_interface *iface;
+ iface = dl_list_first(
+ &sm->interfaces,
+ struct upnp_wps_device_interface,
+ list);
data += os_strlen("uuid:");
- uuid_bin2str(sm->wps->uuid, uuid_string,
+ uuid_bin2str(iface->wps->uuid, uuid_string,
sizeof(uuid_string));
if (str_starts(data, uuid_string))
st_match = 1;
#endif
if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF,
- &ip_addr, sizeof(ip_addr)))
+ &ip_addr, sizeof(ip_addr))) {
+ wpa_printf(MSG_DEBUG, "WPS: setsockopt(IP_MULTICAST_IF) %x: "
+ "%d (%s)", ip_addr, errno, strerror(errno));
return -1;
+ }
if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL,
- &ttl, sizeof(ttl)))
+ &ttl, sizeof(ttl))) {
+ wpa_printf(MSG_DEBUG, "WPS: setsockopt(IP_MULTICAST_TTL): "
+ "%d (%s)", errno, strerror(errno));
return -1;
+ }
#if 0 /* not needed, because we don't receive using multicast_sd */
{
{
const char *s;
char uuid_string[80];
+ struct upnp_wps_device_interface *iface;
+
+ iface = dl_list_first(&sm->interfaces,
+ struct upnp_wps_device_interface, list);
wpabuf_put_str(buf, wps_device_xml_prefix);
* Add required fields with default values if not configured. Add
* optional and recommended fields only if configured.
*/
- s = sm->wps->friendly_name;
+ s = iface->wps->friendly_name;
s = ((s && *s) ? s : "WPS Access Point");
xml_add_tagged_data(buf, "friendlyName", s);
- s = sm->wps->dev.manufacturer;
+ s = iface->wps->dev.manufacturer;
s = ((s && *s) ? s : "");
xml_add_tagged_data(buf, "manufacturer", s);
- if (sm->wps->manufacturer_url)
+ if (iface->wps->manufacturer_url)
xml_add_tagged_data(buf, "manufacturerURL",
- sm->wps->manufacturer_url);
+ iface->wps->manufacturer_url);
- if (sm->wps->model_description)
+ if (iface->wps->model_description)
xml_add_tagged_data(buf, "modelDescription",
- sm->wps->model_description);
+ iface->wps->model_description);
- s = sm->wps->dev.model_name;
+ s = iface->wps->dev.model_name;
s = ((s && *s) ? s : "");
xml_add_tagged_data(buf, "modelName", s);
- if (sm->wps->dev.model_number)
+ if (iface->wps->dev.model_number)
xml_add_tagged_data(buf, "modelNumber",
- sm->wps->dev.model_number);
+ iface->wps->dev.model_number);
- if (sm->wps->model_url)
- xml_add_tagged_data(buf, "modelURL", sm->wps->model_url);
+ if (iface->wps->model_url)
+ xml_add_tagged_data(buf, "modelURL", iface->wps->model_url);
- if (sm->wps->dev.serial_number)
+ if (iface->wps->dev.serial_number)
xml_add_tagged_data(buf, "serialNumber",
- sm->wps->dev.serial_number);
+ iface->wps->dev.serial_number);
- uuid_bin2str(sm->wps->uuid, uuid_string, sizeof(uuid_string));
+ uuid_bin2str(iface->wps->uuid, uuid_string, sizeof(uuid_string));
s = uuid_string;
/* Need "uuid:" prefix, thus we can't use xml_add_tagged_data()
* easily...
xml_data_encode(buf, s, os_strlen(s));
wpabuf_put_str(buf, "</UDN>\n");
- if (sm->wps->upc)
- xml_add_tagged_data(buf, "UPC", sm->wps->upc);
+ if (iface->wps->upc)
+ xml_add_tagged_data(buf, "UPC", iface->wps->upc);
wpabuf_put_str(buf, wps_device_xml_postfix);
}
size_t extra_len = 0;
int body_length;
char len_buf[10];
+ struct upnp_wps_device_interface *iface;
+
+ iface = dl_list_first(&sm->interfaces,
+ struct upnp_wps_device_interface, list);
/*
* It is not required that filenames be case insensitive but it is
wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET for device XML");
req = GET_DEVICE_XML_FILE;
extra_len = 3000;
- if (sm->wps->friendly_name)
- extra_len += os_strlen(sm->wps->friendly_name);
- if (sm->wps->manufacturer_url)
- extra_len += os_strlen(sm->wps->manufacturer_url);
- if (sm->wps->model_description)
- extra_len += os_strlen(sm->wps->model_description);
- if (sm->wps->model_url)
- extra_len += os_strlen(sm->wps->model_url);
- if (sm->wps->upc)
- extra_len += os_strlen(sm->wps->upc);
+ if (iface->wps->friendly_name)
+ extra_len += os_strlen(iface->wps->friendly_name);
+ if (iface->wps->manufacturer_url)
+ extra_len += os_strlen(iface->wps->manufacturer_url);
+ if (iface->wps->model_description)
+ extra_len += os_strlen(iface->wps->model_description);
+ if (iface->wps->model_url)
+ extra_len += os_strlen(iface->wps->model_url);
+ if (iface->wps->upc)
+ extra_len += os_strlen(iface->wps->upc);
} else if (!os_strcasecmp(filename, UPNP_WPS_SCPD_XML_FILE)) {
wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET for SCPD XML");
req = GET_SCPD_XML_FILE;
{
static const char *name = "NewDeviceInfo";
struct wps_config cfg;
- struct upnp_wps_peer *peer = &sm->peer;
+ struct upnp_wps_device_interface *iface;
+ struct upnp_wps_peer *peer;
+
+ iface = dl_list_first(&sm->interfaces,
+ struct upnp_wps_device_interface, list);
+ peer = &iface->peer;
wpa_printf(MSG_DEBUG, "WPS UPnP: GetDeviceInfo");
- if (sm->ctx->ap_pin == NULL)
+ if (iface->ctx->ap_pin == NULL)
return HTTP_INTERNAL_SERVER_ERROR;
/*
wps_deinit(peer->wps);
os_memset(&cfg, 0, sizeof(cfg));
- cfg.wps = sm->wps;
- cfg.pin = (u8 *) sm->ctx->ap_pin;
- cfg.pin_len = os_strlen(sm->ctx->ap_pin);
+ cfg.wps = iface->wps;
+ cfg.pin = (u8 *) iface->ctx->ap_pin;
+ cfg.pin_len = os_strlen(iface->ctx->ap_pin);
peer->wps = wps_init(&cfg);
if (peer->wps) {
enum wsc_op_code op_code;
enum http_reply_code ret;
enum wps_process_res res;
enum wsc_op_code op_code;
+ struct upnp_wps_device_interface *iface;
+
+ iface = dl_list_first(&sm->interfaces,
+ struct upnp_wps_device_interface, list);
/*
* PutMessage is used by external UPnP-based Registrar to perform WPS
msg = xml_get_base64_item(data, "NewInMessage", &ret);
if (msg == NULL)
return ret;
- res = wps_process_msg(sm->peer.wps, WSC_UPnP, msg);
+ res = wps_process_msg(iface->peer.wps, WSC_UPnP, msg);
if (res == WPS_FAILURE)
*reply = NULL;
else
- *reply = wps_get_msg(sm->peer.wps, &op_code);
+ *reply = wps_get_msg(iface->peer.wps, &op_code);
wpabuf_free(msg);
if (*reply == NULL)
return HTTP_INTERNAL_SERVER_ERROR;
int ev_type;
int type;
char *val;
+ struct upnp_wps_device_interface *iface;
+ int ok = 0;
/*
* External UPnP-based Registrar is passing us a message to be proxied
if (hwaddr_aton(val, macaddr)) {
wpa_printf(MSG_DEBUG, "WPS UPnP: Invalid NewWLANEventMAC in "
"PutWLANResponse: '%s'", val);
+#ifdef CONFIG_WPS_STRICT
+ {
+ struct wps_parse_attr attr;
+ if (wps_parse_msg(msg, &attr) < 0 || attr.version2) {
+ wpabuf_free(msg);
+ os_free(val);
+ return UPNP_ARG_VALUE_INVALID;
+ }
+ }
+#endif /* CONFIG_WPS_STRICT */
if (hwaddr_aton2(val, macaddr) > 0) {
/*
* At least some versions of Intel PROset seem to be
*/
wpa_printf(MSG_DEBUG, "WPS UPnP: Workaround - allow "
"incorrect MAC address format in "
- "NewWLANEventMAC");
+ "NewWLANEventMAC: %s -> " MACSTR,
+ val, MAC2STR(macaddr));
} else {
wpabuf_free(msg);
os_free(val);
wpa_printf(MSG_DEBUG, "WPS UPnP: Message Type %d", type);
} else
type = -1;
- if (!sm->ctx->rx_req_put_wlan_response ||
- sm->ctx->rx_req_put_wlan_response(sm->priv, ev_type, macaddr, msg,
- type)) {
+ dl_list_for_each(iface, &sm->interfaces,
+ struct upnp_wps_device_interface, list) {
+ if (iface->ctx->rx_req_put_wlan_response &&
+ iface->ctx->rx_req_put_wlan_response(iface->priv, ev_type,
+ macaddr, msg, type)
+ == 0)
+ ok = 1;
+ }
+
+ if (!ok) {
wpa_printf(MSG_INFO, "WPS UPnP: Fail: sm->ctx->"
"rx_req_put_wlan_response");
wpabuf_free(msg);
struct wpabuf *msg;
enum http_reply_code ret;
struct subscription *s;
+ struct upnp_wps_device_interface *iface;
+ int err = 0;
wpa_printf(MSG_DEBUG, "WPS UPnP: SetSelectedRegistrar");
s = find_er(sm, cli);
msg = xml_get_base64_item(data, "NewMessage", &ret);
if (msg == NULL)
return ret;
- if (upnp_er_set_selected_registrar(sm->wps->registrar, s, msg)) {
- wpabuf_free(msg);
- return HTTP_INTERNAL_SERVER_ERROR;
+ dl_list_for_each(iface, &sm->interfaces,
+ struct upnp_wps_device_interface, list) {
+ if (upnp_er_set_selected_registrar(iface->wps->registrar, s,
+ msg))
+ err = 1;
}
wpabuf_free(msg);
+ if (err)
+ return HTTP_INTERNAL_SERVER_ERROR;
*replyname = NULL;
*reply = NULL;
return HTTP_OK;
return;
}
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS UPnP: HTTP SUBSCRIBE",
+ (u8 *) hdr, os_strlen(hdr));
+
/* Parse/validate headers */
h = hdr;
/* First line: SUBSCRIBE /wps_event HTTP/1.1
break; /* no unterminated lines allowed */
/* NT assures that it is our type of subscription;
- * not used for a renewl.
+ * not used for a renewal.
**/
match = "NT:";
match_len = os_strlen(match);
if (got_uuid) {
/* renewal */
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Subscription renewal");
if (callback_urls) {
ret = HTTP_BAD_REQUEST;
goto error;
}
s = subscription_renew(sm, uuid);
if (s == NULL) {
+ char str[80];
+ uuid_bin2str(uuid, str, sizeof(str));
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Could not find "
+ "SID %s", str);
ret = HTTP_PRECONDITION_FAILED;
goto error;
}
} else if (callback_urls) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: New subscription");
if (!got_nt) {
ret = HTTP_PRECONDITION_FAILED;
goto error;
/* subscription id */
b = wpabuf_put(buf, 0);
uuid_bin2str(s->uuid, b, 80);
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Assigned SID %s", b);
wpabuf_put(buf, os_strlen(b));
wpabuf_put_str(buf, "\r\n");
wpabuf_printf(buf, "Timeout: Second-%d\r\n", UPNP_SUBSCRIBE_SEC);
* HTTP 500-series error code.
* 599 Too many subscriptions (not a standard HTTP error)
*/
+ wpa_printf(MSG_DEBUG, "WPS UPnP: SUBSCRIBE failed - return %d", ret);
http_put_empty(buf, ret);
http_request_send_and_deinit(req, buf);
os_free(callback_urls);
--- /dev/null
+/*
+ * Wi-Fi Protected Setup - Strict protocol validation routines
+ * Copyright (c) 2010, Atheros Communications, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "wps_i.h"
+#include "wps.h"
+
+
+#ifndef WPS_STRICT_ALL
+#define WPS_STRICT_WPS2
+#endif /* WPS_STRICT_ALL */
+
+
+static int wps_validate_version(const u8 *version, int mandatory)
+{
+ if (version == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Version attribute "
+ "missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (*version != 0x10) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Version attribute "
+ "value 0x%x", *version);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_version2(const u8 *version2, int mandatory)
+{
+ if (version2 == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Version2 attribute "
+ "missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (*version2 < 0x20) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Version2 attribute "
+ "value 0x%x", *version2);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_request_type(const u8 *request_type, int mandatory)
+{
+ if (request_type == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Request Type "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (*request_type > 0x03) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Request Type "
+ "attribute value 0x%x", *request_type);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_response_type(const u8 *response_type, int mandatory)
+{
+ if (response_type == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Response Type "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (*response_type > 0x03) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Response Type "
+ "attribute value 0x%x", *response_type);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int valid_config_methods(u16 val, int wps2)
+{
+ if (wps2) {
+ if ((val & 0x6000) && !(val & WPS_CONFIG_DISPLAY)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Physical/Virtual "
+ "Display flag without old Display flag "
+ "set");
+ return 0;
+ }
+ if (!(val & 0x6000) && (val & WPS_CONFIG_DISPLAY)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Display flag "
+ "without Physical/Virtual Display flag");
+ return 0;
+ }
+ if ((val & 0x0600) && !(val & WPS_CONFIG_PUSHBUTTON)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Physical/Virtual "
+ "PushButton flag without old PushButton "
+ "flag set");
+ return 0;
+ }
+ if (!(val & 0x0600) && (val & WPS_CONFIG_PUSHBUTTON)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: PushButton flag "
+ "without Physical/Virtual PushButton flag");
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+
+static int wps_validate_config_methods(const u8 *config_methods, int wps2,
+ int mandatory)
+{
+ u16 val;
+
+ if (config_methods == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Configuration "
+ "Methods attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+
+ val = WPA_GET_BE16(config_methods);
+ if (!valid_config_methods(val, wps2)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Configuration "
+ "Methods attribute value 0x%04x", val);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_ap_config_methods(const u8 *config_methods, int wps2,
+ int mandatory)
+{
+ u16 val;
+
+ if (wps_validate_config_methods(config_methods, wps2, mandatory) < 0)
+ return -1;
+ if (config_methods == NULL)
+ return 0;
+ val = WPA_GET_BE16(config_methods);
+ if (val & WPS_CONFIG_PUSHBUTTON) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Configuration "
+ "Methods attribute value 0x%04x in AP info "
+ "(PushButton not allowed for registering new ER)",
+ val);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_uuid_e(const u8 *uuid_e, int mandatory)
+{
+ if (uuid_e == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: UUID-E "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_uuid_r(const u8 *uuid_r, int mandatory)
+{
+ if (uuid_r == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: UUID-R "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_primary_dev_type(const u8 *primary_dev_type,
+ int mandatory)
+{
+ if (primary_dev_type == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Primary Device Type "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_rf_bands(const u8 *rf_bands, int mandatory)
+{
+ if (rf_bands == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: RF Bands "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (*rf_bands != WPS_RF_24GHZ && *rf_bands != WPS_RF_50GHZ &&
+ *rf_bands != (WPS_RF_24GHZ | WPS_RF_50GHZ)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Rf Bands "
+ "attribute value 0x%x", *rf_bands);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_assoc_state(const u8 *assoc_state, int mandatory)
+{
+ u16 val;
+ if (assoc_state == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Association State "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ val = WPA_GET_BE16(assoc_state);
+ if (val > 4) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Association State "
+ "attribute value 0x%04x", val);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_config_error(const u8 *config_error, int mandatory)
+{
+ u16 val;
+
+ if (config_error == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Configuration Error "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ val = WPA_GET_BE16(config_error);
+ if (val > 18) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Configuration Error "
+ "attribute value 0x%04x", val);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_dev_password_id(const u8 *dev_password_id,
+ int mandatory)
+{
+ u16 val;
+
+ if (dev_password_id == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Device Password ID "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ val = WPA_GET_BE16(dev_password_id);
+ if (val >= 0x0006 && val <= 0x000f) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Device Password ID "
+ "attribute value 0x%04x", val);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_manufacturer(const u8 *manufacturer, size_t len,
+ int mandatory)
+{
+ if (manufacturer == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Manufacturer "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (len > 0 && manufacturer[len - 1] == 0) {
+ wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid Manufacturer "
+ "attribute value", manufacturer, len);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_model_name(const u8 *model_name, size_t len,
+ int mandatory)
+{
+ if (model_name == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Model Name "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (len > 0 && model_name[len - 1] == 0) {
+ wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid Model Name "
+ "attribute value", model_name, len);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_model_number(const u8 *model_number, size_t len,
+ int mandatory)
+{
+ if (model_number == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Model Number "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (len > 0 && model_number[len - 1] == 0) {
+ wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid Model Number "
+ "attribute value", model_number, len);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_serial_number(const u8 *serial_number, size_t len,
+ int mandatory)
+{
+ if (serial_number == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Serial Number "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (len > 0 && serial_number[len - 1] == 0) {
+ wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid Serial "
+ "Number attribute value",
+ serial_number, len);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_dev_name(const u8 *dev_name, size_t len,
+ int mandatory)
+{
+ if (dev_name == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Device Name "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (len > 0 && dev_name[len - 1] == 0) {
+ wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid Device Name "
+ "attribute value", dev_name, len);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_request_to_enroll(const u8 *request_to_enroll,
+ int mandatory)
+{
+ if (request_to_enroll == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Request to Enroll "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (*request_to_enroll > 0x01) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Request to Enroll "
+ "attribute value 0x%x", *request_to_enroll);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_req_dev_type(const u8 *req_dev_type[], size_t num,
+ int mandatory)
+{
+ if (num == 0) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Requested Device "
+ "Type attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_wps_state(const u8 *wps_state, int mandatory)
+{
+ if (wps_state == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Wi-Fi Protected "
+ "Setup State attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (*wps_state != WPS_STATE_NOT_CONFIGURED &&
+ *wps_state != WPS_STATE_CONFIGURED) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Wi-Fi Protected "
+ "Setup State attribute value 0x%x", *wps_state);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_ap_setup_locked(const u8 *ap_setup_locked,
+ int mandatory)
+{
+ if (ap_setup_locked == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: AP Setup Locked "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (*ap_setup_locked > 1) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid AP Setup Locked "
+ "attribute value 0x%x", *ap_setup_locked);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_selected_registrar(const u8 *selected_registrar,
+ int mandatory)
+{
+ if (selected_registrar == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Selected Registrar "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (*selected_registrar > 1) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Selected Registrar "
+ "attribute value 0x%x", *selected_registrar);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_sel_reg_config_methods(const u8 *config_methods,
+ int wps2, int mandatory)
+{
+ u16 val;
+
+ if (config_methods == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Selected Registrar "
+ "Configuration Methods attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+
+ val = WPA_GET_BE16(config_methods);
+ if (!valid_config_methods(val, wps2)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Selected Registrar "
+ "Configuration Methods attribute value 0x%04x",
+ val);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_authorized_macs(const u8 *authorized_macs, size_t len,
+ int mandatory)
+{
+ if (authorized_macs == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Authorized MACs "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (len > 30 && (len % ETH_ALEN) != 0) {
+ wpa_hexdump(MSG_INFO, "WPS-STRICT: Invalid Authorized "
+ "MACs attribute value", authorized_macs, len);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_msg_type(const u8 *msg_type, int mandatory)
+{
+ if (msg_type == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Message Type "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (*msg_type < WPS_Beacon || *msg_type > WPS_WSC_DONE) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Message Type "
+ "attribute value 0x%x", *msg_type);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_mac_addr(const u8 *mac_addr, int mandatory)
+{
+ if (mac_addr == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: MAC Address "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (mac_addr[0] & 0x01) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid MAC Address "
+ "attribute value " MACSTR, MAC2STR(mac_addr));
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_enrollee_nonce(const u8 *enrollee_nonce, int mandatory)
+{
+ if (enrollee_nonce == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Enrollee Nonce "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_registrar_nonce(const u8 *registrar_nonce,
+ int mandatory)
+{
+ if (registrar_nonce == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Registrar Nonce "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_public_key(const u8 *public_key, size_t len,
+ int mandatory)
+{
+ if (public_key == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Public Key "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (len != 192) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Public Key "
+ "attribute length %d", (int) len);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int num_bits_set(u16 val)
+{
+ int c;
+ for (c = 0; val; c++)
+ val &= val - 1;
+ return c;
+}
+
+
+static int wps_validate_auth_type_flags(const u8 *flags, int mandatory)
+{
+ u16 val;
+
+ if (flags == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Authentication Type "
+ "Flags attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ val = WPA_GET_BE16(flags);
+ if ((val & ~WPS_AUTH_TYPES) || !(val & WPS_AUTH_WPA2PSK)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Authentication Type "
+ "Flags attribute value 0x%04x", val);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_auth_type(const u8 *type, int mandatory)
+{
+ u16 val;
+
+ if (type == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Authentication Type "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ val = WPA_GET_BE16(type);
+ if ((val & ~WPS_AUTH_TYPES) || val == 0 ||
+ (num_bits_set(val) > 1 &&
+ val != (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK))) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Authentication Type "
+ "attribute value 0x%04x", val);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_encr_type_flags(const u8 *flags, int mandatory)
+{
+ u16 val;
+
+ if (flags == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Encryption Type "
+ "Flags attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ val = WPA_GET_BE16(flags);
+ if ((val & ~WPS_ENCR_TYPES) || !(val & WPS_ENCR_AES)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Encryption Type "
+ "Flags attribute value 0x%04x", val);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_encr_type(const u8 *type, int mandatory)
+{
+ u16 val;
+
+ if (type == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Encryption Type "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ val = WPA_GET_BE16(type);
+ if ((val & ~WPS_ENCR_TYPES) || val == 0 ||
+ (num_bits_set(val) > 1 && val != (WPS_ENCR_TKIP | WPS_ENCR_AES))) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Encryption Type "
+ "attribute value 0x%04x", val);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_conn_type_flags(const u8 *flags, int mandatory)
+{
+ if (flags == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Connection Type "
+ "Flags attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if ((*flags & ~(WPS_CONN_ESS | WPS_CONN_IBSS)) ||
+ !(*flags & WPS_CONN_ESS)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Connection Type "
+ "Flags attribute value 0x%02x", *flags);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_os_version(const u8 *os_version, int mandatory)
+{
+ if (os_version == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: OS Version "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_authenticator(const u8 *authenticator, int mandatory)
+{
+ if (authenticator == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Authenticator "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_e_hash1(const u8 *hash, int mandatory)
+{
+ if (hash == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: E-Hash1 "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_e_hash2(const u8 *hash, int mandatory)
+{
+ if (hash == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: E-Hash2 "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_r_hash1(const u8 *hash, int mandatory)
+{
+ if (hash == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: R-Hash1 "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_r_hash2(const u8 *hash, int mandatory)
+{
+ if (hash == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: R-Hash2 "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_encr_settings(const u8 *encr_settings, size_t len,
+ int mandatory)
+{
+ if (encr_settings == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Encrypted Settings "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (len < 16) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Encrypted Settings "
+ "attribute length %d", (int) len);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_settings_delay_time(const u8 *delay, int mandatory)
+{
+ if (delay == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Settings Delay Time "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_r_snonce1(const u8 *nonce, int mandatory)
+{
+ if (nonce == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: R-SNonce1 "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_r_snonce2(const u8 *nonce, int mandatory)
+{
+ if (nonce == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: R-SNonce2 "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_e_snonce1(const u8 *nonce, int mandatory)
+{
+ if (nonce == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: E-SNonce1 "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_e_snonce2(const u8 *nonce, int mandatory)
+{
+ if (nonce == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: E-SNonce2 "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_key_wrap_auth(const u8 *auth, int mandatory)
+{
+ if (auth == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Key Wrap "
+ "Authenticator attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_ssid(const u8 *ssid, size_t ssid_len, int mandatory)
+{
+ if (ssid == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: SSID "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (ssid_len == 0 || ssid[ssid_len - 1] == 0) {
+ wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid SSID "
+ "attribute value", ssid, ssid_len);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_network_key_index(const u8 *idx, int mandatory)
+{
+ if (idx == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Network Key Index "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_network_idx(const u8 *idx, int mandatory)
+{
+ if (idx == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Network Index "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_network_key(const u8 *key, size_t key_len,
+ const u8 *encr_type, int mandatory)
+{
+ if (key == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Network Key "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (((encr_type == NULL || WPA_GET_BE16(encr_type) != WPS_ENCR_WEP) &&
+ key_len > 8 && key_len < 64 && key[key_len - 1] == 0) ||
+ key_len > 64) {
+ wpa_hexdump_ascii_key(MSG_INFO, "WPS-STRICT: Invalid Network "
+ "Key attribute value", key, key_len);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_network_key_shareable(const u8 *val, int mandatory)
+{
+ if (val == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Network Key "
+ "Shareable attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (*val > 1) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Network Key "
+ "Shareable attribute value 0x%x", *val);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_cred(const u8 *cred, size_t len)
+{
+ struct wps_parse_attr attr;
+ struct wpabuf buf;
+
+ if (cred == NULL)
+ return -1;
+ wpabuf_set(&buf, cred, len);
+ if (wps_parse_msg(&buf, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse Credential");
+ return -1;
+ }
+
+ if (wps_validate_network_idx(attr.network_idx, 1) ||
+ wps_validate_ssid(attr.ssid, attr.ssid_len, 1) ||
+ wps_validate_auth_type(attr.auth_type, 1) ||
+ wps_validate_encr_type(attr.encr_type, 1) ||
+ wps_validate_network_key_index(attr.network_key_idx, 0) ||
+ wps_validate_network_key(attr.network_key, attr.network_key_len,
+ attr.encr_type, 1) ||
+ wps_validate_mac_addr(attr.mac_addr, 1) ||
+ wps_validate_network_key_shareable(attr.network_key_shareable, 0))
+ {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Credential");
+ return -1;
+ }
+
+
+ return 0;
+}
+
+
+static int wps_validate_credential(const u8 *cred[], size_t len[], size_t num,
+ int mandatory)
+{
+ size_t i;
+
+ if (num == 0) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Credential "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+
+ for (i = 0; i < num; i++) {
+ if (wps_validate_cred(cred[i], len[i]) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int wps_validate_beacon(const struct wpabuf *wps_ie)
+{
+ struct wps_parse_attr attr;
+ int wps2, sel_reg;
+
+ if (wps_ie == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No WPS IE in Beacon frame");
+ return -1;
+ }
+ if (wps_parse_msg(wps_ie, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse WPS IE in "
+ "Beacon frame");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ sel_reg = attr.selected_registrar != NULL &&
+ *attr.selected_registrar != 0;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_wps_state(attr.wps_state, 1) ||
+ wps_validate_ap_setup_locked(attr.ap_setup_locked, 0) ||
+ wps_validate_selected_registrar(attr.selected_registrar, 0) ||
+ wps_validate_dev_password_id(attr.dev_password_id, sel_reg) ||
+ wps_validate_sel_reg_config_methods(attr.sel_reg_config_methods,
+ wps2, sel_reg) ||
+ wps_validate_uuid_e(attr.uuid_e, 0) ||
+ wps_validate_rf_bands(attr.rf_bands, 0) ||
+ wps_validate_version2(attr.version2, wps2) ||
+ wps_validate_authorized_macs(attr.authorized_macs,
+ attr.authorized_macs_len, 0)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Beacon frame");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int wps_validate_beacon_probe_resp(const struct wpabuf *wps_ie, int probe,
+ const u8 *addr)
+{
+ struct wps_parse_attr attr;
+ int wps2, sel_reg;
+
+ if (wps_ie == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No WPS IE in "
+ "%sProbe Response frame", probe ? "" : "Beacon/");
+ return -1;
+ }
+ if (wps_parse_msg(wps_ie, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse WPS IE in "
+ "%sProbe Response frame", probe ? "" : "Beacon/");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ sel_reg = attr.selected_registrar != NULL &&
+ *attr.selected_registrar != 0;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_wps_state(attr.wps_state, 1) ||
+ wps_validate_ap_setup_locked(attr.ap_setup_locked, 0) ||
+ wps_validate_selected_registrar(attr.selected_registrar, 0) ||
+ wps_validate_dev_password_id(attr.dev_password_id, sel_reg) ||
+ wps_validate_sel_reg_config_methods(attr.sel_reg_config_methods,
+ wps2, sel_reg) ||
+ wps_validate_response_type(attr.response_type, probe) ||
+ wps_validate_uuid_e(attr.uuid_e, probe) ||
+ wps_validate_manufacturer(attr.manufacturer, attr.manufacturer_len,
+ probe) ||
+ wps_validate_model_name(attr.model_name, attr.model_name_len,
+ probe) ||
+ wps_validate_model_number(attr.model_number, attr.model_number_len,
+ probe) ||
+ wps_validate_serial_number(attr.serial_number,
+ attr.serial_number_len, probe) ||
+ wps_validate_primary_dev_type(attr.primary_dev_type, probe) ||
+ wps_validate_dev_name(attr.dev_name, attr.dev_name_len, probe) ||
+ wps_validate_ap_config_methods(attr.config_methods, wps2, probe) ||
+ wps_validate_rf_bands(attr.rf_bands, 0) ||
+ wps_validate_version2(attr.version2, wps2) ||
+ wps_validate_authorized_macs(attr.authorized_macs,
+ attr.authorized_macs_len, 0)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid %sProbe Response "
+ "frame from " MACSTR, probe ? "" : "Beacon/",
+ MAC2STR(addr));
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_probe_req(const struct wpabuf *wps_ie, const u8 *addr)
+{
+ struct wps_parse_attr attr;
+ int wps2;
+
+ if (wps_ie == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No WPS IE in "
+ "Probe Request frame");
+ return -1;
+ }
+ if (wps_parse_msg(wps_ie, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse WPS IE in "
+ "Probe Request frame");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_request_type(attr.request_type, 1) ||
+ wps_validate_config_methods(attr.config_methods, wps2, 1) ||
+ wps_validate_uuid_e(attr.uuid_e, attr.uuid_r == NULL) ||
+ wps_validate_uuid_r(attr.uuid_r, attr.uuid_e == NULL) ||
+ wps_validate_primary_dev_type(attr.primary_dev_type, 1) ||
+ wps_validate_rf_bands(attr.rf_bands, 1) ||
+ wps_validate_assoc_state(attr.assoc_state, 1) ||
+ wps_validate_config_error(attr.config_error, 1) ||
+ wps_validate_dev_password_id(attr.dev_password_id, 1) ||
+ wps_validate_version2(attr.version2, wps2) ||
+ wps_validate_manufacturer(attr.manufacturer, attr.manufacturer_len,
+ wps2) ||
+ wps_validate_model_name(attr.model_name, attr.model_name_len,
+ wps2) ||
+ wps_validate_model_number(attr.model_number, attr.model_number_len,
+ wps2) ||
+ wps_validate_dev_name(attr.dev_name, attr.dev_name_len, wps2) ||
+ wps_validate_request_to_enroll(attr.request_to_enroll, 0) ||
+ wps_validate_req_dev_type(attr.req_dev_type, attr.num_req_dev_type,
+ 0)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Probe Request "
+ "frame from " MACSTR, MAC2STR(addr));
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int wps_validate_assoc_req(const struct wpabuf *wps_ie)
+{
+ struct wps_parse_attr attr;
+ int wps2;
+
+ if (wps_ie == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No WPS IE in "
+ "(Re)Association Request frame");
+ return -1;
+ }
+ if (wps_parse_msg(wps_ie, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse WPS IE in "
+ "(Re)Association Request frame");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_request_type(attr.request_type, 1) ||
+ wps_validate_version2(attr.version2, wps2)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid (Re)Association "
+ "Request frame");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int wps_validate_assoc_resp(const struct wpabuf *wps_ie)
+{
+ struct wps_parse_attr attr;
+ int wps2;
+
+ if (wps_ie == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No WPS IE in "
+ "(Re)Association Response frame");
+ return -1;
+ }
+ if (wps_parse_msg(wps_ie, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse WPS IE in "
+ "(Re)Association Response frame");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_response_type(attr.response_type, 1) ||
+ wps_validate_version2(attr.version2, wps2)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid (Re)Association "
+ "Response frame");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int wps_validate_m1(const struct wpabuf *tlvs)
+{
+ struct wps_parse_attr attr;
+ int wps2;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M1");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M1");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_msg_type(attr.msg_type, 1) ||
+ wps_validate_uuid_e(attr.uuid_e, 1) ||
+ wps_validate_mac_addr(attr.mac_addr, 1) ||
+ wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) ||
+ wps_validate_public_key(attr.public_key, attr.public_key_len, 1) ||
+ wps_validate_auth_type_flags(attr.auth_type_flags, 1) ||
+ wps_validate_encr_type_flags(attr.encr_type_flags, 1) ||
+ wps_validate_conn_type_flags(attr.conn_type_flags, 1) ||
+ wps_validate_config_methods(attr.config_methods, wps2, 1) ||
+ wps_validate_wps_state(attr.wps_state, 1) ||
+ wps_validate_manufacturer(attr.manufacturer, attr.manufacturer_len,
+ 1) ||
+ wps_validate_model_name(attr.model_name, attr.model_name_len, 1) ||
+ wps_validate_model_number(attr.model_number, attr.model_number_len,
+ 1) ||
+ wps_validate_serial_number(attr.serial_number,
+ attr.serial_number_len, 1) ||
+ wps_validate_primary_dev_type(attr.primary_dev_type, 1) ||
+ wps_validate_dev_name(attr.dev_name, attr.dev_name_len, 1) ||
+ wps_validate_rf_bands(attr.rf_bands, 1) ||
+ wps_validate_assoc_state(attr.assoc_state, 1) ||
+ wps_validate_dev_password_id(attr.dev_password_id, 1) ||
+ wps_validate_config_error(attr.config_error, 1) ||
+ wps_validate_os_version(attr.os_version, 1) ||
+ wps_validate_version2(attr.version2, wps2) ||
+ wps_validate_request_to_enroll(attr.request_to_enroll, 0)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M1");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_m2(const struct wpabuf *tlvs)
+{
+ struct wps_parse_attr attr;
+ int wps2;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M2");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M2");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_msg_type(attr.msg_type, 1) ||
+ wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) ||
+ wps_validate_registrar_nonce(attr.registrar_nonce, 1) ||
+ wps_validate_uuid_r(attr.uuid_r, 1) ||
+ wps_validate_public_key(attr.public_key, attr.public_key_len, 1) ||
+ wps_validate_auth_type_flags(attr.auth_type_flags, 1) ||
+ wps_validate_encr_type_flags(attr.encr_type_flags, 1) ||
+ wps_validate_conn_type_flags(attr.conn_type_flags, 1) ||
+ wps_validate_config_methods(attr.config_methods, wps2, 1) ||
+ wps_validate_manufacturer(attr.manufacturer, attr.manufacturer_len,
+ 1) ||
+ wps_validate_model_name(attr.model_name, attr.model_name_len, 1) ||
+ wps_validate_model_number(attr.model_number, attr.model_number_len,
+ 1) ||
+ wps_validate_serial_number(attr.serial_number,
+ attr.serial_number_len, 1) ||
+ wps_validate_primary_dev_type(attr.primary_dev_type, 1) ||
+ wps_validate_dev_name(attr.dev_name, attr.dev_name_len, 1) ||
+ wps_validate_rf_bands(attr.rf_bands, 1) ||
+ wps_validate_assoc_state(attr.assoc_state, 1) ||
+ wps_validate_config_error(attr.config_error, 1) ||
+ wps_validate_dev_password_id(attr.dev_password_id, 1) ||
+ wps_validate_os_version(attr.os_version, 1) ||
+ wps_validate_version2(attr.version2, wps2) ||
+ wps_validate_authenticator(attr.authenticator, 1)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M2");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_m2d(const struct wpabuf *tlvs)
+{
+ struct wps_parse_attr attr;
+ int wps2;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M2D");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M2D");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_msg_type(attr.msg_type, 1) ||
+ wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) ||
+ wps_validate_registrar_nonce(attr.registrar_nonce, 1) ||
+ wps_validate_uuid_r(attr.uuid_r, 1) ||
+ wps_validate_auth_type_flags(attr.auth_type_flags, 1) ||
+ wps_validate_encr_type_flags(attr.encr_type_flags, 1) ||
+ wps_validate_conn_type_flags(attr.conn_type_flags, 1) ||
+ wps_validate_config_methods(attr.config_methods, wps2, 1) ||
+ wps_validate_manufacturer(attr.manufacturer, attr.manufacturer_len,
+ 1) ||
+ wps_validate_model_name(attr.model_name, attr.model_name_len, 1) ||
+ wps_validate_model_number(attr.model_number, attr.model_number_len,
+ 1) ||
+ wps_validate_serial_number(attr.serial_number,
+ attr.serial_number_len, 1) ||
+ wps_validate_primary_dev_type(attr.primary_dev_type, 1) ||
+ wps_validate_dev_name(attr.dev_name, attr.dev_name_len, 1) ||
+ wps_validate_rf_bands(attr.rf_bands, 1) ||
+ wps_validate_assoc_state(attr.assoc_state, 1) ||
+ wps_validate_config_error(attr.config_error, 1) ||
+ wps_validate_os_version(attr.os_version, 1) ||
+ wps_validate_version2(attr.version2, wps2)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M2D");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_m3(const struct wpabuf *tlvs)
+{
+ struct wps_parse_attr attr;
+ int wps2;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M3");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M3");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_msg_type(attr.msg_type, 1) ||
+ wps_validate_registrar_nonce(attr.registrar_nonce, 1) ||
+ wps_validate_e_hash1(attr.e_hash1, 1) ||
+ wps_validate_e_hash2(attr.e_hash2, 1) ||
+ wps_validate_version2(attr.version2, wps2) ||
+ wps_validate_authenticator(attr.authenticator, 1)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M3");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_m4(const struct wpabuf *tlvs)
+{
+ struct wps_parse_attr attr;
+ int wps2;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M4");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M4");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_msg_type(attr.msg_type, 1) ||
+ wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) ||
+ wps_validate_r_hash1(attr.r_hash1, 1) ||
+ wps_validate_r_hash2(attr.r_hash2, 1) ||
+ wps_validate_encr_settings(attr.encr_settings,
+ attr.encr_settings_len, 1) ||
+ wps_validate_version2(attr.version2, wps2) ||
+ wps_validate_authenticator(attr.authenticator, 1)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M4");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_m4_encr(const struct wpabuf *tlvs, int wps2)
+{
+ struct wps_parse_attr attr;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M4 encrypted "
+ "settings");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M4 encrypted settings");
+ return -1;
+ }
+
+ if (wps_validate_r_snonce1(attr.r_snonce1, 1) ||
+ wps_validate_key_wrap_auth(attr.key_wrap_auth, 1)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M4 encrypted "
+ "settings");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_m5(const struct wpabuf *tlvs)
+{
+ struct wps_parse_attr attr;
+ int wps2;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M5");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M5");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_msg_type(attr.msg_type, 1) ||
+ wps_validate_registrar_nonce(attr.registrar_nonce, 1) ||
+ wps_validate_encr_settings(attr.encr_settings,
+ attr.encr_settings_len, 1) ||
+ wps_validate_version2(attr.version2, wps2) ||
+ wps_validate_authenticator(attr.authenticator, 1)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M5");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_m5_encr(const struct wpabuf *tlvs, int wps2)
+{
+ struct wps_parse_attr attr;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M5 encrypted "
+ "settings");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M5 encrypted settings");
+ return -1;
+ }
+
+ if (wps_validate_e_snonce1(attr.e_snonce1, 1) ||
+ wps_validate_key_wrap_auth(attr.key_wrap_auth, 1)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M5 encrypted "
+ "settings");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_m6(const struct wpabuf *tlvs)
+{
+ struct wps_parse_attr attr;
+ int wps2;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M6");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M6");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_msg_type(attr.msg_type, 1) ||
+ wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) ||
+ wps_validate_encr_settings(attr.encr_settings,
+ attr.encr_settings_len, 1) ||
+ wps_validate_version2(attr.version2, wps2) ||
+ wps_validate_authenticator(attr.authenticator, 1)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M6");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_m6_encr(const struct wpabuf *tlvs, int wps2)
+{
+ struct wps_parse_attr attr;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M6 encrypted "
+ "settings");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M6 encrypted settings");
+ return -1;
+ }
+
+ if (wps_validate_r_snonce2(attr.r_snonce2, 1) ||
+ wps_validate_key_wrap_auth(attr.key_wrap_auth, 1)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M6 encrypted "
+ "settings");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_m7(const struct wpabuf *tlvs)
+{
+ struct wps_parse_attr attr;
+ int wps2;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M7");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M7");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_msg_type(attr.msg_type, 1) ||
+ wps_validate_registrar_nonce(attr.registrar_nonce, 1) ||
+ wps_validate_encr_settings(attr.encr_settings,
+ attr.encr_settings_len, 1) ||
+ wps_validate_settings_delay_time(attr.settings_delay_time, 0) ||
+ wps_validate_version2(attr.version2, wps2) ||
+ wps_validate_authenticator(attr.authenticator, 1)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M7");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_m7_encr(const struct wpabuf *tlvs, int ap, int wps2)
+{
+ struct wps_parse_attr attr;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M7 encrypted "
+ "settings");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M7 encrypted settings");
+ return -1;
+ }
+
+ if (wps_validate_e_snonce2(attr.e_snonce2, 1) ||
+ wps_validate_ssid(attr.ssid, attr.ssid_len, !ap) ||
+ wps_validate_mac_addr(attr.mac_addr, !ap) ||
+ wps_validate_auth_type(attr.auth_type, !ap) ||
+ wps_validate_encr_type(attr.encr_type, !ap) ||
+ wps_validate_network_key_index(attr.network_key_idx, 0) ||
+ wps_validate_network_key(attr.network_key, attr.network_key_len,
+ attr.encr_type, !ap) ||
+ wps_validate_key_wrap_auth(attr.key_wrap_auth, 1)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M7 encrypted "
+ "settings");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_m8(const struct wpabuf *tlvs)
+{
+ struct wps_parse_attr attr;
+ int wps2;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M8");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M8");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_msg_type(attr.msg_type, 1) ||
+ wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) ||
+ wps_validate_encr_settings(attr.encr_settings,
+ attr.encr_settings_len, 1) ||
+ wps_validate_version2(attr.version2, wps2) ||
+ wps_validate_authenticator(attr.authenticator, 1)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M8");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_m8_encr(const struct wpabuf *tlvs, int ap, int wps2)
+{
+ struct wps_parse_attr attr;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M8 encrypted "
+ "settings");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M8 encrypted settings");
+ return -1;
+ }
+
+ if (wps_validate_ssid(attr.ssid, attr.ssid_len, ap) ||
+ wps_validate_auth_type(attr.auth_type, ap) ||
+ wps_validate_encr_type(attr.encr_type, ap) ||
+ wps_validate_network_key_index(attr.network_key_idx, 0) ||
+ wps_validate_mac_addr(attr.mac_addr, ap) ||
+ wps_validate_credential(attr.cred, attr.cred_len, attr.num_cred,
+ !ap) ||
+ wps_validate_key_wrap_auth(attr.key_wrap_auth, 1)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M8 encrypted "
+ "settings");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_wsc_ack(const struct wpabuf *tlvs)
+{
+ struct wps_parse_attr attr;
+ int wps2;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in WSC_ACK");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in WSC_ACK");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_msg_type(attr.msg_type, 1) ||
+ wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) ||
+ wps_validate_registrar_nonce(attr.registrar_nonce, 1) ||
+ wps_validate_version2(attr.version2, wps2)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid WSC_ACK");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_wsc_nack(const struct wpabuf *tlvs)
+{
+ struct wps_parse_attr attr;
+ int wps2;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in WSC_NACK");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in WSC_NACK");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_msg_type(attr.msg_type, 1) ||
+ wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) ||
+ wps_validate_registrar_nonce(attr.registrar_nonce, 1) ||
+ wps_validate_config_error(attr.config_error, 1) ||
+ wps_validate_version2(attr.version2, wps2)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid WSC_NACK");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_wsc_done(const struct wpabuf *tlvs)
+{
+ struct wps_parse_attr attr;
+ int wps2;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in WSC_Done");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in WSC_Done");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_msg_type(attr.msg_type, 1) ||
+ wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) ||
+ wps_validate_registrar_nonce(attr.registrar_nonce, 1) ||
+ wps_validate_version2(attr.version2, wps2)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid WSC_Done");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_upnp_set_selected_registrar(const struct wpabuf *tlvs)
+{
+ struct wps_parse_attr attr;
+ int wps2;
+ int sel_reg;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in "
+ "SetSelectedRegistrar");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in SetSelectedRegistrar");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ sel_reg = attr.selected_registrar != NULL &&
+ *attr.selected_registrar != 0;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_dev_password_id(attr.dev_password_id, sel_reg) ||
+ wps_validate_sel_reg_config_methods(attr.sel_reg_config_methods,
+ wps2, sel_reg) ||
+ wps_validate_version2(attr.version2, wps2) ||
+ wps_validate_authorized_macs(attr.authorized_macs,
+ attr.authorized_macs_len, wps2) ||
+ wps_validate_uuid_r(attr.uuid_r, wps2)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid "
+ "SetSelectedRegistrar");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
-*.d
-.config
-eapol_test
-preauth_test
-wpa_cli
-wpa_passphrase
-wpa_supplicant
-wpa_priv
+*.service
--- /dev/null
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#
+
+LOCAL_PATH := $(call my-dir)
+PKG_CONFIG ?= pkg-config
+
+WPA_BUILD_SUPPLICANT := false
+ifneq ($(TARGET_SIMULATOR),true)
+ ifneq ($(BOARD_WPA_SUPPLICANT_DRIVER),)
+ WPA_BUILD_SUPPLICANT := true
+ CONFIG_DRIVER_$(BOARD_WPA_SUPPLICANT_DRIVER) := y
+ endif
+endif
+
+include $(LOCAL_PATH)/.config
+
+# To ignore possible wrong network configurations
+L_CFLAGS = -DWPA_IGNORE_CONFIG_ERRORS
+
+# Set Android log name
+L_CFLAGS += -DANDROID_LOG_NAME=\"wpa_supplicant\"
+
+# Use Android specific directory for control interface sockets
+L_CFLAGS += -DCONFIG_CTRL_IFACE_CLIENT_DIR=\"/data/misc/wifi/sockets\"
+L_CFLAGS += -DCONFIG_CTRL_IFACE_DIR=\"/data/system/wpa_supplicant\"
+
+# To force sizeof(enum) = 4
+ifeq ($(TARGET_ARCH),arm)
+L_CFLAGS += -mabi=aapcs-linux
+endif
+
+# To allow non-ASCII characters in SSID
+L_CFLAGS += -DWPA_UNICODE_SSID
+
+# OpenSSL is configured without engines on Android
+L_CFLAGS += -DOPENSSL_NO_ENGINE
+
+INCLUDES = $(LOCAL_PATH)
+INCLUDES += $(LOCAL_PATH)/src
+INCLUDES += $(LOCAL_PATH)/src/common
+# INCLUDES += $(LOCAL_PATH)/src/crypto # To force proper includes
+INCLUDES += $(LOCAL_PATH)/src/drivers
+INCLUDES += $(LOCAL_PATH)/src/eap_common
+INCLUDES += $(LOCAL_PATH)/src/eapol_supp
+INCLUDES += $(LOCAL_PATH)/src/eap_peer
+INCLUDES += $(LOCAL_PATH)/src/eap_server
+INCLUDES += $(LOCAL_PATH)/src/hlr_auc_gw
+INCLUDES += $(LOCAL_PATH)/src/l2_packet
+INCLUDES += $(LOCAL_PATH)/src/radius
+INCLUDES += $(LOCAL_PATH)/src/rsn_supp
+INCLUDES += $(LOCAL_PATH)/src/tls
+INCLUDES += $(LOCAL_PATH)/src/utils
+INCLUDES += $(LOCAL_PATH)/src/wps
+INCLUDES += external/openssl/include
+INCLUDES += frameworks/base/cmds/keystore
+ifdef CONFIG_DRIVER_NL80211
+INCLUDES += external/libnl-headers
+endif
+
+OBJS = config.c
+OBJS += notify.c
+OBJS += bss.c
+OBJS += eap_register.c
+OBJS += src/utils/common.c
+OBJS += src/utils/wpa_debug.c
+OBJS += src/utils/wpabuf.c
+OBJS_p = wpa_passphrase.c
+OBJS_p += src/utils/common.c
+OBJS_p += src/utils/wpa_debug.c
+OBJS_p += src/utils/wpabuf.c
+OBJS_c = wpa_cli.c src/common/wpa_ctrl.c
+OBJS_c += src/utils/wpa_debug.c
+OBJS_c += src/utils/common.c
+OBJS_d =
+OBJS_priv =
+
+ifndef CONFIG_OS
+ifdef CONFIG_NATIVE_WINDOWS
+CONFIG_OS=win32
+else
+CONFIG_OS=unix
+endif
+endif
+
+ifeq ($(CONFIG_OS), internal)
+L_CFLAGS += -DOS_NO_C_LIB_DEFINES
+endif
+
+OBJS += src/utils/os_$(CONFIG_OS).c
+OBJS_p += src/utils/os_$(CONFIG_OS).c
+OBJS_c += src/utils/os_$(CONFIG_OS).c
+
+ifdef CONFIG_WPA_TRACE
+L_CFLAGS += -DWPA_TRACE
+OBJS += src/utils/trace.c
+OBJS_p += src/utils/trace.c
+OBJS_c += src/utils/trace.c
+LDFLAGS += -rdynamic
+L_CFLAGS += -funwind-tables
+ifdef CONFIG_WPA_TRACE_BFD
+L_CFLAGS += -DWPA_TRACE_BFD
+LIBS += -lbfd
+LIBS_p += -lbfd
+LIBS_c += -lbfd
+endif
+endif
+
+ifndef CONFIG_ELOOP
+CONFIG_ELOOP=eloop
+endif
+OBJS += src/utils/$(CONFIG_ELOOP).c
+OBJS_c += src/utils/$(CONFIG_ELOOP).c
+
+
+ifdef CONFIG_EAPOL_TEST
+L_CFLAGS += -Werror -DEAPOL_TEST
+endif
+
+ifndef CONFIG_BACKEND
+CONFIG_BACKEND=file
+endif
+
+ifeq ($(CONFIG_BACKEND), file)
+OBJS += config_file.c
+ifndef CONFIG_NO_CONFIG_BLOBS
+NEED_BASE64=y
+endif
+L_CFLAGS += -DCONFIG_BACKEND_FILE
+endif
+
+ifeq ($(CONFIG_BACKEND), winreg)
+OBJS += config_winreg.c
+endif
+
+ifeq ($(CONFIG_BACKEND), none)
+OBJS += config_none.c
+endif
+
+ifdef CONFIG_NO_CONFIG_WRITE
+L_CFLAGS += -DCONFIG_NO_CONFIG_WRITE
+endif
+
+ifdef CONFIG_NO_CONFIG_BLOBS
+L_CFLAGS += -DCONFIG_NO_CONFIG_BLOBS
+endif
+
+ifdef CONFIG_NO_SCAN_PROCESSING
+L_CFLAGS += -DCONFIG_NO_SCAN_PROCESSING
+endif
+
+ifdef CONFIG_IEEE80211W
+L_CFLAGS += -DCONFIG_IEEE80211W
+NEED_SHA256=y
+NEED_AES_OMAC1=y
+endif
+
+ifdef CONFIG_IEEE80211R
+L_CFLAGS += -DCONFIG_IEEE80211R
+OBJS += src/rsn_supp/wpa_ft.c
+NEED_80211_COMMON=y
+NEED_SHA256=y
+NEED_AES_OMAC1=y
+endif
+
+ifdef CONFIG_TDLS
+L_CFLAGS += -DCONFIG_TDLS
+OBJS += src/rsn_supp/tdls.c
+NEED_SHA256=y
+NEED_AES_OMAC1=y
+endif
+
+ifdef CONFIG_TDLS_TESTING
+L_CFLAGS += -DCONFIG_TDLS_TESTING
+endif
+
+ifdef CONFIG_PEERKEY
+L_CFLAGS += -DCONFIG_PEERKEY
+endif
+
+ifndef CONFIG_NO_WPA
+OBJS += src/rsn_supp/wpa.c
+OBJS += src/rsn_supp/preauth.c
+OBJS += src/rsn_supp/pmksa_cache.c
+OBJS += src/rsn_supp/peerkey.c
+OBJS += src/rsn_supp/wpa_ie.c
+OBJS += src/common/wpa_common.c
+NEED_AES=y
+NEED_SHA1=y
+NEED_MD5=y
+NEED_RC4=y
+else
+L_CFLAGS += -DCONFIG_NO_WPA -DCONFIG_NO_WPA2
+endif
+
+ifdef CONFIG_IBSS_RSN
+NEED_RSN_AUTHENTICATOR=y
+L_CFLAGS += -DCONFIG_IBSS_RSN
+OBJS += ibss_rsn.c
+endif
+
+ifdef CONFIG_P2P
+OBJS += p2p_supplicant.c
+OBJS += src/p2p/p2p.c
+OBJS += src/p2p/p2p_utils.c
+OBJS += src/p2p/p2p_parse.c
+OBJS += src/p2p/p2p_build.c
+OBJS += src/p2p/p2p_go_neg.c
+OBJS += src/p2p/p2p_sd.c
+OBJS += src/p2p/p2p_pd.c
+OBJS += src/p2p/p2p_invitation.c
+OBJS += src/p2p/p2p_dev_disc.c
+OBJS += src/p2p/p2p_group.c
+OBJS += src/ap/p2p_hostapd.c
+L_CFLAGS += -DCONFIG_P2P
+NEED_GAS=y
+NEED_OFFCHANNEL=y
+NEED_80211_COMMON=y
+CONFIG_WPS=y
+CONFIG_AP=y
+ifdef CONFIG_P2P_STRICT
+L_CFLAGS += -DCONFIG_P2P_STRICT
+endif
+endif
+
+ifdef CONFIG_INTERWORKING
+OBJS += interworking.c
+L_CFLAGS += -DCONFIG_INTERWORKING
+NEED_GAS=y
+endif
+
+ifdef CONFIG_NO_WPA2
+L_CFLAGS += -DCONFIG_NO_WPA2
+endif
+
+include $(LOCAL_PATH)/src/drivers/drivers.mk
+
+ifdef CONFIG_AP
+OBJS_d += $(DRV_BOTH_OBJS)
+L_CFLAGS += $(DRV_BOTH_CFLAGS)
+LDFLAGS += $(DRV_BOTH_LDFLAGS)
+LIBS += $(DRV_BOTH_LIBS)
+else
+NEED_AP_MLME=
+OBJS_d += $(DRV_WPA_OBJS)
+L_CFLAGS += $(DRV_WPA_CFLAGS)
+LDFLAGS += $(DRV_WPA_LDFLAGS)
+LIBS += $(DRV_WPA_LIBS)
+endif
+
+ifndef CONFIG_L2_PACKET
+CONFIG_L2_PACKET=linux
+endif
+
+OBJS_l2 += src/l2_packet/l2_packet_$(CONFIG_L2_PACKET).c
+
+ifeq ($(CONFIG_L2_PACKET), pcap)
+ifdef CONFIG_WINPCAP
+L_CFLAGS += -DCONFIG_WINPCAP
+LIBS += -lwpcap -lpacket
+LIBS_w += -lwpcap
+else
+LIBS += -ldnet -lpcap
+endif
+endif
+
+ifeq ($(CONFIG_L2_PACKET), winpcap)
+LIBS += -lwpcap -lpacket
+LIBS_w += -lwpcap
+endif
+
+ifeq ($(CONFIG_L2_PACKET), freebsd)
+LIBS += -lpcap
+endif
+
+ifdef CONFIG_EAP_TLS
+# EAP-TLS
+ifeq ($(CONFIG_EAP_TLS), dyn)
+L_CFLAGS += -DEAP_TLS_DYNAMIC
+EAPDYN += src/eap_peer/eap_tls.so
+else
+L_CFLAGS += -DEAP_TLS
+OBJS += src/eap_peer/eap_tls.c
+OBJS_h += src/eap_server/eap_server_tls.c
+endif
+TLS_FUNCS=y
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_PEAP
+# EAP-PEAP
+ifeq ($(CONFIG_EAP_PEAP), dyn)
+L_CFLAGS += -DEAP_PEAP_DYNAMIC
+EAPDYN += src/eap_peer/eap_peap.so
+else
+L_CFLAGS += -DEAP_PEAP
+OBJS += src/eap_peer/eap_peap.c
+OBJS += src/eap_common/eap_peap_common.c
+OBJS_h += src/eap_server/eap_server_peap.c
+endif
+TLS_FUNCS=y
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_TTLS
+# EAP-TTLS
+ifeq ($(CONFIG_EAP_TTLS), dyn)
+L_CFLAGS += -DEAP_TTLS_DYNAMIC
+EAPDYN += src/eap_peer/eap_ttls.so
+else
+L_CFLAGS += -DEAP_TTLS
+OBJS += src/eap_peer/eap_ttls.c
+OBJS_h += src/eap_server/eap_server_ttls.c
+endif
+MS_FUNCS=y
+TLS_FUNCS=y
+CHAP=y
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_MD5
+# EAP-MD5
+ifeq ($(CONFIG_EAP_MD5), dyn)
+L_CFLAGS += -DEAP_MD5_DYNAMIC
+EAPDYN += src/eap_peer/eap_md5.so
+else
+L_CFLAGS += -DEAP_MD5
+OBJS += src/eap_peer/eap_md5.c
+OBJS_h += src/eap_server/eap_server_md5.c
+endif
+CHAP=y
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+# backwards compatibility for old spelling
+ifdef CONFIG_MSCHAPV2
+ifndef CONFIG_EAP_MSCHAPV2
+CONFIG_EAP_MSCHAPV2=y
+endif
+endif
+
+ifdef CONFIG_EAP_MSCHAPV2
+# EAP-MSCHAPv2
+ifeq ($(CONFIG_EAP_MSCHAPV2), dyn)
+L_CFLAGS += -DEAP_MSCHAPv2_DYNAMIC
+EAPDYN += src/eap_peer/eap_mschapv2.so
+EAPDYN += src/eap_peer/mschapv2.so
+else
+L_CFLAGS += -DEAP_MSCHAPv2
+OBJS += src/eap_peer/eap_mschapv2.c
+OBJS += src/eap_peer/mschapv2.c
+OBJS_h += src/eap_server/eap_server_mschapv2.c
+endif
+MS_FUNCS=y
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_GTC
+# EAP-GTC
+ifeq ($(CONFIG_EAP_GTC), dyn)
+L_CFLAGS += -DEAP_GTC_DYNAMIC
+EAPDYN += src/eap_peer/eap_gtc.so
+else
+L_CFLAGS += -DEAP_GTC
+OBJS += src/eap_peer/eap_gtc.c
+OBJS_h += src/eap_server/eap_server_gtc.c
+endif
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_OTP
+# EAP-OTP
+ifeq ($(CONFIG_EAP_OTP), dyn)
+L_CFLAGS += -DEAP_OTP_DYNAMIC
+EAPDYN += src/eap_peer/eap_otp.so
+else
+L_CFLAGS += -DEAP_OTP
+OBJS += src/eap_peer/eap_otp.c
+endif
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_SIM
+# EAP-SIM
+ifeq ($(CONFIG_EAP_SIM), dyn)
+L_CFLAGS += -DEAP_SIM_DYNAMIC
+EAPDYN += src/eap_peer/eap_sim.so
+else
+L_CFLAGS += -DEAP_SIM
+OBJS += src/eap_peer/eap_sim.c
+OBJS_h += src/eap_server/eap_server_sim.c
+endif
+CONFIG_IEEE8021X_EAPOL=y
+CONFIG_EAP_SIM_COMMON=y
+NEED_AES_CBC=y
+endif
+
+ifdef CONFIG_EAP_LEAP
+# EAP-LEAP
+ifeq ($(CONFIG_EAP_LEAP), dyn)
+L_CFLAGS += -DEAP_LEAP_DYNAMIC
+EAPDYN += src/eap_peer/eap_leap.so
+else
+L_CFLAGS += -DEAP_LEAP
+OBJS += src/eap_peer/eap_leap.c
+endif
+MS_FUNCS=y
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_PSK
+# EAP-PSK
+ifeq ($(CONFIG_EAP_PSK), dyn)
+L_CFLAGS += -DEAP_PSK_DYNAMIC
+EAPDYN += src/eap_peer/eap_psk.so
+else
+L_CFLAGS += -DEAP_PSK
+OBJS += src/eap_peer/eap_psk.c src/eap_common/eap_psk_common.c
+OBJS_h += src/eap_server/eap_server_psk.c
+endif
+CONFIG_IEEE8021X_EAPOL=y
+NEED_AES=y
+NEED_AES_OMAC1=y
+NEED_AES_ENCBLOCK=y
+NEED_AES_EAX=y
+endif
+
+ifdef CONFIG_EAP_AKA
+# EAP-AKA
+ifeq ($(CONFIG_EAP_AKA), dyn)
+L_CFLAGS += -DEAP_AKA_DYNAMIC
+EAPDYN += src/eap_peer/eap_aka.so
+else
+L_CFLAGS += -DEAP_AKA
+OBJS += src/eap_peer/eap_aka.c
+OBJS_h += src/eap_server/eap_server_aka.c
+endif
+CONFIG_IEEE8021X_EAPOL=y
+CONFIG_EAP_SIM_COMMON=y
+NEED_AES_CBC=y
+endif
+
+ifdef CONFIG_EAP_AKA_PRIME
+# EAP-AKA'
+ifeq ($(CONFIG_EAP_AKA_PRIME), dyn)
+L_CFLAGS += -DEAP_AKA_PRIME_DYNAMIC
+else
+L_CFLAGS += -DEAP_AKA_PRIME
+endif
+NEED_SHA256=y
+endif
+
+ifdef CONFIG_EAP_SIM_COMMON
+OBJS += src/eap_common/eap_sim_common.c
+OBJS_h += src/eap_server/eap_sim_db.c
+NEED_AES=y
+NEED_FIPS186_2_PRF=y
+endif
+
+ifdef CONFIG_EAP_FAST
+# EAP-FAST
+ifeq ($(CONFIG_EAP_FAST), dyn)
+L_CFLAGS += -DEAP_FAST_DYNAMIC
+EAPDYN += src/eap_peer/eap_fast.so
+EAPDYN += src/eap_common/eap_fast_common.c
+else
+L_CFLAGS += -DEAP_FAST
+OBJS += src/eap_peer/eap_fast.c src/eap_peer/eap_fast_pac.c
+OBJS += src/eap_common/eap_fast_common.c
+OBJS_h += src/eap_server/eap_server_fast.c
+endif
+TLS_FUNCS=y
+CONFIG_IEEE8021X_EAPOL=y
+NEED_T_PRF=y
+endif
+
+ifdef CONFIG_EAP_PAX
+# EAP-PAX
+ifeq ($(CONFIG_EAP_PAX), dyn)
+L_CFLAGS += -DEAP_PAX_DYNAMIC
+EAPDYN += src/eap_peer/eap_pax.so
+else
+L_CFLAGS += -DEAP_PAX
+OBJS += src/eap_peer/eap_pax.c src/eap_common/eap_pax_common.c
+OBJS_h += src/eap_server/eap_server_pax.c
+endif
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_SAKE
+# EAP-SAKE
+ifeq ($(CONFIG_EAP_SAKE), dyn)
+L_CFLAGS += -DEAP_SAKE_DYNAMIC
+EAPDYN += src/eap_peer/eap_sake.so
+else
+L_CFLAGS += -DEAP_SAKE
+OBJS += src/eap_peer/eap_sake.c src/eap_common/eap_sake_common.c
+OBJS_h += src/eap_server/eap_server_sake.c
+endif
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_GPSK
+# EAP-GPSK
+ifeq ($(CONFIG_EAP_GPSK), dyn)
+L_CFLAGS += -DEAP_GPSK_DYNAMIC
+EAPDYN += src/eap_peer/eap_gpsk.so
+else
+L_CFLAGS += -DEAP_GPSK
+OBJS += src/eap_peer/eap_gpsk.c src/eap_common/eap_gpsk_common.c
+OBJS_h += src/eap_server/eap_server_gpsk.c
+endif
+CONFIG_IEEE8021X_EAPOL=y
+ifdef CONFIG_EAP_GPSK_SHA256
+L_CFLAGS += -DEAP_GPSK_SHA256
+endif
+NEED_SHA256=y
+NEED_AES_OMAC1=y
+endif
+
+ifdef CONFIG_EAP_PWD
+L_CFLAGS += -DEAP_PWD
+OBJS += src/eap_peer/eap_pwd.c src/eap_common/eap_pwd_common.c
+OBJS_h += src/eap_server/eap_pwd.c
+CONFIG_IEEE8021X_EAPOL=y
+NEED_SHA256=y
+endif
+
+ifdef CONFIG_WPS
+ifdef CONFIG_WPS2
+L_CFLAGS += -DCONFIG_WPS2
+endif
+
+# EAP-WSC
+L_CFLAGS += -DCONFIG_WPS -DEAP_WSC
+OBJS += wps_supplicant.c
+OBJS += src/utils/uuid.c
+OBJS += src/eap_peer/eap_wsc.c src/eap_common/eap_wsc_common.c
+OBJS += src/wps/wps.c
+OBJS += src/wps/wps_common.c
+OBJS += src/wps/wps_attr_parse.c
+OBJS += src/wps/wps_attr_build.c
+OBJS += src/wps/wps_attr_process.c
+OBJS += src/wps/wps_dev_attr.c
+OBJS += src/wps/wps_enrollee.c
+OBJS += src/wps/wps_registrar.c
+OBJS_h += src/eap_server/eap_server_wsc.c
+CONFIG_IEEE8021X_EAPOL=y
+NEED_DH_GROUPS=y
+NEED_SHA256=y
+NEED_BASE64=y
+NEED_80211_COMMON=y
+NEED_AES_CBC=y
+NEED_MODEXP=y
+
+ifdef CONFIG_WPS_UFD
+L_CFLAGS += -DCONFIG_WPS_UFD
+OBJS += src/wps/wps_ufd.c
+NEED_WPS_OOB=y
+endif
+
+ifdef CONFIG_WPS_NFC
+L_CFLAGS += -DCONFIG_WPS_NFC
+OBJS += src/wps/ndef.c
+OBJS += src/wps/wps_nfc.c
+NEED_WPS_OOB=y
+ifdef CONFIG_WPS_NFC_PN531
+PN531_PATH ?= /usr/local/src/nfc
+L_CFLAGS += -DCONFIG_WPS_NFC_PN531
+L_CFLAGS += -I${PN531_PATH}/inc
+OBJS += src/wps/wps_nfc_pn531.c
+LIBS += ${PN531_PATH}/lib/wpsnfc.dll
+LIBS += ${PN531_PATH}/lib/libnfc_mapping_pn53x.dll
+endif
+endif
+
+ifdef NEED_WPS_OOB
+L_CFLAGS += -DCONFIG_WPS_OOB
+endif
+
+ifdef CONFIG_WPS_ER
+CONFIG_WPS_UPNP=y
+L_CFLAGS += -DCONFIG_WPS_ER
+OBJS += src/wps/wps_er.c
+OBJS += src/wps/wps_er_ssdp.c
+endif
+
+ifdef CONFIG_WPS_UPNP
+L_CFLAGS += -DCONFIG_WPS_UPNP
+OBJS += src/wps/wps_upnp.c
+OBJS += src/wps/wps_upnp_ssdp.c
+OBJS += src/wps/wps_upnp_web.c
+OBJS += src/wps/wps_upnp_event.c
+OBJS += src/wps/wps_upnp_ap.c
+OBJS += src/wps/upnp_xml.c
+OBJS += src/wps/httpread.c
+OBJS += src/wps/http_client.c
+OBJS += src/wps/http_server.c
+endif
+
+ifdef CONFIG_WPS_STRICT
+L_CFLAGS += -DCONFIG_WPS_STRICT
+OBJS += src/wps/wps_validate.c
+endif
+
+ifdef CONFIG_WPS_TESTING
+L_CFLAGS += -DCONFIG_WPS_TESTING
+endif
+
+ifdef CONFIG_WPS_REG_DISABLE_OPEN
+L_CFLAGS += -DCONFIG_WPS_REG_DISABLE_OPEN
+endif
+
+endif
+
+ifdef CONFIG_EAP_IKEV2
+# EAP-IKEv2
+ifeq ($(CONFIG_EAP_IKEV2), dyn)
+L_CFLAGS += -DEAP_IKEV2_DYNAMIC
+EAPDYN += src/eap_peer/eap_ikev2.so src/eap_peer/ikev2.c
+EAPDYN += src/eap_common/eap_ikev2_common.c src/eap_common/ikev2_common.c
+else
+L_CFLAGS += -DEAP_IKEV2
+OBJS += src/eap_peer/eap_ikev2.c src/eap_peer/ikev2.c
+OBJS += src/eap_common/eap_ikev2_common.c src/eap_common/ikev2_common.c
+OBJS_h += src/eap_server/eap_server_ikev2.c
+OBJS_h += src/eap_server/ikev2.c
+endif
+CONFIG_IEEE8021X_EAPOL=y
+NEED_DH_GROUPS=y
+NEED_DH_GROUPS_ALL=y
+NEED_MODEXP=y
+NEED_CIPHER=y
+endif
+
+ifdef CONFIG_EAP_VENDOR_TEST
+ifeq ($(CONFIG_EAP_VENDOR_TEST), dyn)
+L_CFLAGS += -DEAP_VENDOR_TEST_DYNAMIC
+EAPDYN += src/eap_peer/eap_vendor_test.so
+else
+L_CFLAGS += -DEAP_VENDOR_TEST
+OBJS += src/eap_peer/eap_vendor_test.c
+OBJS_h += src/eap_server/eap_server_vendor_test.c
+endif
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_TNC
+# EAP-TNC
+L_CFLAGS += -DEAP_TNC
+OBJS += src/eap_peer/eap_tnc.c
+OBJS += src/eap_peer/tncc.c
+OBJS_h += src/eap_server/eap_server_tnc.c
+OBJS_h += src/eap_server/tncs.c
+NEED_BASE64=y
+ifndef CONFIG_NATIVE_WINDOWS
+ifndef CONFIG_DRIVER_BSD
+LIBS += -ldl
+endif
+endif
+endif
+
+ifdef CONFIG_IEEE8021X_EAPOL
+# IEEE 802.1X/EAPOL state machines (e.g., for RADIUS authentication)
+L_CFLAGS += -DIEEE8021X_EAPOL
+OBJS += src/eapol_supp/eapol_supp_sm.c
+OBJS += src/eap_peer/eap.c src/eap_peer/eap_methods.c
+NEED_EAP_COMMON=y
+ifdef CONFIG_DYNAMIC_EAP_METHODS
+L_CFLAGS += -DCONFIG_DYNAMIC_EAP_METHODS
+LIBS += -ldl -rdynamic
+endif
+endif
+
+ifdef CONFIG_AP
+NEED_80211_COMMON=y
+NEED_EAP_COMMON=y
+NEED_RSN_AUTHENTICATOR=y
+L_CFLAGS += -DCONFIG_AP
+OBJS += ap.c
+L_CFLAGS += -DCONFIG_NO_RADIUS
+L_CFLAGS += -DCONFIG_NO_ACCOUNTING
+L_CFLAGS += -DCONFIG_NO_VLAN
+OBJS += src/ap/hostapd.c
+OBJS += src/ap/wpa_auth_glue.c
+OBJS += src/ap/utils.c
+OBJS += src/ap/authsrv.c
+OBJS += src/ap/ap_config.c
+OBJS += src/utils/ip_addr.c
+OBJS += src/ap/sta_info.c
+OBJS += src/ap/tkip_countermeasures.c
+OBJS += src/ap/ap_mlme.c
+OBJS += src/ap/ieee802_1x.c
+OBJS += src/eapol_auth/eapol_auth_sm.c
+OBJS += src/ap/ieee802_11_auth.c
+OBJS += src/ap/ieee802_11_shared.c
+OBJS += src/ap/drv_callbacks.c
+OBJS += src/ap/ap_drv_ops.c
+OBJS += src/ap/beacon.c
+ifdef CONFIG_IEEE80211N
+OBJS += src/ap/ieee802_11_ht.c
+endif
+ifdef CONFIG_CTRL_IFACE
+OBJS += src/ap/ctrl_iface_ap.c
+endif
+
+L_CFLAGS += -DEAP_SERVER -DEAP_SERVER_IDENTITY
+OBJS += src/eap_server/eap_server.c
+OBJS += src/eap_server/eap_server_identity.c
+OBJS += src/eap_server/eap_server_methods.c
+
+ifdef CONFIG_IEEE80211N
+L_CFLAGS += -DCONFIG_IEEE80211N
+endif
+
+ifdef NEED_AP_MLME
+OBJS += src/ap/wmm.c
+OBJS += src/ap/ap_list.c
+OBJS += src/ap/ieee802_11.c
+OBJS += src/ap/hw_features.c
+L_CFLAGS += -DNEED_AP_MLME
+endif
+ifdef CONFIG_WPS
+L_CFLAGS += -DEAP_SERVER_WSC
+OBJS += src/ap/wps_hostapd.c
+OBJS += src/eap_server/eap_server_wsc.c
+endif
+endif
+
+ifdef NEED_RSN_AUTHENTICATOR
+L_CFLAGS += -DCONFIG_NO_RADIUS
+NEED_AES_WRAP=y
+OBJS += src/ap/wpa_auth.c
+OBJS += src/ap/wpa_auth_ie.c
+OBJS += src/ap/pmksa_cache_auth.c
+ifdef CONFIG_IEEE80211R
+OBJS += src/ap/wpa_auth_ft.c
+endif
+ifdef CONFIG_PEERKEY
+OBJS += src/ap/peerkey_auth.c
+endif
+endif
+
+ifdef CONFIG_EAP_SERVER
+L_CFLAGS += -DEAP_SERVER
+OBJS_h += src/eap_server/eap_server.c
+OBJS_h += src/eap_server/eap_server_identity.c
+OBJS_h += src/eap_server/eap_server_methods.c
+endif
+
+ifdef CONFIG_RADIUS_CLIENT
+OBJS_h += src/utils/ip_addr.c
+OBJS_h += src/radius/radius.c
+OBJS_h += src/radius/radius_client.c
+endif
+
+ifdef CONFIG_AUTHENTICATOR
+OBJS_h += src/eapol_auth/eapol_auth_sm.c
+OBJS_h += src/ap/ieee802_1x.c
+endif
+
+ifdef CONFIG_WPA_AUTHENTICATOR
+OBJS_h += src/ap/wpa_auth.c
+OBJS_h += src/ap/wpa_auth_ie.c
+OBJS_h += src/ap/pmksa_cache_auth.c
+ifdef CONFIG_IEEE80211R
+OBJS_h += src/ap/wpa_auth_ft.c
+endif
+ifdef CONFIG_PEERKEY
+OBJS_h += src/ap/peerkey_auth.c
+endif
+endif
+
+ifdef CONFIG_PCSC
+# PC/SC interface for smartcards (USIM, GSM SIM)
+L_CFLAGS += -DPCSC_FUNCS -I/usr/include/PCSC
+OBJS += src/utils/pcsc_funcs.c
+# -lpthread may not be needed depending on how pcsc-lite was configured
+ifdef CONFIG_NATIVE_WINDOWS
+#Once MinGW gets support for WinScard, -lwinscard could be used instead of the
+#dynamic symbol loading that is now used in pcsc_funcs.c
+#LIBS += -lwinscard
+else
+LIBS += -lpcsclite -lpthread
+endif
+endif
+
+ifdef CONFIG_SIM_SIMULATOR
+L_CFLAGS += -DCONFIG_SIM_SIMULATOR
+NEED_MILENAGE=y
+endif
+
+ifdef CONFIG_USIM_SIMULATOR
+L_CFLAGS += -DCONFIG_USIM_SIMULATOR
+NEED_MILENAGE=y
+endif
+
+ifdef NEED_MILENAGE
+OBJS += src/crypto/milenage.c
+NEED_AES_ENCBLOCK=y
+endif
+
+ifdef CONFIG_PKCS12
+L_CFLAGS += -DPKCS12_FUNCS
+endif
+
+ifdef CONFIG_SMARTCARD
+L_CFLAGS += -DCONFIG_SMARTCARD
+endif
+
+ifdef MS_FUNCS
+OBJS += src/crypto/ms_funcs.c
+NEED_DES=y
+NEED_MD4=y
+endif
+
+ifdef CHAP
+OBJS += src/eap_common/chap.c
+endif
+
+ifdef TLS_FUNCS
+NEED_DES=y
+# Shared TLS functions (needed for EAP_TLS, EAP_PEAP, EAP_TTLS, and EAP_FAST)
+OBJS += src/eap_peer/eap_tls_common.c
+OBJS_h += src/eap_server/eap_server_tls_common.c
+NEED_TLS_PRF=y
+endif
+
+ifndef CONFIG_TLS
+CONFIG_TLS=openssl
+endif
+
+ifdef CONFIG_TLSV11
+L_CFLAGS += -DCONFIG_TLSV11
+endif
+
+ifeq ($(CONFIG_TLS), openssl)
+ifdef TLS_FUNCS
+L_CFLAGS += -DEAP_TLS_OPENSSL
+OBJS += src/crypto/tls_openssl.c
+LIBS += -lssl
+endif
+OBJS += src/crypto/crypto_openssl.c
+OBJS_p += src/crypto/crypto_openssl.c
+ifdef NEED_FIPS186_2_PRF
+OBJS += src/crypto/fips_prf_openssl.c
+endif
+LIBS += -lcrypto
+LIBS_p += -lcrypto
+endif
+
+ifeq ($(CONFIG_TLS), gnutls)
+ifdef TLS_FUNCS
+OBJS += src/crypto/tls_gnutls.c
+LIBS += -lgnutls -lgpg-error
+endif
+OBJS += src/crypto/crypto_gnutls.c
+OBJS_p += src/crypto/crypto_gnutls.c
+ifdef NEED_FIPS186_2_PRF
+OBJS += src/crypto/fips_prf_gnutls.c
+endif
+LIBS += -lgcrypt
+LIBS_p += -lgcrypt
+CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_RC4=y
+CONFIG_INTERNAL_DH_GROUP5=y
+endif
+
+ifeq ($(CONFIG_TLS), schannel)
+ifdef TLS_FUNCS
+OBJS += src/crypto/tls_schannel.c
+endif
+OBJS += src/crypto/crypto_cryptoapi.c
+OBJS_p += src/crypto/crypto_cryptoapi.c
+ifdef NEED_FIPS186_2_PRF
+OBJS += src/crypto/fips_prf_cryptoapi.c
+endif
+CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_RC4=y
+CONFIG_INTERNAL_DH_GROUP5=y
+endif
+
+ifeq ($(CONFIG_TLS), nss)
+ifdef TLS_FUNCS
+OBJS += src/crypto/tls_nss.c
+LIBS += -lssl3
+endif
+OBJS += src/crypto/crypto_nss.c
+OBJS_p += src/crypto/crypto_nss.c
+ifdef NEED_FIPS186_2_PRF
+OBJS += src/crypto/fips_prf_nss.c
+endif
+LIBS += -lnss3
+LIBS_p += -lnss3
+CONFIG_INTERNAL_MD4=y
+CONFIG_INTERNAL_DH_GROUP5=y
+endif
+
+ifeq ($(CONFIG_TLS), internal)
+ifndef CONFIG_CRYPTO
+CONFIG_CRYPTO=internal
+endif
+ifdef TLS_FUNCS
+OBJS += src/crypto/crypto_internal-rsa.c
+OBJS += src/crypto/tls_internal.c
+OBJS += src/tls/tlsv1_common.c
+OBJS += src/tls/tlsv1_record.c
+OBJS += src/tls/tlsv1_cred.c
+OBJS += src/tls/tlsv1_client.c
+OBJS += src/tls/tlsv1_client_write.c
+OBJS += src/tls/tlsv1_client_read.c
+OBJS += src/tls/asn1.c
+OBJS += src/tls/rsa.c
+OBJS += src/tls/x509v3.c
+OBJS += src/tls/pkcs1.c
+OBJS += src/tls/pkcs5.c
+OBJS += src/tls/pkcs8.c
+NEED_SHA256=y
+NEED_BASE64=y
+NEED_TLS_PRF=y
+NEED_MODEXP=y
+NEED_CIPHER=y
+L_CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT
+endif
+ifdef NEED_CIPHER
+NEED_DES=y
+OBJS += src/crypto/crypto_internal-cipher.c
+endif
+ifdef NEED_MODEXP
+OBJS += src/crypto/crypto_internal-modexp.c
+OBJS += src/tls/bignum.c
+endif
+ifeq ($(CONFIG_CRYPTO), libtomcrypt)
+OBJS += src/crypto/crypto_libtomcrypt.c
+OBJS_p += src/crypto/crypto_libtomcrypt.c
+LIBS += -ltomcrypt -ltfm
+LIBS_p += -ltomcrypt -ltfm
+CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_RC4=y
+CONFIG_INTERNAL_DH_GROUP5=y
+endif
+ifeq ($(CONFIG_CRYPTO), internal)
+OBJS += src/crypto/crypto_internal.c
+OBJS_p += src/crypto/crypto_internal.c
+NEED_AES_ENC=y
+L_CFLAGS += -DCONFIG_CRYPTO_INTERNAL
+ifdef CONFIG_INTERNAL_LIBTOMMATH
+L_CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH
+ifdef CONFIG_INTERNAL_LIBTOMMATH_FAST
+L_CFLAGS += -DLTM_FAST
+endif
+else
+LIBS += -ltommath
+LIBS_p += -ltommath
+endif
+CONFIG_INTERNAL_AES=y
+CONFIG_INTERNAL_DES=y
+CONFIG_INTERNAL_SHA1=y
+CONFIG_INTERNAL_MD4=y
+CONFIG_INTERNAL_MD5=y
+CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_RC4=y
+CONFIG_INTERNAL_DH_GROUP5=y
+endif
+ifeq ($(CONFIG_CRYPTO), cryptoapi)
+OBJS += src/crypto/crypto_cryptoapi.c
+OBJS_p += src/crypto/crypto_cryptoapi.c
+L_CFLAGS += -DCONFIG_CRYPTO_CRYPTOAPI
+CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_RC4=y
+endif
+endif
+
+ifeq ($(CONFIG_TLS), none)
+ifdef TLS_FUNCS
+OBJS += src/crypto/tls_none.c
+L_CFLAGS += -DEAP_TLS_NONE
+CONFIG_INTERNAL_AES=y
+CONFIG_INTERNAL_SHA1=y
+CONFIG_INTERNAL_MD5=y
+endif
+OBJS += src/crypto/crypto_none.c
+OBJS_p += src/crypto/crypto_none.c
+CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_RC4=y
+endif
+
+ifdef TLS_FUNCS
+ifdef CONFIG_SMARTCARD
+ifndef CONFIG_NATIVE_WINDOWS
+ifneq ($(CONFIG_L2_PACKET), freebsd)
+LIBS += -ldl
+endif
+endif
+endif
+endif
+
+ifndef TLS_FUNCS
+OBJS += src/crypto/tls_none.c
+ifeq ($(CONFIG_TLS), internal)
+CONFIG_INTERNAL_AES=y
+CONFIG_INTERNAL_SHA1=y
+CONFIG_INTERNAL_MD5=y
+CONFIG_INTERNAL_RC4=y
+endif
+endif
+
+AESOBJS = # none so far (see below)
+ifdef CONFIG_INTERNAL_AES
+AESOBJS += src/crypto/aes-internal.c src/crypto/aes-internal-dec.c
+endif
+
+AESOBJS += src/crypto/aes-unwrap.c
+ifdef NEED_AES_EAX
+AESOBJS += src/crypto/aes-eax.c
+NEED_AES_CTR=y
+endif
+ifdef NEED_AES_CTR
+AESOBJS += src/crypto/aes-ctr.c
+endif
+ifdef NEED_AES_ENCBLOCK
+AESOBJS += src/crypto/aes-encblock.c
+endif
+ifdef NEED_AES_OMAC1
+NEED_AES_ENC=y
+AESOBJS += src/crypto/aes-omac1.c
+endif
+ifdef NEED_AES_WRAP
+NEED_AES_ENC=y
+AESOBJS += src/crypto/aes-wrap.c
+endif
+ifdef NEED_AES_CBC
+NEED_AES_ENC=y
+AESOBJS += src/crypto/aes-cbc.c
+endif
+ifdef NEED_AES_ENC
+ifdef CONFIG_INTERNAL_AES
+AESOBJS += src/crypto/aes-internal-enc.c
+endif
+endif
+ifdef NEED_AES
+OBJS += $(AESOBJS)
+endif
+
+SHA1OBJS =
+ifdef NEED_SHA1
+SHA1OBJS += src/crypto/sha1.c
+ifdef CONFIG_INTERNAL_SHA1
+SHA1OBJS += src/crypto/sha1-internal.c
+ifdef NEED_FIPS186_2_PRF
+SHA1OBJS += src/crypto/fips_prf_internal.c
+endif
+endif
+ifdef CONFIG_NO_WPA_PASSPHRASE
+L_CFLAGS += -DCONFIG_NO_PBKDF2
+else
+SHA1OBJS += src/crypto/sha1-pbkdf2.c
+endif
+ifdef NEED_T_PRF
+SHA1OBJS += src/crypto/sha1-tprf.c
+endif
+ifdef NEED_TLS_PRF
+SHA1OBJS += src/crypto/sha1-tlsprf.c
+endif
+endif
+
+MD5OBJS = src/crypto/md5.c
+ifdef NEED_MD5
+ifdef CONFIG_INTERNAL_MD5
+MD5OBJS += src/crypto/md5-internal.c
+endif
+ifdef CONFIG_FIPS
+MD5OBJS += src/crypto/md5-non-fips.c
+endif
+OBJS += $(MD5OBJS)
+OBJS_p += $(MD5OBJS)
+endif
+
+ifdef NEED_MD4
+ifdef CONFIG_INTERNAL_MD4
+OBJS += src/crypto/md4-internal.c
+endif
+endif
+
+DESOBJS = # none needed when not internal
+ifdef NEED_DES
+ifdef CONFIG_INTERNAL_DES
+DESOBJS += src/crypto/des-internal.c
+endif
+endif
+
+ifdef NEED_RC4
+ifdef CONFIG_INTERNAL_RC4
+OBJS += src/crypto/rc4.c
+endif
+endif
+
+SHA256OBJS = # none by default
+ifdef NEED_SHA256
+L_CFLAGS += -DCONFIG_SHA256
+SHA256OBJS += src/crypto/sha256.c
+ifdef CONFIG_INTERNAL_SHA256
+SHA256OBJS += src/crypto/sha256-internal.c
+endif
+OBJS += $(SHA256OBJS)
+endif
+
+ifdef NEED_DH_GROUPS
+OBJS += src/crypto/dh_groups.c
+endif
+ifdef NEED_DH_GROUPS_ALL
+L_CFLAGS += -DALL_DH_GROUPS
+endif
+ifdef CONFIG_INTERNAL_DH_GROUP5
+ifdef NEED_DH_GROUPS
+OBJS += src/crypto/dh_group5.c
+endif
+endif
+
+ifdef CONFIG_NO_RANDOM_POOL
+L_CFLAGS += -DCONFIG_NO_RANDOM_POOL
+else
+OBJS += src/crypto/random.c
+endif
+
+ifdef CONFIG_CTRL_IFACE
+ifeq ($(CONFIG_CTRL_IFACE), y)
+ifdef CONFIG_NATIVE_WINDOWS
+CONFIG_CTRL_IFACE=named_pipe
+else
+CONFIG_CTRL_IFACE=unix
+endif
+endif
+L_CFLAGS += -DCONFIG_CTRL_IFACE
+ifeq ($(CONFIG_CTRL_IFACE), unix)
+L_CFLAGS += -DCONFIG_CTRL_IFACE_UNIX
+endif
+ifeq ($(CONFIG_CTRL_IFACE), udp)
+L_CFLAGS += -DCONFIG_CTRL_IFACE_UDP
+endif
+ifeq ($(CONFIG_CTRL_IFACE), named_pipe)
+L_CFLAGS += -DCONFIG_CTRL_IFACE_NAMED_PIPE
+endif
+OBJS += ctrl_iface.c ctrl_iface_$(CONFIG_CTRL_IFACE).c
+endif
+
+ifdef CONFIG_CTRL_IFACE_DBUS
+DBUS=y
+DBUS_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS -DDBUS_API_SUBJECT_TO_CHANGE
+DBUS_OBJS += dbus/dbus_old.c dbus/dbus_old_handlers.c
+ifdef CONFIG_WPS
+DBUS_OBJS += dbus/dbus_old_handlers_wps.c
+endif
+DBUS_OBJS += dbus/dbus_dict_helpers.c
+ifndef DBUS_LIBS
+DBUS_LIBS := $(shell $(PKG_CONFIG) --libs dbus-1)
+endif
+ifndef DBUS_INCLUDE
+DBUS_INCLUDE := $(shell $(PKG_CONFIG) --cflags dbus-1)
+endif
+DBUS_CFLAGS += $(DBUS_INCLUDE)
+endif
+
+ifdef CONFIG_CTRL_IFACE_DBUS_NEW
+DBUS=y
+DBUS_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_NEW
+DBUS_OBJS ?= dbus/dbus_dict_helpers.c
+DBUS_OBJS += dbus/dbus_new_helpers.c
+DBUS_OBJS += dbus/dbus_new.c dbus/dbus_new_handlers.c
+ifdef CONFIG_WPS
+DBUS_OBJS += dbus/dbus_new_handlers_wps.c
+endif
+ifdef CONFIG_P2P
+DBUS_OBJS += dbus/dbus_new_handlers_p2p.c
+endif
+ifndef DBUS_LIBS
+DBUS_LIBS := $(shell $(PKG_CONFIG) --libs dbus-1)
+endif
+ifndef DBUS_INCLUDE
+DBUS_INCLUDE := $(shell $(PKG_CONFIG) --cflags dbus-1)
+endif
+ifdef CONFIG_CTRL_IFACE_DBUS_INTRO
+DBUS_OBJS += dbus/dbus_new_introspect.c
+DBUS_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_INTRO
+endif
+DBUS_CFLAGS += $(DBUS_INCLUDE)
+endif
+
+ifdef DBUS
+DBUS_CFLAGS += -DCONFIG_DBUS
+DBUS_OBJS += dbus/dbus_common.c
+endif
+
+OBJS += $(DBUS_OBJS)
+L_CFLAGS += $(DBUS_CFLAGS)
+LIBS += $(DBUS_LIBS)
+
+ifdef CONFIG_READLINE
+OBJS_c += src/utils/edit_readline.c
+LIBS_c += -lncurses -lreadline
+else
+ifdef CONFIG_WPA_CLI_EDIT
+OBJS_c += src/utils/edit.c
+else
+OBJS_c += src/utils/edit_simple.c
+endif
+endif
+
+ifdef CONFIG_NATIVE_WINDOWS
+L_CFLAGS += -DCONFIG_NATIVE_WINDOWS
+LIBS += -lws2_32 -lgdi32 -lcrypt32
+LIBS_c += -lws2_32
+LIBS_p += -lws2_32 -lgdi32
+ifeq ($(CONFIG_CRYPTO), cryptoapi)
+LIBS_p += -lcrypt32
+endif
+endif
+
+ifdef CONFIG_NO_STDOUT_DEBUG
+L_CFLAGS += -DCONFIG_NO_STDOUT_DEBUG
+ifndef CONFIG_CTRL_IFACE
+L_CFLAGS += -DCONFIG_NO_WPA_MSG
+endif
+endif
+
+ifdef CONFIG_ANDROID_LOG
+L_CFLAGS += -DCONFIG_ANDROID_LOG
+endif
+
+ifdef CONFIG_IPV6
+# for eapol_test only
+L_CFLAGS += -DCONFIG_IPV6
+endif
+
+ifdef NEED_BASE64
+OBJS += src/utils/base64.c
+endif
+
+ifdef NEED_SME
+NEED_80211_COMMON=y
+OBJS += sme.c
+L_CFLAGS += -DCONFIG_SME
+endif
+
+ifdef NEED_80211_COMMON
+OBJS += src/common/ieee802_11_common.c
+endif
+
+ifdef NEED_EAP_COMMON
+OBJS += src/eap_common/eap_common.c
+endif
+
+ifndef CONFIG_MAIN
+CONFIG_MAIN=main
+endif
+
+ifdef CONFIG_DEBUG_SYSLOG
+L_CFLAGS += -DCONFIG_DEBUG_SYSLOG
+ifdef CONFIG_DEBUG_SYSLOG_FACILITY
+L_CFLAGS += -DLOG_HOSTAPD="$(CONFIG_DEBUG_SYSLOG_FACILITY)"
+endif
+endif
+
+ifdef CONFIG_DEBUG_FILE
+L_CFLAGS += -DCONFIG_DEBUG_FILE
+endif
+
+ifdef CONFIG_DELAYED_MIC_ERROR_REPORT
+L_CFLAGS += -DCONFIG_DELAYED_MIC_ERROR_REPORT
+endif
+
+ifdef CONFIG_FIPS
+L_CFLAGS += -DCONFIG_FIPS
+endif
+
+OBJS += $(SHA1OBJS) $(DESOBJS)
+
+OBJS_p += $(SHA1OBJS)
+
+ifdef CONFIG_BGSCAN_SIMPLE
+L_CFLAGS += -DCONFIG_BGSCAN_SIMPLE
+OBJS += bgscan_simple.c
+NEED_BGSCAN=y
+endif
+
+ifdef CONFIG_BGSCAN_LEARN
+L_CFLAGS += -DCONFIG_BGSCAN_LEARN
+OBJS += bgscan_learn.c
+NEED_BGSCAN=y
+endif
+
+ifdef NEED_BGSCAN
+L_CFLAGS += -DCONFIG_BGSCAN
+OBJS += bgscan.c
+endif
+
+ifdef NEED_GAS
+OBJS += ../src/common/gas.c
+OBJS += gas_query.c
+L_CFLAGS += -DCONFIG_GAS
+NEED_OFFCHANNEL=y
+endif
+
+ifdef NEED_OFFCHANNEL
+OBJS += offchannel.c
+L_CFLAGS += -DCONFIG_OFFCHANNEL
+endif
+
+OBJS += src/drivers/driver_common.c
+
+OBJS_wpa_rm := ctrl_iface.c ctrl_iface_unix.c
+OBJS_wpa := $(filter-out $(OBJS_wpa_rm),$(OBJS)) $(OBJS_h) tests/test_wpa.c
+ifdef CONFIG_AUTHENTICATOR
+OBJS_wpa += tests/link_test.c
+endif
+OBJS_wpa += $(OBJS_l2)
+OBJS += wpa_supplicant.c events.c blacklist.c wpas_glue.c scan.c
+OBJS_t := $(OBJS) $(OBJS_l2) eapol_test.c
+OBJS_t += src/radius/radius_client.c
+OBJS_t += src/radius/radius.c
+ifndef CONFIG_AP
+OBJS_t += src/utils/ip_addr.c
+endif
+OBJS_t2 := $(OBJS) $(OBJS_l2) preauth_test.c
+OBJS += $(CONFIG_MAIN).c
+
+ifdef CONFIG_PRIVSEP
+OBJS_priv += $(OBJS_d) src/drivers/drivers.c
+OBJS_priv += $(OBJS_l2)
+OBJS_priv += src/utils/os_$(CONFIG_OS).c
+OBJS_priv += src/utils/$(CONFIG_ELOOP).c
+OBJS_priv += src/utils/common.c
+OBJS_priv += src/utils/wpa_debug.c
+OBJS_priv += src/utils/wpabuf.c
+OBJS_priv += wpa_priv.c
+ifdef CONFIG_DRIVER_TEST
+OBJS_priv += $(SHA1OBJS)
+OBJS_priv += $(MD5OBJS)
+ifeq ($(CONFIG_TLS), openssl)
+OBJS_priv += src/crypto/crypto_openssl.c
+endif
+ifeq ($(CONFIG_TLS), gnutls)
+OBJS_priv += src/crypto/crypto_gnutls.c
+endif
+ifeq ($(CONFIG_TLS), nss)
+OBJS_priv += src/crypto/crypto_nss.c
+endif
+ifeq ($(CONFIG_TLS), internal)
+ifeq ($(CONFIG_CRYPTO), libtomcrypt)
+OBJS_priv += src/crypto/crypto_libtomcrypt.c
+else
+OBJS_priv += src/crypto/crypto_internal.c
+endif
+endif
+endif # CONFIG_DRIVER_TEST
+OBJS += src/l2_packet/l2_packet_privsep.c
+OBJS += src/drivers/driver_privsep.c
+EXTRA_progs += wpa_priv
+else
+OBJS += $(OBJS_d) src/drivers/drivers.c
+OBJS += $(OBJS_l2)
+endif
+
+ifdef CONFIG_NDIS_EVENTS_INTEGRATED
+L_CFLAGS += -DCONFIG_NDIS_EVENTS_INTEGRATED
+OBJS += src/drivers/ndis_events.c
+EXTRALIBS += -loleaut32 -lole32 -luuid
+ifdef PLATFORMSDKLIB
+EXTRALIBS += $(PLATFORMSDKLIB)/WbemUuid.Lib
+else
+EXTRALIBS += WbemUuid.Lib
+endif
+endif
+
+ifndef LDO
+LDO=$(CC)
+endif
+
+ifeq ($(WPA_BUILD_SUPPLICANT),true)
+
+########################
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := wpa_cli
+LOCAL_MODULE_TAGS := debug
+LOCAL_SHARED_LIBRARIES := libc libcutils
+LOCAL_CFLAGS := $(L_CFLAGS)
+LOCAL_SRC_FILES := $(OBJS_c)
+LOCAL_C_INCLUDES := $(INCLUDES)
+include $(BUILD_EXECUTABLE)
+
+########################
+include $(CLEAR_VARS)
+LOCAL_MODULE := wpa_supplicant
+ifdef CONFIG_DRIVER_CUSTOM
+LOCAL_STATIC_LIBRARIES := libCustomWifi
+endif
+ifneq ($(BOARD_WPA_SUPPLICANT_PRIVATE_LIB),)
+LOCAL_STATIC_LIBRARIES += $(BOARD_WPA_SUPPLICANT_PRIVATE_LIB)
+endif
+LOCAL_SHARED_LIBRARIES := libc libcutils
+ifeq ($(CONFIG_TLS), openssl)
+LOCAL_SHARED_LIBRARIES += libcrypto libssl
+endif
+ifdef CONFIG_DRIVER_NL80211
+LOCAL_STATIC_LIBRARIES += libnl_2
+endif
+LOCAL_CFLAGS := $(L_CFLAGS)
+LOCAL_SRC_FILES := $(OBJS)
+LOCAL_C_INCLUDES := $(INCLUDES)
+include $(BUILD_EXECUTABLE)
+
+########################
+#
+#include $(CLEAR_VARS)
+#LOCAL_MODULE := eapol_test
+#ifdef CONFIG_DRIVER_CUSTOM
+#LOCAL_STATIC_LIBRARIES := libCustomWifi
+#endif
+#LOCAL_SHARED_LIBRARIES := libc libcrypto libssl
+#LOCAL_CFLAGS := $(L_CFLAGS)
+#LOCAL_SRC_FILES := $(OBJS_t)
+#LOCAL_C_INCLUDES := $(INCLUDES)
+#include $(BUILD_EXECUTABLE)
+#
+########################
+#
+#local_target_dir := $(TARGET_OUT)/etc/wifi
+#
+#include $(CLEAR_VARS)
+#LOCAL_MODULE := wpa_supplicant.conf
+#LOCAL_MODULE_TAGS := user
+#LOCAL_MODULE_CLASS := ETC
+#LOCAL_MODULE_PATH := $(local_target_dir)
+#LOCAL_SRC_FILES := $(LOCAL_MODULE)
+#include $(BUILD_PREBUILT)
+#
+########################
+
+endif # ifeq ($(WPA_BUILD_SUPPLICANT),true)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE = libwpa_client
+LOCAL_CFLAGS = $(L_CFLAGS)
+LOCAL_SRC_FILES = src/common/wpa_ctrl.c src/utils/os_$(CONFIG_OS).c
+LOCAL_C_INCLUDES = $(INCLUDES)
+LOCAL_SHARED_LIBRARIES := libcutils
+LOCAL_COPY_HEADERS_TO := libwpa_client
+LOCAL_COPY_HEADERS := src/common/wpa_ctrl.h
+include $(BUILD_SHARED_LIBRARY)
ChangeLog for wpa_supplicant
-2010-09-07 - v0.7.3
- * fixed fallback from failed PMKSA caching into full EAP authentication
- [Bug 355]
- * fixed issue with early D-Bus signals during initialization
- * fixed X.509 name handling in internal TLS
- * fixed WPS ER to use corrent Enrollee MAC Address in Credential
- * fixed scanning routines ot improve AP selection for WPS
- * added WPS workaround for open networks
- * fixed WPS Diffie-Hellman derivation to use correct public key length
- * fixed wpa_supplicant AP mode operations to ignore Supplicant and
- scan result events
- * improved SME operations with nl80211
- * fixed WPS ER event_id handling in some cases
- * fixed some issues with bgscan simple to avoid unnecessary scans
- * fixed issue with l2_packet_ndis overlapped writes corrupting stack
- [Bug 328]
- * updated WinPcap to the latest stable version 4.1.2 in Windows
- installer
+2012-04-18 - v1.0
+ * bsd: Add support for setting HT values in IFM_MMASK.
+ * Delay STA entry removal until Deauth/Disassoc TX status in AP mode.
+ This allows the driver to use PS buffering of Deauthentication and
+ Disassociation frames when the STA is in power save sleep. Only
+ available with drivers that provide TX status events for Deauth/
+ Disassoc frames (nl80211).
+ * Drop oldest unknown BSS table entries first. This makes it less
+ likely to hit connection issues in environments with huge number
+ of visible APs.
+ * Add systemd support.
+ * Add support for setting the syslog facility from the config file
+ at build time.
+ * atheros: Add support for IEEE 802.11w configuration.
+ * AP mode: Allow enable HT20 if driver supports it, by setting the
+ config parameter ieee80211n.
+ * Allow AP mode to disconnect STAs based on low ACK condition (when
+ the data connection is not working properly, e.g., due to the STA
+ going outside the range of the AP). Disabled by default, enable by
+ config option disassoc_low_ack.
+ * nl80211:
+ - Support GTK rekey offload.
+ - Support PMKSA candidate events. This adds support for RSN
+ pre-authentication with nl80211 interface and drivers that handle
+ roaming internally.
+ * dbus:
+ - Add a DBus signal for EAP SM requests, emitted on the Interface
+ object.
+ - Export max scan ssids supported by the driver as MaxScanSSID.
+ - Add signal Certification for information about server certification.
+ - Add BSSExpireAge and BSSExpireCount interface properties and
+ support set/get, which allows for setting BSS cache expiration age
+ and expiration scan count.
+ - Add ConfigFile to AddInterface properties.
+ - Add Interface.Country property and support to get/set the value.
+ - Add DBus property CurrentAuthMode.
+ - P2P DBus API added.
+ - Emit property changed events (for property BSSs) when adding/
+ removing BSSs.
+ - Treat '' in SSIDs of Interface.Scan as a request for broadcast
+ scan, instead of ignoring it.
+ - Add DBus getter/setter for FastReauth.
+ - Raise PropertiesChanged on org.freedesktop.DBus.Properties.
+ * wpa_cli:
+ - Send AP-STA-DISCONNECTED event when an AP disconnects a station
+ due to inactivity.
+ - Make second argument to set command optional. This can be used to
+ indicate a zero length value.
+ - Add signal_poll command.
+ - Add bss_expire_age and bss_expire_count commands to set/get BSS
+ cache expiration age and expiration scan count.
+ - Add ability to set scan interval (the time in seconds wpa_s waits
+ before requesting a new scan after failing to find a suitable
+ network in scan results) using scan_interval command.
+ - Add event CTRL-EVENT-ASSOC-REJECT for association rejected.
+ - Add command get version, that returns wpa_supplicant version string.
+ - Add command sta_autoconnect for disabling automatic reconnection
+ on receiving disconnection event.
+ - Setting bssid parameter to an empty string "" or any can now be
+ used to clear the bssid_set flag in a network block, i.e., to remove
+ bssid filtering.
+ - Add tdls_testing command to add a special testing feature for
+ changing TDLS behavior. Build param CONFIG_TDLS_TESTING must be
+ enabled as well.
+ - For interworking, add wpa_cli commands interworking_select,
+ interworking_connect, anqp_get, fetch_anqp, and stop_fetch_anqp.
+ - Many P2P commands were added. See README-P2P.
+ - Many WPS/WPS ER commands - see WPS/WPS ER sections for details.
+ - Allow set command to change global config parameters.
+ - Add log_level command, which can be used to display the current
+ debugging level and to change the log level during run time.
+ - Add note command, which can be used to insert notes to the debug
+ log.
+ - Add internal line edit implementation. CONFIG_WPA_CLI_EDIT=y
+ can now be used to build wpa_cli with internal implementation of
+ line editing and history support. This can be used as a replacement
+ for CONFIG_READLINE=y.
+ * AP mode: Add max_num_sta config option, which can be used to limit
+ the number of stations allowed to connect to the AP.
+ * Add WPA_IGNORE_CONFIG_ERRORS build option to continue in case of bad
+ config file.
+ * wext: Increase scan timeout from 5 to 10 seconds.
+ * Add blacklist command, allowing an external program to
+ manage the BSS blacklist and display its current contents.
+ * WPS:
+ - Add wpa_cli wps_pin get command for generating random PINs. This can
+ be used in a UI to generate a PIN without starting WPS (or P2P)
+ operation.
+ - Set RF bands based on driver capabilities, instead of hardcoding
+ them.
+ - Add mechanism for indicating non-standard WPS errors.
+ - Add CONFIG_WPS_REG_DISABLE_OPEN=y option to disable open networks
+ by default.
+ - Add wps_ap_pin cli command for wpa_supplicant AP mode.
+ - Add wps_check_pin cli command for processing PIN from user input.
+ UIs can use this command to process a PIN entered by a user and to
+ validate the checksum digit (if present).
+ - Cancel WPS operation on PBC session overlap detection.
+ - New wps_cancel command in wpa_cli will cancel a pending WPS
+ operation.
+ - wpa_cli action: Add WPS_EVENT_SUCCESS and WPS_EVENT_FAIL handlers.
+ - Trigger WPS config update on Manufacturer, Model Name, Model
+ Number, and Serial Number changes.
+ - Fragment size is now configurable for EAP-WSC peer. Use
+ wpa_cli set wps_fragment_size <val>.
+ - Disable AP PIN after 10 consecutive failures. Slow down attacks on
+ failures up to 10.
+ - Allow AP to start in Enrollee mode without AP PIN for probing, to
+ be compatible with Windows 7.
+ - Add Config Error into WPS-FAIL events to provide more info to the
+ user on how to resolve the issue.
+ - Label and Display config methods are not allowed to be enabled
+ at the same time, since it is unclear which PIN to use if both
+ methods are advertised.
+ - When controlling multiple interfaces:
+ - apply WPS commands to all interfaces configured to use WPS
+ - apply WPS config changes to all interfaces that use WPS
+ - when an attack is detected on any interface, disable AP PIN on
+ all interfaces
+ * WPS ER:
+ - Add special AP Setup Locked mode to allow read only ER.
+ ap_setup_locked=2 can now be used to enable a special mode where
+ WPS ER can learn the current AP settings, but cannot change them.
+ - Show SetSelectedRegistrar events as ctrl_iface events
+ - Add wps_er_set_config to enroll a network based on a local
+ network configuration block instead of having to (re-)learn the
+ current AP settings with wps_er_learn.
+ - Allow AP filtering based on IP address, add ctrl_iface event for
+ learned AP settings, add wps_er_config command to configure an AP.
+ * WPS 2.0: Add support for WPS 2.0 (CONFIG_WPS2)
+ - Add build option CONFIG_WPS_EXTENSIBILITY_TESTING to enable tool
+ for testing protocol extensibility.
+ - Add build option CONFIG_WPS_STRICT to allow disabling of WPS
+ workarounds.
+ - Add support for AuthorizedMACs attribute.
+ * TDLS:
+ - Propogate TDLS related nl80211 capability flags from kernel and
+ add them as driver capability flags. If the driver doesn't support
+ capabilities, assume TDLS is supported internally. When TDLS is
+ explicitly not supported, disable all user facing TDLS operations.
+ - Allow TDLS to be disabled at runtime (mostly for testing).
+ Use set tdls_disabled.
+ - Honor AP TDLS settings that prohibit/allow TDLS.
+ - Add a special testing feature for changing TDLS behavior. Use
+ CONFIG_TDLS_TESTING build param to enable. Configure at runtime
+ with tdls_testing cli command.
+ - Add support for TDLS 802.11z.
+ * wlantest: Add a tool wlantest for IEEE802.11 protocol testing.
+ wlantest can be used to capture frames from a monitor interface
+ for realtime capturing or from pcap files for offline analysis.
+ * Interworking: Support added for 802.11u. Enable in .config with
+ CONFIG_INTERWORKING. See wpa_supplicant.conf for config parameters
+ for interworking. wpa_cli commands added to support this are
+ interworking_select, interworking_connect, anqp_get, fetch_anqp,
+ and stop_fetch_anqp.
+ * Android: Add build and runtime support for Android wpa_supplicant.
+ * bgscan learn: Add new bgscan that learns BSS information based on
+ previous scans, and uses that information to dynamically generate
+ the list of channels for background scans.
+ * Add a new debug message level for excessive information. Use
+ -ddd to enable.
+ * TLS: Add support for tls_disable_time_checks=1 in client mode.
+ * Internal TLS:
+ - Add support for TLS v1.1 (RFC 4346). Enable with build parameter
+ CONFIG_TLSV11.
+ - Add domainComponent parser for X.509 names.
+ * Linux: Add RFKill support by adding an interface state "disabled".
+ * Reorder some IEs to get closer to IEEE 802.11 standard. Move
+ WMM into end of Beacon, Probe Resp and (Re)Assoc Resp frames.
+ Move HT IEs to be later in (Re)Assoc Resp.
+ * Solaris: Add support for wired 802.1X client.
+ * Wi-Fi Direct support. See README-P2P for more information.
+ * Many bugfixes.
2010-04-18 - v0.7.2
* nl80211: fixed number of issues with roaming
export LIBDIR ?= /usr/local/lib/
export BINDIR ?= /usr/local/sbin/
+PKG_CONFIG ?= pkg-config
CFLAGS += -I../src
CFLAGS += -I../src/utils
-ALL=wpa_supplicant wpa_passphrase wpa_cli
+-include .config
+
+BINALL=wpa_supplicant wpa_cli
+
+ifndef CONFIG_NO_WPA_PASSPHRASE
+BINALL += wpa_passphrase
+endif
+
+ALL = $(BINALL)
+ALL += systemd/wpa_supplicant.service
+ALL += systemd/wpa_supplicant@.service
+ALL += systemd/wpa_supplicant-nl80211@.service
+ALL += systemd/wpa_supplicant-wired@.service
+ALL += dbus/fi.epitest.hostap.WPASupplicant.service
+ALL += dbus/fi.w1.wpa_supplicant1.service
+
all: verify_config $(ALL) dynamic_eap_methods
echo CONFIG_DRIVER_HOSTAP=y >> .config
echo CONFIG_DRIVER_WEXT=y >> .config
-install: all
- mkdir -p $(DESTDIR)$(BINDIR)
- for i in $(ALL); do cp $$i $(DESTDIR)$(BINDIR)/$$i; done
+$(DESTDIR)$(BINDIR)/%: %
+ install -D $(<) $(@)
+
+install: $(addprefix $(DESTDIR)$(BINDIR)/,$(BINALL))
$(MAKE) -C ../src install
OBJS = config.o
OBJS_p += ../src/utils/wpa_debug.o
OBJS_p += ../src/utils/wpabuf.o
OBJS_c = wpa_cli.o ../src/common/wpa_ctrl.o
-
--include .config
+OBJS_c += ../src/utils/wpa_debug.o
+OBJS_c += ../src/utils/common.o
ifndef CONFIG_OS
ifdef CONFIG_NATIVE_WINDOWS
OBJS += ../src/utils/trace.o
OBJS_p += ../src/utils/trace.o
OBJS_c += ../src/utils/trace.o
-OBJS_c += ../src/utils/wpa_debug.o
+OBJS_priv += ../src/utils/trace.o
LDFLAGS += -rdynamic
CFLAGS += -funwind-tables
ifdef CONFIG_WPA_TRACE_BFD
CONFIG_ELOOP=eloop
endif
OBJS += ../src/utils/$(CONFIG_ELOOP).o
+OBJS_c += ../src/utils/$(CONFIG_ELOOP).o
ifdef CONFIG_EAPOL_TEST
NEED_AES_OMAC1=y
endif
+ifdef CONFIG_TDLS
+CFLAGS += -DCONFIG_TDLS
+OBJS += ../src/rsn_supp/tdls.o
+NEED_SHA256=y
+NEED_AES_OMAC1=y
+endif
+
+ifdef CONFIG_TDLS_TESTING
+CFLAGS += -DCONFIG_TDLS_TESTING
+endif
+
ifdef CONFIG_PEERKEY
CFLAGS += -DCONFIG_PEERKEY
endif
OBJS += ibss_rsn.o
endif
+ifdef CONFIG_P2P
+OBJS += p2p_supplicant.o
+OBJS += ../src/p2p/p2p.o
+OBJS += ../src/p2p/p2p_utils.o
+OBJS += ../src/p2p/p2p_parse.o
+OBJS += ../src/p2p/p2p_build.o
+OBJS += ../src/p2p/p2p_go_neg.o
+OBJS += ../src/p2p/p2p_sd.o
+OBJS += ../src/p2p/p2p_pd.o
+OBJS += ../src/p2p/p2p_invitation.o
+OBJS += ../src/p2p/p2p_dev_disc.o
+OBJS += ../src/p2p/p2p_group.o
+OBJS += ../src/ap/p2p_hostapd.o
+CFLAGS += -DCONFIG_P2P
+NEED_GAS=y
+NEED_OFFCHANNEL=y
+NEED_80211_COMMON=y
+CONFIG_WPS=y
+CONFIG_AP=y
+ifdef CONFIG_P2P_STRICT
+CFLAGS += -DCONFIG_P2P_STRICT
+endif
+endif
+
+ifdef CONFIG_INTERWORKING
+OBJS += interworking.o
+CFLAGS += -DCONFIG_INTERWORKING
+NEED_GAS=y
+endif
+
ifdef CONFIG_NO_WPA2
CFLAGS += -DCONFIG_NO_WPA2
endif
NEED_AES_OMAC1=y
endif
+ifdef CONFIG_EAP_PWD
+CFLAGS += -DEAP_PWD
+OBJS += ../src/eap_peer/eap_pwd.o ../src/eap_common/eap_pwd_common.o
+OBJS_h += ../src/eap_server/eap_pwd.o
+CONFIG_IEEE8021X_EAPOL=y
+NEED_SHA256=y
+endif
+
ifdef CONFIG_WPS
+ifdef CONFIG_WPS2
+CFLAGS += -DCONFIG_WPS2
+endif
+
# EAP-WSC
CFLAGS += -DCONFIG_WPS -DEAP_WSC
OBJS += wps_supplicant.o
OBJS += ../src/wps/http_server.o
endif
+ifdef CONFIG_WPS_STRICT
+CFLAGS += -DCONFIG_WPS_STRICT
+OBJS += ../src/wps/wps_validate.o
+endif
+
+ifdef CONFIG_WPS_TESTING
+CFLAGS += -DCONFIG_WPS_TESTING
+endif
+
+ifdef CONFIG_WPS_REG_DISABLE_OPEN
+CFLAGS += -DCONFIG_WPS_REG_DISABLE_OPEN
+endif
+
endif
ifdef CONFIG_EAP_IKEV2
OBJS += ../src/ap/ieee802_1x.o
OBJS += ../src/eapol_auth/eapol_auth_sm.o
OBJS += ../src/ap/ieee802_11_auth.o
+OBJS += ../src/ap/ieee802_11_shared.o
OBJS += ../src/ap/drv_callbacks.o
OBJS += ../src/ap/ap_drv_ops.o
+OBJS += ../src/ap/beacon.o
+ifdef CONFIG_IEEE80211N
+OBJS += ../src/ap/ieee802_11_ht.o
+endif
ifdef CONFIG_CTRL_IFACE
OBJS += ../src/ap/ctrl_iface_ap.o
endif
endif
ifdef NEED_AP_MLME
-OBJS += ../src/ap/beacon.o
OBJS += ../src/ap/wmm.o
OBJS += ../src/ap/ap_list.o
OBJS += ../src/ap/ieee802_11.o
OBJS += ../src/ap/hw_features.o
-ifdef CONFIG_IEEE80211N
-OBJS += ../src/ap/ieee802_11_ht.o
-endif
CFLAGS += -DNEED_AP_MLME
endif
ifdef CONFIG_WPS
ifdef NEED_MILENAGE
OBJS += ../src/crypto/milenage.o
+NEED_AES_ENCBLOCK=y
endif
ifdef CONFIG_PKCS12
CONFIG_TLS=openssl
endif
+ifdef CONFIG_TLSV11
+CFLAGS += -DCONFIG_TLSV11
+endif
+
ifeq ($(CONFIG_TLS), openssl)
ifdef TLS_FUNCS
CFLAGS += -DEAP_TLS_OPENSSL
ifdef TLS_FUNCS
OBJS += ../src/crypto/tls_gnutls.o
LIBS += -lgnutls -lgpg-error
-ifdef CONFIG_GNUTLS_EXTRA
-CFLAGS += -DCONFIG_GNUTLS_EXTRA
-LIBS += -lgnutls-extra
-endif
endif
OBJS += ../src/crypto/crypto_gnutls.o
OBJS_p += ../src/crypto/crypto_gnutls.o
SHA1OBJS += ../src/crypto/fips_prf_internal.o
endif
endif
-ifndef CONFIG_NO_WPA_PASSPHRASE
+ifdef CONFIG_NO_WPA_PASSPHRASE
+CFLAGS += -DCONFIG_NO_PBKDF2
+else
SHA1OBJS += ../src/crypto/sha1-pbkdf2.o
endif
ifdef NEED_T_PRF
endif
endif
+ifdef CONFIG_NO_RANDOM_POOL
+CFLAGS += -DCONFIG_NO_RANDOM_POOL
+else
+OBJS += ../src/crypto/random.o
+endif
+
ifdef CONFIG_CTRL_IFACE
ifeq ($(CONFIG_CTRL_IFACE), y)
ifdef CONFIG_NATIVE_WINDOWS
endif
DBUS_OBJS += dbus/dbus_dict_helpers.o
ifndef DBUS_LIBS
-DBUS_LIBS := $(shell pkg-config --libs dbus-1)
+DBUS_LIBS := $(shell $(PKG_CONFIG) --libs dbus-1)
endif
ifndef DBUS_INCLUDE
-DBUS_INCLUDE := $(shell pkg-config --cflags dbus-1)
-endif
-dbus_version=$(subst ., ,$(shell pkg-config --modversion dbus-1))
-DBUS_VERSION_MAJOR=$(word 1,$(dbus_version))
-DBUS_VERSION_MINOR=$(word 2,$(dbus_version))
-ifeq ($(DBUS_VERSION_MAJOR),)
-DBUS_VERSION_MAJOR=0
-endif
-ifeq ($(DBUS_VERSION_MINOR),)
-DBUS_VERSION_MINOR=0
+DBUS_INCLUDE := $(shell $(PKG_CONFIG) --cflags dbus-1)
endif
-DBUS_INCLUDE += -DDBUS_VERSION_MAJOR=$(DBUS_VERSION_MAJOR)
-DBUS_INCLUDE += -DDBUS_VERSION_MINOR=$(DBUS_VERSION_MINOR)
DBUS_CFLAGS += $(DBUS_INCLUDE)
endif
ifdef CONFIG_WPS
DBUS_OBJS += dbus/dbus_new_handlers_wps.o
endif
+ifdef CONFIG_P2P
+DBUS_OBJS += dbus/dbus_new_handlers_p2p.o
+endif
ifndef DBUS_LIBS
-DBUS_LIBS := $(shell pkg-config --libs dbus-1)
+DBUS_LIBS := $(shell $(PKG_CONFIG) --libs dbus-1)
endif
ifndef DBUS_INCLUDE
-DBUS_INCLUDE := $(shell pkg-config --cflags dbus-1)
+DBUS_INCLUDE := $(shell $(PKG_CONFIG) --cflags dbus-1)
endif
ifdef CONFIG_CTRL_IFACE_DBUS_INTRO
DBUS_OBJS += dbus/dbus_new_introspect.o
LIBS += $(DBUS_LIBS)
ifdef CONFIG_READLINE
-CFLAGS += -DCONFIG_READLINE
+OBJS_c += ../src/utils/edit_readline.o
LIBS_c += -lncurses -lreadline
+else
+ifdef CONFIG_WPA_CLI_EDIT
+OBJS_c += ../src/utils/edit.o
+else
+OBJS_c += ../src/utils/edit_simple.o
+endif
endif
ifdef CONFIG_NATIVE_WINDOWS
CFLAGS += -DCONFIG_SME
endif
-ifdef CONFIG_CLIENT_MLME
-OBJS += mlme.o
-CFLAGS += -DCONFIG_CLIENT_MLME
-NEED_80211_COMMON=y
-endif
-
ifdef NEED_80211_COMMON
OBJS += ../src/common/ieee802_11_common.o
endif
ifdef CONFIG_DEBUG_SYSLOG
CFLAGS += -DCONFIG_DEBUG_SYSLOG
+ifdef CONFIG_DEBUG_SYSLOG_FACILITY
+CFLAGS += -DLOG_HOSTAPD="$(CONFIG_DEBUG_SYSLOG_FACILITY)"
+endif
endif
ifdef CONFIG_DEBUG_FILE
NEED_BGSCAN=y
endif
+ifdef CONFIG_BGSCAN_LEARN
+CFLAGS += -DCONFIG_BGSCAN_LEARN
+OBJS += bgscan_learn.o
+NEED_BGSCAN=y
+endif
+
ifdef NEED_BGSCAN
CFLAGS += -DCONFIG_BGSCAN
OBJS += bgscan.o
endif
-OBJS_wpa_rm := ctrl_iface.o mlme.o ctrl_iface_unix.o
+ifdef NEED_GAS
+OBJS += ../src/common/gas.o
+OBJS += gas_query.o
+CFLAGS += -DCONFIG_GAS
+NEED_OFFCHANNEL=y
+endif
+
+ifdef NEED_OFFCHANNEL
+OBJS += offchannel.o
+CFLAGS += -DCONFIG_OFFCHANNEL
+endif
+
+OBJS += ../src/drivers/driver_common.o
+
+OBJS_wpa_rm := ctrl_iface.o ctrl_iface_unix.o
OBJS_wpa := $(filter-out $(OBJS_wpa_rm),$(OBJS)) $(OBJS_h) tests/test_wpa.o
ifdef CONFIG_AUTHENTICATOR
OBJS_wpa += tests/link_test.o
OBJS_priv += ../src/utils/wpa_debug.o
OBJS_priv += ../src/utils/wpabuf.o
OBJS_priv += wpa_priv.o
+ifdef CONFIG_DRIVER_NL80211
+OBJS_priv += ../src/common/ieee802_11_common.o
+endif
ifdef CONFIG_DRIVER_TEST
OBJS_priv += $(SHA1OBJS)
OBJS_priv += $(MD5OBJS)
LDO=$(CC)
endif
+Q=@
+E=echo
+ifeq ($(V), 1)
+Q=
+E=true
+endif
+
dynamic_eap_methods: $(EAPDYN)
../src/drivers/build.wpa_supplicant:
BCHECK=../src/drivers/build.wpa_supplicant
wpa_priv: $(BCHECK) $(OBJS_priv)
- $(LDO) $(LDFLAGS) -o wpa_priv $(OBJS_priv) $(LIBS)
+ $(Q)$(LDO) $(LDFLAGS) -o wpa_priv $(OBJS_priv) $(LIBS)
+ @$(E) " LD " $@
wpa_supplicant: .config $(BCHECK) $(OBJS) $(EXTRA_progs)
- $(LDO) $(LDFLAGS) -o wpa_supplicant $(OBJS) $(LIBS) $(EXTRALIBS)
+ $(Q)$(LDO) $(LDFLAGS) -o wpa_supplicant $(OBJS) $(LIBS) $(EXTRALIBS)
+ @$(E) " LD " $@
eapol_test: .config $(OBJS_t)
- $(LDO) $(LDFLAGS) -o eapol_test $(OBJS_t) $(LIBS)
+ $(Q)$(LDO) $(LDFLAGS) -o eapol_test $(OBJS_t) $(LIBS)
+ @$(E) " LD " $@
preauth_test: .config $(OBJS_t2)
- $(LDO) $(LDFLAGS) -o preauth_test $(OBJS_t2) $(LIBS)
+ $(Q)$(LDO) $(LDFLAGS) -o preauth_test $(OBJS_t2) $(LIBS)
+ @$(E) " LD " $@
wpa_passphrase: $(OBJS_p)
- $(LDO) $(LDFLAGS) -o wpa_passphrase $(OBJS_p) $(LIBS_p)
+ $(Q)$(LDO) $(LDFLAGS) -o wpa_passphrase $(OBJS_p) $(LIBS_p)
+ @$(E) " LD " $@
wpa_cli: $(OBJS_c)
- $(LDO) $(LDFLAGS) -o wpa_cli $(OBJS_c) $(LIBS_c)
+ $(Q)$(LDO) $(LDFLAGS) -o wpa_cli $(OBJS_c) $(LIBS_c)
+ @$(E) " LD " $@
link_test: $(OBJS) $(OBJS_h) tests/link_test.o
- $(LDO) $(LDFLAGS) -o link_test $(OBJS) $(OBJS_h) tests/link_test.o $(LIBS)
+ $(Q)$(LDO) $(LDFLAGS) -o link_test $(OBJS) $(OBJS_h) tests/link_test.o $(LIBS)
+ @$(E) " LD " $@
test_wpa: $(OBJS_wpa) $(OBJS_h)
- $(LDO) $(LDFLAGS) -o test_wpa $(OBJS_wpa) $(LIBS)
+ $(Q)$(LDO) $(LDFLAGS) -o test_wpa $(OBJS_wpa) $(LIBS)
+ @$(E) " LD " $@
win_if_list: win_if_list.c
- $(LDO) $(LDFLAGS) -o $@ win_if_list.c $(CFLAGS) $(LIBS_w)
+ $(Q)$(LDO) $(LDFLAGS) -o $@ win_if_list.c $(CFLAGS) $(LIBS_w)
+ @$(E) " LD " $@
eap_psk.so: ../src/eap_peer/eap_psk.c ../src/eap_common/eap_psk_common.c
$(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \
$(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $< \
-D$(*F:eap_%=eap_peer_%)_register=eap_peer_method_dynamic_init
-Q=@
-E=echo
-ifeq ($(V), 1)
-Q=
-E=true
-endif
-
%.o: %.c
$(Q)$(CC) -c -o $@ $(CFLAGS) $<
@$(E) " CC " $<
+%.service: %.service.in
+ sed -e 's|\@BINDIR\@|$(BINDIR)|g' $< >$@
+
wpa_supplicant.exe: wpa_supplicant
mv -f $< $@
wpa_cli.exe: wpa_cli
WPA Supplicant
==============
-Copyright (c) 2003-2010, Jouni Malinen <j@w1.fi> and contributors
+Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi> and contributors
All Rights Reserved.
This program is dual-licensed under both the GPL version 2 and BSD
Linuxant DriverLoader (http://www.linuxant.com/driverloader/)
with Windows NDIS driver for your wlan card supporting WPA.
- Agere Systems Inc. Linux Driver
- (http://www.agere.com/support/drivers/)
- Please note that the driver interface file (driver_hermes.c) and
- hardware specific include files are not included in the
- wpa_supplicant distribution. You will need to copy these from the
- source package of the Agere driver.
-
madwifi driver for cards based on Atheros chip set (ar521x)
(http://sourceforge.net/projects/madwifi/)
Please note that you will need to modify the wpa_supplicant .config
file to use the correct path for the madwifi driver root directory
(CFLAGS += -I../madwifi/wpa line in example defconfig).
- ATMEL AT76C5XXx driver for USB and PCMCIA cards
- (http://atmelwlandriver.sourceforge.net/).
-
Linux ndiswrapper (http://ndiswrapper.sourceforge.net/) with
Windows NDIS driver.
not need (or even work) with the specific driver wrapper. Use -Dwext
with that driver.
- Intel ipw2100 driver
- (http://sourceforge.net/projects/ipw2100/)
-
- Intel ipw2200 driver
- (http://sourceforge.net/projects/ipw2200/)
-
In theory, any driver that supports Linux wireless extensions can be
used with IEEE 802.1X (i.e., not WPA) when using ap_scan=0 option in
configuration file.
CONFIG_PCSC=y
Following options can be added to .config to select which driver
-interfaces are included. Hermes driver interface needs to be downloaded
-from Agere (see above).
+interfaces are included.
CONFIG_DRIVER_HOSTAP=y
-CONFIG_DRIVER_HERMES=y
CONFIG_DRIVER_MADWIFI=y
-CONFIG_DRIVER_ATMEL=y
CONFIG_DRIVER_WEXT=y
CONFIG_DRIVER_RALINK=y
-CONFIG_DRIVER_NDISWRAPPER=y
CONFIG_DRIVER_BROADCOM=y
-CONFIG_DRIVER_IPW=y
CONFIG_DRIVER_BSD=y
CONFIG_DRIVER_NDIS=y
included in the wpa_supplicant package:
CONFIG_DRIVER_HOSTAP=y
-CONFIG_DRIVER_HERMES=y
CONFIG_DRIVER_MADWIFI=y
-CONFIG_DRIVER_ATMEL=y
CONFIG_DRIVER_WEXT=y
-CONFIG_DRIVER_NDISWRAPPER=y
CONFIG_DRIVER_BROADCOM=y
-CONFIG_DRIVER_IPW=y
CONFIG_DRIVER_BSD=y
CONFIG_DRIVER_NDIS=y
CONFIG_IEEE8021X_EAPOL=y
drivers:
hostap = Host AP driver (Intersil Prism2/2.5/3) [default]
(this can also be used with Linuxant DriverLoader)
- hermes = Agere Systems Inc. driver (Hermes-I/Hermes-II)
madwifi = MADWIFI 802.11 support (Atheros, etc.) (deprecated; use wext)
- atmel = ATMEL AT76C5XXx (USB, PCMCIA)
wext = Linux wireless extensions (generic)
ralink = Ralink Client driver
- ndiswrapper = Linux ndiswrapper (deprecated; use wext)
broadcom = Broadcom wl.o driver
- ipw = Intel ipw2100/2200 driver (old; use wext with Linux 2.6.13 or newer)
wired = wpa_supplicant wired Ethernet driver
roboswitch = wpa_supplicant Broadcom switch driver
bsd = BSD 802.11 support (Atheros, etc.)
IFNAME=$1
CMD=$2
-if [ "$CMD" == "CONNECTED" ]; then
+if [ "$CMD" = "CONNECTED" ]; then
SSID=`wpa_cli -i$IFNAME status | grep ^ssid= | cut -f2- -d=`
# configure network, signal DHCP client, etc.
fi
-if [ "$CMD" == "DISCONNECTED" ]; then
+if [ "$CMD" = "DISCONNECTED" ]; then
# remove network configuration, if needed
+ SSID=
fi
--- /dev/null
+wpa_supplicant and Wi-Fi P2P
+============================
+
+This document describes how the Wi-Fi P2P implementation in
+wpa_supplicant can be configured and how an external component on the
+client (e.g., management GUI) is used to enable WPS enrollment and
+registrar registration.
+
+
+Introduction to Wi-Fi P2P
+-------------------------
+
+TODO
+
+More information about Wi-Fi P2P is available from Wi-Fi Alliance:
+http://www.wi-fi.org/Wi-Fi_Direct.php
+
+
+wpa_supplicant implementation
+-----------------------------
+
+TODO
+
+
+wpa_supplicant configuration
+----------------------------
+
+Wi-Fi P2P is an optional component that needs to be enabled in the
+wpa_supplicant build configuration (.config). Here is an example
+configuration that includes Wi-Fi P2P support and Linux nl80211
+-based driver interface:
+
+CONFIG_DRIVER_NL80211=y
+CONFIG_CTRL_IFACE=y
+CONFIG_P2P=y
+CONFIG_AP=y
+CONFIG_WPS=y
+
+
+In run-time configuration file (wpa_supplicant.conf), some parameters
+for P2P may be set. In order to make the devices easier to recognize,
+device_name and device_type should be specified. For example,
+something like this should be included:
+
+ctrl_interface=/var/run/wpa_supplicant
+device_name=My P2P Device
+device_type=1-0050F204-1
+
+
+wpa_cli
+-------
+
+Actual Wi-Fi P2P operations are requested during runtime. These can be
+done for example using wpa_cli (which is described below) or a GUI
+like wpa_gui-qt4.
+
+
+wpa_cli starts in interactive mode if no command string is included on
+the command line. By default, it will select the first network interface
+that it can find (and that wpa_supplicant controls). If more than one
+interface is in use, it may be necessary to select one of the explicitly
+by adding -i argument on the command line (e.g., 'wpa_cli -i wlan1').
+
+Most of the P2P operations are done on the main interface (e.g., the
+interface that is automatically added when the driver is loaded, e.g.,
+wlan0). When using a separate virtual interface for group operations
+(e.g., wlan1), the control interface for that group interface may need
+to be used for some operations (mainly WPS activation in GO). This may
+change in the future so that all the needed operations could be done
+over the main control interface.
+
+Device Discovery
+
+p2p_find [timeout in seconds] [type=<social|progressive>]
+
+The default behavior is to run a single full scan in the beginning and
+then scan only social channels. type=social will scan only social
+channels, i.e., it skips the initial full scan. type=progressive is
+like the default behavior, but it will scan through all the channels
+progressively one channel at the time in the Search state rounds. This
+will help in finding new groups or groups missed during the initial
+full scan.
+
+p2p_listen [timeout in seconds]
+
+Start Listen-only state (become discoverable without searching for
+other devices). Optional parameter can be used to specify the duration
+for the Listen operation in seconds. This command may not be of that
+much use during normal operations and is mainly designed for
+testing. It can also be used to keep the device discoverable without
+having to maintain a group.
+
+p2p_stop_find
+
+Stop ongoing P2P device discovery or other operation (connect, listen
+mode).
+
+p2p_flush
+
+Flush P2P peer table and state.
+
+Group Formation
+
+p2p_prov_disc <peer device address> <display|keypad|pbc> [join]
+
+Send P2P provision discovery request to the specified peer. The
+parameters for this command are the P2P device address of the peer and
+the desired configuration method. For example, "p2p_prov_disc
+02:01:02:03:04:05 display" would request the peer to display a PIN for
+us and "p2p_prov_disc 02:01:02:03:04:05 keypad" would request the peer
+to enter a PIN that we display.
+
+The optional "join" parameter can be used to indicate that this command
+is requesting an already running GO to prepare for a new client. This is
+mainly used with "display" to request it to display a PIN.
+
+p2p_connect <peer device address> <pbc|pin|PIN#> [display|keypad]
+ [persistent] [join|auth] [go_intent=<0..15>] [freq=<in MHz>]
+
+Start P2P group formation with a discovered P2P peer. This includes
+optional group owner negotiation, group interface setup, provisioning,
+and establishing data connection.
+
+The <pbc|pin|PIN#> parameter specifies the WPS provisioning
+method. "pbc" string starts pushbutton method, "pin" string start PIN
+method using an automatically generated PIN (which will be returned as
+the command return code), PIN# means that a pre-selected PIN can be
+used (e.g., 12345670). [display|keypad] is used with PIN method
+to specify which PIN is used (display=dynamically generated random PIN
+from local display, keypad=PIN entered from peer display). "persistent"
+parameter can be used to request a persistent group to be formed.
+
+"join" indicates that this is a command to join an existing group as a
+client. It skips the GO Negotiation part. This will send a Provision
+Discovery Request message to the target GO before associating for WPS
+provisioning.
+
+"auth" indicates that the WPS parameters are authorized for the peer
+device without actually starting GO Negotiation (i.e., the peer is
+expected to initiate GO Negotiation). This is mainly for testing
+purposes.
+
+"go_intent" can be used to override the default GO Intent for this GO
+Negotiation.
+
+"freq" can be used to set a forced operating channel (e.g., freq=2412
+to select 2.4 GHz channel 1).
+
+p2p_group_add [persistent|persistent=<network id>] [freq=<freq in MHz>]
+
+Set up a P2P group owner manually (i.e., without group owner
+negotiation with a specific peer). This is also known as autonomous
+GO. Optional persistent=<network id> can be used to specify restart of
+a persistent group. Optional freq=<freq in MHz> can be used to force
+the GO to be started on a specific frequency. Special freq=2 or freq=5
+options can be used to request the best 2.4 GHz or 5 GHz band channel
+to be selected automatically.
+
+p2p_reject <peer device address>
+
+Reject connection attempt from a peer (specified with a device
+address). This is a mechanism to reject a pending GO Negotiation with
+a peer and request to automatically block any further connection or
+discovery of the peer.
+
+p2p_group_remove <group interface>
+
+Terminate a P2P group. If a new virtual network interface was used for
+the group, it will also be removed. The network interface name of the
+group interface is used as a parameter for this command.
+
+p2p_cancel
+
+Cancel an ongoing P2P group formation related operation.
+
+Service Discovery
+
+p2p_serv_disc_req
+
+Schedule a P2P service discovery request. The parameters for this
+command are the device address of the peer device (or 00:00:00:00:00:00
+for wildcard query that is sent to every discovered P2P peer that
+supports service discovery) and P2P Service Query TLV(s) as hexdump. For
+example,
+
+p2p_serv_disc_req 00:00:00:00:00:00 02000001
+
+schedules a request for listing all available services of all service
+discovery protocols and requests this to be sent to all discovered
+peers (note: this can result in long response frames). The pending
+requests are sent during device discovery (see p2p_find).
+
+Only a single pending wildcard query is supported, but there can be
+multiple pending peer device specific queries (each will be sent in
+sequence whenever the peer is found).
+
+This command returns an identifier for the pending query (e.g.,
+"1f77628") that can be used to cancel the request. Directed requests
+will be automatically removed when the specified peer has replied to
+it.
+
+For UPnP, an alternative command format can be used to specify a
+single query TLV (i.e., a service discovery for a specific UPnP
+service):
+
+p2p_serv_disc_req 00:00:00:00:00:00 upnp <version hex> <ST: from M-SEARCH>
+
+For example:
+
+p2p_serv_disc_req 00:00:00:00:00:00 upnp 10 urn:schemas-upnp-org:device:InternetGatewayDevice:1
+
+Additional examples for queries:
+
+# list of all Bonjour services
+p2p_serv_disc_req 00:00:00:00:00:00 02000101
+
+# list of all UPnP services
+p2p_serv_disc_req 00:00:00:00:00:00 02000201
+
+# list of all WS-Discovery services
+p2p_serv_disc_req 00:00:00:00:00:00 02000301
+
+# list of all Bonjour and UPnP services
+p2p_serv_disc_req 00:00:00:00:00:00 0200010102000202
+
+# Apple File Sharing over TCP
+p2p_serv_disc_req 00:00:00:00:00:00 130001010b5f6166706f766572746370c00c000c01
+
+# Bonjour SSTH (supported service type hash)
+p2p_serv_disc_req 00:00:00:00:00:00 05000101000000
+
+# UPnP examples
+p2p_serv_disc_req 00:00:00:00:00:00 upnp 10 ssdp:all
+p2p_serv_disc_req 00:00:00:00:00:00 upnp 10 upnp:rootdevice
+p2p_serv_disc_req 00:00:00:00:00:00 upnp 10 urn:schemas-upnp-org:service:ContentDirectory:2
+p2p_serv_disc_req 00:00:00:00:00:00 upnp 10 uuid:6859dede-8574-59ab-9332-123456789012
+p2p_serv_disc_req 00:00:00:00:00:00 upnp 10 urn:schemas-upnp-org:device:InternetGatewayDevice:1
+
+p2p_serv_disc_cancel_req <query identifier>
+
+Cancel a pending P2P service discovery request. This command takes a
+single parameter: identifier for the pending query (the value returned
+by p2p_serv_disc_req, e.g., "p2p_serv_disc_cancel_req 1f77628".
+
+p2p_serv_disc_resp
+
+Reply to a service discovery query. This command takes following
+parameters: frequency in MHz, destination address, dialog token,
+response TLV(s). The first three parameters are copied from the
+request event. For example, "p2p_serv_disc_resp 2437 02:40:61:c2:f3:b7
+1 0300000101". This command is used only if external program is used
+to process the request (see p2p_serv_disc_external).
+
+p2p_service_update
+
+Indicate that local services have changed. This is used to increment
+the P2P service indicator value so that peers know when previously
+cached information may have changed. This is only needed when external
+service discovery processing is enabled since the commands to
+pre-configure services for internal processing will increment the
+indicator automatically.
+
+p2p_serv_disc_external <0|1>
+
+Configure external processing of P2P service requests: 0 (default) =
+no external processing of requests (i.e., internal code will process
+each request based on pre-configured services), 1 = external
+processing of requests (external program is responsible for replying
+to service discovery requests with p2p_serv_disc_resp). Please note
+that there is quite strict limit on how quickly the response needs to
+be transmitted, so use of the internal processing is strongly
+recommended.
+
+p2p_service_add bonjour <query hexdump> <RDATA hexdump>
+
+Add a local Bonjour service for internal SD query processing.
+
+Examples:
+
+# AFP Over TCP (PTR)
+p2p_service_add bonjour 0b5f6166706f766572746370c00c000c01 074578616d706c65c027
+# AFP Over TCP (TXT) (RDATA=null)
+p2p_service_add bonjour 076578616d706c650b5f6166706f766572746370c00c001001 00
+
+# IP Printing over TCP (PTR) (RDATA=MyPrinter._ipp._tcp.local.)
+p2p_service_add bonjour 045f697070c00c000c01 094d795072696e746572c027
+# IP Printing over TCP (TXT) (RDATA=txtvers=1,pdl=application/postscript)
+p2p_service_add bonjour 096d797072696e746572045f697070c00c001001 09747874766572733d311a70646c3d6170706c69636174696f6e2f706f7374736372797074
+
+# Supported Service Type Hash (SSTH)
+p2p_service_add bonjour 000000 <32-byte bitfield as hexdump>
+(note: see P2P spec Annex E.4 for information on how to construct the bitfield)
+
+p2p_service_del bonjour <query hexdump>
+
+Remove a local Bonjour service from internal SD query processing.
+
+p2p_service_add upnp <version hex> <service>
+
+Add a local UPnP service for internal SD query processing.
+
+Examples:
+
+p2p_service_add upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::upnp:rootdevice
+p2p_service_add upnp 10 uuid:5566d33e-9774-09ab-4822-333456785632::upnp:rootdevice
+p2p_service_add upnp 10 uuid:1122de4e-8574-59ab-9322-333456789044::urn:schemas-upnp-org:service:ContentDirectory:2
+p2p_service_add upnp 10 uuid:5566d33e-9774-09ab-4822-333456785632::urn:schemas-upnp-org:service:ContentDirectory:2
+p2p_service_add upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::urn:schemas-upnp-org:device:InternetGatewayDevice:1
+
+p2p_service_del upnp <version hex> <service>
+
+Remove a local UPnP service from internal SD query processing.
+
+p2p_service_flush
+
+Remove all local services from internal SD query processing.
+
+Invitation
+
+p2p_invite [persistent=<network id>|group=<group ifname>] [peer=address]
+ [go_dev_addr=address]
+
+Invite a peer to join a group (e.g., group=wlan1) or to reinvoke a
+persistent group (e.g., persistent=4). If the peer device is the GO of
+the persisten group, the peer parameter is not needed. Otherwise it is
+used to specify which device to invite. go_dev_addr parameter can be
+used to override the GO device address for Invitation Request should
+it be not known for some reason (this should not be needed in most
+cases).
+
+Group Operations
+
+(These are used on the group interface.)
+
+wps_pin <any|address> <PIN>
+
+Start WPS PIN method. This allows a single WPS Enrollee to connect to
+the AP/GO. This is used on the GO when a P2P client joins an existing
+group. The second parameter is the address of the Enrollee or a string
+"any" to allow any station to use the entered PIN (which will restrict
+the PIN for one-time-use). PIN is the Enrollee PIN read either from a
+label or display on the P2P Client/WPS Enrollee.
+
+wps_pbc
+
+Start WPS PBC method (i.e., push the button). This allows a single WPS
+Enrollee to connect to the AP/GO. This is used on the GO when a P2P
+client joins an existing group.
+
+p2p_get_passphrase
+
+Get the passphrase for a group (only available when acting as a GO).
+
+p2p_presence_req [<duration> <interval>] [<duration> <interval>]
+
+Send a P2P Presence Request to the GO (this is only available when
+acting as a P2P client). If no duration/interval pairs are given, the
+request indicates that this client has no special needs for GO
+presence. the first parameter pair gives the preferred duration and
+interval values in microseconds. If the second pair is included, that
+indicates which value would be acceptable.
+
+Parameters
+
+p2p_ext_listen [<period> <interval>]
+
+Configure Extended Listen Timing. If the parameters are omitted, this
+feature is disabled. If the parameters are included, Listen State will
+be entered every interval msec for at least period msec. Both values
+have acceptable range of 1-65535 (with interval obviously having to be
+larger than or equal to duration). If the P2P module is not idle at
+the time the Extended Listen Timing timeout occurs, the Listen State
+operation will be skipped.
+
+The configured values will also be advertised to other P2P Devices. The
+received values are available in the p2p_peer command output:
+
+ext_listen_period=100 ext_listen_interval=5000
+
+p2p_set <field> <value>
+
+Change dynamic P2P parameters
+
+p2p_set discoverability <0/1>
+
+Disable/enable advertisement of client discoverability. This is
+enabled by default and this parameter is mainly used to allow testing
+of device discoverability.
+
+p2p_set managed <0/1>
+
+Disable/enable managed P2P Device operations. This is disabled by
+default.
+
+p2p_set listen_channel <1/6/11>
+
+Set P2P Listen channel. This is mainly meant for testing purposes and
+changing the Listen channel during normal operations can result in
+protocol failures.
+
+p2p_set ssid_postfix <postfix>
+
+Set postfix string to be added to the automatically generated P2P SSID
+(DIRECT-<two random characters>). For example, postfix of "-testing"
+could result in the SSID becoming DIRECT-ab-testing.
+
+set <field> <value>
+
+Set global configuration parameters which may also affect P2P
+operations. The format on these parameters is same as is used in
+wpa_supplicant.conf. Only the parameters listen here should be
+changed. Modifying other parameters may result in incorrect behavior
+since not all existing users of the parameters are updated.
+
+set uuid <UUID>
+
+Set WPS UUID (by default, this is generated based on the MAC address).
+
+set device_name <device name>
+
+Set WPS Device Name (also included in some P2P messages).
+
+set manufacturer <manufacturer>
+
+Set WPS Manufacturer.
+
+set model_name <model name>
+
+Set WPS Model Name.
+
+set model_number <model number>
+
+Set WPS Model Number.
+
+set serial_number <serial number>
+
+Set WPS Serial Number.
+
+set device_type <device type>
+
+Set WPS Device Type.
+
+set os_version <OS version>
+
+Set WPS OS Version.
+
+set config_methods <config methods>
+
+Set WPS Configuration Methods.
+
+set sec_device_type <device type>
+
+Add a new Secondary Device Type.
+
+set p2p_go_intent <GO intent>
+
+Set the default P2P GO Intent. Note: This value can be overridden in
+p2p_connect command and as such, there should be no need to change the
+default value here during normal operations.
+
+set p2p_ssid_postfix <P2P SSID postfix>
+
+Set P2P SSID postfix.
+
+set persistent_reconnect <0/1>
+
+Disable/enabled persistent reconnect for reinvocation of persistent
+groups. If enabled, invitations to reinvoke a persistent group will be
+accepted without separate authorization (e.g., user interaction).
+
+set country <two character country code>
+
+Set country code (this is included in some P2P messages).
+
+Status
+
+p2p_peers [discovered]
+
+List P2P Device Addresses of all the P2P peers we know. The optional
+"discovered" parameter filters out the peers that we have not fully
+discovered, i.e., which we have only seen in a received Probe Request
+frame.
+
+p2p_peer <P2P Device Address>
+
+Fetch information about a known P2P peer.
+
+Group Status
+
+(These are used on the group interface.)
+
+status
+
+Show status information (connection state, role, use encryption
+parameters, IP address, etc.).
+
+sta
+
+Show information about an associated station (when acting in AP/GO role).
+
+all_sta
+
+Lists the currently associated stations.
+
+Configuration data
+
+list_networks
+
+Lists the configured networks, including stored information for
+persistent groups. The identifier in this list is used with
+p2p_group_add and p2p_invite to indicate which persistent group is to
+be reinvoked.
+
+remove_network <network id>
+
+Remove a network entry from configuration.
+
+
+wpa_cli action script
+---------------------
+
+See examples/p2p-action.sh
+
+TODO: describe DHCP/DNS setup
+TODO: cross-connection
wpa_supplicant includes an optional WPS component that can be used as
an Enrollee to enroll new network credential or as a Registrar to
-configure an AP. The current version of wpa_supplicant does not
-support operation as an external WLAN Management Registrar for adding
-new client devices or configuring the AP over UPnP.
+configure an AP.
wpa_supplicant configuration
WPS is an optional component that needs to be enabled in
wpa_supplicant build configuration (.config). Here is an example
-configuration that includes WPS support and Linux wireless extensions
--based driver interface:
+configuration that includes WPS support and Linux nl80211 -based
+driver interface:
-CONFIG_DRIVER_WEXT=y
+CONFIG_DRIVER_NL80211=y
CONFIG_WPS=y
+CONFIG_WPS2=y
+
+If you want to enable WPS external registrar (ER) functionality, you
+will also need to add following line:
+
+CONFIG_WPS_ER=y
WPS needs the Universally Unique IDentifier (UUID; see RFC 4122) for
network. wpa_supplicant uses the control interface as an input channel
for these events.
+The PIN value used in the commands must be processed by an UI to
+remove non-digit characters and potentially, to verify the checksum
+digit. "wpa_cli wps_check_pin <PIN>" can be used to do such processing.
+It returns FAIL if the PIN is invalid, or FAIL-CHECKSUM if the checksum
+digit is incorrect, or the processed PIN (non-digit characters removed)
+if the PIN is valid.
+
If the client device has a display, a random PIN has to be generated
for each WPS registration session. wpa_supplicant can do this with a
control interface request, e.g., by calling wpa_cli:
generated PIN.
+If a random PIN is needed for a user interface, "wpa_cli wps_pin get"
+can be used to generate a new PIN without starting WPS negotiation.
+This random PIN can then be passed as an argument to another wps_pin
+call when the actual operation should be started.
+
If the client design wants to support optional WPS PBC mode, this can
be enabled by either a physical button in the client device or a
virtual button in the user interface. The PBC operation requires that
WPS-CRED-RECEIVED <hexdump of Credential attribute(s)>
For example:
<2>WPS-CRED-RECEIVED 100e006f10260001011045000c6a6b6d2d7770732d74657374100300020020100f000200081027004030653462303435366332363666653064333961643135353461316634626637313234333761636664623766333939653534663166316230323061643434386235102000060266a0ee1727
+
+
+wpa_supplicant as WPS External Registrar (ER)
+---------------------------------------------
+
+wpa_supplicant can be used as a WPS ER to configure an AP or enroll
+new Enrollee to join the network. This functionality uses UPnP and
+requires that a working IP connectivity is available with the AP (this
+can be either over a wired or wireless connection).
+
+Separate wpa_supplicant process can be started for WPS ER
+operations. A special "none" driver can be used in such a case to
+indicate that no local network interface is actually controlled. For
+example, following command could be used to start the ER:
+
+wpa_supplicant -Dnone -c er.conf -ieth0
+
+Sample er.conf:
+
+ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=admin
+device_name=WPS External Registrar
+
+
+wpa_cli commands for ER functionality:
+
+wps_er_start [IP address]
+- start WPS ER functionality
+- the optional IP address parameter can be used to filter operations only
+ to include a single AP
+- if run again while ER is active, the stored information (discovered APs
+ and Enrollees) are shown again
+
+wps_er_stop
+- stop WPS ER functionality
+
+wps_er_learn <UUID> <AP PIN>
+- learn AP configuration
+
+wps_er_set_config <UUID> <network id>
+- use AP configuration from a locally configured network (e.g., from
+ wps_reg command); this does not change the AP's configuration, but
+ only prepares a configuration to be used when enrolling a new device
+ to the AP
+
+wps_er_config <UUID> <AP PIN> <new SSID> <auth> <encr> <new key>
+- examples:
+ wps_er_config 87654321-9abc-def0-1234-56789abc0002 12345670 testing WPA2PSK CCMP 12345678
+ wpa_er_config 87654321-9abc-def0-1234-56789abc0002 12345670 clear OPEN NONE ""
+
+<auth> must be one of the following: OPEN WPAPSK WPA2PSK
+<encr> must be one of the following: NONE WEP TKIP CCMP
+
+
+wps_er_pbc <Enrollee UUID>
+- accept an Enrollee PBC using External Registrar
+
+wps_er_pin <Enrollee UUID> <PIN> [Enrollee MAC address]
+- add an Enrollee PIN to External Registrar
+- if Enrollee UUID is not known, "any" can be used to add a wildcard PIN
+- if the MAC address of the enrollee is known, it should be configured
+ to allow the AP to advertise list of authorized enrollees
+
+
+WPS ER events:
+
+WPS_EVENT_ER_AP_ADD
+- WPS ER discovered an AP
+
+WPS-ER-AP-ADD 87654321-9abc-def0-1234-56789abc0002 02:11:22:33:44:55 pri_dev_type=6-0050F204-1 wps_state=1 |Very friendly name|Company|Long description of the model|WAP|http://w1.fi/|http://w1.fi/hostapd/
+
+WPS_EVENT_ER_AP_REMOVE
+- WPS ER removed an AP entry
+
+WPS-ER-AP-REMOVE 87654321-9abc-def0-1234-56789abc0002
+
+WPS_EVENT_ER_ENROLLEE_ADD
+- WPS ER discovered a new Enrollee
+
+WPS-ER-ENROLLEE-ADD 2b7093f1-d6fb-5108-adbb-bea66bb87333 02:66:a0:ee:17:27 M1=1 config_methods=0x14d dev_passwd_id=0 pri_dev_type=1-0050F204-1 |Wireless Client|Company|cmodel|123|12345|
+
+WPS_EVENT_ER_ENROLLEE_REMOVE
+- WPS ER removed an Enrollee entry
+
+WPS-ER-ENROLLEE-REMOVE 2b7093f1-d6fb-5108-adbb-bea66bb87333 02:66:a0:ee:17:27
+
+WPS-ER-AP-SETTINGS
+- WPS ER learned AP settings
+
+WPS-ER-AP-SETTINGS uuid=fd91b4ec-e3fa-5891-a57d-8c59efeed1d2 ssid=test-wps auth_type=0x0020 encr_type=0x0008 key=12345678
#include "utils/includes.h"
#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/uuid.h"
#include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
#include "ap/hostapd.h"
#include "ap/ap_config.h"
+#include "ap/ap_drv_ops.h"
#ifdef NEED_AP_MLME
#include "ap/ieee802_11.h"
#endif /* NEED_AP_MLME */
+#include "ap/beacon.h"
#include "ap/ieee802_1x.h"
#include "ap/wps_hostapd.h"
#include "ap/ctrl_iface_ap.h"
#include "eap_server/eap_methods.h"
#include "eap_common/eap_wsc_common.h"
#include "wps/wps.h"
+#include "common/ieee802_11_defs.h"
#include "config_ssid.h"
#include "config.h"
#include "wpa_supplicant_i.h"
#include "driver_i.h"
+#include "p2p_supplicant.h"
#include "ap.h"
+#include "ap/sta_info.h"
+#include "notify.h"
+
+
+#ifdef CONFIG_WPS
+static void wpas_wps_ap_pin_timeout(void *eloop_data, void *user_ctx);
+#endif /* CONFIG_WPS */
static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s,
return -1;
}
- /* TODO: enable HT if driver supports it;
+ /* TODO: enable HT40 if driver supports it;
* drop to 11b if driver does not support 11g */
+#ifdef CONFIG_IEEE80211N
+ /*
+ * Enable HT20 if the driver supports it, by setting conf->ieee80211n.
+ * Using default config settings for: conf->ht_op_mode_fixed,
+ * conf->ht_capab, conf->secondary_channel, conf->require_ht
+ */
+ if (wpa_s->hw.modes) {
+ struct hostapd_hw_modes *mode = NULL;
+ int i;
+ for (i = 0; i < wpa_s->hw.num_modes; i++) {
+ if (wpa_s->hw.modes[i].mode == conf->hw_mode) {
+ mode = &wpa_s->hw.modes[i];
+ break;
+ }
+ }
+ if (mode && mode->ht_capab)
+ conf->ieee80211n = 1;
+ }
+#endif /* CONFIG_IEEE80211N */
+
+#ifdef CONFIG_P2P
+ if (conf->hw_mode == HOSTAPD_MODE_IEEE80211G) {
+ /* Remove 802.11b rates from supported and basic rate sets */
+ int *list = os_malloc(4 * sizeof(int));
+ if (list) {
+ list[0] = 60;
+ list[1] = 120;
+ list[2] = 240;
+ list[3] = -1;
+ }
+ conf->basic_rates = list;
+
+ list = os_malloc(9 * sizeof(int));
+ if (list) {
+ list[0] = 60;
+ list[1] = 90;
+ list[2] = 120;
+ list[3] = 180;
+ list[4] = 240;
+ list[5] = 360;
+ list[6] = 480;
+ list[7] = 540;
+ list[8] = -1;
+ }
+ conf->supported_rates = list;
+ }
+
+ bss->isolate = !wpa_s->conf->p2p_intra_bss;
+#endif /* CONFIG_P2P */
+
if (ssid->ssid_len == 0) {
wpa_printf(MSG_ERROR, "No SSID configured for AP mode");
return -1;
bss->ssid.ssid_len = ssid->ssid_len;
bss->ssid.ssid_set = 1;
+ if (ssid->auth_alg)
+ bss->auth_algs = ssid->auth_alg;
+
if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt))
bss->wpa = ssid->proto;
bss->wpa_key_mgmt = ssid->key_mgmt;
return -1;
os_memcpy(bss->ssid.wpa_psk->psk, ssid->psk, PMK_LEN);
bss->ssid.wpa_psk->group = 1;
+ } else if (ssid->wep_key_len[0] || ssid->wep_key_len[1] ||
+ ssid->wep_key_len[2] || ssid->wep_key_len[3]) {
+ struct hostapd_wep_keys *wep = &bss->ssid.wep;
+ int i;
+ for (i = 0; i < NUM_WEP_KEYS; i++) {
+ if (ssid->wep_key_len[i] == 0)
+ continue;
+ wep->key[i] = os_malloc(ssid->wep_key_len[i]);
+ if (wep->key[i] == NULL)
+ return -1;
+ os_memcpy(wep->key[i], ssid->wep_key[i],
+ ssid->wep_key_len[i]);
+ wep->len[i] = ssid->wep_key_len[i];
+ }
+ wep->idx = ssid->wep_tx_keyidx;
+ wep->keys_set = 1;
}
/* Select group cipher based on the enabled pairwise cipher suites */
else if (bss->wpa)
bss->ssid.security_policy = SECURITY_WPA_PSK;
else if (bss->ieee802_1x) {
+ int cipher = WPA_CIPHER_NONE;
bss->ssid.security_policy = SECURITY_IEEE_802_1X;
bss->ssid.wep.default_len = bss->default_wep_key_len;
- } else if (bss->ssid.wep.keys_set)
+ if (bss->default_wep_key_len)
+ cipher = bss->default_wep_key_len >= 13 ?
+ WPA_CIPHER_WEP104 : WPA_CIPHER_WEP40;
+ bss->wpa_group = cipher;
+ bss->wpa_pairwise = cipher;
+ bss->rsn_pairwise = cipher;
+ } else if (bss->ssid.wep.keys_set) {
+ int cipher = WPA_CIPHER_WEP40;
+ if (bss->ssid.wep.len[0] >= 13)
+ cipher = WPA_CIPHER_WEP104;
bss->ssid.security_policy = SECURITY_STATIC_WEP;
- else
+ bss->wpa_group = cipher;
+ bss->wpa_pairwise = cipher;
+ bss->rsn_pairwise = cipher;
+ } else {
bss->ssid.security_policy = SECURITY_PLAINTEXT;
+ bss->wpa_group = WPA_CIPHER_NONE;
+ bss->wpa_pairwise = WPA_CIPHER_NONE;
+ bss->rsn_pairwise = WPA_CIPHER_NONE;
+ }
#ifdef CONFIG_WPS
/*
- * Enable WPS by default, but require user interaction to actually use
- * it. Only the internal Registrar is supported.
+ * Enable WPS by default for open and WPA/WPA2-Personal network, but
+ * require user interaction to actually use it. Only the internal
+ * Registrar is supported.
*/
+ if (bss->ssid.security_policy != SECURITY_WPA_PSK &&
+ bss->ssid.security_policy != SECURITY_PLAINTEXT)
+ goto no_wps;
+#ifdef CONFIG_WPS2
+ if (bss->ssid.security_policy == SECURITY_WPA_PSK &&
+ (!(pairwise & WPA_CIPHER_CCMP) || !(bss->wpa & 2)))
+ goto no_wps; /* WPS2 does not allow WPA/TKIP-only
+ * configuration */
+#endif /* CONFIG_WPS2 */
bss->eap_server = 1;
bss->wps_state = 2;
- bss->ap_setup_locked = 1;
+ bss->ap_setup_locked = 2;
if (wpa_s->conf->config_methods)
bss->config_methods = os_strdup(wpa_s->conf->config_methods);
- if (wpa_s->conf->device_type)
- bss->device_type = os_strdup(wpa_s->conf->device_type);
+ os_memcpy(bss->device_type, wpa_s->conf->device_type,
+ WPS_DEV_TYPE_LEN);
+ if (wpa_s->conf->device_name) {
+ bss->device_name = os_strdup(wpa_s->conf->device_name);
+ bss->friendly_name = os_strdup(wpa_s->conf->device_name);
+ }
+ if (wpa_s->conf->manufacturer)
+ bss->manufacturer = os_strdup(wpa_s->conf->manufacturer);
+ if (wpa_s->conf->model_name)
+ bss->model_name = os_strdup(wpa_s->conf->model_name);
+ if (wpa_s->conf->model_number)
+ bss->model_number = os_strdup(wpa_s->conf->model_number);
+ if (wpa_s->conf->serial_number)
+ bss->serial_number = os_strdup(wpa_s->conf->serial_number);
+ if (is_nil_uuid(wpa_s->conf->uuid))
+ os_memcpy(bss->uuid, wpa_s->wps->uuid, WPS_UUID_LEN);
+ else
+ os_memcpy(bss->uuid, wpa_s->conf->uuid, WPS_UUID_LEN);
+ os_memcpy(bss->os_version, wpa_s->conf->os_version, 4);
+no_wps:
#endif /* CONFIG_WPS */
+ if (wpa_s->max_stations &&
+ wpa_s->max_stations < wpa_s->conf->max_num_sta)
+ bss->max_num_sta = wpa_s->max_stations;
+ else
+ bss->max_num_sta = wpa_s->conf->max_num_sta;
+
+ bss->disassoc_low_ack = wpa_s->conf->disassoc_low_ack;
+
return 0;
}
static void ap_public_action_rx(void *ctx, const u8 *buf, size_t len, int freq)
{
+#ifdef CONFIG_P2P
+ struct wpa_supplicant *wpa_s = ctx;
+ const struct ieee80211_mgmt *mgmt;
+ size_t hdr_len;
+
+ mgmt = (const struct ieee80211_mgmt *) buf;
+ hdr_len = (const u8 *) &mgmt->u.action.u.vs_public_action.action - buf;
+ if (hdr_len > len)
+ return;
+ wpas_p2p_rx_action(wpa_s, mgmt->da, mgmt->sa, mgmt->bssid,
+ mgmt->u.action.category,
+ &mgmt->u.action.u.vs_public_action.action,
+ len - hdr_len, freq);
+#endif /* CONFIG_P2P */
+}
+
+
+static void ap_wps_event_cb(void *ctx, enum wps_event event,
+ union wps_event_data *data)
+{
+#ifdef CONFIG_P2P
+ struct wpa_supplicant *wpa_s = ctx;
+
+ if (event == WPS_EV_FAIL) {
+ struct wps_event_fail *fail = &data->fail;
+
+ if (wpa_s->parent && wpa_s->parent != wpa_s &&
+ wpa_s == wpa_s->global->p2p_group_formation) {
+ /*
+ * src/ap/wps_hostapd.c has already sent this on the
+ * main interface, so only send on the parent interface
+ * here if needed.
+ */
+ wpa_msg(wpa_s->parent, MSG_INFO, WPS_EVENT_FAIL
+ "msg=%d config_error=%d",
+ fail->msg, fail->config_error);
+ }
+ wpas_p2p_wps_failed(wpa_s, fail);
+ }
+#endif /* CONFIG_P2P */
+}
+
+
+static void ap_sta_authorized_cb(void *ctx, const u8 *mac_addr,
+ int authorized, const u8 *p2p_dev_addr)
+{
+ wpas_notify_sta_authorized(ctx, mac_addr, authorized, p2p_dev_addr);
+}
+
+
+static int ap_vendor_action_rx(void *ctx, const u8 *buf, size_t len, int freq)
+{
+#ifdef CONFIG_P2P
+ struct wpa_supplicant *wpa_s = ctx;
+ const struct ieee80211_mgmt *mgmt;
+ size_t hdr_len;
+
+ mgmt = (const struct ieee80211_mgmt *) buf;
+ hdr_len = (const u8 *) &mgmt->u.action.u.vs_public_action.action - buf;
+ if (hdr_len > len)
+ return -1;
+ wpas_p2p_rx_action(wpa_s, mgmt->da, mgmt->sa, mgmt->bssid,
+ mgmt->u.action.category,
+ &mgmt->u.action.u.vs_public_action.action,
+ len - hdr_len, freq);
+#endif /* CONFIG_P2P */
+ return 0;
}
-static int ap_probe_req_rx(void *ctx, const u8 *addr, const u8 *ie,
- size_t ie_len)
+static int ap_probe_req_rx(void *ctx, const u8 *sa, const u8 *da,
+ const u8 *bssid, const u8 *ie, size_t ie_len)
{
+#ifdef CONFIG_P2P
+ struct wpa_supplicant *wpa_s = ctx;
+ return wpas_p2p_probe_req_rx(wpa_s, sa, da, bssid, ie, ie_len);
+#else /* CONFIG_P2P */
return 0;
+#endif /* CONFIG_P2P */
}
static void ap_wps_reg_success_cb(void *ctx, const u8 *mac_addr,
const u8 *uuid_e)
{
+#ifdef CONFIG_P2P
+ struct wpa_supplicant *wpa_s = ctx;
+ wpas_p2p_wps_success(wpa_s, mac_addr, 1);
+#endif /* CONFIG_P2P */
+}
+
+
+static void wpas_ap_configured_cb(void *ctx)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
+
+ if (wpa_s->ap_configured_cb)
+ wpa_s->ap_configured_cb(wpa_s->ap_configured_cb_ctx,
+ wpa_s->ap_configured_cb_data);
}
params.mode = IEEE80211_MODE_IBSS;
break;
case WPAS_MODE_AP:
+ case WPAS_MODE_P2P_GO:
+ case WPAS_MODE_P2P_GROUP_FORMATION:
params.mode = IEEE80211_MODE_AP;
break;
}
params.freq = ssid->frequency;
+ params.wpa_proto = ssid->proto;
if (ssid->key_mgmt & WPA_KEY_MGMT_PSK)
wpa_s->key_mgmt = WPA_KEY_MGMT_PSK;
else
params.pairwise_suite = cipher_suite2driver(wpa_s->pairwise_cipher);
params.group_suite = params.pairwise_suite;
+#ifdef CONFIG_P2P
+ if (ssid->mode == WPAS_MODE_P2P_GO ||
+ ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION)
+ params.p2p = 1;
+#endif /* CONFIG_P2P */
+
+ if (wpa_s->parent->set_ap_uapsd)
+ params.uapsd = wpa_s->parent->ap_uapsd;
+ else
+ params.uapsd = -1;
+
if (wpa_drv_associate(wpa_s, ¶ms) < 0) {
wpa_msg(wpa_s, MSG_INFO, "Failed to start AP functionality");
return -1;
if (hapd_iface == NULL)
return -1;
hapd_iface->owner = wpa_s;
+ hapd_iface->drv_flags = wpa_s->drv_flags;
wpa_s->ap_iface->conf = conf = hostapd_config_defaults();
if (conf == NULL) {
return -1;
}
+ if (params.uapsd > 0) {
+ conf->bss->wmm_enabled = 1;
+ conf->bss->wmm_uapsd = 1;
+ }
+
if (wpa_supplicant_conf_ap(wpa_s, ssid, conf)) {
wpa_printf(MSG_ERROR, "Failed to create AP configuration");
wpa_supplicant_ap_deinit(wpa_s);
return -1;
}
+#ifdef CONFIG_P2P
+ if (ssid->mode == WPAS_MODE_P2P_GO)
+ conf->bss[0].p2p = P2P_ENABLED | P2P_GROUP_OWNER;
+ else if (ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION)
+ conf->bss[0].p2p = P2P_ENABLED | P2P_GROUP_OWNER |
+ P2P_GROUP_FORMATION;
+#endif /* CONFIG_P2P */
+
hapd_iface->num_bss = conf->num_bss;
hapd_iface->bss = os_zalloc(conf->num_bss *
sizeof(struct hostapd_data *));
}
hapd_iface->bss[i]->msg_ctx = wpa_s;
+ hapd_iface->bss[i]->msg_ctx_parent = wpa_s->parent;
hapd_iface->bss[i]->public_action_cb = ap_public_action_rx;
hapd_iface->bss[i]->public_action_cb_ctx = wpa_s;
+ hapd_iface->bss[i]->vendor_action_cb = ap_vendor_action_rx;
+ hapd_iface->bss[i]->vendor_action_cb_ctx = wpa_s;
hostapd_register_probereq_cb(hapd_iface->bss[i],
ap_probe_req_rx, wpa_s);
hapd_iface->bss[i]->wps_reg_success_cb = ap_wps_reg_success_cb;
hapd_iface->bss[i]->wps_reg_success_cb_ctx = wpa_s;
+ hapd_iface->bss[i]->wps_event_cb = ap_wps_event_cb;
+ hapd_iface->bss[i]->wps_event_cb_ctx = wpa_s;
+ hapd_iface->bss[i]->sta_authorized_cb = ap_sta_authorized_cb;
+ hapd_iface->bss[i]->sta_authorized_cb_ctx = wpa_s;
+#ifdef CONFIG_P2P
+ hapd_iface->bss[i]->p2p = wpa_s->global->p2p;
+ hapd_iface->bss[i]->p2p_group = wpas_p2p_group_init(
+ wpa_s, ssid->p2p_persistent_group,
+ ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION);
+#endif /* CONFIG_P2P */
+ hapd_iface->bss[i]->setup_complete_cb = wpas_ap_configured_cb;
+ hapd_iface->bss[i]->setup_complete_cb_ctx = wpa_s;
}
os_memcpy(hapd_iface->bss[0]->own_addr, wpa_s->own_addr, ETH_ALEN);
hapd_iface->bss[0]->driver = wpa_s->driver;
hapd_iface->bss[0]->drv_priv = wpa_s->drv_priv;
+ wpa_s->current_ssid = ssid;
+ os_memcpy(wpa_s->bssid, wpa_s->own_addr, ETH_ALEN);
+ wpa_s->assoc_freq = ssid->frequency;
+
if (hostapd_setup_interface(wpa_s->ap_iface)) {
wpa_printf(MSG_ERROR, "Failed to initialize AP interface");
wpa_supplicant_ap_deinit(wpa_s);
return -1;
}
- wpa_s->current_ssid = ssid;
- os_memcpy(wpa_s->bssid, wpa_s->own_addr, ETH_ALEN);
- wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
-
- if (wpa_s->ap_configured_cb)
- wpa_s->ap_configured_cb(wpa_s->ap_configured_cb_ctx,
- wpa_s->ap_configured_cb_data);
-
return 0;
}
void wpa_supplicant_ap_deinit(struct wpa_supplicant *wpa_s)
{
+#ifdef CONFIG_WPS
+ eloop_cancel_timeout(wpas_wps_ap_pin_timeout, wpa_s, NULL);
+#endif /* CONFIG_WPS */
+
if (wpa_s->ap_iface == NULL)
return;
wpa_s->current_ssid = NULL;
+ wpa_s->assoc_freq = 0;
+ wpa_s->reassociated_connection = 0;
+#ifdef CONFIG_P2P
+ if (wpa_s->ap_iface->bss)
+ wpa_s->ap_iface->bss[0]->p2p_group = NULL;
+ wpas_p2p_group_deinit(wpa_s);
+#endif /* CONFIG_P2P */
hostapd_interface_deinit(wpa_s->ap_iface);
hostapd_interface_free(wpa_s->ap_iface);
wpa_s->ap_iface = NULL;
}
-void ap_rx_from_unknown_sta(void *ctx, const u8 *frame, size_t len)
+void ap_client_poll_ok(void *ctx, const u8 *addr)
{
#ifdef NEED_AP_MLME
struct wpa_supplicant *wpa_s = ctx;
- const struct ieee80211_hdr *hdr =
- (const struct ieee80211_hdr *) frame;
- u16 fc = le_to_host16(hdr->frame_control);
- ieee802_11_rx_from_unknown(wpa_s->ap_iface->bss[0], hdr->addr2,
- (fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) ==
- (WLAN_FC_TODS | WLAN_FC_FROMDS));
+ if (wpa_s->ap_iface)
+ hostapd_client_poll_ok(wpa_s->ap_iface->bss[0], addr);
+#endif /* NEED_AP_MLME */
+}
+
+
+void ap_rx_from_unknown_sta(void *ctx, const u8 *addr, int wds)
+{
+#ifdef NEED_AP_MLME
+ struct wpa_supplicant *wpa_s = ctx;
+ ieee802_11_rx_from_unknown(wpa_s->ap_iface->bss[0], addr, wds);
#endif /* NEED_AP_MLME */
}
#ifdef CONFIG_WPS
-int wpa_supplicant_ap_wps_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid)
+int wpa_supplicant_ap_wps_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid,
+ const u8 *p2p_dev_addr)
{
if (!wpa_s->ap_iface)
return -1;
- return hostapd_wps_button_pushed(wpa_s->ap_iface->bss[0]);
+ return hostapd_wps_button_pushed(wpa_s->ap_iface->bss[0],
+ p2p_dev_addr);
+}
+
+
+static int wpa_supplicant_ap_wps_sta_cancel(struct hostapd_data *hapd,
+ struct sta_info *sta, void *ctx)
+{
+ if (sta && (sta->flags & WLAN_STA_WPS)) {
+ ap_sta_deauthenticate(hapd, sta,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ wpa_printf(MSG_DEBUG, "WPS: %s: Deauth sta=" MACSTR,
+ __func__, MAC2STR(sta->addr));
+ return 1;
+ }
+
+ return 0;
+}
+
+
+int wpa_supplicant_ap_wps_cancel(struct wpa_supplicant *wpa_s)
+{
+ struct wps_registrar *reg;
+ int reg_sel = 0, wps_sta = 0;
+
+ if (!wpa_s->ap_iface || !wpa_s->ap_iface->bss[0]->wps)
+ return -1;
+
+ reg = wpa_s->ap_iface->bss[0]->wps->registrar;
+ reg_sel = wps_registrar_wps_cancel(reg);
+ wps_sta = ap_for_each_sta(wpa_s->ap_iface->bss[0],
+ wpa_supplicant_ap_wps_sta_cancel, NULL);
+
+ if (!reg_sel && !wps_sta) {
+ wpa_printf(MSG_DEBUG, "No WPS operation in progress at this "
+ "time");
+ return -1;
+ }
+
+ /*
+ * There are 2 cases to return wps cancel as success:
+ * 1. When wps cancel was initiated but no connection has been
+ * established with client yet.
+ * 2. Client is in the middle of exchanging WPS messages.
+ */
+
+ return 0;
}
if (pin == NULL) {
unsigned int rpin = wps_generate_pin();
- ret_len = os_snprintf(buf, buflen, "%d", rpin);
+ ret_len = os_snprintf(buf, buflen, "%08d", rpin);
pin = buf;
- }
+ } else
+ ret_len = os_snprintf(buf, buflen, "%s", pin);
- ret = hostapd_wps_add_pin(wpa_s->ap_iface->bss[0], "any", pin, 0);
+ ret = hostapd_wps_add_pin(wpa_s->ap_iface->bss[0], bssid, "any", pin,
+ 0);
if (ret)
return -1;
return ret_len;
}
+
+static void wpas_wps_ap_pin_timeout(void *eloop_data, void *user_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_data;
+ wpa_printf(MSG_DEBUG, "WPS: AP PIN timed out");
+ wpas_wps_ap_pin_disable(wpa_s);
+}
+
+
+static void wpas_wps_ap_pin_enable(struct wpa_supplicant *wpa_s, int timeout)
+{
+ struct hostapd_data *hapd;
+
+ if (wpa_s->ap_iface == NULL)
+ return;
+ hapd = wpa_s->ap_iface->bss[0];
+ wpa_printf(MSG_DEBUG, "WPS: Enabling AP PIN (timeout=%d)", timeout);
+ hapd->ap_pin_failures = 0;
+ eloop_cancel_timeout(wpas_wps_ap_pin_timeout, wpa_s, NULL);
+ if (timeout > 0)
+ eloop_register_timeout(timeout, 0,
+ wpas_wps_ap_pin_timeout, wpa_s, NULL);
+}
+
+
+void wpas_wps_ap_pin_disable(struct wpa_supplicant *wpa_s)
+{
+ struct hostapd_data *hapd;
+
+ if (wpa_s->ap_iface == NULL)
+ return;
+ wpa_printf(MSG_DEBUG, "WPS: Disabling AP PIN");
+ hapd = wpa_s->ap_iface->bss[0];
+ os_free(hapd->conf->ap_pin);
+ hapd->conf->ap_pin = NULL;
+ eloop_cancel_timeout(wpas_wps_ap_pin_timeout, wpa_s, NULL);
+}
+
+
+const char * wpas_wps_ap_pin_random(struct wpa_supplicant *wpa_s, int timeout)
+{
+ struct hostapd_data *hapd;
+ unsigned int pin;
+ char pin_txt[9];
+
+ if (wpa_s->ap_iface == NULL)
+ return NULL;
+ hapd = wpa_s->ap_iface->bss[0];
+ pin = wps_generate_pin();
+ os_snprintf(pin_txt, sizeof(pin_txt), "%08u", pin);
+ os_free(hapd->conf->ap_pin);
+ hapd->conf->ap_pin = os_strdup(pin_txt);
+ if (hapd->conf->ap_pin == NULL)
+ return NULL;
+ wpas_wps_ap_pin_enable(wpa_s, timeout);
+
+ return hapd->conf->ap_pin;
+}
+
+
+const char * wpas_wps_ap_pin_get(struct wpa_supplicant *wpa_s)
+{
+ struct hostapd_data *hapd;
+ if (wpa_s->ap_iface == NULL)
+ return NULL;
+ hapd = wpa_s->ap_iface->bss[0];
+ return hapd->conf->ap_pin;
+}
+
+
+int wpas_wps_ap_pin_set(struct wpa_supplicant *wpa_s, const char *pin,
+ int timeout)
+{
+ struct hostapd_data *hapd;
+ char pin_txt[9];
+ int ret;
+
+ if (wpa_s->ap_iface == NULL)
+ return -1;
+ hapd = wpa_s->ap_iface->bss[0];
+ ret = os_snprintf(pin_txt, sizeof(pin_txt), "%s", pin);
+ if (ret < 0 || ret >= (int) sizeof(pin_txt))
+ return -1;
+ os_free(hapd->conf->ap_pin);
+ hapd->conf->ap_pin = os_strdup(pin_txt);
+ if (hapd->conf->ap_pin == NULL)
+ return -1;
+ wpas_wps_ap_pin_enable(wpa_s, timeout);
+
+ return 0;
+}
+
+
+void wpa_supplicant_ap_pwd_auth_fail(struct wpa_supplicant *wpa_s)
+{
+ struct hostapd_data *hapd;
+
+ if (wpa_s->ap_iface == NULL)
+ return;
+ hapd = wpa_s->ap_iface->bss[0];
+
+ /*
+ * Registrar failed to prove its knowledge of the AP PIN. Disable AP
+ * PIN if this happens multiple times to slow down brute force attacks.
+ */
+ hapd->ap_pin_failures++;
+ wpa_printf(MSG_DEBUG, "WPS: AP PIN authentication failure number %u",
+ hapd->ap_pin_failures);
+ if (hapd->ap_pin_failures < 3)
+ return;
+
+ wpa_printf(MSG_DEBUG, "WPS: Disable AP PIN");
+ hapd->ap_pin_failures = 0;
+ os_free(hapd->conf->ap_pin);
+ hapd->conf->ap_pin = NULL;
+}
+
#endif /* CONFIG_WPS */
#endif /* CONFIG_CTRL_IFACE */
+int wpa_supplicant_ap_update_beacon(struct wpa_supplicant *wpa_s)
+{
+ struct hostapd_iface *iface = wpa_s->ap_iface;
+ struct wpa_ssid *ssid = wpa_s->current_ssid;
+ struct hostapd_data *hapd;
+
+ if (ssid == NULL || wpa_s->ap_iface == NULL ||
+ ssid->mode == WPAS_MODE_INFRA ||
+ ssid->mode == WPAS_MODE_IBSS)
+ return -1;
+
+#ifdef CONFIG_P2P
+ if (ssid->mode == WPAS_MODE_P2P_GO)
+ iface->conf->bss[0].p2p = P2P_ENABLED | P2P_GROUP_OWNER;
+ else if (ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION)
+ iface->conf->bss[0].p2p = P2P_ENABLED | P2P_GROUP_OWNER |
+ P2P_GROUP_FORMATION;
+#endif /* CONFIG_P2P */
+
+ hapd = iface->bss[0];
+ if (hapd->drv_priv == NULL)
+ return -1;
+ ieee802_11_set_beacons(iface);
+ hostapd_set_ap_wps_ie(hapd);
+
+ return 0;
+}
+
+
int wpa_supplicant_ap_mac_addr_filter(struct wpa_supplicant *wpa_s,
const u8 *addr)
{
void wpa_supplicant_ap_deinit(struct wpa_supplicant *wpa_s);
void wpa_supplicant_ap_rx_eapol(struct wpa_supplicant *wpa_s,
const u8 *src_addr, const u8 *buf, size_t len);
-int wpa_supplicant_ap_wps_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid);
+int wpa_supplicant_ap_wps_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid,
+ const u8 *p2p_dev_addr);
int wpa_supplicant_ap_wps_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
const char *pin, char *buf, size_t buflen);
+int wpa_supplicant_ap_wps_cancel(struct wpa_supplicant *wpa_s);
+void wpas_wps_ap_pin_disable(struct wpa_supplicant *wpa_s);
+const char * wpas_wps_ap_pin_random(struct wpa_supplicant *wpa_s, int timeout);
+const char * wpas_wps_ap_pin_get(struct wpa_supplicant *wpa_s);
+int wpas_wps_ap_pin_set(struct wpa_supplicant *wpa_s, const char *pin,
+ int timeout);
int ap_ctrl_iface_sta_first(struct wpa_supplicant *wpa_s,
char *buf, size_t buflen);
int ap_ctrl_iface_sta(struct wpa_supplicant *wpa_s, const char *txtaddr,
size_t buflen, int verbose);
void ap_tx_status(void *ctx, const u8 *addr,
const u8 *buf, size_t len, int ack);
-void ap_rx_from_unknown_sta(void *ctx, const u8 *frame, size_t len);
+void ap_client_poll_ok(void *ctx, const u8 *addr);
+void ap_rx_from_unknown_sta(void *ctx, const u8 *addr, int wds);
void ap_mgmt_rx(void *ctx, struct rx_mgmt *rx_mgmt);
void ap_mgmt_tx_cb(void *ctx, const u8 *buf, size_t len, u16 stype, int ok);
+int wpa_supplicant_ap_update_beacon(struct wpa_supplicant *wpa_s);
int wpa_supplicant_ap_mac_addr_filter(struct wpa_supplicant *wpa_s,
const u8 *addr);
+void wpa_supplicant_ap_pwd_auth_fail(struct wpa_supplicant *wpa_s);
#endif /* AP_H */
#ifdef CONFIG_BGSCAN_SIMPLE
extern const struct bgscan_ops bgscan_simple_ops;
#endif /* CONFIG_BGSCAN_SIMPLE */
+#ifdef CONFIG_BGSCAN_LEARN
+extern const struct bgscan_ops bgscan_learn_ops;
+#endif /* CONFIG_BGSCAN_LEARN */
static const struct bgscan_ops * bgscan_modules[] = {
#ifdef CONFIG_BGSCAN_SIMPLE
&bgscan_simple_ops,
#endif /* CONFIG_BGSCAN_SIMPLE */
+#ifdef CONFIG_BGSCAN_LEARN
+ &bgscan_learn_ops,
+#endif /* CONFIG_BGSCAN_LEARN */
NULL
};
}
-int bgscan_notify_scan(struct wpa_supplicant *wpa_s)
+int bgscan_notify_scan(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_results *scan_res)
{
if (wpa_s->bgscan && wpa_s->bgscan_priv)
- return wpa_s->bgscan->notify_scan(wpa_s->bgscan_priv);
+ return wpa_s->bgscan->notify_scan(wpa_s->bgscan_priv,
+ scan_res);
return 0;
}
}
-void bgscan_notify_signal_change(struct wpa_supplicant *wpa_s, int above)
+void bgscan_notify_signal_change(struct wpa_supplicant *wpa_s, int above,
+ int current_signal, int current_noise,
+ int current_txrate)
{
if (wpa_s->bgscan && wpa_s->bgscan_priv)
- wpa_s->bgscan->notify_signal_change(wpa_s->bgscan_priv, above);
+ wpa_s->bgscan->notify_signal_change(wpa_s->bgscan_priv, above,
+ current_signal,
+ current_noise,
+ current_txrate);
}
const struct wpa_ssid *ssid);
void (*deinit)(void *priv);
- int (*notify_scan)(void *priv);
+ int (*notify_scan)(void *priv, struct wpa_scan_results *scan_res);
void (*notify_beacon_loss)(void *priv);
- void (*notify_signal_change)(void *priv, int above);
+ void (*notify_signal_change)(void *priv, int above,
+ int current_signal,
+ int current_noise,
+ int current_txrate);
};
#ifdef CONFIG_BGSCAN
int bgscan_init(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
void bgscan_deinit(struct wpa_supplicant *wpa_s);
-int bgscan_notify_scan(struct wpa_supplicant *wpa_s);
+int bgscan_notify_scan(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_results *scan_res);
void bgscan_notify_beacon_loss(struct wpa_supplicant *wpa_s);
-void bgscan_notify_signal_change(struct wpa_supplicant *wpa_s, int above);
+void bgscan_notify_signal_change(struct wpa_supplicant *wpa_s, int above,
+ int current_signal, int current_noise,
+ int current_txrate);
#else /* CONFIG_BGSCAN */
{
}
-static inline int bgscan_notify_scan(struct wpa_supplicant *wpa_s)
+static inline int bgscan_notify_scan(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_results *scan_res)
{
return 0;
}
}
static inline void bgscan_notify_signal_change(struct wpa_supplicant *wpa_s,
- int above)
+ int above, int current_signal,
+ int current_noise,
+ int current_txrate)
{
}
--- /dev/null
+/*
+ * WPA Supplicant - background scan and roaming module: learn
+ * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eloop.h"
+#include "list.h"
+#include "common/ieee802_11_defs.h"
+#include "drivers/driver.h"
+#include "config_ssid.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "scan.h"
+#include "bgscan.h"
+
+struct bgscan_learn_bss {
+ struct dl_list list;
+ u8 bssid[ETH_ALEN];
+ int freq;
+ u8 *neigh; /* num_neigh * ETH_ALEN buffer */
+ size_t num_neigh;
+};
+
+struct bgscan_learn_data {
+ struct wpa_supplicant *wpa_s;
+ const struct wpa_ssid *ssid;
+ int scan_interval;
+ int signal_threshold;
+ int short_interval; /* use if signal < threshold */
+ int long_interval; /* use if signal > threshold */
+ struct os_time last_bgscan;
+ char *fname;
+ struct dl_list bss;
+ int *supp_freqs;
+ int probe_idx;
+};
+
+
+static void bss_free(struct bgscan_learn_bss *bss)
+{
+ os_free(bss->neigh);
+ os_free(bss);
+}
+
+
+static int bssid_in_array(u8 *array, size_t array_len, const u8 *bssid)
+{
+ size_t i;
+
+ if (array == NULL || array_len == 0)
+ return 0;
+
+ for (i = 0; i < array_len; i++) {
+ if (os_memcmp(array + i * ETH_ALEN, bssid, ETH_ALEN) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static void bgscan_learn_add_neighbor(struct bgscan_learn_bss *bss,
+ const u8 *bssid)
+{
+ u8 *n;
+
+ if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0)
+ return;
+ if (bssid_in_array(bss->neigh, bss->num_neigh, bssid))
+ return;
+
+ n = os_realloc(bss->neigh, (bss->num_neigh + 1) * ETH_ALEN);
+ if (n == NULL)
+ return;
+
+ os_memcpy(n + bss->num_neigh * ETH_ALEN, bssid, ETH_ALEN);
+ bss->neigh = n;
+ bss->num_neigh++;
+}
+
+
+static struct bgscan_learn_bss * bgscan_learn_get_bss(
+ struct bgscan_learn_data *data, const u8 *bssid)
+{
+ struct bgscan_learn_bss *bss;
+
+ dl_list_for_each(bss, &data->bss, struct bgscan_learn_bss, list) {
+ if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0)
+ return bss;
+ }
+ return NULL;
+}
+
+
+static int bgscan_learn_load(struct bgscan_learn_data *data)
+{
+ FILE *f;
+ char buf[128];
+ struct bgscan_learn_bss *bss;
+
+ if (data->fname == NULL)
+ return 0;
+
+ f = fopen(data->fname, "r");
+ if (f == NULL)
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "bgscan learn: Loading data from %s",
+ data->fname);
+
+ if (fgets(buf, sizeof(buf), f) == NULL ||
+ os_strncmp(buf, "wpa_supplicant-bgscan-learn\n", 28) != 0) {
+ wpa_printf(MSG_INFO, "bgscan learn: Invalid data file %s",
+ data->fname);
+ fclose(f);
+ return -1;
+ }
+
+ while (fgets(buf, sizeof(buf), f)) {
+ if (os_strncmp(buf, "BSS ", 4) == 0) {
+ bss = os_zalloc(sizeof(*bss));
+ if (!bss)
+ continue;
+ if (hwaddr_aton(buf + 4, bss->bssid) < 0) {
+ bss_free(bss);
+ continue;
+ }
+ bss->freq = atoi(buf + 4 + 18);
+ dl_list_add(&data->bss, &bss->list);
+ wpa_printf(MSG_DEBUG, "bgscan learn: Loaded BSS "
+ "entry: " MACSTR " freq=%d",
+ MAC2STR(bss->bssid), bss->freq);
+ }
+
+ if (os_strncmp(buf, "NEIGHBOR ", 9) == 0) {
+ u8 addr[ETH_ALEN];
+
+ if (hwaddr_aton(buf + 9, addr) < 0)
+ continue;
+ bss = bgscan_learn_get_bss(data, addr);
+ if (bss == NULL)
+ continue;
+ if (hwaddr_aton(buf + 9 + 18, addr) < 0)
+ continue;
+
+ bgscan_learn_add_neighbor(bss, addr);
+ }
+ }
+
+ fclose(f);
+ return 0;
+}
+
+
+static void bgscan_learn_save(struct bgscan_learn_data *data)
+{
+ FILE *f;
+ struct bgscan_learn_bss *bss;
+
+ if (data->fname == NULL)
+ return;
+
+ wpa_printf(MSG_DEBUG, "bgscan learn: Saving data to %s",
+ data->fname);
+
+ f = fopen(data->fname, "w");
+ if (f == NULL)
+ return;
+ fprintf(f, "wpa_supplicant-bgscan-learn\n");
+
+ dl_list_for_each(bss, &data->bss, struct bgscan_learn_bss, list) {
+ fprintf(f, "BSS " MACSTR " %d\n",
+ MAC2STR(bss->bssid), bss->freq);
+ }
+
+ dl_list_for_each(bss, &data->bss, struct bgscan_learn_bss, list) {
+ size_t i;
+ for (i = 0; i < bss->num_neigh; i++) {
+ fprintf(f, "NEIGHBOR " MACSTR " " MACSTR "\n",
+ MAC2STR(bss->bssid),
+ MAC2STR(bss->neigh + i * ETH_ALEN));
+ }
+ }
+
+ fclose(f);
+}
+
+
+static int in_array(int *array, int val)
+{
+ int i;
+
+ if (array == NULL)
+ return 0;
+
+ for (i = 0; array[i]; i++) {
+ if (array[i] == val)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static int * bgscan_learn_get_freqs(struct bgscan_learn_data *data,
+ size_t *count)
+{
+ struct bgscan_learn_bss *bss;
+ int *freqs = NULL, *n;
+
+ *count = 0;
+
+ dl_list_for_each(bss, &data->bss, struct bgscan_learn_bss, list) {
+ if (in_array(freqs, bss->freq))
+ continue;
+ n = os_realloc(freqs, (*count + 2) * sizeof(int));
+ if (n == NULL)
+ return freqs;
+ freqs = n;
+ freqs[*count] = bss->freq;
+ (*count)++;
+ freqs[*count] = 0;
+ }
+
+ return freqs;
+}
+
+
+static int * bgscan_learn_get_probe_freq(struct bgscan_learn_data *data,
+ int *freqs, size_t count)
+{
+ int idx, *n;
+
+ if (data->supp_freqs == NULL)
+ return freqs;
+
+ idx = data->probe_idx + 1;
+ while (idx != data->probe_idx) {
+ if (data->supp_freqs[idx] == 0)
+ idx = 0;
+ if (!in_array(freqs, data->supp_freqs[idx])) {
+ wpa_printf(MSG_DEBUG, "bgscan learn: Probe new freq "
+ "%u", data->supp_freqs[idx]);
+ data->probe_idx = idx;
+ n = os_realloc(freqs, (count + 2) * sizeof(int));
+ if (n == NULL)
+ return freqs;
+ freqs = n;
+ freqs[count] = data->supp_freqs[idx];
+ count++;
+ freqs[count] = 0;
+ break;
+ }
+
+ idx++;
+ }
+
+ return freqs;
+}
+
+
+static void bgscan_learn_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct bgscan_learn_data *data = eloop_ctx;
+ struct wpa_supplicant *wpa_s = data->wpa_s;
+ struct wpa_driver_scan_params params;
+ int *freqs = NULL;
+ size_t count, i;
+ char msg[100], *pos;
+
+ os_memset(¶ms, 0, sizeof(params));
+ params.num_ssids = 1;
+ params.ssids[0].ssid = data->ssid->ssid;
+ params.ssids[0].ssid_len = data->ssid->ssid_len;
+ if (data->ssid->scan_freq)
+ params.freqs = data->ssid->scan_freq;
+ else {
+ freqs = bgscan_learn_get_freqs(data, &count);
+ wpa_printf(MSG_DEBUG, "bgscan learn: BSSes in this ESS have "
+ "been seen on %u channels", (unsigned int) count);
+ freqs = bgscan_learn_get_probe_freq(data, freqs, count);
+
+ msg[0] = '\0';
+ pos = msg;
+ for (i = 0; freqs && freqs[i]; i++) {
+ int ret;
+ ret = os_snprintf(pos, msg + sizeof(msg) - pos, " %d",
+ freqs[i]);
+ if (ret < 0 || ret >= msg + sizeof(msg) - pos)
+ break;
+ pos += ret;
+ }
+ pos[0] = '\0';
+ wpa_printf(MSG_DEBUG, "bgscan learn: Scanning frequencies:%s",
+ msg);
+ params.freqs = freqs;
+ }
+
+ wpa_printf(MSG_DEBUG, "bgscan learn: Request a background scan");
+ if (wpa_supplicant_trigger_scan(wpa_s, ¶ms)) {
+ wpa_printf(MSG_DEBUG, "bgscan learn: Failed to trigger scan");
+ eloop_register_timeout(data->scan_interval, 0,
+ bgscan_learn_timeout, data, NULL);
+ } else
+ os_get_time(&data->last_bgscan);
+ os_free(freqs);
+}
+
+
+static int bgscan_learn_get_params(struct bgscan_learn_data *data,
+ const char *params)
+{
+ const char *pos;
+
+ if (params == NULL)
+ return 0;
+
+ data->short_interval = atoi(params);
+
+ pos = os_strchr(params, ':');
+ if (pos == NULL)
+ return 0;
+ pos++;
+ data->signal_threshold = atoi(pos);
+ pos = os_strchr(pos, ':');
+ if (pos == NULL) {
+ wpa_printf(MSG_ERROR, "bgscan learn: Missing scan interval "
+ "for high signal");
+ return -1;
+ }
+ pos++;
+ data->long_interval = atoi(pos);
+ pos = os_strchr(pos, ':');
+ if (pos) {
+ pos++;
+ data->fname = os_strdup(pos);
+ }
+
+ return 0;
+}
+
+
+static int * bgscan_learn_get_supp_freqs(struct wpa_supplicant *wpa_s)
+{
+ struct hostapd_hw_modes *modes;
+ int i, j, *freqs = NULL, *n;
+ size_t count = 0;
+
+ modes = wpa_s->hw.modes;
+ if (modes == NULL)
+ return NULL;
+
+ for (i = 0; i < wpa_s->hw.num_modes; i++) {
+ for (j = 0; j < modes[i].num_channels; j++) {
+ if (modes[i].channels[j].flag & HOSTAPD_CHAN_DISABLED)
+ continue;
+ n = os_realloc(freqs, (count + 2) * sizeof(int));
+ if (n == NULL)
+ continue;
+
+ freqs = n;
+ freqs[count] = modes[i].channels[j].freq;
+ count++;
+ freqs[count] = 0;
+ }
+ }
+
+ return freqs;
+}
+
+
+static void * bgscan_learn_init(struct wpa_supplicant *wpa_s,
+ const char *params,
+ const struct wpa_ssid *ssid)
+{
+ struct bgscan_learn_data *data;
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ dl_list_init(&data->bss);
+ data->wpa_s = wpa_s;
+ data->ssid = ssid;
+ if (bgscan_learn_get_params(data, params) < 0) {
+ os_free(data->fname);
+ os_free(data);
+ return NULL;
+ }
+ if (data->short_interval <= 0)
+ data->short_interval = 30;
+ if (data->long_interval <= 0)
+ data->long_interval = 30;
+
+ if (bgscan_learn_load(data) < 0) {
+ os_free(data->fname);
+ os_free(data);
+ return NULL;
+ }
+
+ wpa_printf(MSG_DEBUG, "bgscan learn: Signal strength threshold %d "
+ "Short bgscan interval %d Long bgscan interval %d",
+ data->signal_threshold, data->short_interval,
+ data->long_interval);
+
+ if (data->signal_threshold &&
+ wpa_drv_signal_monitor(wpa_s, data->signal_threshold, 4) < 0) {
+ wpa_printf(MSG_ERROR, "bgscan learn: Failed to enable "
+ "signal strength monitoring");
+ }
+
+ data->supp_freqs = bgscan_learn_get_supp_freqs(wpa_s);
+ data->scan_interval = data->short_interval;
+ eloop_register_timeout(data->scan_interval, 0, bgscan_learn_timeout,
+ data, NULL);
+
+ /*
+ * This function is called immediately after an association, so it is
+ * reasonable to assume that a scan was completed recently. This makes
+ * us skip an immediate new scan in cases where the current signal
+ * level is below the bgscan threshold.
+ */
+ os_get_time(&data->last_bgscan);
+
+ return data;
+}
+
+
+static void bgscan_learn_deinit(void *priv)
+{
+ struct bgscan_learn_data *data = priv;
+ struct bgscan_learn_bss *bss, *n;
+
+ bgscan_learn_save(data);
+ eloop_cancel_timeout(bgscan_learn_timeout, data, NULL);
+ if (data->signal_threshold)
+ wpa_drv_signal_monitor(data->wpa_s, 0, 0);
+ os_free(data->fname);
+ dl_list_for_each_safe(bss, n, &data->bss, struct bgscan_learn_bss,
+ list) {
+ dl_list_del(&bss->list);
+ bss_free(bss);
+ }
+ os_free(data->supp_freqs);
+ os_free(data);
+}
+
+
+static int bgscan_learn_bss_match(struct bgscan_learn_data *data,
+ struct wpa_scan_res *bss)
+{
+ const u8 *ie;
+
+ ie = wpa_scan_get_ie(bss, WLAN_EID_SSID);
+ if (ie == NULL)
+ return 0;
+
+ if (data->ssid->ssid_len != ie[1] ||
+ os_memcmp(data->ssid->ssid, ie + 2, ie[1]) != 0)
+ return 0; /* SSID mismatch */
+
+ return 1;
+}
+
+
+static int bgscan_learn_notify_scan(void *priv,
+ struct wpa_scan_results *scan_res)
+{
+ struct bgscan_learn_data *data = priv;
+ size_t i, j;
+#define MAX_BSS 50
+ u8 bssid[MAX_BSS * ETH_ALEN];
+ size_t num_bssid = 0;
+
+ wpa_printf(MSG_DEBUG, "bgscan learn: scan result notification");
+
+ eloop_cancel_timeout(bgscan_learn_timeout, data, NULL);
+ eloop_register_timeout(data->scan_interval, 0, bgscan_learn_timeout,
+ data, NULL);
+
+ for (i = 0; i < scan_res->num; i++) {
+ struct wpa_scan_res *res = scan_res->res[i];
+ if (!bgscan_learn_bss_match(data, res))
+ continue;
+
+ if (num_bssid < MAX_BSS) {
+ os_memcpy(bssid + num_bssid * ETH_ALEN, res->bssid,
+ ETH_ALEN);
+ num_bssid++;
+ }
+ }
+ wpa_printf(MSG_DEBUG, "bgscan learn: %u matching BSSes in scan "
+ "results", (unsigned int) num_bssid);
+
+ for (i = 0; i < scan_res->num; i++) {
+ struct wpa_scan_res *res = scan_res->res[i];
+ struct bgscan_learn_bss *bss;
+
+ if (!bgscan_learn_bss_match(data, res))
+ continue;
+
+ bss = bgscan_learn_get_bss(data, res->bssid);
+ if (bss && bss->freq != res->freq) {
+ wpa_printf(MSG_DEBUG, "bgscan learn: Update BSS "
+ MACSTR " freq %d -> %d",
+ MAC2STR(res->bssid), bss->freq, res->freq);
+ bss->freq = res->freq;
+ } else if (!bss) {
+ wpa_printf(MSG_DEBUG, "bgscan learn: Add BSS " MACSTR
+ " freq=%d", MAC2STR(res->bssid), res->freq);
+ bss = os_zalloc(sizeof(*bss));
+ if (!bss)
+ continue;
+ os_memcpy(bss->bssid, res->bssid, ETH_ALEN);
+ bss->freq = res->freq;
+ dl_list_add(&data->bss, &bss->list);
+ }
+
+ for (j = 0; j < num_bssid; j++) {
+ u8 *addr = bssid + j * ETH_ALEN;
+ bgscan_learn_add_neighbor(bss, addr);
+ }
+ }
+
+ /*
+ * A more advanced bgscan could process scan results internally, select
+ * the BSS and request roam if needed. This sample uses the existing
+ * BSS/ESS selection routine. Change this to return 1 if selection is
+ * done inside the bgscan module.
+ */
+
+ return 0;
+}
+
+
+static void bgscan_learn_notify_beacon_loss(void *priv)
+{
+ wpa_printf(MSG_DEBUG, "bgscan learn: beacon loss");
+ /* TODO: speed up background scanning */
+}
+
+
+static void bgscan_learn_notify_signal_change(void *priv, int above,
+ int current_signal,
+ int current_noise,
+ int current_txrate)
+{
+ struct bgscan_learn_data *data = priv;
+ int scan = 0;
+ struct os_time now;
+
+ if (data->short_interval == data->long_interval ||
+ data->signal_threshold == 0)
+ return;
+
+ wpa_printf(MSG_DEBUG, "bgscan learn: signal level changed "
+ "(above=%d current_signal=%d current_noise=%d "
+ "current_txrate=%d)", above, current_signal,
+ current_noise, current_txrate);
+ if (data->scan_interval == data->long_interval && !above) {
+ wpa_printf(MSG_DEBUG, "bgscan learn: Start using short bgscan "
+ "interval");
+ data->scan_interval = data->short_interval;
+ os_get_time(&now);
+ if (now.sec > data->last_bgscan.sec + 1)
+ scan = 1;
+ } else if (data->scan_interval == data->short_interval && above) {
+ wpa_printf(MSG_DEBUG, "bgscan learn: Start using long bgscan "
+ "interval");
+ data->scan_interval = data->long_interval;
+ eloop_cancel_timeout(bgscan_learn_timeout, data, NULL);
+ eloop_register_timeout(data->scan_interval, 0,
+ bgscan_learn_timeout, data, NULL);
+ } else if (!above) {
+ /*
+ * Signal dropped further 4 dB. Request a new scan if we have
+ * not yet scanned in a while.
+ */
+ os_get_time(&now);
+ if (now.sec > data->last_bgscan.sec + 10)
+ scan = 1;
+ }
+
+ if (scan) {
+ wpa_printf(MSG_DEBUG, "bgscan learn: Trigger immediate scan");
+ eloop_cancel_timeout(bgscan_learn_timeout, data, NULL);
+ eloop_register_timeout(0, 0, bgscan_learn_timeout, data, NULL);
+ }
+}
+
+
+const struct bgscan_ops bgscan_learn_ops = {
+ .name = "learn",
+ .init = bgscan_learn_init,
+ .deinit = bgscan_learn_deinit,
+ .notify_scan = bgscan_learn_notify_scan,
+ .notify_beacon_loss = bgscan_learn_notify_beacon_loss,
+ .notify_signal_change = bgscan_learn_notify_signal_change,
+};
const struct wpa_ssid *ssid;
int scan_interval;
int signal_threshold;
+ int short_scan_count; /* counter for scans using short scan interval */
int short_interval; /* use if signal < threshold */
int long_interval; /* use if signal > threshold */
struct os_time last_bgscan;
wpa_printf(MSG_DEBUG, "bgscan simple: Failed to trigger scan");
eloop_register_timeout(data->scan_interval, 0,
bgscan_simple_timeout, data, NULL);
- } else
+ } else {
+ if (data->scan_interval == data->short_interval) {
+ data->short_scan_count++;
+ /*
+ * Spend at most the duration of a long scan interval
+ * scanning at the short scan interval. After that,
+ * revert to the long scan interval.
+ */
+ if (data->short_scan_count >
+ data->long_interval / data->short_interval + 1) {
+ data->scan_interval = data->long_interval;
+ wpa_printf(MSG_DEBUG, "bgscan simple: Backing "
+ "off to long scan interval");
+ }
+ }
os_get_time(&data->last_bgscan);
+ }
}
}
data->scan_interval = data->short_interval;
+ if (data->signal_threshold) {
+ /* Poll for signal info to set initial scan interval */
+ struct wpa_signal_info siginfo;
+ if (wpa_drv_signal_poll(wpa_s, &siginfo) == 0 &&
+ siginfo.current_signal >= data->signal_threshold)
+ data->scan_interval = data->long_interval;
+ }
+ wpa_printf(MSG_DEBUG, "bgscan simple: Init scan interval: %d",
+ data->scan_interval);
eloop_register_timeout(data->scan_interval, 0, bgscan_simple_timeout,
data, NULL);
}
-static int bgscan_simple_notify_scan(void *priv)
+static int bgscan_simple_notify_scan(void *priv,
+ struct wpa_scan_results *scan_res)
{
struct bgscan_simple_data *data = priv;
}
-static void bgscan_simple_notify_signal_change(void *priv, int above)
+static void bgscan_simple_notify_signal_change(void *priv, int above,
+ int current_signal,
+ int current_noise,
+ int current_txrate)
{
struct bgscan_simple_data *data = priv;
int scan = 0;
return;
wpa_printf(MSG_DEBUG, "bgscan simple: signal level changed "
- "(above=%d)", above);
+ "(above=%d current_signal=%d current_noise=%d "
+ "current_txrate=%d))", above, current_signal,
+ current_noise, current_txrate);
if (data->scan_interval == data->long_interval && !above) {
wpa_printf(MSG_DEBUG, "bgscan simple: Start using short "
"bgscan interval");
data->scan_interval = data->short_interval;
+ data->short_scan_count = 0;
os_get_time(&now);
if (now.sec > data->last_bgscan.sec + 1)
scan = 1;
+ else if (data->last_bgscan.sec + data->long_interval >
+ now.sec + data->scan_interval) {
+ /*
+ * Restart scan interval timer if currently scheduled
+ * scan is too far in the future.
+ */
+ eloop_cancel_timeout(bgscan_simple_timeout, data,
+ NULL);
+ eloop_register_timeout(data->scan_interval, 0,
+ bgscan_simple_timeout, data,
+ NULL);
+ }
} else if (data->scan_interval == data->short_interval && above) {
wpa_printf(MSG_DEBUG, "bgscan simple: Start using long bgscan "
"interval");
* wpa_blacklist_add - Add an BSSID to the blacklist
* @wpa_s: Pointer to wpa_supplicant data
* @bssid: BSSID to be added to the blacklist
- * Returns: 0 on success, -1 on failure
+ * Returns: Current blacklist count on success, -1 on failure
*
* This function adds the specified BSSID to the blacklist or increases the
* blacklist count if the BSSID was already listed. It should be called when
wpa_printf(MSG_DEBUG, "BSSID " MACSTR " blacklist count "
"incremented to %d",
MAC2STR(bssid), e->count);
- return 0;
+ return e->count;
}
e = os_zalloc(sizeof(*e));
wpa_printf(MSG_DEBUG, "Added BSSID " MACSTR " into blacklist",
MAC2STR(bssid));
- return 0;
+ return e->count;
}
*/
#define WPA_BSS_EXPIRATION_PERIOD 10
-/**
- * WPA_BSS_EXPIRATION_AGE - BSS entry age after which it can be expired
- *
- * This value control the time in seconds after which a BSS entry gets removed
- * if it has not been updated or is not in use.
- */
-#define WPA_BSS_EXPIRATION_AGE 180
-
-/**
- * WPA_BSS_EXPIRATION_SCAN_COUNT - Expire BSS after number of scans
- *
- * If the BSS entry has not been seen in this many scans, it will be removed.
- * Value 1 means that the entry is removed after the first scan without the
- * BSSID being seen. Larger values can be used to avoid BSS entries
- * disappearing if they are not visible in every scan (e.g., low signal quality
- * or interference).
- */
-#define WPA_BSS_EXPIRATION_SCAN_COUNT 2
-
#define WPA_BSS_FREQ_CHANGED_FLAG BIT(0)
#define WPA_BSS_SIGNAL_CHANGED_FLAG BIT(1)
#define WPA_BSS_PRIVACY_CHANGED_FLAG BIT(2)
dl_list_del(&bss->list);
dl_list_del(&bss->list_id);
wpa_s->num_bss--;
- wpa_printf(MSG_DEBUG, "BSS: Remove id %u BSSID " MACSTR " SSID '%s'",
- bss->id, MAC2STR(bss->bssid),
- wpa_ssid_txt(bss->ssid, bss->ssid_len));
+ wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Remove id %u BSSID " MACSTR
+ " SSID '%s'", bss->id, MAC2STR(bss->bssid),
+ wpa_ssid_txt(bss->ssid, bss->ssid_len));
wpas_notify_bss_removed(wpa_s, bss->bssid, bss->id);
+#ifdef CONFIG_INTERWORKING
+ wpabuf_free(bss->anqp_venue_name);
+ wpabuf_free(bss->anqp_network_auth_type);
+ wpabuf_free(bss->anqp_roaming_consortium);
+ wpabuf_free(bss->anqp_ip_addr_type_availability);
+ wpabuf_free(bss->anqp_nai_realm);
+ wpabuf_free(bss->anqp_3gpp);
+ wpabuf_free(bss->anqp_domain_name);
+#endif /* CONFIG_INTERWORKING */
os_free(bss);
}
}
+static int wpa_bss_known(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+{
+ struct wpa_ssid *ssid;
+
+ for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+ if (ssid->ssid == NULL || ssid->ssid_len == 0)
+ continue;
+ if (ssid->ssid_len == bss->ssid_len &&
+ os_memcmp(ssid->ssid, bss->ssid, ssid->ssid_len) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static int wpa_bss_remove_oldest_unknown(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_bss *bss;
+
+ dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+ if (!wpa_bss_known(wpa_s, bss)) {
+ wpa_bss_remove(wpa_s, bss);
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+
+static void wpa_bss_remove_oldest(struct wpa_supplicant *wpa_s)
+{
+ /*
+ * Remove the oldest entry that does not match with any configured
+ * network.
+ */
+ if (wpa_bss_remove_oldest_unknown(wpa_s) == 0)
+ return;
+
+ /*
+ * Remove the oldest entry since no better candidate for removal was
+ * found.
+ */
+ wpa_bss_remove(wpa_s, dl_list_first(&wpa_s->bss,
+ struct wpa_bss, list));
+}
+
+
static void wpa_bss_add(struct wpa_supplicant *wpa_s,
const u8 *ssid, size_t ssid_len,
struct wpa_scan_res *res)
dl_list_add_tail(&wpa_s->bss, &bss->list);
dl_list_add_tail(&wpa_s->bss_id, &bss->list_id);
wpa_s->num_bss++;
- wpa_printf(MSG_DEBUG, "BSS: Add new id %u BSSID " MACSTR " SSID '%s'",
- bss->id, MAC2STR(bss->bssid), wpa_ssid_txt(ssid, ssid_len));
+ wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Add new id %u BSSID " MACSTR
+ " SSID '%s'",
+ bss->id, MAC2STR(bss->bssid), wpa_ssid_txt(ssid, ssid_len));
wpas_notify_bss_added(wpa_s, bss->bssid, bss->id);
- if (wpa_s->num_bss > wpa_s->conf->bss_max_count) {
- /* Remove the oldest entry */
- wpa_bss_remove(wpa_s, dl_list_first(&wpa_s->bss,
- struct wpa_bss, list));
- }
+ if (wpa_s->num_bss > wpa_s->conf->bss_max_count)
+ wpa_bss_remove_oldest(wpa_s);
}
new_ie_len = new_ie ? new_ie[1] + 2 : 0;
}
- ret = (old_ie_len == new_ie_len &&
- os_memcmp(old_ie, new_ie, old_ie_len) == 0);
+ if (!old_ie || !new_ie)
+ ret = !old_ie && !new_ie;
+ else
+ ret = (old_ie_len == new_ie_len &&
+ os_memcmp(old_ie, new_ie, old_ie_len) == 0);
wpabuf_free(old_ie_buff);
wpabuf_free(new_ie_buff);
nbss = os_realloc(bss, sizeof(*bss) + res->ie_len +
res->beacon_ie_len);
if (nbss) {
+ if (wpa_s->current_bss == bss)
+ wpa_s->current_bss = nbss;
bss = nbss;
os_memcpy(bss + 1, res + 1,
res->ie_len + res->beacon_ie_len);
void wpa_bss_update_start(struct wpa_supplicant *wpa_s)
{
wpa_s->bss_update_idx++;
- wpa_printf(MSG_DEBUG, "BSS: Start scan result update %u",
- wpa_s->bss_update_idx);
+ wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Start scan result update %u",
+ wpa_s->bss_update_idx);
}
void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s,
struct wpa_scan_res *res)
{
- const u8 *ssid;
+ const u8 *ssid, *p2p;
struct wpa_bss *bss;
ssid = wpa_scan_get_ie(res, WLAN_EID_SSID);
if (ssid == NULL) {
- wpa_printf(MSG_DEBUG, "BSS: No SSID IE included for " MACSTR,
- MAC2STR(res->bssid));
+ wpa_dbg(wpa_s, MSG_DEBUG, "BSS: No SSID IE included for "
+ MACSTR, MAC2STR(res->bssid));
return;
}
if (ssid[1] > 32) {
- wpa_printf(MSG_DEBUG, "BSS: Too long SSID IE included for "
- MACSTR, MAC2STR(res->bssid));
+ wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Too long SSID IE included for "
+ MACSTR, MAC2STR(res->bssid));
return;
}
+ p2p = wpa_scan_get_vendor_ie(res, P2P_IE_VENDOR_TYPE);
+ if (p2p && ssid[1] == P2P_WILDCARD_SSID_LEN &&
+ os_memcmp(ssid + 2, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) == 0)
+ return; /* Skip P2P listen discovery results here */
+
/* TODO: add option for ignoring BSSes we are not interested in
* (to save memory) */
bss = wpa_bss_get(wpa_s, res->bssid, ssid + 2, ssid[1]);
continue; /* expire only BSSes that were scanned */
if (bss->last_update_idx < wpa_s->bss_update_idx)
bss->scan_miss_count++;
- if (bss->scan_miss_count >= WPA_BSS_EXPIRATION_SCAN_COUNT) {
- wpa_printf(MSG_DEBUG, "BSS: Expire BSS %u due to no "
- "match in scan", bss->id);
+ if (bss->scan_miss_count >=
+ wpa_s->conf->bss_expiration_scan_count) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Expire BSS %u due to "
+ "no match in scan", bss->id);
wpa_bss_remove(wpa_s, bss);
}
}
}
-static void wpa_bss_timeout(void *eloop_ctx, void *timeout_ctx)
+void wpa_bss_flush_by_age(struct wpa_supplicant *wpa_s, int age)
{
- struct wpa_supplicant *wpa_s = eloop_ctx;
struct wpa_bss *bss, *n;
struct os_time t;
return;
os_get_time(&t);
- t.sec -= WPA_BSS_EXPIRATION_AGE;
+ t.sec -= age;
dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) {
if (wpa_bss_in_use(wpa_s, bss))
continue;
if (os_time_before(&bss->last_update, &t)) {
- wpa_printf(MSG_DEBUG, "BSS: Expire BSS %u due to age",
- bss->id);
+ wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Expire BSS %u due to "
+ "age", bss->id);
wpa_bss_remove(wpa_s, bss);
} else
break;
}
+}
+
+
+static void wpa_bss_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+
+ wpa_bss_flush_by_age(wpa_s, wpa_s->conf->bss_expiration_age);
eloop_register_timeout(WPA_BSS_EXPIRATION_PERIOD, 0,
wpa_bss_timeout, wpa_s, NULL);
}
}
-void wpa_bss_deinit(struct wpa_supplicant *wpa_s)
+void wpa_bss_flush(struct wpa_supplicant *wpa_s)
{
struct wpa_bss *bss, *n;
- eloop_cancel_timeout(wpa_bss_timeout, wpa_s, NULL);
+
if (wpa_s->bss.next == NULL)
return; /* BSS table not yet initialized */
- dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list)
+
+ dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) {
+ if (wpa_bss_in_use(wpa_s, bss))
+ continue;
wpa_bss_remove(wpa_s, bss);
+ }
+}
+
+
+void wpa_bss_deinit(struct wpa_supplicant *wpa_s)
+{
+ eloop_cancel_timeout(wpa_bss_timeout, wpa_s, NULL);
+ wpa_bss_flush(wpa_s);
}
const u8 *bssid)
{
struct wpa_bss *bss;
- dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+ dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss, list) {
if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0)
return bss;
}
}
+#ifdef CONFIG_P2P
+struct wpa_bss * wpa_bss_get_p2p_dev_addr(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr)
+{
+ struct wpa_bss *bss;
+ dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss, list) {
+ u8 addr[ETH_ALEN];
+ if (p2p_parse_dev_addr((const u8 *) (bss + 1), bss->ie_len,
+ addr) == 0 &&
+ os_memcmp(addr, dev_addr, ETH_ALEN) == 0)
+ return bss;
+ }
+ return NULL;
+}
+#endif /* CONFIG_P2P */
+
+
struct wpa_bss * wpa_bss_get_id(struct wpa_supplicant *wpa_s, unsigned int id)
{
struct wpa_bss *bss;
#define WPA_BSS_LEVEL_DBM BIT(3)
#define WPA_BSS_AUTHENTICATED BIT(4)
#define WPA_BSS_ASSOCIATED BIT(5)
+#define WPA_BSS_ANQP_FETCH_TRIED BIT(6)
/**
* struct wpa_bss - BSS table
int level;
u64 tsf;
struct os_time last_update;
+#ifdef CONFIG_INTERWORKING
+ struct wpabuf *anqp_venue_name;
+ struct wpabuf *anqp_network_auth_type;
+ struct wpabuf *anqp_roaming_consortium;
+ struct wpabuf *anqp_ip_addr_type_availability;
+ struct wpabuf *anqp_nai_realm;
+ struct wpabuf *anqp_3gpp;
+ struct wpabuf *anqp_domain_name;
+#endif /* CONFIG_INTERWORKING */
size_t ie_len;
size_t beacon_ie_len;
/* followed by ie_len octets of IEs */
int new_scan);
int wpa_bss_init(struct wpa_supplicant *wpa_s);
void wpa_bss_deinit(struct wpa_supplicant *wpa_s);
+void wpa_bss_flush(struct wpa_supplicant *wpa_s);
+void wpa_bss_flush_by_age(struct wpa_supplicant *wpa_s, int age);
struct wpa_bss * wpa_bss_get(struct wpa_supplicant *wpa_s, const u8 *bssid,
const u8 *ssid, size_t ssid_len);
struct wpa_bss * wpa_bss_get_bssid(struct wpa_supplicant *wpa_s,
const u8 *bssid);
+struct wpa_bss * wpa_bss_get_p2p_dev_addr(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr);
struct wpa_bss * wpa_bss_get_id(struct wpa_supplicant *wpa_s, unsigned int id);
const u8 * wpa_bss_get_ie(const struct wpa_bss *bss, u8 ie);
const u8 * wpa_bss_get_vendor_ie(const struct wpa_bss *bss, u32 vendor_type);
#include "includes.h"
#include "common.h"
+#include "utils/uuid.h"
#include "crypto/sha1.h"
#include "rsn_supp/wpa.h"
#include "eap_peer/eap.h"
+#include "p2p/p2p.h"
#include "config.h"
struct wpa_ssid *ssid, int line,
const char *value)
{
+ if (value[0] == '\0' || os_strcmp(value, "\"\"") == 0 ||
+ os_strcmp(value, "any") == 0) {
+ ssid->bssid_set = 0;
+ wpa_printf(MSG_MSGDUMP, "BSSID any");
+ return 0;
+ }
if (hwaddr_aton(value, ssid->bssid)) {
wpa_printf(MSG_ERROR, "Line %d: Invalid BSSID '%s'.",
line, value);
wpa_hexdump(MSG_MSGDUMP, "eap methods",
(u8 *) methods, num_methods * sizeof(*methods));
+ os_free(ssid->eap.eap_methods);
ssid->eap.eap_methods = methods;
return errors ? -1 : 0;
}
#endif /* NO_CONFIG_WRITE */
+#ifdef CONFIG_P2P
+
+static int wpa_config_parse_p2p_client_list(const struct parse_data *data,
+ struct wpa_ssid *ssid, int line,
+ const char *value)
+{
+ const char *pos;
+ u8 *buf, *n, addr[ETH_ALEN];
+ size_t count;
+
+ buf = NULL;
+ count = 0;
+
+ pos = value;
+ while (pos && *pos) {
+ while (*pos == ' ')
+ pos++;
+
+ if (hwaddr_aton(pos, addr)) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid "
+ "p2p_client_list address '%s'.",
+ line, value);
+ /* continue anyway */
+ } else {
+ n = os_realloc(buf, (count + 1) * ETH_ALEN);
+ if (n == NULL) {
+ os_free(buf);
+ return -1;
+ }
+ buf = n;
+ os_memcpy(buf + count * ETH_ALEN, addr, ETH_ALEN);
+ count++;
+ wpa_hexdump(MSG_MSGDUMP, "p2p_client_list",
+ addr, ETH_ALEN);
+ }
+
+ pos = os_strchr(pos, ' ');
+ }
+
+ os_free(ssid->p2p_client_list);
+ ssid->p2p_client_list = buf;
+ ssid->num_p2p_clients = count;
+
+ return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_p2p_client_list(const struct parse_data *data,
+ struct wpa_ssid *ssid)
+{
+ char *value, *end, *pos;
+ int res;
+ size_t i;
+
+ if (ssid->p2p_client_list == NULL || ssid->num_p2p_clients == 0)
+ return NULL;
+
+ value = os_malloc(20 * ssid->num_p2p_clients);
+ if (value == NULL)
+ return NULL;
+ pos = value;
+ end = value + 20 * ssid->num_p2p_clients;
+
+ for (i = 0; i < ssid->num_p2p_clients; i++) {
+ res = os_snprintf(pos, end - pos, MACSTR " ",
+ MAC2STR(ssid->p2p_client_list +
+ i * ETH_ALEN));
+ if (res < 0 || res >= end - pos) {
+ os_free(value);
+ return NULL;
+ }
+ pos += res;
+ }
+
+ if (pos > value)
+ pos[-1] = '\0';
+
+ return value;
+}
+#endif /* NO_CONFIG_WRITE */
+
+#endif /* CONFIG_P2P */
+
/* Helper macros for network block parser */
#ifdef OFFSET
{ STRe(pac_file) },
{ INTe(fragment_size) },
#endif /* IEEE8021X_EAPOL */
- { INT_RANGE(mode, 0, 2) },
+ { INT_RANGE(mode, 0, 4) },
{ INT_RANGE(proactive_key_caching, 0, 1) },
- { INT_RANGE(disabled, 0, 1) },
+ { INT_RANGE(disabled, 0, 2) },
{ STR(id_str) },
#ifdef CONFIG_IEEE80211W
{ INT_RANGE(ieee80211w, 0, 2) },
{ INT_RANGE(frequency, 0, 10000) },
{ INT(wpa_ptk_rekey) },
{ STR(bgscan) },
+#ifdef CONFIG_P2P
+ { FUNC(p2p_client_list) },
+#endif /* CONFIG_P2P */
};
#undef OFFSET
os_free(ssid->scan_freq);
os_free(ssid->freq_list);
os_free(ssid->bgscan);
+ os_free(ssid->p2p_client_list);
os_free(ssid);
}
struct wpa_config_blob *blob, *prevblob;
#endif /* CONFIG_NO_CONFIG_BLOBS */
struct wpa_ssid *ssid, *prev = NULL;
+
ssid = config->ssid;
while (ssid) {
prev = ssid;
os_free(config->model_name);
os_free(config->model_number);
os_free(config->serial_number);
- os_free(config->device_type);
os_free(config->config_methods);
+ os_free(config->p2p_ssid_postfix);
os_free(config->pssid);
+ os_free(config->home_realm);
+ os_free(config->home_username);
+ os_free(config->home_password);
+ os_free(config->home_ca_cert);
+ os_free(config->home_imsi);
+ os_free(config->home_milenage);
+ os_free(config->p2p_pref_chan);
os_free(config);
}
/**
+ * wpa_config_foreach_network - Iterate over each configured network
+ * @config: Configuration data from wpa_config_read()
+ * @func: Callback function to process each network
+ * @arg: Opaque argument to pass to callback function
+ *
+ * Iterate over the set of configured networks calling the specified
+ * function for each item. We guard against callbacks removing the
+ * supplied network.
+ */
+void wpa_config_foreach_network(struct wpa_config *config,
+ void (*func)(void *, struct wpa_ssid *),
+ void *arg)
+{
+ struct wpa_ssid *ssid, *next;
+
+ ssid = config->ssid;
+ while (ssid) {
+ next = ssid->next;
+ func(arg, ssid);
+ ssid = next;
+ }
+}
+
+
+/**
* wpa_config_get_network - Get configured network based on id
* @config: Configuration data from wpa_config_read()
* @id: Unique network id to search for
}
+int wpa_config_set_quoted(struct wpa_ssid *ssid, const char *var,
+ const char *value)
+{
+ size_t len;
+ char *buf;
+ int ret;
+
+ len = os_strlen(value);
+ buf = os_malloc(len + 3);
+ if (buf == NULL)
+ return -1;
+ buf[0] = '"';
+ os_memcpy(buf + 1, value, len);
+ buf[len + 1] = '"';
+ buf[len + 2] = '\0';
+ ret = wpa_config_set(ssid, var, buf, 0);
+ os_free(buf);
+ return ret;
+}
+
+
/**
* wpa_config_get_all - Get all options from network configuration
* @ssid: Pointer to network configuration data
* @get_keys: Determines if keys/passwords will be included in returned list
+ * (if they may be exported)
* Returns: %NULL terminated list of all set keys and their values in the form
* of [key1, val1, key2, val2, ... , NULL]
*
char **props;
int fields_num;
+ get_keys = get_keys && ssid->export_keys;
+
props = os_zalloc(sizeof(char *) * ((2 * NUM_SSID_FIELDS) + 1));
if (!props)
return NULL;
config->eapol_version = DEFAULT_EAPOL_VERSION;
config->ap_scan = DEFAULT_AP_SCAN;
config->fast_reauth = DEFAULT_FAST_REAUTH;
+ config->p2p_go_intent = DEFAULT_P2P_GO_INTENT;
+ config->p2p_intra_bss = DEFAULT_P2P_INTRA_BSS;
config->bss_max_count = DEFAULT_BSS_MAX_COUNT;
+ config->bss_expiration_age = DEFAULT_BSS_EXPIRATION_AGE;
+ config->bss_expiration_scan_count = DEFAULT_BSS_EXPIRATION_SCAN_COUNT;
+ config->max_num_sta = DEFAULT_MAX_NUM_STA;
+ config->access_network_type = DEFAULT_ACCESS_NETWORK_TYPE;
if (ctrl_interface)
config->ctrl_interface = os_strdup(ctrl_interface);
}
}
#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+
+struct global_parse_data {
+ char *name;
+ int (*parser)(const struct global_parse_data *data,
+ struct wpa_config *config, int line, const char *value);
+ void *param1, *param2, *param3;
+ unsigned int changed_flag;
+};
+
+
+static int wpa_global_config_parse_int(const struct global_parse_data *data,
+ struct wpa_config *config, int line,
+ const char *pos)
+{
+ int *dst;
+ dst = (int *) (((u8 *) config) + (long) data->param1);
+ *dst = atoi(pos);
+ wpa_printf(MSG_DEBUG, "%s=%d", data->name, *dst);
+
+ if (data->param2 && *dst < (long) data->param2) {
+ wpa_printf(MSG_ERROR, "Line %d: too small %s (value=%d "
+ "min_value=%ld)", line, data->name, *dst,
+ (long) data->param2);
+ *dst = (long) data->param2;
+ return -1;
+ }
+
+ if (data->param3 && *dst > (long) data->param3) {
+ wpa_printf(MSG_ERROR, "Line %d: too large %s (value=%d "
+ "max_value=%ld)", line, data->name, *dst,
+ (long) data->param3);
+ *dst = (long) data->param3;
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int wpa_global_config_parse_str(const struct global_parse_data *data,
+ struct wpa_config *config, int line,
+ const char *pos)
+{
+ size_t len;
+ char **dst, *tmp;
+
+ len = os_strlen(pos);
+ if (data->param2 && len < (size_t) data->param2) {
+ wpa_printf(MSG_ERROR, "Line %d: too short %s (len=%lu "
+ "min_len=%ld)", line, data->name,
+ (unsigned long) len, (long) data->param2);
+ return -1;
+ }
+
+ if (data->param3 && len > (size_t) data->param3) {
+ wpa_printf(MSG_ERROR, "Line %d: too long %s (len=%lu "
+ "max_len=%ld)", line, data->name,
+ (unsigned long) len, (long) data->param3);
+ return -1;
+ }
+
+ tmp = os_strdup(pos);
+ if (tmp == NULL)
+ return -1;
+
+ dst = (char **) (((u8 *) config) + (long) data->param1);
+ os_free(*dst);
+ *dst = tmp;
+ wpa_printf(MSG_DEBUG, "%s='%s'", data->name, *dst);
+
+ return 0;
+}
+
+
+static int wpa_config_process_country(const struct global_parse_data *data,
+ struct wpa_config *config, int line,
+ const char *pos)
+{
+ if (!pos[0] || !pos[1]) {
+ wpa_printf(MSG_DEBUG, "Invalid country set");
+ return -1;
+ }
+ config->country[0] = pos[0];
+ config->country[1] = pos[1];
+ wpa_printf(MSG_DEBUG, "country='%c%c'",
+ config->country[0], config->country[1]);
+ return 0;
+}
+
+
+static int wpa_config_process_load_dynamic_eap(
+ const struct global_parse_data *data, struct wpa_config *config,
+ int line, const char *so)
+{
+ int ret;
+ wpa_printf(MSG_DEBUG, "load_dynamic_eap=%s", so);
+ ret = eap_peer_method_load(so);
+ if (ret == -2) {
+ wpa_printf(MSG_DEBUG, "This EAP type was already loaded - not "
+ "reloading.");
+ } else if (ret) {
+ wpa_printf(MSG_ERROR, "Line %d: Failed to load dynamic EAP "
+ "method '%s'.", line, so);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+#ifdef CONFIG_WPS
+
+static int wpa_config_process_uuid(const struct global_parse_data *data,
+ struct wpa_config *config, int line,
+ const char *pos)
+{
+ char buf[40];
+ if (uuid_str2bin(pos, config->uuid)) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid UUID", line);
+ return -1;
+ }
+ uuid_bin2str(config->uuid, buf, sizeof(buf));
+ wpa_printf(MSG_DEBUG, "uuid=%s", buf);
+ return 0;
+}
+
+
+static int wpa_config_process_device_type(
+ const struct global_parse_data *data,
+ struct wpa_config *config, int line, const char *pos)
+{
+ return wps_dev_type_str2bin(pos, config->device_type);
+}
+
+
+static int wpa_config_process_os_version(const struct global_parse_data *data,
+ struct wpa_config *config, int line,
+ const char *pos)
+{
+ if (hexstr2bin(pos, config->os_version, 4)) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid os_version", line);
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "os_version=%08x",
+ WPA_GET_BE32(config->os_version));
+ return 0;
+}
+
+#endif /* CONFIG_WPS */
+
+#ifdef CONFIG_P2P
+static int wpa_config_process_sec_device_type(
+ const struct global_parse_data *data,
+ struct wpa_config *config, int line, const char *pos)
+{
+ int idx;
+
+ if (config->num_sec_device_types >= MAX_SEC_DEVICE_TYPES) {
+ wpa_printf(MSG_ERROR, "Line %d: too many sec_device_type "
+ "items", line);
+ return -1;
+ }
+
+ idx = config->num_sec_device_types;
+
+ if (wps_dev_type_str2bin(pos, config->sec_device_type[idx]))
+ return -1;
+
+ config->num_sec_device_types++;
+ return 0;
+}
+
+
+static int wpa_config_process_p2p_pref_chan(
+ const struct global_parse_data *data,
+ struct wpa_config *config, int line, const char *pos)
+{
+ struct p2p_channel *pref = NULL, *n;
+ unsigned int num = 0;
+ const char *pos2;
+ u8 op_class, chan;
+
+ /* format: class:chan,class:chan,... */
+
+ while (*pos) {
+ op_class = atoi(pos);
+ pos2 = os_strchr(pos, ':');
+ if (pos2 == NULL)
+ goto fail;
+ pos2++;
+ chan = atoi(pos2);
+
+ n = os_realloc(pref, (num + 1) * sizeof(struct p2p_channel));
+ if (n == NULL)
+ goto fail;
+ pref = n;
+ pref[num].op_class = op_class;
+ pref[num].chan = chan;
+ num++;
+
+ pos = os_strchr(pos2, ',');
+ if (pos == NULL)
+ break;
+ pos++;
+ }
+
+ os_free(config->p2p_pref_chan);
+ config->p2p_pref_chan = pref;
+ config->num_p2p_pref_chan = num;
+ wpa_hexdump(MSG_DEBUG, "P2P: Preferred class/channel pairs",
+ (u8 *) config->p2p_pref_chan,
+ config->num_p2p_pref_chan * sizeof(struct p2p_channel));
+
+ return 0;
+
+fail:
+ os_free(pref);
+ wpa_printf(MSG_ERROR, "Line %d: Invalid p2p_pref_chan list", line);
+ return -1;
+}
+#endif /* CONFIG_P2P */
+
+
+static int wpa_config_process_hessid(
+ const struct global_parse_data *data,
+ struct wpa_config *config, int line, const char *pos)
+{
+ if (hwaddr_aton2(pos, config->hessid) < 0) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid hessid '%s'",
+ line, pos);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+#ifdef OFFSET
+#undef OFFSET
+#endif /* OFFSET */
+/* OFFSET: Get offset of a variable within the wpa_config structure */
+#define OFFSET(v) ((void *) &((struct wpa_config *) 0)->v)
+
+#define FUNC(f) #f, wpa_config_process_ ## f, OFFSET(f), NULL, NULL
+#define FUNC_NO_VAR(f) #f, wpa_config_process_ ## f, NULL, NULL, NULL
+#define _INT(f) #f, wpa_global_config_parse_int, OFFSET(f)
+#define INT(f) _INT(f), NULL, NULL
+#define INT_RANGE(f, min, max) _INT(f), (void *) min, (void *) max
+#define _STR(f) #f, wpa_global_config_parse_str, OFFSET(f)
+#define STR(f) _STR(f), NULL, NULL
+#define STR_RANGE(f, min, max) _STR(f), (void *) min, (void *) max
+
+static const struct global_parse_data global_fields[] = {
+#ifdef CONFIG_CTRL_IFACE
+ { STR(ctrl_interface), 0 },
+ { STR(ctrl_interface_group), 0 } /* deprecated */,
+#endif /* CONFIG_CTRL_IFACE */
+ { INT_RANGE(eapol_version, 1, 2), 0 },
+ { INT(ap_scan), 0 },
+ { INT(fast_reauth), 0 },
+ { STR(opensc_engine_path), 0 },
+ { STR(pkcs11_engine_path), 0 },
+ { STR(pkcs11_module_path), 0 },
+ { STR(driver_param), 0 },
+ { INT(dot11RSNAConfigPMKLifetime), 0 },
+ { INT(dot11RSNAConfigPMKReauthThreshold), 0 },
+ { INT(dot11RSNAConfigSATimeout), 0 },
+#ifndef CONFIG_NO_CONFIG_WRITE
+ { INT(update_config), 0 },
+#endif /* CONFIG_NO_CONFIG_WRITE */
+ { FUNC_NO_VAR(load_dynamic_eap), 0 },
+#ifdef CONFIG_WPS
+ { FUNC(uuid), CFG_CHANGED_UUID },
+ { STR_RANGE(device_name, 0, 32), CFG_CHANGED_DEVICE_NAME },
+ { STR_RANGE(manufacturer, 0, 64), CFG_CHANGED_WPS_STRING },
+ { STR_RANGE(model_name, 0, 32), CFG_CHANGED_WPS_STRING },
+ { STR_RANGE(model_number, 0, 32), CFG_CHANGED_WPS_STRING },
+ { STR_RANGE(serial_number, 0, 32), CFG_CHANGED_WPS_STRING },
+ { FUNC(device_type), CFG_CHANGED_DEVICE_TYPE },
+ { FUNC(os_version), CFG_CHANGED_OS_VERSION },
+ { STR(config_methods), CFG_CHANGED_CONFIG_METHODS },
+ { INT_RANGE(wps_cred_processing, 0, 2), 0 },
+#endif /* CONFIG_WPS */
+#ifdef CONFIG_P2P
+ { FUNC(sec_device_type), CFG_CHANGED_SEC_DEVICE_TYPE },
+ { INT(p2p_listen_reg_class), 0 },
+ { INT(p2p_listen_channel), 0 },
+ { INT(p2p_oper_reg_class), 0 },
+ { INT(p2p_oper_channel), 0 },
+ { INT_RANGE(p2p_go_intent, 0, 15), 0 },
+ { STR(p2p_ssid_postfix), CFG_CHANGED_P2P_SSID_POSTFIX },
+ { INT_RANGE(persistent_reconnect, 0, 1), 0 },
+ { INT_RANGE(p2p_intra_bss, 0, 1), CFG_CHANGED_P2P_INTRA_BSS },
+ { INT(p2p_group_idle), 0 },
+ { FUNC(p2p_pref_chan), CFG_CHANGED_P2P_PREF_CHAN },
+#endif /* CONFIG_P2P */
+ { FUNC(country), CFG_CHANGED_COUNTRY },
+ { INT(bss_max_count), 0 },
+ { INT(bss_expiration_age), 0 },
+ { INT(bss_expiration_scan_count), 0 },
+ { INT_RANGE(filter_ssids, 0, 1), 0 },
+ { INT(max_num_sta), 0 },
+ { INT_RANGE(disassoc_low_ack, 0, 1), 0 },
+ { STR(home_realm), 0 },
+ { STR(home_username), 0 },
+ { STR(home_password), 0 },
+ { STR(home_ca_cert), 0 },
+ { STR(home_imsi), 0 },
+ { STR(home_milenage), 0 },
+ { INT_RANGE(interworking, 0, 1), 0 },
+ { FUNC(hessid), 0 },
+ { INT_RANGE(access_network_type, 0, 15), 0 }
+};
+
+#undef FUNC
+#undef _INT
+#undef INT
+#undef INT_RANGE
+#undef _STR
+#undef STR
+#undef STR_RANGE
+#define NUM_GLOBAL_FIELDS (sizeof(global_fields) / sizeof(global_fields[0]))
+
+
+int wpa_config_process_global(struct wpa_config *config, char *pos, int line)
+{
+ size_t i;
+ int ret = 0;
+
+ for (i = 0; i < NUM_GLOBAL_FIELDS; i++) {
+ const struct global_parse_data *field = &global_fields[i];
+ size_t flen = os_strlen(field->name);
+ if (os_strncmp(pos, field->name, flen) != 0 ||
+ pos[flen] != '=')
+ continue;
+
+ if (field->parser(field, config, line, pos + flen + 1)) {
+ wpa_printf(MSG_ERROR, "Line %d: failed to "
+ "parse '%s'.", line, pos);
+ ret = -1;
+ }
+ config->changed_parameters |= field->changed_flag;
+ break;
+ }
+ if (i == NUM_GLOBAL_FIELDS) {
+ if (line < 0)
+ return -1;
+ wpa_printf(MSG_ERROR, "Line %d: unknown global field '%s'.",
+ line, pos);
+ ret = -1;
+ }
+
+ return ret;
+}
#define DEFAULT_AP_SCAN 1
#endif /* CONFIG_NO_SCAN_PROCESSING */
#define DEFAULT_FAST_REAUTH 1
+#define DEFAULT_P2P_GO_INTENT 7
+#define DEFAULT_P2P_INTRA_BSS 1
#define DEFAULT_BSS_MAX_COUNT 200
+#define DEFAULT_BSS_EXPIRATION_AGE 180
+#define DEFAULT_BSS_EXPIRATION_SCAN_COUNT 2
+#define DEFAULT_MAX_NUM_STA 128
+#define DEFAULT_ACCESS_NETWORK_TYPE 15
#include "config_ssid.h"
-
+#include "wps/wps.h"
+
+
+#define CFG_CHANGED_DEVICE_NAME BIT(0)
+#define CFG_CHANGED_CONFIG_METHODS BIT(1)
+#define CFG_CHANGED_DEVICE_TYPE BIT(2)
+#define CFG_CHANGED_OS_VERSION BIT(3)
+#define CFG_CHANGED_UUID BIT(4)
+#define CFG_CHANGED_COUNTRY BIT(5)
+#define CFG_CHANGED_SEC_DEVICE_TYPE BIT(6)
+#define CFG_CHANGED_P2P_SSID_POSTFIX BIT(7)
+#define CFG_CHANGED_WPS_STRING BIT(8)
+#define CFG_CHANGED_P2P_INTRA_BSS BIT(9)
+#define CFG_CHANGED_VENDOR_EXTENSION BIT(10)
+#define CFG_CHANGED_P2P_LISTEN_CHANNEL BIT(11)
+#define CFG_CHANGED_P2P_OPER_CHANNEL BIT(12)
+#define CFG_CHANGED_P2P_PREF_CHAN BIT(13)
/**
* struct wpa_config - wpa_supplicant configuration data
* If this is specified, %wpa_supplicant will open a control interface
* that is available for external programs to manage %wpa_supplicant.
* The meaning of this string depends on which control interface
- * mechanism is used. For all cases, the existance of this parameter
+ * mechanism is used. For all cases, the existence of this parameter
* in configuration is used to determine whether the control interface
* is enabled.
*
/**
* device_type - Primary Device Type (WPS)
- * Used format: categ-OUI-subcateg
- * categ = Category as an integer value
- * OUI = OUI and type octet as a 4-octet hex-encoded value;
- * 0050F204 for default WPS OUI
- * subcateg = OUI-specific Sub Category as an integer value
- * Examples:
- * 1-0050F204-1 (Computer / PC)
- * 1-0050F204-2 (Computer / Server)
- * 5-0050F204-1 (Storage / NAS)
- * 6-0050F204-1 (Network Infrastructure / AP)
*/
- char *device_type;
+ u8 device_type[WPS_DEV_TYPE_LEN];
/**
* config_methods - Config Methods
*
* This is a space-separated list of supported WPS configuration
- * methods. For example, "label display push_button keypad".
+ * methods. For example, "label virtual_display virtual_push_button
+ * keypad".
* Available methods: usba ethernet label display ext_nfc_token
- * int_nfc_token nfc_interface push_button keypad.
+ * int_nfc_token nfc_interface push_button keypad
+ * virtual_display physical_display
+ * virtual_push_button physical_push_button.
*/
char *config_methods;
*/
int wps_cred_processing;
+#define MAX_SEC_DEVICE_TYPES 5
+ /**
+ * sec_device_types - Secondary Device Types (P2P)
+ */
+ u8 sec_device_type[MAX_SEC_DEVICE_TYPES][WPS_DEV_TYPE_LEN];
+ int num_sec_device_types;
+
+ int p2p_listen_reg_class;
+ int p2p_listen_channel;
+ int p2p_oper_reg_class;
+ int p2p_oper_channel;
+ int p2p_go_intent;
+ char *p2p_ssid_postfix;
+ int persistent_reconnect;
+ int p2p_intra_bss;
+ unsigned int num_p2p_pref_chan;
+ struct p2p_channel *p2p_pref_chan;
+
+#define MAX_WPS_VENDOR_EXT 10
+ /**
+ * wps_vendor_ext - Vendor extension attributes in WPS
+ */
+ struct wpabuf *wps_vendor_ext[MAX_WPS_VENDOR_EXT];
+
+ /**
+ * p2p_group_idle - Maximum idle time in seconds for P2P group
+ *
+ * This value controls how long a P2P group is maintained after there
+ * is no other members in the group. As a GO, this means no associated
+ * stations in the group. As a P2P client, this means no GO seen in
+ * scan results. The maximum idle time is specified in seconds with 0
+ * indicating no time limit, i.e., the P2P group remains in active
+ * state indefinitely until explicitly removed. As a P2P client, the
+ * maximum idle time of P2P_MAX_CLIENT_IDLE seconds is enforced, i.e.,
+ * this parameter is mainly meant for GO use and for P2P client, it can
+ * only be used to reduce the default timeout to smaller value.
+ */
+ unsigned int p2p_group_idle;
+
/**
* bss_max_count - Maximum number of BSS entries to keep in memory
*/
unsigned int bss_max_count;
/**
+ * bss_expiration_age - BSS entry age after which it can be expired
+ *
+ * This value controls the time in seconds after which a BSS entry
+ * gets removed if it has not been updated or is not in use.
+ */
+ unsigned int bss_expiration_age;
+
+ /**
+ * bss_expiration_scan_count - Expire BSS after number of scans
+ *
+ * If the BSS entry has not been seen in this many scans, it will be
+ * removed. A value of 1 means that entry is removed after the first
+ * scan in which the BSSID is not seen. Larger values can be used
+ * to avoid BSS entries disappearing if they are not visible in
+ * every scan (e.g., low signal quality or interference).
+ */
+ unsigned int bss_expiration_scan_count;
+
+ /**
* filter_ssids - SSID-based scan result filtering
*
* 0 = do not filter scan results
* 1 = only include configured SSIDs in scan results/BSS table
*/
int filter_ssids;
+
+ /**
+ * max_num_sta - Maximum number of STAs in an AP/P2P GO
+ */
+ unsigned int max_num_sta;
+
+ /**
+ * changed_parameters - Bitmap of changed parameters since last update
+ */
+ unsigned int changed_parameters;
+
+ /**
+ * disassoc_low_ack - Disassocicate stations with massive packet loss
+ */
+ int disassoc_low_ack;
+
+ /**
+ * interworking - Whether Interworking (IEEE 802.11u) is enabled
+ */
+ int interworking;
+
+ /**
+ * access_network_type - Access Network Type
+ *
+ * When Interworking is enabled, scans will be limited to APs that
+ * advertise the specified Access Network Type (0..15; with 15
+ * indicating wildcard match).
+ */
+ int access_network_type;
+
+ /**
+ * hessid - Homogenous ESS identifier
+ *
+ * If this is set (any octet is non-zero), scans will be used to
+ * request response only from BSSes belonging to the specified
+ * Homogeneous ESS. This is used only if interworking is enabled.
+ */
+ u8 hessid[ETH_ALEN];
+
+ /**
+ * home_realm - Home Realm for Interworking
+ */
+ char *home_realm;
+
+ /**
+ * home_username - Username for Interworking network selection
+ */
+ char *home_username;
+
+ /**
+ * home_password - Password for Interworking network selection
+ */
+ char *home_password;
+
+ /**
+ * home_ca_cert - CA certificate for Interworking network selection
+ */
+ char *home_ca_cert;
+
+ /**
+ * home_imsi - IMSI in <MCC> | <MNC> | '-' | <MSIN> format
+ */
+ char *home_imsi;
+
+ /**
+ * home_milenage - Milenage parameters for SIM/USIM simulator in
+ * <Ki>:<OPc>:<SQN> format
+ */
+ char *home_milenage;
};
void wpa_config_free(struct wpa_config *ssid);
void wpa_config_free_ssid(struct wpa_ssid *ssid);
+void wpa_config_foreach_network(struct wpa_config *config,
+ void (*func)(void *, struct wpa_ssid *),
+ void *arg);
struct wpa_ssid * wpa_config_get_network(struct wpa_config *config, int id);
struct wpa_ssid * wpa_config_add_network(struct wpa_config *config);
int wpa_config_remove_network(struct wpa_config *config, int id);
void wpa_config_set_network_defaults(struct wpa_ssid *ssid);
int wpa_config_set(struct wpa_ssid *ssid, const char *var, const char *value,
int line);
+int wpa_config_set_quoted(struct wpa_ssid *ssid, const char *var,
+ const char *value);
char ** wpa_config_get_all(struct wpa_ssid *ssid, int get_keys);
char * wpa_config_get(struct wpa_ssid *ssid, const char *var);
char * wpa_config_get_no_key(struct wpa_ssid *ssid, const char *var);
#endif /* CONFIG_NO_STDOUT_DEBUG */
+/* Prototypes for common functions from config.c */
+int wpa_config_process_global(struct wpa_config *config, char *pos, int line);
+
+
/* Prototypes for backend specific functions from the selected config_*.c */
/**
#include "config.h"
#include "base64.h"
#include "uuid.h"
-#include "eap_peer/eap_methods.h"
+#include "p2p/p2p.h"
/**
#endif /* CONFIG_NO_CONFIG_BLOBS */
-struct global_parse_data {
- char *name;
- int (*parser)(const struct global_parse_data *data,
- struct wpa_config *config, int line, const char *value);
- void *param1, *param2, *param3;
-};
-
-
-static int wpa_config_parse_int(const struct global_parse_data *data,
- struct wpa_config *config, int line,
- const char *pos)
-{
- int *dst;
- dst = (int *) (((u8 *) config) + (long) data->param1);
- *dst = atoi(pos);
- wpa_printf(MSG_DEBUG, "%s=%d", data->name, *dst);
-
- if (data->param2 && *dst < (long) data->param2) {
- wpa_printf(MSG_ERROR, "Line %d: too small %s (value=%d "
- "min_value=%ld)", line, data->name, *dst,
- (long) data->param2);
- *dst = (long) data->param2;
- return -1;
- }
-
- if (data->param3 && *dst > (long) data->param3) {
- wpa_printf(MSG_ERROR, "Line %d: too large %s (value=%d "
- "max_value=%ld)", line, data->name, *dst,
- (long) data->param3);
- *dst = (long) data->param3;
- return -1;
- }
-
- return 0;
-}
-
-
-static int wpa_config_parse_str(const struct global_parse_data *data,
- struct wpa_config *config, int line,
- const char *pos)
-{
- size_t len;
- char **dst, *tmp;
-
- len = os_strlen(pos);
- if (data->param2 && len < (size_t) data->param2) {
- wpa_printf(MSG_ERROR, "Line %d: too short %s (len=%lu "
- "min_len=%ld)", line, data->name,
- (unsigned long) len, (long) data->param2);
- return -1;
- }
-
- if (data->param3 && len > (size_t) data->param3) {
- wpa_printf(MSG_ERROR, "Line %d: too long %s (len=%lu "
- "max_len=%ld)", line, data->name,
- (unsigned long) len, (long) data->param3);
- return -1;
- }
-
- tmp = os_strdup(pos);
- if (tmp == NULL)
- return -1;
-
- dst = (char **) (((u8 *) config) + (long) data->param1);
- os_free(*dst);
- *dst = tmp;
- wpa_printf(MSG_DEBUG, "%s='%s'", data->name, *dst);
-
- return 0;
-}
-
-
-static int wpa_config_process_country(const struct global_parse_data *data,
- struct wpa_config *config, int line,
- const char *pos)
-{
- if (!pos[0] || !pos[1]) {
- wpa_printf(MSG_DEBUG, "Invalid country set");
- return -1;
- }
- config->country[0] = pos[0];
- config->country[1] = pos[1];
- wpa_printf(MSG_DEBUG, "country='%c%c'",
- config->country[0], config->country[1]);
- return 0;
-}
-
-
-static int wpa_config_process_load_dynamic_eap(
- const struct global_parse_data *data, struct wpa_config *config,
- int line, const char *so)
-{
- int ret;
- wpa_printf(MSG_DEBUG, "load_dynamic_eap=%s", so);
- ret = eap_peer_method_load(so);
- if (ret == -2) {
- wpa_printf(MSG_DEBUG, "This EAP type was already loaded - not "
- "reloading.");
- } else if (ret) {
- wpa_printf(MSG_ERROR, "Line %d: Failed to load dynamic EAP "
- "method '%s'.", line, so);
- return -1;
- }
-
- return 0;
-}
-
-
-#ifdef CONFIG_WPS
-
-static int wpa_config_process_uuid(const struct global_parse_data *data,
- struct wpa_config *config, int line,
- const char *pos)
-{
- char buf[40];
- if (uuid_str2bin(pos, config->uuid)) {
- wpa_printf(MSG_ERROR, "Line %d: invalid UUID", line);
- return -1;
- }
- uuid_bin2str(config->uuid, buf, sizeof(buf));
- wpa_printf(MSG_DEBUG, "uuid=%s", buf);
- return 0;
-}
-
-
-static int wpa_config_process_os_version(const struct global_parse_data *data,
- struct wpa_config *config, int line,
- const char *pos)
-{
- if (hexstr2bin(pos, config->os_version, 4)) {
- wpa_printf(MSG_ERROR, "Line %d: invalid os_version", line);
- return -1;
- }
- wpa_printf(MSG_DEBUG, "os_version=%08x",
- WPA_GET_BE32(config->os_version));
- return 0;
-}
-
-#endif /* CONFIG_WPS */
-
-
-#ifdef OFFSET
-#undef OFFSET
-#endif /* OFFSET */
-/* OFFSET: Get offset of a variable within the wpa_config structure */
-#define OFFSET(v) ((void *) &((struct wpa_config *) 0)->v)
-
-#define FUNC(f) #f, wpa_config_process_ ## f, OFFSET(f), NULL, NULL
-#define FUNC_NO_VAR(f) #f, wpa_config_process_ ## f, NULL, NULL, NULL
-#define _INT(f) #f, wpa_config_parse_int, OFFSET(f)
-#define INT(f) _INT(f), NULL, NULL
-#define INT_RANGE(f, min, max) _INT(f), (void *) min, (void *) max
-#define _STR(f) #f, wpa_config_parse_str, OFFSET(f)
-#define STR(f) _STR(f), NULL, NULL
-#define STR_RANGE(f, min, max) _STR(f), (void *) min, (void *) max
-
-static const struct global_parse_data global_fields[] = {
-#ifdef CONFIG_CTRL_IFACE
- { STR(ctrl_interface) },
- { STR(ctrl_interface_group) } /* deprecated */,
-#endif /* CONFIG_CTRL_IFACE */
- { INT_RANGE(eapol_version, 1, 2) },
- { INT(ap_scan) },
- { INT(fast_reauth) },
- { STR(opensc_engine_path) },
- { STR(pkcs11_engine_path) },
- { STR(pkcs11_module_path) },
- { STR(driver_param) },
- { INT(dot11RSNAConfigPMKLifetime) },
- { INT(dot11RSNAConfigPMKReauthThreshold) },
- { INT(dot11RSNAConfigSATimeout) },
-#ifndef CONFIG_NO_CONFIG_WRITE
- { INT(update_config) },
-#endif /* CONFIG_NO_CONFIG_WRITE */
- { FUNC_NO_VAR(load_dynamic_eap) },
-#ifdef CONFIG_WPS
- { FUNC(uuid) },
- { STR_RANGE(device_name, 0, 32) },
- { STR_RANGE(manufacturer, 0, 64) },
- { STR_RANGE(model_name, 0, 32) },
- { STR_RANGE(model_number, 0, 32) },
- { STR_RANGE(serial_number, 0, 32) },
- { STR(device_type) },
- { FUNC(os_version) },
- { STR(config_methods) },
- { INT_RANGE(wps_cred_processing, 0, 2) },
-#endif /* CONFIG_WPS */
- { FUNC(country) },
- { INT(bss_max_count) },
- { INT_RANGE(filter_ssids, 0, 1) }
-};
-
-#undef FUNC
-#undef _INT
-#undef INT
-#undef INT_RANGE
-#undef _STR
-#undef STR
-#undef STR_RANGE
-#define NUM_GLOBAL_FIELDS (sizeof(global_fields) / sizeof(global_fields[0]))
-
-
-static int wpa_config_process_global(struct wpa_config *config, char *pos,
- int line)
-{
- size_t i;
- int ret = 0;
-
- for (i = 0; i < NUM_GLOBAL_FIELDS; i++) {
- const struct global_parse_data *field = &global_fields[i];
- size_t flen = os_strlen(field->name);
- if (os_strncmp(pos, field->name, flen) != 0 ||
- pos[flen] != '=')
- continue;
-
- if (field->parser(field, config, line, pos + flen + 1)) {
- wpa_printf(MSG_ERROR, "Line %d: failed to "
- "parse '%s'.", line, pos);
- ret = -1;
- }
- break;
- }
- if (i == NUM_GLOBAL_FIELDS) {
- wpa_printf(MSG_ERROR, "Line %d: unknown global field '%s'.",
- line, pos);
- ret = -1;
- }
-
- return ret;
-}
-
-
struct wpa_config * wpa_config_read(const char *name)
{
FILE *f;
config->ssid = head;
wpa_config_debug_dump_networks(config);
+#ifndef WPA_IGNORE_CONFIG_ERRORS
if (errors) {
wpa_config_free(config);
config = NULL;
head = NULL;
}
+#endif /* WPA_IGNORE_CONFIG_ERRORS */
return config;
}
}
+#ifdef CONFIG_P2P
+static void write_p2p_client_list(FILE *f, struct wpa_ssid *ssid)
+{
+ char *value = wpa_config_get(ssid, "p2p_client_list");
+ if (value == NULL)
+ return;
+ fprintf(f, "\tp2p_client_list=%s\n", value);
+ os_free(value);
+}
+#endif /* CONFIG_P2P */
+
+
static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid)
{
int i;
write_pairwise(f, ssid);
write_group(f, ssid);
write_auth_alg(f, ssid);
+ STR(bgscan);
#ifdef IEEE8021X_EAPOL
write_eap(f, ssid);
STR(identity);
INT(ieee80211w);
#endif /* CONFIG_IEEE80211W */
STR(id_str);
+#ifdef CONFIG_P2P
+ write_p2p_client_list(f, ssid);
+#endif /* CONFIG_P2P */
#undef STR
#undef INT
fprintf(f, "model_number=%s\n", config->model_number);
if (config->serial_number)
fprintf(f, "serial_number=%s\n", config->serial_number);
- if (config->device_type)
- fprintf(f, "device_type=%s\n", config->device_type);
+ {
+ char _buf[WPS_DEV_TYPE_BUFSIZE], *buf;
+ buf = wps_dev_type_bin2str(config->device_type,
+ _buf, sizeof(_buf));
+ if (os_strcmp(buf, "0-00000000-0") != 0)
+ fprintf(f, "device_type=%s\n", buf);
+ }
if (WPA_GET_BE32(config->os_version))
fprintf(f, "os_version=%08x\n",
WPA_GET_BE32(config->os_version));
fprintf(f, "wps_cred_processing=%d\n",
config->wps_cred_processing);
#endif /* CONFIG_WPS */
+#ifdef CONFIG_P2P
+ if (config->p2p_listen_reg_class)
+ fprintf(f, "p2p_listen_reg_class=%u\n",
+ config->p2p_listen_reg_class);
+ if (config->p2p_listen_channel)
+ fprintf(f, "p2p_listen_channel=%u\n",
+ config->p2p_listen_channel);
+ if (config->p2p_oper_reg_class)
+ fprintf(f, "p2p_oper_reg_class=%u\n",
+ config->p2p_oper_reg_class);
+ if (config->p2p_oper_channel)
+ fprintf(f, "p2p_oper_channel=%u\n", config->p2p_oper_channel);
+ if (config->p2p_go_intent != DEFAULT_P2P_GO_INTENT)
+ fprintf(f, "p2p_go_intent=%u\n", config->p2p_go_intent);
+ if (config->p2p_ssid_postfix)
+ fprintf(f, "p2p_ssid_postfix=%s\n", config->p2p_ssid_postfix);
+ if (config->persistent_reconnect)
+ fprintf(f, "persistent_reconnect=%u\n",
+ config->persistent_reconnect);
+ if (config->p2p_intra_bss != DEFAULT_P2P_INTRA_BSS)
+ fprintf(f, "p2p_intra_bss=%u\n", config->p2p_intra_bss);
+ if (config->p2p_group_idle)
+ fprintf(f, "p2p_group_idle=%u\n", config->p2p_group_idle);
+ if (config->p2p_pref_chan) {
+ unsigned int i;
+ fprintf(f, "p2p_pref_chan=");
+ for (i = 0; i < config->num_p2p_pref_chan; i++) {
+ fprintf(f, "%s%u:%u", i > 0 ? "," : "",
+ config->p2p_pref_chan[i].op_class,
+ config->p2p_pref_chan[i].chan);
+ }
+ fprintf(f, "\n");
+ }
+#endif /* CONFIG_P2P */
if (config->country[0] && config->country[1]) {
fprintf(f, "country=%c%c\n",
config->country[0], config->country[1]);
}
if (config->bss_max_count != DEFAULT_BSS_MAX_COUNT)
fprintf(f, "bss_max_count=%u\n", config->bss_max_count);
+ if (config->bss_expiration_age != DEFAULT_BSS_EXPIRATION_AGE)
+ fprintf(f, "bss_expiration_age=%u\n",
+ config->bss_expiration_age);
+ if (config->bss_expiration_scan_count !=
+ DEFAULT_BSS_EXPIRATION_SCAN_COUNT)
+ fprintf(f, "bss_expiration_scan_count=%u\n",
+ config->bss_expiration_scan_count);
if (config->filter_ssids)
fprintf(f, "filter_ssids=%d\n", config->filter_ssids);
+ if (config->max_num_sta != DEFAULT_MAX_NUM_STA)
+ fprintf(f, "max_num_sta=%u\n", config->max_num_sta);
+ if (config->disassoc_low_ack)
+ fprintf(f, "disassoc_low_ack=%u\n", config->disassoc_low_ack);
+#ifdef CONFIG_INTERWORKING
+ if (config->home_realm)
+ fprintf(f, "home_realm=%s\n", config->home_realm);
+ if (config->home_username)
+ fprintf(f, "home_username=%s\n", config->home_username);
+ if (config->home_password)
+ fprintf(f, "home_password=%s\n", config->home_password);
+ if (config->home_ca_cert)
+ fprintf(f, "home_ca_cert=%s\n", config->home_ca_cert);
+ if (config->home_imsi)
+ fprintf(f, "home_imsi=%s\n", config->home_imsi);
+ if (config->home_milenage)
+ fprintf(f, "home_milenage=%s\n", config->home_milenage);
+ if (config->interworking)
+ fprintf(f, "interworking=%u\n", config->interworking);
+ if (!is_zero_ether_addr(config->hessid))
+ fprintf(f, "hessid=" MACSTR "\n", MAC2STR(config->hessid));
+ if (config->access_network_type != DEFAULT_ACCESS_NETWORK_TYPE)
+ fprintf(f, "access_network_type=%d\n",
+ config->access_network_type);
+#endif /* CONFIG_INTERWORKING */
}
#endif /* CONFIG_NO_CONFIG_WRITE */
wpa_config_write_global(f, config);
for (ssid = config->ssid; ssid; ssid = ssid->next) {
- if (ssid->key_mgmt == WPA_KEY_MGMT_WPS)
- continue; /* do not save temporary WPS networks */
+ if (ssid->key_mgmt == WPA_KEY_MGMT_WPS || ssid->temporary)
+ continue; /* do not save temporary networks */
+ if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt) && !ssid->psk_set &&
+ !ssid->passphrase)
+ continue; /* do not save invalid network */
fprintf(f, "\nnetwork={\n");
wpa_config_write_network(f, ssid);
fprintf(f, "}\n");
*
* If set, this network block is used only when associating with the AP
* using the configured BSSID
+ *
+ * If this is a persistent P2P group (disabled == 2), this is the GO
+ * Device Address.
*/
u8 bssid[ETH_ALEN];
*
* 2 = AP (access point)
*
+ * 3 = P2P Group Owner (can be set in the configuration file)
+ *
+ * 4 = P2P Group Formation (used internally; not in configuration
+ * files)
+ *
* Note: IBSS can only be used with key_mgmt NONE (plaintext and
* static WEP) and key_mgmt=WPA-NONE (fixed group key TKIP/CCMP). In
* addition, ap_scan has to be set to 2 for IBSS. WPA-None requires
WPAS_MODE_INFRA = 0,
WPAS_MODE_IBSS = 1,
WPAS_MODE_AP = 2,
+ WPAS_MODE_P2P_GO = 3,
+ WPAS_MODE_P2P_GROUP_FORMATION = 4,
} mode;
/**
* 0 = this network can be used (default).
* 1 = this network block is disabled (can be enabled through
* ctrl_iface, e.g., with wpa_cli or wpa_gui).
+ * 2 = this network block includes parameters for a persistent P2P
+ * group (can be used with P2P ctrl_iface commands)
*/
int disabled;
* considered when selecting a BSS.
*/
int *freq_list;
+
+ /**
+ * p2p_client_list - List of P2P Clients in a persistent group (GO)
+ *
+ * This is a list of P2P Clients (P2P Device Address) that have joined
+ * the persistent group. This is maintained on the GO for persistent
+ * group entries (disabled == 2).
+ */
+ u8 *p2p_client_list;
+
+ /**
+ * num_p2p_clients - Number of entries in p2p_client_list
+ */
+ size_t num_p2p_clients;
+
+ /**
+ * p2p_group - Network generated as a P2P group (used internally)
+ */
+ int p2p_group;
+
+ /**
+ * p2p_persistent_group - Whether this is a persistent group
+ */
+ int p2p_persistent_group;
+
+ /**
+ * temporary - Whether this network is temporary and not to be saved
+ */
+ int temporary;
+
+ /**
+ * export_keys - Whether keys may be exported
+ *
+ * This attribute will be set when keys are determined through
+ * WPS or similar so that they may be exported.
+ */
+ int export_keys;
};
#endif /* CONFIG_SSID_H */
hk, TEXT("model_name"));
config->serial_number = wpa_config_read_reg_string(
hk, TEXT("serial_number"));
- config->device_type = wpa_config_read_reg_string(
- hk, TEXT("device_type"));
+ {
+ char *t = wpa_config_read_reg_string(
+ hk, TEXT("device_type"));
+ if (t && wps_dev_type_str2bin(t, config->device_type))
+ errors++;
+ os_free(t);
+ }
config->config_methods = wpa_config_read_reg_string(
hk, TEXT("config_methods"));
if (wpa_config_read_global_os_version(config, hk))
wpa_config_read_reg_dword(hk, TEXT("wps_cred_processing"),
&config->wps_cred_processing);
#endif /* CONFIG_WPS */
+#ifdef CONFIG_P2P
+ config->p2p_ssid_postfix = wpa_config_read_reg_string(
+ hk, TEXT("p2p_ssid_postfix"));
+ wpa_config_read_reg_dword(hk, TEXT("p2p_group_idle"),
+ (int *) &config->p2p_group_idle);
+#endif /* CONFIG_P2P */
wpa_config_read_reg_dword(hk, TEXT("bss_max_count"),
(int *) &config->bss_max_count);
wpa_config_read_reg_dword(hk, TEXT("filter_ssids"),
&config->filter_ssids);
+ wpa_config_read_reg_dword(hk, TEXT("max_num_sta"),
+ (int *) &config->max_num_sta);
+ wpa_config_read_reg_dword(hk, TEXT("disassoc_low_ack"),
+ (int *) &config->disassoc_low_ack);
return errors ? -1 : 0;
}
wpa_config_write_reg_string(hk, "model_number", config->model_number);
wpa_config_write_reg_string(hk, "serial_number",
config->serial_number);
- wpa_config_write_reg_string(hk, "device_type", config->device_type);
+ {
+ char _buf[WPS_DEV_TYPE_BUFSIZE], *buf;
+ buf = wps_dev_type_bin2str(config->device_type,
+ _buf, sizeof(_buf));
+ wpa_config_write_reg_string(hk, "device_type", buf);
+ }
wpa_config_write_reg_string(hk, "config_methods",
config->config_methods);
if (WPA_GET_BE32(config->os_version)) {
wpa_config_write_reg_dword(hk, TEXT("wps_cred_processing"),
config->wps_cred_processing, 0);
#endif /* CONFIG_WPS */
+#ifdef CONFIG_P2P
+ wpa_config_write_reg_string(hk, "p2p_ssid_postfix",
+ config->p2p_ssid_postfix);
+ wpa_config_write_reg_dword(hk, TEXT("p2p_group_idle"),
+ config->p2p_group_idle, 0);
+#endif /* CONFIG_P2P */
wpa_config_write_reg_dword(hk, TEXT("bss_max_count"),
config->bss_max_count,
DEFAULT_BSS_MAX_COUNT);
wpa_config_write_reg_dword(hk, TEXT("filter_ssids"),
config->filter_ssids, 0);
+ wpa_config_write_reg_dword(hk, TEXT("max_num_sta"),
+ config->max_num_sta, DEFAULT_MAX_NUM_STA);
+ wpa_config_write_reg_dword(hk, TEXT("disassoc_low_ack"),
+ config->disassoc_low_ack, 0);
return 0;
}
#include "utils/common.h"
#include "utils/eloop.h"
+#include "common/version.h"
#include "common/ieee802_11_defs.h"
#include "common/wpa_ctrl.h"
#include "eap_peer/eap.h"
#include "wps_supplicant.h"
#include "ibss_rsn.h"
#include "ap.h"
+#include "p2p_supplicant.h"
+#include "p2p/p2p.h"
#include "notify.h"
#include "bss.h"
#include "scan.h"
#include "ctrl_iface.h"
+#include "interworking.h"
+#include "blacklist.h"
+#include "wpas_glue.h"
extern struct wpa_driver_ops *wpa_drivers[];
} else if (os_strcasecmp(cmd, "dot11RSNAConfigSATimeout") == 0) {
if (wpa_sm_set_param(wpa_s->wpa, RSNA_SA_TIMEOUT, atoi(value)))
ret = -1;
- } else
- ret = -1;
+ } else if (os_strcasecmp(cmd, "wps_fragment_size") == 0) {
+ wpa_s->wps_fragment_size = atoi(value);
+#ifdef CONFIG_WPS_TESTING
+ } else if (os_strcasecmp(cmd, "wps_version_number") == 0) {
+ long int val;
+ val = strtol(value, NULL, 0);
+ if (val < 0 || val > 0xff) {
+ ret = -1;
+ wpa_printf(MSG_DEBUG, "WPS: Invalid "
+ "wps_version_number %ld", val);
+ } else {
+ wps_version_number = val;
+ wpa_printf(MSG_DEBUG, "WPS: Testing - force WPS "
+ "version %u.%u",
+ (wps_version_number & 0xf0) >> 4,
+ wps_version_number & 0x0f);
+ }
+ } else if (os_strcasecmp(cmd, "wps_testing_dummy_cred") == 0) {
+ wps_testing_dummy_cred = atoi(value);
+ wpa_printf(MSG_DEBUG, "WPS: Testing - dummy_cred=%d",
+ wps_testing_dummy_cred);
+#endif /* CONFIG_WPS_TESTING */
+ } else if (os_strcasecmp(cmd, "ampdu") == 0) {
+ if (wpa_drv_ampdu(wpa_s, atoi(value)) < 0)
+ ret = -1;
+#ifdef CONFIG_TDLS_TESTING
+ } else if (os_strcasecmp(cmd, "tdls_testing") == 0) {
+ extern unsigned int tdls_testing;
+ tdls_testing = strtol(value, NULL, 0);
+ wpa_printf(MSG_DEBUG, "TDLS: tdls_testing=0x%x", tdls_testing);
+#endif /* CONFIG_TDLS_TESTING */
+#ifdef CONFIG_TDLS
+ } else if (os_strcasecmp(cmd, "tdls_disabled") == 0) {
+ int disabled = atoi(value);
+ wpa_printf(MSG_DEBUG, "TDLS: tdls_disabled=%d", disabled);
+ if (disabled) {
+ if (wpa_drv_tdls_oper(wpa_s, TDLS_DISABLE, NULL) < 0)
+ ret = -1;
+ } else if (wpa_drv_tdls_oper(wpa_s, TDLS_ENABLE, NULL) < 0)
+ ret = -1;
+ wpa_tdls_enable(wpa_s->wpa, !disabled);
+#endif /* CONFIG_TDLS */
+ } else {
+ value[-1] = '=';
+ ret = wpa_config_process_global(wpa_s->conf, cmd, -1);
+ if (ret == 0)
+ wpa_supplicant_update_config(wpa_s);
+ }
return ret;
}
+static int wpa_supplicant_ctrl_iface_get(struct wpa_supplicant *wpa_s,
+ char *cmd, char *buf, size_t buflen)
+{
+ int res;
+
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE GET '%s'", cmd);
+
+ if (os_strcmp(cmd, "version") == 0) {
+ res = os_snprintf(buf, buflen, "%s", VERSION_STR);
+ if (res < 0 || (unsigned int) res >= buflen)
+ return -1;
+ return res;
+ }
+
+ return -1;
+}
+
+
#ifdef IEEE8021X_EAPOL
static int wpa_supplicant_ctrl_iface_preauth(struct wpa_supplicant *wpa_s,
char *addr)
#endif /* CONFIG_PEERKEY */
+#ifdef CONFIG_TDLS
+
+static int wpa_supplicant_ctrl_iface_tdls_discover(
+ struct wpa_supplicant *wpa_s, char *addr)
+{
+ u8 peer[ETH_ALEN];
+ int ret;
+
+ if (hwaddr_aton(addr, peer)) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_DISCOVER: invalid "
+ "address '%s'", addr);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_DISCOVER " MACSTR,
+ MAC2STR(peer));
+
+ if (wpa_tdls_is_external_setup(wpa_s->wpa))
+ ret = wpa_tdls_send_discovery_request(wpa_s->wpa, peer);
+ else
+ ret = wpa_drv_tdls_oper(wpa_s, TDLS_DISCOVERY_REQ, peer);
+
+ return ret;
+}
+
+
+static int wpa_supplicant_ctrl_iface_tdls_setup(
+ struct wpa_supplicant *wpa_s, char *addr)
+{
+ u8 peer[ETH_ALEN];
+ int ret;
+
+ if (hwaddr_aton(addr, peer)) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_SETUP: invalid "
+ "address '%s'", addr);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_SETUP " MACSTR,
+ MAC2STR(peer));
+
+ ret = wpa_tdls_reneg(wpa_s->wpa, peer);
+ if (ret) {
+ if (wpa_tdls_is_external_setup(wpa_s->wpa))
+ ret = wpa_tdls_start(wpa_s->wpa, peer);
+ else
+ ret = wpa_drv_tdls_oper(wpa_s, TDLS_SETUP, peer);
+ }
+
+ return ret;
+}
+
+
+static int wpa_supplicant_ctrl_iface_tdls_teardown(
+ struct wpa_supplicant *wpa_s, char *addr)
+{
+ u8 peer[ETH_ALEN];
+
+ if (hwaddr_aton(addr, peer)) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_TEARDOWN: invalid "
+ "address '%s'", addr);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_TEARDOWN " MACSTR,
+ MAC2STR(peer));
+
+ return wpa_tdls_teardown_link(wpa_s->wpa, peer,
+ WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
+}
+
+#endif /* CONFIG_TDLS */
+
+
#ifdef CONFIG_IEEE80211R
static int wpa_supplicant_ctrl_iface_ft_ds(
struct wpa_supplicant *wpa_s, char *addr)
char *cmd)
{
u8 bssid[ETH_ALEN], *_bssid = bssid;
+#ifdef CONFIG_P2P
+ u8 p2p_dev_addr[ETH_ALEN];
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_AP
+ u8 *_p2p_dev_addr = NULL;
+#endif /* CONFIG_AP */
- if (cmd == NULL || os_strcmp(cmd, "any") == 0)
+ if (cmd == NULL || os_strcmp(cmd, "any") == 0) {
_bssid = NULL;
- else if (hwaddr_aton(cmd, bssid)) {
+#ifdef CONFIG_P2P
+ } else if (os_strncmp(cmd, "p2p_dev_addr=", 13) == 0) {
+ if (hwaddr_aton(cmd + 13, p2p_dev_addr)) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_PBC: invalid "
+ "P2P Device Address '%s'",
+ cmd + 13);
+ return -1;
+ }
+ _p2p_dev_addr = p2p_dev_addr;
+#endif /* CONFIG_P2P */
+ } else if (hwaddr_aton(cmd, bssid)) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_PBC: invalid BSSID '%s'",
cmd);
return -1;
#ifdef CONFIG_AP
if (wpa_s->ap_iface)
- return wpa_supplicant_ap_wps_pbc(wpa_s, _bssid);
+ return wpa_supplicant_ap_wps_pbc(wpa_s, _bssid, _p2p_dev_addr);
#endif /* CONFIG_AP */
- return wpas_wps_start_pbc(wpa_s, _bssid);
+ return wpas_wps_start_pbc(wpa_s, _bssid, 0);
}
if (os_strcmp(cmd, "any") == 0)
_bssid = NULL;
- else if (hwaddr_aton(cmd, bssid)) {
+ else if (os_strcmp(cmd, "get") == 0) {
+ ret = wps_generate_pin();
+ goto done;
+ } else if (hwaddr_aton(cmd, bssid)) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_PIN: invalid BSSID '%s'",
cmd);
return -1;
#endif /* CONFIG_AP */
if (pin) {
- ret = wpas_wps_start_pin(wpa_s, _bssid, pin);
+ ret = wpas_wps_start_pin(wpa_s, _bssid, pin, 0,
+ DEV_PW_DEFAULT);
if (ret < 0)
return -1;
ret = os_snprintf(buf, buflen, "%s", pin);
return ret;
}
- ret = wpas_wps_start_pin(wpa_s, _bssid, NULL);
+ ret = wpas_wps_start_pin(wpa_s, _bssid, NULL, 0, DEV_PW_DEFAULT);
if (ret < 0)
return -1;
+done:
/* Return the generated PIN */
ret = os_snprintf(buf, buflen, "%08d", ret);
if (ret < 0 || (size_t) ret >= buflen)
}
+static int wpa_supplicant_ctrl_iface_wps_check_pin(
+ struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen)
+{
+ char pin[9];
+ size_t len;
+ char *pos;
+ int ret;
+
+ wpa_hexdump_ascii_key(MSG_DEBUG, "WPS_CHECK_PIN",
+ (u8 *) cmd, os_strlen(cmd));
+ for (pos = cmd, len = 0; *pos != '\0'; pos++) {
+ if (*pos < '0' || *pos > '9')
+ continue;
+ pin[len++] = *pos;
+ if (len == 9) {
+ wpa_printf(MSG_DEBUG, "WPS: Too long PIN");
+ return -1;
+ }
+ }
+ if (len != 4 && len != 8) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid PIN length %d", (int) len);
+ return -1;
+ }
+ pin[len] = '\0';
+
+ if (len == 8) {
+ unsigned int pin_val;
+ pin_val = atoi(pin);
+ if (!wps_pin_valid(pin_val)) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid checksum digit");
+ ret = os_snprintf(buf, buflen, "FAIL-CHECKSUM\n");
+ if (ret < 0 || (size_t) ret >= buflen)
+ return -1;
+ return ret;
+ }
+ }
+
+ ret = os_snprintf(buf, buflen, "%s", pin);
+ if (ret < 0 || (size_t) ret >= buflen)
+ return -1;
+
+ return ret;
+}
+
+
#ifdef CONFIG_WPS_OOB
static int wpa_supplicant_ctrl_iface_wps_oob(struct wpa_supplicant *wpa_s,
char *cmd)
static int wpa_supplicant_ctrl_iface_wps_reg(struct wpa_supplicant *wpa_s,
char *cmd)
{
- u8 bssid[ETH_ALEN], *_bssid = bssid;
+ u8 bssid[ETH_ALEN];
char *pin;
char *new_ssid;
char *new_auth;
return -1;
*pin++ = '\0';
- if (os_strcmp(cmd, "any") == 0)
- _bssid = NULL;
- else if (hwaddr_aton(cmd, bssid)) {
+ if (hwaddr_aton(cmd, bssid)) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_REG: invalid BSSID '%s'",
cmd);
return -1;
new_ssid = os_strchr(pin, ' ');
if (new_ssid == NULL)
- return wpas_wps_start_reg(wpa_s, _bssid, pin, NULL);
+ return wpas_wps_start_reg(wpa_s, bssid, pin, NULL);
*new_ssid++ = '\0';
new_auth = os_strchr(new_ssid, ' ');
ap.auth = new_auth;
ap.encr = new_encr;
ap.key_hex = new_key;
- return wpas_wps_start_reg(wpa_s, _bssid, pin, &ap);
+ return wpas_wps_start_reg(wpa_s, bssid, pin, &ap);
+}
+
+
+#ifdef CONFIG_AP
+static int wpa_supplicant_ctrl_iface_wps_ap_pin(struct wpa_supplicant *wpa_s,
+ char *cmd, char *buf,
+ size_t buflen)
+{
+ int timeout = 300;
+ char *pos;
+ const char *pin_txt;
+
+ if (!wpa_s->ap_iface)
+ return -1;
+
+ pos = os_strchr(cmd, ' ');
+ if (pos)
+ *pos++ = '\0';
+
+ if (os_strcmp(cmd, "disable") == 0) {
+ wpas_wps_ap_pin_disable(wpa_s);
+ return os_snprintf(buf, buflen, "OK\n");
+ }
+
+ if (os_strcmp(cmd, "random") == 0) {
+ if (pos)
+ timeout = atoi(pos);
+ pin_txt = wpas_wps_ap_pin_random(wpa_s, timeout);
+ if (pin_txt == NULL)
+ return -1;
+ return os_snprintf(buf, buflen, "%s", pin_txt);
+ }
+
+ if (os_strcmp(cmd, "get") == 0) {
+ pin_txt = wpas_wps_ap_pin_get(wpa_s);
+ if (pin_txt == NULL)
+ return -1;
+ return os_snprintf(buf, buflen, "%s", pin_txt);
+ }
+
+ if (os_strcmp(cmd, "set") == 0) {
+ char *pin;
+ if (pos == NULL)
+ return -1;
+ pin = pos;
+ pos = os_strchr(pos, ' ');
+ if (pos) {
+ *pos++ = '\0';
+ timeout = atoi(pos);
+ }
+ if (os_strlen(pin) > buflen)
+ return -1;
+ if (wpas_wps_ap_pin_set(wpa_s, pin, timeout) < 0)
+ return -1;
+ return os_snprintf(buf, buflen, "%s", pin);
+ }
+
+ return -1;
}
+#endif /* CONFIG_AP */
#ifdef CONFIG_WPS_ER
static int wpa_supplicant_ctrl_iface_wps_er_pin(struct wpa_supplicant *wpa_s,
char *cmd)
{
- char *uuid = cmd, *pin;
+ char *uuid = cmd, *pin, *pos;
+ u8 addr_buf[ETH_ALEN], *addr = NULL;
pin = os_strchr(uuid, ' ');
if (pin == NULL)
return -1;
*pin++ = '\0';
- return wpas_wps_er_add_pin(wpa_s, uuid, pin);
+ pos = os_strchr(pin, ' ');
+ if (pos) {
+ *pos++ = '\0';
+ if (hwaddr_aton(pos, addr_buf) == 0)
+ addr = addr_buf;
+ }
+ return wpas_wps_er_add_pin(wpa_s, addr, uuid, pin);
}
*pin++ = '\0';
return wpas_wps_er_learn(wpa_s, uuid, pin);
}
+
+
+static int wpa_supplicant_ctrl_iface_wps_er_set_config(
+ struct wpa_supplicant *wpa_s, char *cmd)
+{
+ char *uuid = cmd, *id;
+ id = os_strchr(uuid, ' ');
+ if (id == NULL)
+ return -1;
+ *id++ = '\0';
+ return wpas_wps_er_set_config(wpa_s, uuid, atoi(id));
+}
+
+
+static int wpa_supplicant_ctrl_iface_wps_er_config(
+ struct wpa_supplicant *wpa_s, char *cmd)
+{
+ char *pin;
+ char *new_ssid;
+ char *new_auth;
+ char *new_encr;
+ char *new_key;
+ struct wps_new_ap_settings ap;
+
+ pin = os_strchr(cmd, ' ');
+ if (pin == NULL)
+ return -1;
+ *pin++ = '\0';
+
+ new_ssid = os_strchr(pin, ' ');
+ if (new_ssid == NULL)
+ return -1;
+ *new_ssid++ = '\0';
+
+ new_auth = os_strchr(new_ssid, ' ');
+ if (new_auth == NULL)
+ return -1;
+ *new_auth++ = '\0';
+
+ new_encr = os_strchr(new_auth, ' ');
+ if (new_encr == NULL)
+ return -1;
+ *new_encr++ = '\0';
+
+ new_key = os_strchr(new_encr, ' ');
+ if (new_key == NULL)
+ return -1;
+ *new_key++ = '\0';
+
+ os_memset(&ap, 0, sizeof(ap));
+ ap.ssid_hex = new_ssid;
+ ap.auth = new_auth;
+ ap.encr = new_encr;
+ ap.key_hex = new_key;
+ return wpas_wps_er_config(wpa_s, cmd, pin, &ap);
+}
#endif /* CONFIG_WPS_ER */
#endif /* CONFIG_WPS */
#endif /* CONFIG_IBSS_RSN */
-static int wpa_supplicant_ctrl_iface_ctrl_rsp(struct wpa_supplicant *wpa_s,
- char *rsp)
+int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid,
+ const char *field,
+ const char *value)
{
#ifdef IEEE8021X_EAPOL
- char *pos, *id_pos;
- int id;
- struct wpa_ssid *ssid;
- struct eap_peer_config *eap;
-
- pos = os_strchr(rsp, '-');
- if (pos == NULL)
- return -1;
- *pos++ = '\0';
- id_pos = pos;
- pos = os_strchr(pos, ':');
- if (pos == NULL)
- return -1;
- *pos++ = '\0';
- id = atoi(id_pos);
- wpa_printf(MSG_DEBUG, "CTRL_IFACE: field=%s id=%d", rsp, id);
- wpa_hexdump_ascii_key(MSG_DEBUG, "CTRL_IFACE: value",
- (u8 *) pos, os_strlen(pos));
+ struct eap_peer_config *eap = &ssid->eap;
- ssid = wpa_config_get_network(wpa_s->conf, id);
- if (ssid == NULL) {
- wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d "
- "to update", id);
- return -1;
- }
- eap = &ssid->eap;
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: response handle field=%s", field);
+ wpa_hexdump_ascii_key(MSG_DEBUG, "CTRL_IFACE: response value",
+ (const u8 *) value, os_strlen(value));
- if (os_strcmp(rsp, "IDENTITY") == 0) {
+ switch (wpa_supplicant_ctrl_req_from_string(field)) {
+ case WPA_CTRL_REQ_EAP_IDENTITY:
os_free(eap->identity);
- eap->identity = (u8 *) os_strdup(pos);
- eap->identity_len = os_strlen(pos);
+ eap->identity = (u8 *) os_strdup(value);
+ eap->identity_len = os_strlen(value);
eap->pending_req_identity = 0;
if (ssid == wpa_s->current_ssid)
wpa_s->reassociate = 1;
- } else if (os_strcmp(rsp, "PASSWORD") == 0) {
+ break;
+ case WPA_CTRL_REQ_EAP_PASSWORD:
os_free(eap->password);
- eap->password = (u8 *) os_strdup(pos);
- eap->password_len = os_strlen(pos);
+ eap->password = (u8 *) os_strdup(value);
+ eap->password_len = os_strlen(value);
eap->pending_req_password = 0;
if (ssid == wpa_s->current_ssid)
wpa_s->reassociate = 1;
- } else if (os_strcmp(rsp, "NEW_PASSWORD") == 0) {
+ break;
+ case WPA_CTRL_REQ_EAP_NEW_PASSWORD:
os_free(eap->new_password);
- eap->new_password = (u8 *) os_strdup(pos);
- eap->new_password_len = os_strlen(pos);
+ eap->new_password = (u8 *) os_strdup(value);
+ eap->new_password_len = os_strlen(value);
eap->pending_req_new_password = 0;
if (ssid == wpa_s->current_ssid)
wpa_s->reassociate = 1;
- } else if (os_strcmp(rsp, "PIN") == 0) {
+ break;
+ case WPA_CTRL_REQ_EAP_PIN:
os_free(eap->pin);
- eap->pin = os_strdup(pos);
+ eap->pin = os_strdup(value);
eap->pending_req_pin = 0;
if (ssid == wpa_s->current_ssid)
wpa_s->reassociate = 1;
- } else if (os_strcmp(rsp, "OTP") == 0) {
+ break;
+ case WPA_CTRL_REQ_EAP_OTP:
os_free(eap->otp);
- eap->otp = (u8 *) os_strdup(pos);
- eap->otp_len = os_strlen(pos);
+ eap->otp = (u8 *) os_strdup(value);
+ eap->otp_len = os_strlen(value);
os_free(eap->pending_req_otp);
eap->pending_req_otp = NULL;
eap->pending_req_otp_len = 0;
- } else if (os_strcmp(rsp, "PASSPHRASE") == 0) {
+ break;
+ case WPA_CTRL_REQ_EAP_PASSPHRASE:
os_free(eap->private_key_passwd);
- eap->private_key_passwd = (u8 *) os_strdup(pos);
+ eap->private_key_passwd = (u8 *) os_strdup(value);
eap->pending_req_passphrase = 0;
if (ssid == wpa_s->current_ssid)
wpa_s->reassociate = 1;
- } else {
- wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown field '%s'", rsp);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown field '%s'", field);
return -1;
}
return 0;
#else /* IEEE8021X_EAPOL */
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: IEEE 802.1X not included");
+ return -1;
+#endif /* IEEE8021X_EAPOL */
+}
+
+
+static int wpa_supplicant_ctrl_iface_ctrl_rsp(struct wpa_supplicant *wpa_s,
+ char *rsp)
+{
+#ifdef IEEE8021X_EAPOL
+ char *pos, *id_pos;
+ int id;
+ struct wpa_ssid *ssid;
+
+ pos = os_strchr(rsp, '-');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+ id_pos = pos;
+ pos = os_strchr(pos, ':');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+ id = atoi(id_pos);
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: field=%s id=%d", rsp, id);
+ wpa_hexdump_ascii_key(MSG_DEBUG, "CTRL_IFACE: value",
+ (u8 *) pos, os_strlen(pos));
+
+ ssid = wpa_config_get_network(wpa_s->conf, id);
+ if (ssid == NULL) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d "
+ "to update", id);
+ return -1;
+ }
+
+ return wpa_supplicant_ctrl_iface_ctrl_rsp_handle(wpa_s, ssid, rsp,
+ pos);
+#else /* IEEE8021X_EAPOL */
wpa_printf(MSG_DEBUG, "CTRL_IFACE: 802.1X not included");
return -1;
#endif /* IEEE8021X_EAPOL */
ret = os_snprintf(pos, end - pos,
"mode=AP\n");
break;
+ case WPAS_MODE_P2P_GO:
+ ret = os_snprintf(pos, end - pos,
+ "mode=P2P GO\n");
+ break;
+ case WPAS_MODE_P2P_GROUP_FORMATION:
+ ret = os_snprintf(pos, end - pos,
+ "mode=P2P GO - group "
+ "formation\n");
+ break;
default:
ret = 0;
break;
pos += ret;
}
- if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) ||
- wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
- res = eapol_sm_get_status(wpa_s->eapol, pos, end - pos,
- verbose);
- if (res >= 0)
- pos += res;
- }
-
+#ifdef CONFIG_P2P
+ if (wpa_s->global->p2p) {
+ ret = os_snprintf(pos, end - pos, "p2p_device_address=" MACSTR
+ "\n", MAC2STR(wpa_s->global->p2p_dev_addr));
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ }
+#endif /* CONFIG_P2P */
+
+ ret = os_snprintf(pos, end - pos, "address=" MACSTR "\n",
+ MAC2STR(wpa_s->own_addr));
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+
+ if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) ||
+ wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
+ res = eapol_sm_get_status(wpa_s->eapol, pos, end - pos,
+ verbose);
+ if (res >= 0)
+ pos += res;
+ }
+
res = rsn_preauth_get_status(wpa_s->wpa, pos, end - pos, verbose);
if (res >= 0)
pos += res;
}
+static int wpa_supplicant_ctrl_iface_blacklist(struct wpa_supplicant *wpa_s,
+ char *cmd, char *buf,
+ size_t buflen)
+{
+ u8 bssid[ETH_ALEN];
+ struct wpa_blacklist *e;
+ char *pos, *end;
+ int ret;
+
+ /* cmd: "BLACKLIST [<BSSID>]" */
+ if (*cmd == '\0') {
+ pos = buf;
+ end = buf + buflen;
+ e = wpa_s->blacklist;
+ while (e) {
+ ret = os_snprintf(pos, end - pos, MACSTR "\n",
+ MAC2STR(e->bssid));
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ e = e->next;
+ }
+ return pos - buf;
+ }
+
+ cmd++;
+ if (os_strncmp(cmd, "clear", 5) == 0) {
+ wpa_blacklist_clear(wpa_s);
+ os_memcpy(buf, "OK\n", 3);
+ return 3;
+ }
+
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: BLACKLIST bssid='%s'", cmd);
+ if (hwaddr_aton(cmd, bssid)) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: invalid BSSID '%s'", cmd);
+ return -1;
+ }
+
+ /*
+ * Add the BSSID twice, so its count will be 2, causing it to be
+ * skipped when processing scan results.
+ */
+ ret = wpa_blacklist_add(wpa_s, bssid);
+ if (ret != 0)
+ return -1;
+ ret = wpa_blacklist_add(wpa_s, bssid);
+ if (ret != 0)
+ return -1;
+ os_memcpy(buf, "OK\n", 3);
+ return 3;
+}
+
+
+extern int wpa_debug_level;
+extern int wpa_debug_timestamp;
+
+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)
+{
+ char *pos, *end, *stamp;
+ int ret;
+
+ if (cmd == NULL) {
+ return -1;
+ }
+
+ /* cmd: "LOG_LEVEL [<level>]" */
+ if (*cmd == '\0') {
+ pos = buf;
+ end = buf + buflen;
+ ret = os_snprintf(pos, end - pos, "Current level: %s\n"
+ "Timestamp: %d\n",
+ debug_level_str(wpa_debug_level),
+ wpa_debug_timestamp);
+ if (ret < 0 || ret >= end - pos)
+ ret = 0;
+
+ return ret;
+ }
+
+ while (*cmd == ' ')
+ cmd++;
+
+ stamp = os_strchr(cmd, ' ');
+ if (stamp) {
+ *stamp++ = '\0';
+ while (*stamp == ' ') {
+ stamp++;
+ }
+ }
+
+ if (cmd && os_strlen(cmd)) {
+ int level = str_to_debug_level(cmd);
+ if (level < 0)
+ return -1;
+ wpa_debug_level = level;
+ }
+
+ if (stamp && os_strlen(stamp))
+ wpa_debug_timestamp = atoi(stamp);
+
+ os_memcpy(buf, "OK\n", 3);
+ return 3;
+}
+
+
static int wpa_supplicant_ctrl_iface_list_networks(
struct wpa_supplicant *wpa_s, char *buf, size_t buflen)
{
if (ret < 0 || ret >= end - pos)
return pos - buf;
pos += ret;
- ret = os_snprintf(pos, end - pos, "\t%s%s",
+ ret = os_snprintf(pos, end - pos, "\t%s%s%s",
ssid == wpa_s->current_ssid ?
"[CURRENT]" : "",
- ssid->disabled ? "[DISABLED]" : "");
+ ssid->disabled ? "[DISABLED]" : "",
+ ssid->disabled == 2 ? "[P2P-PERSISTENT]" :
+ "");
if (ret < 0 || ret >= end - pos)
return pos - buf;
pos += ret;
#ifdef CONFIG_WPS
-static char * wpa_supplicant_wps_ie_txt_buf(char *pos, char *end,
+static char * wpa_supplicant_wps_ie_txt_buf(struct wpa_supplicant *wpa_s,
+ char *pos, char *end,
struct wpabuf *wps_ie)
{
int ret;
return pos;
if (wps_is_selected_pbc_registrar(wps_ie))
txt = "[WPS-PBC]";
+#ifdef CONFIG_WPS2
+ else if (wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 0))
+ txt = "[WPS-AUTH]";
+#endif /* CONFIG_WPS2 */
else if (wps_is_selected_pin_registrar(wps_ie))
txt = "[WPS-PIN]";
else
#endif /* CONFIG_WPS */
-static char * wpa_supplicant_wps_ie_txt(char *pos, char *end,
+static char * wpa_supplicant_wps_ie_txt(struct wpa_supplicant *wpa_s,
+ char *pos, char *end,
const struct wpa_bss *bss)
{
#ifdef CONFIG_WPS
struct wpabuf *wps_ie;
wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
- return wpa_supplicant_wps_ie_txt_buf(pos, end, wps_ie);
+ return wpa_supplicant_wps_ie_txt_buf(wpa_s, pos, end, wps_ie);
#else /* CONFIG_WPS */
return pos;
#endif /* CONFIG_WPS */
/* Format one result on one text line into a buffer. */
static int wpa_supplicant_ctrl_iface_scan_result(
+ struct wpa_supplicant *wpa_s,
const struct wpa_bss *bss, char *buf, size_t buflen)
{
char *pos, *end;
int ret;
- const u8 *ie, *ie2;
+ const u8 *ie, *ie2, *p2p;
+
+ p2p = wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE);
+ if (p2p && bss->ssid_len == P2P_WILDCARD_SSID_LEN &&
+ os_memcmp(bss->ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) ==
+ 0)
+ return 0; /* Do not show P2P listen discovery results here */
pos = buf;
end = buf + buflen;
ret = os_snprintf(pos, end - pos, MACSTR "\t%d\t%d\t",
MAC2STR(bss->bssid), bss->freq, bss->level);
if (ret < 0 || ret >= end - pos)
- return pos - buf;
+ return -1;
pos += ret;
ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
if (ie)
ie2 = wpa_bss_get_ie(bss, WLAN_EID_RSN);
if (ie2)
pos = wpa_supplicant_ie_txt(pos, end, "WPA2", ie2, 2 + ie2[1]);
- pos = wpa_supplicant_wps_ie_txt(pos, end, bss);
+ pos = wpa_supplicant_wps_ie_txt(wpa_s, pos, end, bss);
if (!ie && !ie2 && bss->caps & IEEE80211_CAP_PRIVACY) {
ret = os_snprintf(pos, end - pos, "[WEP]");
if (ret < 0 || ret >= end - pos)
- return pos - buf;
+ return -1;
pos += ret;
}
if (bss->caps & IEEE80211_CAP_IBSS) {
ret = os_snprintf(pos, end - pos, "[IBSS]");
if (ret < 0 || ret >= end - pos)
- return pos - buf;
+ return -1;
pos += ret;
}
if (bss->caps & IEEE80211_CAP_ESS) {
ret = os_snprintf(pos, end - pos, "[ESS]");
if (ret < 0 || ret >= end - pos)
- return pos - buf;
+ return -1;
+ pos += ret;
+ }
+ if (p2p) {
+ ret = os_snprintf(pos, end - pos, "[P2P]");
+ if (ret < 0 || ret >= end - pos)
+ return -1;
pos += ret;
}
ret = os_snprintf(pos, end - pos, "\t%s",
wpa_ssid_txt(bss->ssid, bss->ssid_len));
if (ret < 0 || ret >= end - pos)
- return pos - buf;
+ return -1;
pos += ret;
ret = os_snprintf(pos, end - pos, "\n");
if (ret < 0 || ret >= end - pos)
- return pos - buf;
+ return -1;
pos += ret;
return pos - buf;
pos += ret;
dl_list_for_each(bss, &wpa_s->bss_id, struct wpa_bss, list_id) {
- ret = wpa_supplicant_ctrl_iface_scan_result(bss, pos,
+ ret = wpa_supplicant_ctrl_iface_scan_result(wpa_s, bss, pos,
end - pos);
if (ret < 0 || ret >= end - pos)
return pos - buf;
"network id=%d", id);
return -1;
}
+ if (ssid->disabled == 2) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Cannot use "
+ "SELECT_NETWORK with persistent P2P group");
+ return -1;
+ }
}
wpa_supplicant_select_network(wpa_s, ssid);
"network id=%d", id);
return -1;
}
+ if (ssid->disabled == 2) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Cannot use "
+ "ENABLE_NETWORK with persistent P2P group");
+ return -1;
+ }
}
wpa_supplicant_enable_network(wpa_s, ssid);
"network id=%d", id);
return -1;
}
+ if (ssid->disabled == 2) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Cannot use "
+ "DISABLE_NETWORK with persistent P2P "
+ "group");
+ return -1;
+ }
}
wpa_supplicant_disable_network(wpa_s, ssid);
wpas_notify_network_removed(wpa_s, remove_ssid);
wpa_config_remove_network(wpa_s->conf, id);
}
+ eapol_sm_invalidate_cached_session(wpa_s->eapol);
if (wpa_s->current_ssid) {
- eapol_sm_invalidate_cached_session(wpa_s->eapol);
+#ifdef CONFIG_SME
+ wpa_s->sme.prev_bssid_set = 0;
+#endif /* CONFIG_SME */
+ wpa_sm_set_config(wpa_s->wpa, NULL);
+ eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
wpa_supplicant_disassociate(wpa_s,
WLAN_REASON_DEAUTH_LEAVING);
}
wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_NETWORK id=%d", id);
ssid = wpa_config_get_network(wpa_s->conf, id);
+ if (ssid)
+ wpas_notify_network_removed(wpa_s, ssid);
if (ssid == NULL ||
wpa_config_remove_network(wpa_s->conf, id) < 0) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find network "
return -1;
}
- if (ssid == wpa_s->current_ssid) {
+ if (ssid == wpa_s->current_ssid || wpa_s->current_ssid == NULL) {
+#ifdef CONFIG_SME
+ wpa_s->sme.prev_bssid_set = 0;
+#endif /* CONFIG_SME */
/*
- * Invalidate the EAP session cache if the current network is
- * removed.
+ * Invalidate the EAP session cache if the current or
+ * previously used network is removed.
*/
eapol_sm_invalidate_cached_session(wpa_s->eapol);
+ }
+
+ if (ssid == wpa_s->current_ssid) {
+ wpa_sm_set_config(wpa_s->wpa, NULL);
+ eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
wpa_supplicant_disassociate(wpa_s, WLAN_REASON_DEAUTH_LEAVING);
}
return -1;
}
- if (wpa_s->current_ssid == ssid) {
+ wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
+
+ if (wpa_s->current_ssid == ssid || wpa_s->current_ssid == NULL) {
/*
* Invalidate the EAP session cache if anything in the current
- * configuration changes.
+ * or previously used configuration changes.
*/
eapol_sm_invalidate_cached_session(wpa_s->eapol);
}
}
+#ifdef CONFIG_INTERWORKING
+static char * anqp_add_hex(char *pos, char *end, const char *title,
+ struct wpabuf *data)
+{
+ char *start = pos;
+ size_t i;
+ int ret;
+ const u8 *d;
+
+ if (data == NULL)
+ return start;
+
+ ret = os_snprintf(pos, end - pos, "%s=", title);
+ if (ret < 0 || ret >= end - pos)
+ return start;
+ pos += ret;
+
+ d = wpabuf_head_u8(data);
+ for (i = 0; i < wpabuf_len(data); i++) {
+ ret = os_snprintf(pos, end - pos, "%02x", *d++);
+ if (ret < 0 || ret >= end - pos)
+ return start;
+ pos += ret;
+ }
+
+ ret = os_snprintf(pos, end - pos, "\n");
+ if (ret < 0 || ret >= end - pos)
+ return start;
+ pos += ret;
+
+ return pos;
+}
+#endif /* CONFIG_INTERWORKING */
+
+
static int wpa_supplicant_ctrl_iface_bss(struct wpa_supplicant *wpa_s,
const char *cmd, char *buf,
size_t buflen)
bss = dl_list_entry(next, struct wpa_bss,
list_id);
}
+#ifdef CONFIG_P2P
+ } else if (os_strncmp(cmd, "p2p_dev_addr=", 13) == 0) {
+ if (hwaddr_aton(cmd + 13, bssid) == 0)
+ bss = wpa_bss_get_p2p_dev_addr(wpa_s, bssid);
+ else
+ bss = NULL;
+#endif /* CONFIG_P2P */
} else if (hwaddr_aton(cmd, bssid) == 0)
bss = wpa_bss_get_bssid(wpa_s, bssid);
else {
ie2 = wpa_bss_get_ie(bss, WLAN_EID_RSN);
if (ie2)
pos = wpa_supplicant_ie_txt(pos, end, "WPA2", ie2, 2 + ie2[1]);
- pos = wpa_supplicant_wps_ie_txt(pos, end, bss);
+ pos = wpa_supplicant_wps_ie_txt(wpa_s, pos, end, bss);
if (!ie && !ie2 && bss->caps & IEEE80211_CAP_PRIVACY) {
ret = os_snprintf(pos, end - pos, "[WEP]");
if (ret < 0 || ret >= end - pos)
return pos - buf;
pos += ret;
}
+ if (wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE)) {
+ ret = os_snprintf(pos, end - pos, "[P2P]");
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ }
ret = os_snprintf(pos, end - pos, "\n");
if (ret < 0 || ret >= end - pos)
pos += ret;
#endif /* CONFIG_WPS */
+#ifdef CONFIG_P2P
+ ie = (const u8 *) (bss + 1);
+ ret = wpas_p2p_scan_result_text(ie, bss->ie_len, pos, end);
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_INTERWORKING
+ pos = anqp_add_hex(pos, end, "anqp_venue_name", bss->anqp_venue_name);
+ pos = anqp_add_hex(pos, end, "anqp_network_auth_type",
+ bss->anqp_network_auth_type);
+ pos = anqp_add_hex(pos, end, "anqp_roaming_consortium",
+ bss->anqp_roaming_consortium);
+ pos = anqp_add_hex(pos, end, "anqp_ip_addr_type_availability",
+ bss->anqp_ip_addr_type_availability);
+ pos = anqp_add_hex(pos, end, "anqp_nai_realm", bss->anqp_nai_realm);
+ pos = anqp_add_hex(pos, end, "anqp_3gpp", bss->anqp_3gpp);
+ pos = anqp_add_hex(pos, end, "anqp_domain_name",
+ bss->anqp_domain_name);
+#endif /* CONFIG_INTERWORKING */
+
return pos - buf;
}
}
-static void wpa_supplicant_ctrl_iface_drop_sa(struct wpa_supplicant *wpa_s)
+static int wpa_supplicant_ctrl_iface_scan_interval(
+ struct wpa_supplicant *wpa_s, char *cmd)
+{
+ int scan_int = atoi(cmd);
+ if (scan_int < 0)
+ return -1;
+ wpa_s->scan_interval = scan_int;
+ return 0;
+}
+
+
+static int wpa_supplicant_ctrl_iface_bss_expire_age(
+ struct wpa_supplicant *wpa_s, char *cmd)
+{
+ int expire_age = atoi(cmd);
+ return wpa_supplicant_set_bss_expiration_age(wpa_s, expire_age);
+}
+
+
+static int wpa_supplicant_ctrl_iface_bss_expire_count(
+ struct wpa_supplicant *wpa_s, char *cmd)
{
- u8 *bcast = (u8 *) "\xff\xff\xff\xff\xff\xff";
+ int expire_count = atoi(cmd);
+ return wpa_supplicant_set_bss_expiration_count(wpa_s, expire_count);
+}
+
+static void wpa_supplicant_ctrl_iface_drop_sa(struct wpa_supplicant *wpa_s)
+{
wpa_printf(MSG_DEBUG, "Dropping SA without deauthentication");
/* MLME-DELETEKEYS.request */
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 0, 0, NULL, 0, NULL, 0);
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 1, 0, NULL, 0, NULL, 0);
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 2, 0, NULL, 0, NULL, 0);
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 3, 0, NULL, 0, NULL, 0);
+ wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 0, 0, NULL, 0, NULL, 0);
+ wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 1, 0, NULL, 0, NULL, 0);
+ wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 2, 0, NULL, 0, NULL, 0);
+ wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 3, 0, NULL, 0, NULL, 0);
#ifdef CONFIG_IEEE80211W
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 4, 0, NULL, 0, NULL, 0);
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 5, 0, NULL, 0, NULL, 0);
+ wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 4, 0, NULL, 0, NULL, 0);
+ wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 5, 0, NULL, 0, NULL, 0);
#endif /* CONFIG_IEEE80211W */
wpa_drv_set_key(wpa_s, WPA_ALG_NONE, wpa_s->bssid, 0, 0, NULL, 0, NULL,
static int wpa_supplicant_ctrl_iface_roam(struct wpa_supplicant *wpa_s,
char *addr)
{
+#ifdef CONFIG_NO_SCAN_PROCESSING
+ return -1;
+#else /* CONFIG_NO_SCAN_PROCESSING */
u8 bssid[ETH_ALEN];
struct wpa_bss *bss;
struct wpa_ssid *ssid = wpa_s->current_ssid;
wpa_supplicant_connect(wpa_s, bss, ssid);
return 0;
+#endif /* CONFIG_NO_SCAN_PROCESSING */
}
-char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
- char *buf, size_t *resp_len)
+#ifdef CONFIG_P2P
+static int p2p_ctrl_find(struct wpa_supplicant *wpa_s, char *cmd)
{
- char *reply;
- const int reply_size = 2048;
- int ctrl_rsp = 0;
- int reply_len;
+ unsigned int timeout = atoi(cmd);
+ enum p2p_discovery_type type = P2P_FIND_START_WITH_FULL;
+ u8 dev_id[ETH_ALEN], *_dev_id = NULL;
+ char *pos;
- if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0 ||
- os_strncmp(buf, "SET_NETWORK ", 12) == 0) {
- wpa_hexdump_ascii_key(MSG_DEBUG, "RX ctrl_iface",
- (const u8 *) buf, os_strlen(buf));
+ if (os_strstr(cmd, "type=social"))
+ type = P2P_FIND_ONLY_SOCIAL;
+ else if (os_strstr(cmd, "type=progressive"))
+ type = P2P_FIND_PROGRESSIVE;
+
+ pos = os_strstr(cmd, "dev_id=");
+ if (pos) {
+ pos += 7;
+ if (hwaddr_aton(pos, dev_id))
+ return -1;
+ _dev_id = dev_id;
+ }
+
+ return wpas_p2p_find(wpa_s, timeout, type, 0, NULL, _dev_id);
+}
+
+
+static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd,
+ char *buf, size_t buflen)
+{
+ u8 addr[ETH_ALEN];
+ char *pos, *pos2;
+ char *pin = NULL;
+ enum p2p_wps_method wps_method;
+ int new_pin;
+ int ret;
+ int persistent_group;
+ int join;
+ int auth;
+ int go_intent = -1;
+ int freq = 0;
+
+ /* <addr> <"pbc" | "pin" | PIN> [label|display|keypad] [persistent]
+ * [join] [auth] [go_intent=<0..15>] [freq=<in MHz>] */
+
+ if (hwaddr_aton(cmd, addr))
+ return -1;
+
+ pos = cmd + 17;
+ if (*pos != ' ')
+ return -1;
+ pos++;
+
+ persistent_group = os_strstr(pos, " persistent") != NULL;
+ join = os_strstr(pos, " join") != NULL;
+ auth = os_strstr(pos, " auth") != NULL;
+
+ pos2 = os_strstr(pos, " go_intent=");
+ if (pos2) {
+ pos2 += 11;
+ go_intent = atoi(pos2);
+ if (go_intent < 0 || go_intent > 15)
+ return -1;
+ }
+
+ pos2 = os_strstr(pos, " freq=");
+ if (pos2) {
+ pos2 += 6;
+ freq = atoi(pos2);
+ if (freq <= 0)
+ return -1;
+ }
+
+ if (os_strncmp(pos, "pin", 3) == 0) {
+ /* Request random PIN (to be displayed) and enable the PIN */
+ wps_method = WPS_PIN_DISPLAY;
+ } else if (os_strncmp(pos, "pbc", 3) == 0) {
+ wps_method = WPS_PBC;
} else {
- wpa_hexdump_ascii(MSG_DEBUG, "RX ctrl_iface",
- (const u8 *) buf, os_strlen(buf));
+ pin = pos;
+ pos = os_strchr(pin, ' ');
+ wps_method = WPS_PIN_KEYPAD;
+ if (pos) {
+ *pos++ = '\0';
+ if (os_strncmp(pos, "display", 7) == 0)
+ wps_method = WPS_PIN_DISPLAY;
+ }
}
- reply = os_malloc(reply_size);
- if (reply == NULL) {
- *resp_len = 1;
- return NULL;
+ new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method,
+ persistent_group, join, auth, go_intent,
+ freq);
+ if (new_pin == -2) {
+ os_memcpy(buf, "FAIL-CHANNEL-UNAVAILABLE\n", 25);
+ return 25;
+ }
+ if (new_pin == -3) {
+ os_memcpy(buf, "FAIL-CHANNEL-UNSUPPORTED\n", 25);
+ return 25;
+ }
+ if (new_pin < 0)
+ return -1;
+ if (wps_method == WPS_PIN_DISPLAY && pin == NULL) {
+ ret = os_snprintf(buf, buflen, "%08d", new_pin);
+ if (ret < 0 || (size_t) ret >= buflen)
+ return -1;
+ return ret;
}
- os_memcpy(reply, "OK\n", 3);
- reply_len = 3;
+ os_memcpy(buf, "OK\n", 3);
+ return 3;
+}
- if (os_strcmp(buf, "PING") == 0) {
- os_memcpy(reply, "PONG\n", 5);
- reply_len = 5;
- } else if (os_strcmp(buf, "MIB") == 0) {
- reply_len = wpa_sm_get_mib(wpa_s->wpa, reply, reply_size);
- if (reply_len >= 0) {
- int res;
- res = eapol_sm_get_mib(wpa_s->eapol, reply + reply_len,
- reply_size - reply_len);
- if (res < 0)
- reply_len = -1;
- else
- reply_len += res;
+
+static int p2p_ctrl_listen(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ unsigned int timeout = atoi(cmd);
+ return wpas_p2p_listen(wpa_s, timeout);
+}
+
+
+static int p2p_ctrl_prov_disc(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ u8 addr[ETH_ALEN];
+ char *pos;
+
+ /* <addr> <config method> [join] */
+
+ if (hwaddr_aton(cmd, addr))
+ return -1;
+
+ pos = cmd + 17;
+ if (*pos != ' ')
+ return -1;
+ pos++;
+
+ return wpas_p2p_prov_disc(wpa_s, addr, pos,
+ os_strstr(pos, "join") != NULL);
+}
+
+
+static int p2p_get_passphrase(struct wpa_supplicant *wpa_s, char *buf,
+ size_t buflen)
+{
+ struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+ if (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GO ||
+ ssid->passphrase == NULL)
+ return -1;
+
+ os_strlcpy(buf, ssid->passphrase, buflen);
+ return os_strlen(buf);
+}
+
+
+static int p2p_ctrl_serv_disc_req(struct wpa_supplicant *wpa_s, char *cmd,
+ char *buf, size_t buflen)
+{
+ u64 ref;
+ int res;
+ u8 dst_buf[ETH_ALEN], *dst;
+ struct wpabuf *tlvs;
+ char *pos;
+ size_t len;
+
+ if (hwaddr_aton(cmd, dst_buf))
+ return -1;
+ dst = dst_buf;
+ if (dst[0] == 0 && dst[1] == 0 && dst[2] == 0 &&
+ dst[3] == 0 && dst[4] == 0 && dst[5] == 0)
+ dst = NULL;
+ pos = cmd + 17;
+ if (*pos != ' ')
+ return -1;
+ pos++;
+
+ if (os_strncmp(pos, "upnp ", 5) == 0) {
+ u8 version;
+ pos += 5;
+ if (hexstr2bin(pos, &version, 1) < 0)
+ return -1;
+ pos += 2;
+ if (*pos != ' ')
+ return -1;
+ pos++;
+ ref = (unsigned long) wpas_p2p_sd_request_upnp(wpa_s, dst,
+ version, pos);
+ } else {
+ len = os_strlen(pos);
+ if (len & 1)
+ return -1;
+ len /= 2;
+ tlvs = wpabuf_alloc(len);
+ if (tlvs == NULL)
+ return -1;
+ if (hexstr2bin(pos, wpabuf_put(tlvs, len), len) < 0) {
+ wpabuf_free(tlvs);
+ return -1;
}
- } else if (os_strncmp(buf, "STATUS", 6) == 0) {
- reply_len = wpa_supplicant_ctrl_iface_status(
- wpa_s, buf + 6, reply, reply_size);
+
+ ref = (unsigned long) wpas_p2p_sd_request(wpa_s, dst, tlvs);
+ wpabuf_free(tlvs);
+ }
+ res = os_snprintf(buf, buflen, "%llx", (long long unsigned) ref);
+ if (res < 0 || (unsigned) res >= buflen)
+ return -1;
+ return res;
+}
+
+
+static int p2p_ctrl_serv_disc_cancel_req(struct wpa_supplicant *wpa_s,
+ char *cmd)
+{
+ long long unsigned val;
+ u64 req;
+ if (sscanf(cmd, "%llx", &val) != 1)
+ return -1;
+ req = val;
+ return wpas_p2p_sd_cancel_request(wpa_s, (void *) (unsigned long) req);
+}
+
+
+static int p2p_ctrl_serv_disc_resp(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ int freq;
+ u8 dst[ETH_ALEN];
+ u8 dialog_token;
+ struct wpabuf *resp_tlvs;
+ char *pos, *pos2;
+ size_t len;
+
+ pos = os_strchr(cmd, ' ');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+ freq = atoi(cmd);
+ if (freq == 0)
+ return -1;
+
+ if (hwaddr_aton(pos, dst))
+ return -1;
+ pos += 17;
+ if (*pos != ' ')
+ return -1;
+ pos++;
+
+ pos2 = os_strchr(pos, ' ');
+ if (pos2 == NULL)
+ return -1;
+ *pos2++ = '\0';
+ dialog_token = atoi(pos);
+
+ len = os_strlen(pos2);
+ if (len & 1)
+ return -1;
+ len /= 2;
+ resp_tlvs = wpabuf_alloc(len);
+ if (resp_tlvs == NULL)
+ return -1;
+ if (hexstr2bin(pos2, wpabuf_put(resp_tlvs, len), len) < 0) {
+ wpabuf_free(resp_tlvs);
+ return -1;
+ }
+
+ wpas_p2p_sd_response(wpa_s, freq, dst, dialog_token, resp_tlvs);
+ wpabuf_free(resp_tlvs);
+ return 0;
+}
+
+
+static int p2p_ctrl_serv_disc_external(struct wpa_supplicant *wpa_s,
+ char *cmd)
+{
+ if (os_strcmp(cmd, "0") && os_strcmp(cmd, "1"))
+ return -1;
+ wpa_s->p2p_sd_over_ctrl_iface = atoi(cmd);
+ return 0;
+}
+
+
+static int p2p_ctrl_service_add_bonjour(struct wpa_supplicant *wpa_s,
+ char *cmd)
+{
+ char *pos;
+ size_t len;
+ struct wpabuf *query, *resp;
+
+ pos = os_strchr(cmd, ' ');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+
+ len = os_strlen(cmd);
+ if (len & 1)
+ return -1;
+ len /= 2;
+ query = wpabuf_alloc(len);
+ if (query == NULL)
+ return -1;
+ if (hexstr2bin(cmd, wpabuf_put(query, len), len) < 0) {
+ wpabuf_free(query);
+ return -1;
+ }
+
+ len = os_strlen(pos);
+ if (len & 1) {
+ wpabuf_free(query);
+ return -1;
+ }
+ len /= 2;
+ resp = wpabuf_alloc(len);
+ if (resp == NULL) {
+ wpabuf_free(query);
+ return -1;
+ }
+ if (hexstr2bin(pos, wpabuf_put(resp, len), len) < 0) {
+ wpabuf_free(query);
+ wpabuf_free(resp);
+ return -1;
+ }
+
+ if (wpas_p2p_service_add_bonjour(wpa_s, query, resp) < 0) {
+ wpabuf_free(query);
+ wpabuf_free(resp);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int p2p_ctrl_service_add_upnp(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ char *pos;
+ u8 version;
+
+ pos = os_strchr(cmd, ' ');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+
+ if (hexstr2bin(cmd, &version, 1) < 0)
+ return -1;
+
+ return wpas_p2p_service_add_upnp(wpa_s, version, pos);
+}
+
+
+static int p2p_ctrl_service_add(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ char *pos;
+
+ pos = os_strchr(cmd, ' ');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+
+ if (os_strcmp(cmd, "bonjour") == 0)
+ return p2p_ctrl_service_add_bonjour(wpa_s, pos);
+ if (os_strcmp(cmd, "upnp") == 0)
+ return p2p_ctrl_service_add_upnp(wpa_s, pos);
+ wpa_printf(MSG_DEBUG, "Unknown service '%s'", cmd);
+ return -1;
+}
+
+
+static int p2p_ctrl_service_del_bonjour(struct wpa_supplicant *wpa_s,
+ char *cmd)
+{
+ size_t len;
+ struct wpabuf *query;
+ int ret;
+
+ len = os_strlen(cmd);
+ if (len & 1)
+ return -1;
+ len /= 2;
+ query = wpabuf_alloc(len);
+ if (query == NULL)
+ return -1;
+ if (hexstr2bin(cmd, wpabuf_put(query, len), len) < 0) {
+ wpabuf_free(query);
+ return -1;
+ }
+
+ ret = wpas_p2p_service_del_bonjour(wpa_s, query);
+ wpabuf_free(query);
+ return ret;
+}
+
+
+static int p2p_ctrl_service_del_upnp(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ char *pos;
+ u8 version;
+
+ pos = os_strchr(cmd, ' ');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+
+ if (hexstr2bin(cmd, &version, 1) < 0)
+ return -1;
+
+ return wpas_p2p_service_del_upnp(wpa_s, version, pos);
+}
+
+
+static int p2p_ctrl_service_del(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ char *pos;
+
+ pos = os_strchr(cmd, ' ');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+
+ if (os_strcmp(cmd, "bonjour") == 0)
+ return p2p_ctrl_service_del_bonjour(wpa_s, pos);
+ if (os_strcmp(cmd, "upnp") == 0)
+ return p2p_ctrl_service_del_upnp(wpa_s, pos);
+ wpa_printf(MSG_DEBUG, "Unknown service '%s'", cmd);
+ return -1;
+}
+
+
+static int p2p_ctrl_reject(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ u8 addr[ETH_ALEN];
+
+ /* <addr> */
+
+ if (hwaddr_aton(cmd, addr))
+ return -1;
+
+ return wpas_p2p_reject(wpa_s, addr);
+}
+
+
+static int p2p_ctrl_invite_persistent(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ char *pos;
+ int id;
+ struct wpa_ssid *ssid;
+ u8 peer[ETH_ALEN];
+
+ id = atoi(cmd);
+ pos = os_strstr(cmd, " peer=");
+ if (pos) {
+ pos += 6;
+ if (hwaddr_aton(pos, peer))
+ return -1;
+ }
+ 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 "
+ "for persistent P2P group",
+ id);
+ return -1;
+ }
+
+ return wpas_p2p_invite(wpa_s, pos ? peer : NULL, ssid, NULL);
+}
+
+
+static int p2p_ctrl_invite_group(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ char *pos;
+ u8 peer[ETH_ALEN], go_dev_addr[ETH_ALEN], *go_dev = NULL;
+
+ pos = os_strstr(cmd, " peer=");
+ if (!pos)
+ return -1;
+
+ *pos = '\0';
+ pos += 6;
+ if (hwaddr_aton(pos, peer)) {
+ wpa_printf(MSG_DEBUG, "P2P: Invalid MAC address '%s'", pos);
+ return -1;
+ }
+
+ pos = os_strstr(pos, " go_dev_addr=");
+ if (pos) {
+ pos += 13;
+ if (hwaddr_aton(pos, go_dev_addr)) {
+ wpa_printf(MSG_DEBUG, "P2P: Invalid MAC address '%s'",
+ pos);
+ return -1;
+ }
+ go_dev = go_dev_addr;
+ }
+
+ return wpas_p2p_invite_group(wpa_s, cmd, peer, go_dev);
+}
+
+
+static int p2p_ctrl_invite(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ if (os_strncmp(cmd, "persistent=", 11) == 0)
+ return p2p_ctrl_invite_persistent(wpa_s, cmd + 11);
+ if (os_strncmp(cmd, "group=", 6) == 0)
+ return p2p_ctrl_invite_group(wpa_s, cmd + 6);
+
+ return -1;
+}
+
+
+static int p2p_ctrl_group_add_persistent(struct wpa_supplicant *wpa_s,
+ char *cmd, int freq)
+{
+ 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 "
+ "for persistent P2P group",
+ id);
+ return -1;
+ }
+
+ return wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq);
+}
+
+
+static int p2p_ctrl_group_add(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ int freq = 0;
+ char *pos;
+
+ pos = os_strstr(cmd, "freq=");
+ if (pos)
+ freq = atoi(pos + 5);
+
+ if (os_strncmp(cmd, "persistent=", 11) == 0)
+ return p2p_ctrl_group_add_persistent(wpa_s, cmd + 11, freq);
+ if (os_strcmp(cmd, "persistent") == 0 ||
+ os_strncmp(cmd, "persistent ", 11) == 0)
+ return wpas_p2p_group_add(wpa_s, 1, freq);
+ if (os_strncmp(cmd, "freq=", 5) == 0)
+ return wpas_p2p_group_add(wpa_s, 0, freq);
+
+ wpa_printf(MSG_DEBUG, "CTRL: Invalid P2P_GROUP_ADD parameters '%s'",
+ cmd);
+ return -1;
+}
+
+
+static int p2p_ctrl_peer(struct wpa_supplicant *wpa_s, char *cmd,
+ char *buf, size_t buflen)
+{
+ u8 addr[ETH_ALEN], *addr_ptr;
+ int next, res;
+ const struct p2p_peer_info *info;
+ char *pos, *end;
+ char devtype[WPS_DEV_TYPE_BUFSIZE];
+ struct wpa_ssid *ssid;
+
+ if (!wpa_s->global->p2p)
+ return -1;
+
+ if (os_strcmp(cmd, "FIRST") == 0) {
+ addr_ptr = NULL;
+ next = 0;
+ } else if (os_strncmp(cmd, "NEXT-", 5) == 0) {
+ if (hwaddr_aton(cmd + 5, addr) < 0)
+ return -1;
+ addr_ptr = addr;
+ next = 1;
+ } else {
+ if (hwaddr_aton(cmd, addr) < 0)
+ return -1;
+ addr_ptr = addr;
+ next = 0;
+ }
+
+ info = p2p_get_peer_info(wpa_s->global->p2p, addr_ptr, next);
+ if (info == NULL)
+ return -1;
+
+ pos = buf;
+ end = buf + buflen;
+
+ res = os_snprintf(pos, end - pos, MACSTR "\n"
+ "pri_dev_type=%s\n"
+ "device_name=%s\n"
+ "manufacturer=%s\n"
+ "model_name=%s\n"
+ "model_number=%s\n"
+ "serial_number=%s\n"
+ "config_methods=0x%x\n"
+ "dev_capab=0x%x\n"
+ "group_capab=0x%x\n"
+ "level=%d\n",
+ MAC2STR(info->p2p_device_addr),
+ wps_dev_type_bin2str(info->pri_dev_type,
+ devtype, sizeof(devtype)),
+ info->device_name,
+ info->manufacturer,
+ info->model_name,
+ info->model_number,
+ info->serial_number,
+ info->config_methods,
+ info->dev_capab,
+ info->group_capab,
+ info->level);
+ if (res < 0 || res >= end - pos)
+ return pos - buf;
+ pos += res;
+
+ ssid = wpas_p2p_get_persistent(wpa_s, info->p2p_device_addr, NULL, 0);
+ if (ssid) {
+ res = os_snprintf(pos, end - pos, "persistent=%d\n", ssid->id);
+ if (res < 0 || res >= end - pos)
+ return pos - buf;
+ pos += res;
+ }
+
+ res = p2p_get_peer_info_txt(info, pos, end - pos);
+ if (res < 0)
+ return pos - buf;
+ pos += res;
+
+ return pos - buf;
+}
+
+
+static int p2p_ctrl_set(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ char *param;
+
+ if (wpa_s->global->p2p == NULL)
+ return -1;
+
+ param = os_strchr(cmd, ' ');
+ if (param == NULL)
+ return -1;
+ *param++ = '\0';
+
+ if (os_strcmp(cmd, "discoverability") == 0) {
+ p2p_set_client_discoverability(wpa_s->global->p2p,
+ atoi(param));
+ return 0;
+ }
+
+ if (os_strcmp(cmd, "managed") == 0) {
+ p2p_set_managed_oper(wpa_s->global->p2p, atoi(param));
+ return 0;
+ }
+
+ if (os_strcmp(cmd, "listen_channel") == 0) {
+ return p2p_set_listen_channel(wpa_s->global->p2p, 81,
+ atoi(param));
+ }
+
+ if (os_strcmp(cmd, "ssid_postfix") == 0) {
+ return p2p_set_ssid_postfix(wpa_s->global->p2p, (u8 *) param,
+ os_strlen(param));
+ }
+
+ if (os_strcmp(cmd, "noa") == 0) {
+ char *pos;
+ int count, start, duration;
+ /* GO NoA parameters: count,start_offset(ms),duration(ms) */
+ count = atoi(param);
+ pos = os_strchr(param, ',');
+ if (pos == NULL)
+ return -1;
+ pos++;
+ start = atoi(pos);
+ pos = os_strchr(pos, ',');
+ if (pos == NULL)
+ return -1;
+ pos++;
+ duration = atoi(pos);
+ if (count < 0 || count > 255 || start < 0 || duration < 0)
+ return -1;
+ if (count == 0 && duration > 0)
+ return -1;
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: P2P_SET GO NoA: count=%d "
+ "start=%d duration=%d", count, start, duration);
+ return wpas_p2p_set_noa(wpa_s, count, start, duration);
+ }
+
+ if (os_strcmp(cmd, "ps") == 0)
+ return wpa_drv_set_p2p_powersave(wpa_s, atoi(param), -1, -1);
+
+ if (os_strcmp(cmd, "oppps") == 0)
+ return wpa_drv_set_p2p_powersave(wpa_s, -1, atoi(param), -1);
+
+ if (os_strcmp(cmd, "ctwindow") == 0)
+ return wpa_drv_set_p2p_powersave(wpa_s, -1, -1, atoi(param));
+
+ if (os_strcmp(cmd, "disabled") == 0) {
+ wpa_s->global->p2p_disabled = atoi(param);
+ wpa_printf(MSG_DEBUG, "P2P functionality %s",
+ wpa_s->global->p2p_disabled ?
+ "disabled" : "enabled");
+ if (wpa_s->global->p2p_disabled) {
+ wpas_p2p_stop_find(wpa_s);
+ os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN);
+ p2p_flush(wpa_s->global->p2p);
+ }
+ return 0;
+ }
+
+ if (os_strcmp(cmd, "force_long_sd") == 0) {
+ wpa_s->force_long_sd = atoi(param);
+ return 0;
+ }
+
+ if (os_strcmp(cmd, "peer_filter") == 0) {
+ u8 addr[ETH_ALEN];
+ if (hwaddr_aton(param, addr))
+ return -1;
+ p2p_set_peer_filter(wpa_s->global->p2p, addr);
+ return 0;
+ }
+
+ if (os_strcmp(cmd, "cross_connect") == 0)
+ return wpas_p2p_set_cross_connect(wpa_s, atoi(param));
+
+ if (os_strcmp(cmd, "go_apsd") == 0) {
+ if (os_strcmp(param, "disable") == 0)
+ wpa_s->set_ap_uapsd = 0;
+ else {
+ wpa_s->set_ap_uapsd = 1;
+ wpa_s->ap_uapsd = atoi(param);
+ }
+ return 0;
+ }
+
+ if (os_strcmp(cmd, "client_apsd") == 0) {
+ if (os_strcmp(param, "disable") == 0)
+ wpa_s->set_sta_uapsd = 0;
+ else {
+ int be, bk, vi, vo;
+ char *pos;
+ /* format: BE,BK,VI,VO;max SP Length */
+ be = atoi(param);
+ pos = os_strchr(param, ',');
+ if (pos == NULL)
+ return -1;
+ pos++;
+ bk = atoi(pos);
+ pos = os_strchr(pos, ',');
+ if (pos == NULL)
+ return -1;
+ pos++;
+ vi = atoi(pos);
+ pos = os_strchr(pos, ',');
+ if (pos == NULL)
+ return -1;
+ pos++;
+ vo = atoi(pos);
+ /* ignore max SP Length for now */
+
+ wpa_s->set_sta_uapsd = 1;
+ wpa_s->sta_uapsd = 0;
+ if (be)
+ wpa_s->sta_uapsd |= BIT(0);
+ if (bk)
+ wpa_s->sta_uapsd |= BIT(1);
+ if (vi)
+ wpa_s->sta_uapsd |= BIT(2);
+ if (vo)
+ wpa_s->sta_uapsd |= BIT(3);
+ }
+ return 0;
+ }
+
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown P2P_SET field value '%s'",
+ cmd);
+
+ return -1;
+}
+
+
+static int p2p_ctrl_presence_req(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ char *pos, *pos2;
+ unsigned int dur1 = 0, int1 = 0, dur2 = 0, int2 = 0;
+
+ if (cmd[0]) {
+ pos = os_strchr(cmd, ' ');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+ dur1 = atoi(cmd);
+
+ pos2 = os_strchr(pos, ' ');
+ if (pos2)
+ *pos2++ = '\0';
+ int1 = atoi(pos);
+ } else
+ pos2 = NULL;
+
+ if (pos2) {
+ pos = os_strchr(pos2, ' ');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+ dur2 = atoi(pos2);
+ int2 = atoi(pos);
+ }
+
+ return wpas_p2p_presence_req(wpa_s, dur1, int1, dur2, int2);
+}
+
+
+static int p2p_ctrl_ext_listen(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ char *pos;
+ unsigned int period = 0, interval = 0;
+
+ if (cmd[0]) {
+ pos = os_strchr(cmd, ' ');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+ period = atoi(cmd);
+ interval = atoi(pos);
+ }
+
+ return wpas_p2p_ext_listen(wpa_s, period, interval);
+}
+
+#endif /* CONFIG_P2P */
+
+
+#ifdef CONFIG_INTERWORKING
+static int ctrl_interworking_connect(struct wpa_supplicant *wpa_s, char *dst)
+{
+ u8 bssid[ETH_ALEN];
+ struct wpa_bss *bss;
+
+ if (hwaddr_aton(dst, bssid)) {
+ wpa_printf(MSG_DEBUG, "Invalid BSSID '%s'", dst);
+ return -1;
+ }
+
+ bss = wpa_bss_get_bssid(wpa_s, bssid);
+ if (bss == NULL) {
+ wpa_printf(MSG_DEBUG, "Could not find BSS " MACSTR,
+ MAC2STR(bssid));
+ return -1;
+ }
+
+ return interworking_connect(wpa_s, bss);
+}
+
+
+static int get_anqp(struct wpa_supplicant *wpa_s, char *dst)
+{
+ u8 dst_addr[ETH_ALEN];
+ int used;
+ char *pos;
+#define MAX_ANQP_INFO_ID 100
+ u16 id[MAX_ANQP_INFO_ID];
+ size_t num_id = 0;
+
+ used = hwaddr_aton2(dst, dst_addr);
+ if (used < 0)
+ return -1;
+ pos = dst + used;
+ while (num_id < MAX_ANQP_INFO_ID) {
+ id[num_id] = atoi(pos);
+ if (id[num_id])
+ num_id++;
+ pos = os_strchr(pos + 1, ',');
+ if (pos == NULL)
+ break;
+ pos++;
+ }
+
+ if (num_id == 0)
+ return -1;
+
+ return anqp_send_req(wpa_s, dst_addr, id, num_id);
+}
+#endif /* CONFIG_INTERWORKING */
+
+
+static int wpa_supplicant_ctrl_iface_sta_autoconnect(
+ struct wpa_supplicant *wpa_s, char *cmd)
+{
+ wpa_s->auto_reconnect_disabled = atoi(cmd) == 0 ? 1 : 0;
+ return 0;
+}
+
+
+static int wpa_supplicant_signal_poll(struct wpa_supplicant *wpa_s, char *buf,
+ size_t buflen)
+{
+ struct wpa_signal_info si;
+ int ret;
+
+ ret = wpa_drv_signal_poll(wpa_s, &si);
+ if (ret)
+ return -1;
+
+ ret = os_snprintf(buf, buflen, "RSSI=%d\nLINKSPEED=%d\n"
+ "NOISE=%d\nFREQUENCY=%u\n",
+ si.current_signal, si.current_txrate / 1000,
+ si.current_noise, si.frequency);
+ if (ret < 0 || (unsigned int) ret > buflen)
+ return -1;
+ return ret;
+}
+
+
+char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
+ char *buf, size_t *resp_len)
+{
+ char *reply;
+ const int reply_size = 4096;
+ int ctrl_rsp = 0;
+ int reply_len;
+
+ if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0 ||
+ os_strncmp(buf, "SET_NETWORK ", 12) == 0) {
+ 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;
+ wpa_hexdump_ascii(level, "RX ctrl_iface",
+ (const u8 *) buf, os_strlen(buf));
+ }
+
+ reply = os_malloc(reply_size);
+ if (reply == NULL) {
+ *resp_len = 1;
+ return NULL;
+ }
+
+ os_memcpy(reply, "OK\n", 3);
+ reply_len = 3;
+
+ if (os_strcmp(buf, "PING") == 0) {
+ os_memcpy(reply, "PONG\n", 5);
+ reply_len = 5;
+ } else if (os_strncmp(buf, "RELOG", 5) == 0) {
+ if (wpa_debug_reopen_file() < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "NOTE ", 5) == 0) {
+ wpa_printf(MSG_INFO, "NOTE: %s", buf + 5);
+ } else if (os_strcmp(buf, "MIB") == 0) {
+ reply_len = wpa_sm_get_mib(wpa_s->wpa, reply, reply_size);
+ if (reply_len >= 0) {
+ int res;
+ res = eapol_sm_get_mib(wpa_s->eapol, reply + reply_len,
+ reply_size - reply_len);
+ if (res < 0)
+ reply_len = -1;
+ else
+ reply_len += res;
+ }
+ } else if (os_strncmp(buf, "STATUS", 6) == 0) {
+ reply_len = wpa_supplicant_ctrl_iface_status(
+ wpa_s, buf + 6, reply, reply_size);
} else if (os_strcmp(buf, "PMKSA") == 0) {
reply_len = wpa_sm_pmksa_cache_list(wpa_s->wpa, reply,
reply_size);
} else if (os_strncmp(buf, "SET ", 4) == 0) {
if (wpa_supplicant_ctrl_iface_set(wpa_s, buf + 4))
reply_len = -1;
+ } else if (os_strncmp(buf, "GET ", 4) == 0) {
+ reply_len = wpa_supplicant_ctrl_iface_get(wpa_s, buf + 4,
+ reply, reply_size);
} else if (os_strcmp(buf, "LOGON") == 0) {
eapol_sm_notify_logoff(wpa_s->eapol, FALSE);
} else if (os_strcmp(buf, "LOGOFF") == 0) {
eapol_sm_notify_logoff(wpa_s->eapol, TRUE);
} else if (os_strcmp(buf, "REASSOCIATE") == 0) {
- wpa_s->disconnected = 0;
- wpa_s->reassociate = 1;
- wpa_supplicant_req_scan(wpa_s, 0, 0);
+ if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
+ reply_len = -1;
+ else {
+ wpa_s->disconnected = 0;
+ wpa_s->reassociate = 1;
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+ }
} else if (os_strcmp(buf, "RECONNECT") == 0) {
- if (wpa_s->disconnected) {
+ if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
+ reply_len = -1;
+ else if (wpa_s->disconnected) {
wpa_s->disconnected = 0;
wpa_s->reassociate = 1;
wpa_supplicant_req_scan(wpa_s, 0, 0);
#endif /* CONFIG_IEEE80211R */
#ifdef CONFIG_WPS
} else if (os_strcmp(buf, "WPS_PBC") == 0) {
- if (wpa_supplicant_ctrl_iface_wps_pbc(wpa_s, NULL))
+ int res = wpa_supplicant_ctrl_iface_wps_pbc(wpa_s, NULL);
+ if (res == -2) {
+ os_memcpy(reply, "FAIL-PBC-OVERLAP\n", 17);
+ reply_len = 17;
+ } else if (res)
reply_len = -1;
} else if (os_strncmp(buf, "WPS_PBC ", 8) == 0) {
- if (wpa_supplicant_ctrl_iface_wps_pbc(wpa_s, buf + 8))
+ int res = wpa_supplicant_ctrl_iface_wps_pbc(wpa_s, buf + 8);
+ if (res == -2) {
+ os_memcpy(reply, "FAIL-PBC-OVERLAP\n", 17);
+ reply_len = 17;
+ } else if (res)
reply_len = -1;
} else if (os_strncmp(buf, "WPS_PIN ", 8) == 0) {
reply_len = wpa_supplicant_ctrl_iface_wps_pin(wpa_s, buf + 8,
reply,
reply_size);
+ } else if (os_strncmp(buf, "WPS_CHECK_PIN ", 14) == 0) {
+ reply_len = wpa_supplicant_ctrl_iface_wps_check_pin(
+ wpa_s, buf + 14, reply, reply_size);
+ } else if (os_strcmp(buf, "WPS_CANCEL") == 0) {
+ if (wpas_wps_cancel(wpa_s))
+ reply_len = -1;
#ifdef CONFIG_WPS_OOB
} else if (os_strncmp(buf, "WPS_OOB ", 8) == 0) {
if (wpa_supplicant_ctrl_iface_wps_oob(wpa_s, buf + 8))
} else if (os_strncmp(buf, "WPS_REG ", 8) == 0) {
if (wpa_supplicant_ctrl_iface_wps_reg(wpa_s, buf + 8))
reply_len = -1;
+#ifdef CONFIG_AP
+ } else if (os_strncmp(buf, "WPS_AP_PIN ", 11) == 0) {
+ reply_len = wpa_supplicant_ctrl_iface_wps_ap_pin(
+ wpa_s, buf + 11, reply, reply_size);
+#endif /* CONFIG_AP */
#ifdef CONFIG_WPS_ER
} else if (os_strcmp(buf, "WPS_ER_START") == 0) {
- if (wpas_wps_er_start(wpa_s))
+ if (wpas_wps_er_start(wpa_s, NULL))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "WPS_ER_START ", 13) == 0) {
+ if (wpas_wps_er_start(wpa_s, buf + 13))
reply_len = -1;
} else if (os_strcmp(buf, "WPS_ER_STOP") == 0) {
if (wpas_wps_er_stop(wpa_s))
if (wpa_supplicant_ctrl_iface_wps_er_pin(wpa_s, buf + 11))
reply_len = -1;
} else if (os_strncmp(buf, "WPS_ER_PBC ", 11) == 0) {
- if (wpas_wps_er_pbc(wpa_s, buf + 11))
+ int ret = wpas_wps_er_pbc(wpa_s, buf + 11);
+ if (ret == -2) {
+ os_memcpy(reply, "FAIL-PBC-OVERLAP\n", 17);
+ reply_len = 17;
+ } else if (ret == -3) {
+ os_memcpy(reply, "FAIL-UNKNOWN-UUID\n", 18);
+ reply_len = 18;
+ } else if (ret == -4) {
+ os_memcpy(reply, "FAIL-NO-AP-SETTINGS\n", 20);
+ reply_len = 20;
+ } else if (ret)
reply_len = -1;
} else if (os_strncmp(buf, "WPS_ER_LEARN ", 13) == 0) {
if (wpa_supplicant_ctrl_iface_wps_er_learn(wpa_s, buf + 13))
reply_len = -1;
+ } else if (os_strncmp(buf, "WPS_ER_SET_CONFIG ", 18) == 0) {
+ if (wpa_supplicant_ctrl_iface_wps_er_set_config(wpa_s,
+ buf + 18))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "WPS_ER_CONFIG ", 14) == 0) {
+ if (wpa_supplicant_ctrl_iface_wps_er_config(wpa_s, buf + 14))
+ reply_len = -1;
#endif /* CONFIG_WPS_ER */
#endif /* CONFIG_WPS */
#ifdef CONFIG_IBSS_RSN
if (wpa_supplicant_ctrl_iface_ibss_rsn(wpa_s, buf + 9))
reply_len = -1;
#endif /* CONFIG_IBSS_RSN */
+#ifdef CONFIG_P2P
+ } else if (os_strncmp(buf, "P2P_FIND ", 9) == 0) {
+ if (p2p_ctrl_find(wpa_s, buf + 9))
+ reply_len = -1;
+ } else if (os_strcmp(buf, "P2P_FIND") == 0) {
+ if (p2p_ctrl_find(wpa_s, ""))
+ reply_len = -1;
+ } else if (os_strcmp(buf, "P2P_STOP_FIND") == 0) {
+ wpas_p2p_stop_find(wpa_s);
+ } else if (os_strncmp(buf, "P2P_CONNECT ", 12) == 0) {
+ reply_len = p2p_ctrl_connect(wpa_s, buf + 12, reply,
+ reply_size);
+ } else if (os_strncmp(buf, "P2P_LISTEN ", 11) == 0) {
+ if (p2p_ctrl_listen(wpa_s, buf + 11))
+ reply_len = -1;
+ } else if (os_strcmp(buf, "P2P_LISTEN") == 0) {
+ if (p2p_ctrl_listen(wpa_s, ""))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "P2P_GROUP_REMOVE ", 17) == 0) {
+ 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))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "P2P_GROUP_ADD ", 14) == 0) {
+ if (p2p_ctrl_group_add(wpa_s, buf + 14))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "P2P_PROV_DISC ", 14) == 0) {
+ if (p2p_ctrl_prov_disc(wpa_s, buf + 14))
+ reply_len = -1;
+ } else if (os_strcmp(buf, "P2P_GET_PASSPHRASE") == 0) {
+ reply_len = p2p_get_passphrase(wpa_s, reply, reply_size);
+ } else if (os_strncmp(buf, "P2P_SERV_DISC_REQ ", 18) == 0) {
+ reply_len = p2p_ctrl_serv_disc_req(wpa_s, buf + 18, reply,
+ reply_size);
+ } else if (os_strncmp(buf, "P2P_SERV_DISC_CANCEL_REQ ", 25) == 0) {
+ if (p2p_ctrl_serv_disc_cancel_req(wpa_s, buf + 25) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "P2P_SERV_DISC_RESP ", 19) == 0) {
+ if (p2p_ctrl_serv_disc_resp(wpa_s, buf + 19) < 0)
+ reply_len = -1;
+ } else if (os_strcmp(buf, "P2P_SERVICE_UPDATE") == 0) {
+ wpas_p2p_sd_service_update(wpa_s);
+ } else if (os_strncmp(buf, "P2P_SERV_DISC_EXTERNAL ", 23) == 0) {
+ if (p2p_ctrl_serv_disc_external(wpa_s, buf + 23) < 0)
+ reply_len = -1;
+ } else if (os_strcmp(buf, "P2P_SERVICE_FLUSH") == 0) {
+ wpas_p2p_service_flush(wpa_s);
+ } else if (os_strncmp(buf, "P2P_SERVICE_ADD ", 16) == 0) {
+ if (p2p_ctrl_service_add(wpa_s, buf + 16) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "P2P_SERVICE_DEL ", 16) == 0) {
+ if (p2p_ctrl_service_del(wpa_s, buf + 16) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "P2P_REJECT ", 11) == 0) {
+ if (p2p_ctrl_reject(wpa_s, buf + 11) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "P2P_INVITE ", 11) == 0) {
+ if (p2p_ctrl_invite(wpa_s, buf + 11) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "P2P_PEER ", 9) == 0) {
+ reply_len = p2p_ctrl_peer(wpa_s, buf + 9, reply,
+ reply_size);
+ } else if (os_strncmp(buf, "P2P_SET ", 8) == 0) {
+ if (p2p_ctrl_set(wpa_s, buf + 8) < 0)
+ reply_len = -1;
+ } else if (os_strcmp(buf, "P2P_FLUSH") == 0) {
+ os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN);
+ wpa_s->force_long_sd = 0;
+ if (wpa_s->global->p2p)
+ p2p_flush(wpa_s->global->p2p);
+ } else if (os_strncmp(buf, "P2P_UNAUTHORIZE ", 16) == 0) {
+ if (wpas_p2p_unauthorize(wpa_s, buf + 16) < 0)
+ reply_len = -1;
+ } else if (os_strcmp(buf, "P2P_CANCEL") == 0) {
+ if (wpas_p2p_cancel(wpa_s))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "P2P_PRESENCE_REQ ", 17) == 0) {
+ if (p2p_ctrl_presence_req(wpa_s, buf + 17) < 0)
+ reply_len = -1;
+ } else if (os_strcmp(buf, "P2P_PRESENCE_REQ") == 0) {
+ if (p2p_ctrl_presence_req(wpa_s, "") < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "P2P_EXT_LISTEN ", 15) == 0) {
+ if (p2p_ctrl_ext_listen(wpa_s, buf + 15) < 0)
+ reply_len = -1;
+ } else if (os_strcmp(buf, "P2P_EXT_LISTEN") == 0) {
+ if (p2p_ctrl_ext_listen(wpa_s, "") < 0)
+ reply_len = -1;
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_INTERWORKING
+ } else if (os_strcmp(buf, "FETCH_ANQP") == 0) {
+ if (interworking_fetch_anqp(wpa_s) < 0)
+ reply_len = -1;
+ } else if (os_strcmp(buf, "STOP_FETCH_ANQP") == 0) {
+ interworking_stop_fetch_anqp(wpa_s);
+ } else if (os_strncmp(buf, "INTERWORKING_SELECT", 19) == 0) {
+ if (interworking_select(wpa_s, os_strstr(buf + 19, "auto") !=
+ NULL) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "INTERWORKING_CONNECT ", 21) == 0) {
+ if (ctrl_interworking_connect(wpa_s, buf + 21) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "ANQP_GET ", 9) == 0) {
+ if (get_anqp(wpa_s, buf + 9) < 0)
+ reply_len = -1;
+#endif /* CONFIG_INTERWORKING */
} else if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0)
{
if (wpa_supplicant_ctrl_iface_ctrl_rsp(
} else if (os_strncmp(buf, "BSSID ", 6) == 0) {
if (wpa_supplicant_ctrl_iface_bssid(wpa_s, buf + 6))
reply_len = -1;
+ } else if (os_strncmp(buf, "BLACKLIST", 9) == 0) {
+ reply_len = wpa_supplicant_ctrl_iface_blacklist(
+ wpa_s, buf + 9, reply, reply_size);
+ } else if (os_strncmp(buf, "LOG_LEVEL", 9) == 0) {
+ reply_len = wpa_supplicant_ctrl_iface_log_level(
+ wpa_s, buf + 9, reply, reply_size);
} else if (os_strcmp(buf, "LIST_NETWORKS") == 0) {
reply_len = wpa_supplicant_ctrl_iface_list_networks(
wpa_s, reply, reply_size);
} else if (os_strcmp(buf, "DISCONNECT") == 0) {
+#ifdef CONFIG_SME
+ wpa_s->sme.prev_bssid_set = 0;
+#endif /* CONFIG_SME */
wpa_s->reassociate = 0;
wpa_s->disconnected = 1;
wpa_supplicant_deauthenticate(wpa_s,
WLAN_REASON_DEAUTH_LEAVING);
} else if (os_strcmp(buf, "SCAN") == 0) {
- wpa_s->scan_req = 2;
- wpa_supplicant_req_scan(wpa_s, 0, 0);
+ if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
+ reply_len = -1;
+ else {
+ if (!wpa_s->scanning &&
+ ((wpa_s->wpa_state <= WPA_SCANNING) ||
+ (wpa_s->wpa_state == WPA_COMPLETED))) {
+ wpa_s->scan_req = 2;
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+ } else {
+ wpa_printf(MSG_DEBUG, "Ongoing scan action - "
+ "reject new request");
+ reply_len = os_snprintf(reply, reply_size,
+ "FAIL-BUSY\n");
+ }
+ }
} else if (os_strcmp(buf, "SCAN_RESULTS") == 0) {
reply_len = wpa_supplicant_ctrl_iface_scan_results(
wpa_s, reply, reply_size);
} else if (os_strncmp(buf, "AP_SCAN ", 8) == 0) {
if (wpa_supplicant_ctrl_iface_ap_scan(wpa_s, buf + 8))
reply_len = -1;
+ } else if (os_strncmp(buf, "SCAN_INTERVAL ", 14) == 0) {
+ if (wpa_supplicant_ctrl_iface_scan_interval(wpa_s, buf + 14))
+ reply_len = -1;
} else if (os_strcmp(buf, "INTERFACE_LIST") == 0) {
reply_len = wpa_supplicant_global_iface_list(
wpa_s->global, reply, reply_size);
} else if (os_strncmp(buf, "ROAM ", 5) == 0) {
if (wpa_supplicant_ctrl_iface_roam(wpa_s, buf + 5))
reply_len = -1;
+ } else if (os_strncmp(buf, "STA_AUTOCONNECT ", 16) == 0) {
+ if (wpa_supplicant_ctrl_iface_sta_autoconnect(wpa_s, buf + 16))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "BSS_EXPIRE_AGE ", 15) == 0) {
+ if (wpa_supplicant_ctrl_iface_bss_expire_age(wpa_s, buf + 15))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "BSS_EXPIRE_COUNT ", 17) == 0) {
+ if (wpa_supplicant_ctrl_iface_bss_expire_count(wpa_s,
+ buf + 17))
+ reply_len = -1;
+#ifdef CONFIG_TDLS
+ } else if (os_strncmp(buf, "TDLS_DISCOVER ", 14) == 0) {
+ if (wpa_supplicant_ctrl_iface_tdls_discover(wpa_s, buf + 14))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "TDLS_SETUP ", 11) == 0) {
+ if (wpa_supplicant_ctrl_iface_tdls_setup(wpa_s, buf + 11))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "TDLS_TEARDOWN ", 14) == 0) {
+ if (wpa_supplicant_ctrl_iface_tdls_teardown(wpa_s, buf + 14))
+ reply_len = -1;
+#endif /* CONFIG_TDLS */
+ } else if (os_strncmp(buf, "SIGNAL_POLL", 11) == 0) {
+ reply_len = wpa_supplicant_signal_poll(wpa_s, reply,
+ reply_size);
} else {
os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
reply_len = 16;
char *reply;
const int reply_size = 2048;
int reply_len;
+ int level = MSG_DEBUG;
- wpa_hexdump_ascii(MSG_DEBUG, "RX global ctrl_iface",
+ if (os_strcmp(buf, "PING") == 0)
+ level = MSG_EXCESSIVE;
+ wpa_hexdump_ascii(level, "RX global ctrl_iface",
(const u8 *) buf, os_strlen(buf));
reply = os_malloc(reply_size);
void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv);
/**
+ * wpa_supplicant_ctrl_iface_ctrl_rsp_handle - Handle a control response
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @ssid: Pointer to the network block the reply is for
+ * @field: field the response is a reply for
+ * @value: value (ie, password, etc) for @field
+ * Returns: 0 on success, non-zero on error
+ *
+ * Helper function to handle replies to control interface requests.
+ */
+int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid,
+ const char *field,
+ const char *value);
+
+/**
* wpa_supplicant_global_ctrl_iface_init - Initialize global control interface
* @global: Pointer to global data from wpa_supplicant_init()
* Returns: Pointer to private data on success, %NULL on failure
#include <sys/stat.h>
#include <grp.h>
#include <stddef.h>
+#ifdef ANDROID
+#include <cutils/sockets.h>
+#endif /* ANDROID */
#include "utils/common.h"
#include "utils/eloop.h"
{
struct wpa_supplicant *wpa_s = eloop_ctx;
struct ctrl_iface_priv *priv = sock_ctx;
- char buf[256];
+ char buf[4096];
int res;
struct sockaddr_un from;
socklen_t fromlen = sizeof(from);
buf = os_strdup(wpa_s->conf->ctrl_interface);
if (buf == NULL)
goto fail;
+#ifdef ANDROID
+ os_snprintf(addr.sun_path, sizeof(addr.sun_path), "wpa_%s",
+ wpa_s->conf->ctrl_interface);
+ priv->sock = android_get_control_socket(addr.sun_path);
+ if (priv->sock >= 0)
+ goto havesock;
+#endif /* ANDROID */
if (os_strncmp(buf, "DIR=", 4) == 0) {
dir = buf + 4;
gid_str = os_strstr(dir, " GROUP=");
}
os_free(fname);
+#ifdef ANDROID
+havesock:
+#endif /* ANDROID */
eloop_register_read_sock(priv->sock, wpa_supplicant_ctrl_iface_receive,
wpa_s, priv);
wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb);
if (global->params.ctrl_interface == NULL)
return priv;
+#ifdef ANDROID
+ priv->sock = android_get_control_socket(global->params.ctrl_interface);
+ if (priv->sock >= 0)
+ goto havesock;
+#endif /* ANDROID */
+
wpa_printf(MSG_DEBUG, "Global control interface '%s'",
global->params.ctrl_interface);
}
}
+#ifdef ANDROID
+havesock:
+#endif /* ANDROID */
eloop_register_read_sock(priv->sock,
wpa_supplicant_global_ctrl_iface_receive,
global, NULL);
CFLAGS = -MMD -O2 -Wall -g
endif
+PKG_CONFIG ?= pkg-config
CFLAGS += -I../../src -I../../src/utils
CFLAGS += -DCONFIG_CTRL_IFACE_DBUS
ifndef DBUS_LIBS
-DBUS_LIBS := $(shell pkg-config --libs dbus-1)
+DBUS_LIBS := $(shell $(PKG_CONFIG) --libs dbus-1)
endif
ifndef DBUS_INCLUDE
-DBUS_INCLUDE := $(shell pkg-config --cflags dbus-1)
+DBUS_INCLUDE := $(shell $(PKG_CONFIG) --cflags dbus-1)
endif
ifdef CONFIG_CTRL_IFACE_DBUS_INTRO
CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_INTRO
DBUS_LIBS += $(shell xml2-config --libs)
endif
-dbus_version=$(subst ., ,$(shell pkg-config --modversion dbus-1))
-DBUS_VERSION_MAJOR=$(word 1,$(dbus_version))
-DBUS_VERSION_MINOR=$(word 2,$(dbus_version))
-ifeq ($(DBUS_VERSION_MAJOR),)
-DBUS_VERSION_MAJOR=0
-endif
-ifeq ($(DBUS_VERSION_MINOR),)
-DBUS_VERSION_MINOR=0
-endif
-DBUS_INCLUDE += -DDBUS_VERSION_MAJOR=$(DBUS_VERSION_MAJOR)
-DBUS_INCLUDE += -DDBUS_VERSION_MINOR=$(DBUS_VERSION_MINOR)
-
CFLAGS += $(DBUS_INCLUDE)
LIB_OBJS= \
#include <dbus/dbus.h>
#include "common.h"
+#include "wpabuf.h"
#include "dbus_dict_helpers.h"
/**
- * Begin a string array entry in the dict
+ * Begin an array entry in the dict
*
* @param iter_dict A valid DBusMessageIter returned from
* wpa_dbus_dict_open_write()
* @param key The key of the dict item
+ * @param type The type of the contained data
* @param iter_dict_entry A private DBusMessageIter provided by the caller to
* be passed to wpa_dbus_dict_end_string_array()
* @param iter_dict_val A private DBusMessageIter provided by the caller to
* @return TRUE on success, FALSE on failure
*
*/
-dbus_bool_t wpa_dbus_dict_begin_string_array(DBusMessageIter *iter_dict,
- const char *key,
- DBusMessageIter *iter_dict_entry,
- DBusMessageIter *iter_dict_val,
- DBusMessageIter *iter_array)
+dbus_bool_t wpa_dbus_dict_begin_array(DBusMessageIter *iter_dict,
+ const char *key, const char *type,
+ DBusMessageIter *iter_dict_entry,
+ DBusMessageIter *iter_dict_val,
+ DBusMessageIter *iter_array)
{
+ char array_type[10];
+ int err;
+
+ err = os_snprintf(array_type, sizeof(array_type),
+ DBUS_TYPE_ARRAY_AS_STRING "%s",
+ type);
+ if (err < 0 || err > (int) sizeof(array_type))
+ return FALSE;
+
if (!iter_dict || !iter_dict_entry || !iter_dict_val || !iter_array)
return FALSE;
if (!dbus_message_iter_open_container(iter_dict_entry,
DBUS_TYPE_VARIANT,
- DBUS_TYPE_ARRAY_AS_STRING
- DBUS_TYPE_STRING_AS_STRING,
+ array_type,
iter_dict_val))
return FALSE;
if (!dbus_message_iter_open_container(iter_dict_val, DBUS_TYPE_ARRAY,
- DBUS_TYPE_BYTE_AS_STRING,
- iter_array))
+ type, iter_array))
return FALSE;
return TRUE;
}
+dbus_bool_t wpa_dbus_dict_begin_string_array(DBusMessageIter *iter_dict,
+ const char *key,
+ DBusMessageIter *iter_dict_entry,
+ DBusMessageIter *iter_dict_val,
+ DBusMessageIter *iter_array)
+{
+ return wpa_dbus_dict_begin_array(
+ iter_dict, key,
+ DBUS_TYPE_STRING_AS_STRING,
+ iter_dict_entry, iter_dict_val, iter_array);
+}
+
+
/**
* Add a single string element to a string array dict entry
*
/**
- * End a string array dict entry
+ * Add a single byte array element to a string array dict entry
+ *
+ * @param iter_array A valid DBusMessageIter returned from
+ * wpa_dbus_dict_begin_array()'s iter_array
+ * parameter -- note that wpa_dbus_dict_begin_array()
+ * must have been called with "ay" as the type
+ * @param value The data to be added to the dict entry's array
+ * @param value_len The length of the data
+ * @return TRUE on success, FALSE on failure
+ *
+ */
+dbus_bool_t wpa_dbus_dict_bin_array_add_element(DBusMessageIter *iter_array,
+ const u8 *value,
+ size_t value_len)
+{
+ DBusMessageIter iter_bytes;
+ size_t i;
+
+ if (!iter_array || !value)
+ return FALSE;
+
+ if (!dbus_message_iter_open_container(iter_array, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_BYTE_AS_STRING,
+ &iter_bytes))
+ return FALSE;
+
+ for (i = 0; i < value_len; i++) {
+ if (!dbus_message_iter_append_basic(&iter_bytes,
+ DBUS_TYPE_BYTE,
+ &(value[i])))
+ return FALSE;
+ }
+
+ if (!dbus_message_iter_close_container(iter_array, &iter_bytes))
+ return FALSE;
+
+ return TRUE;
+}
+
+
+/**
+ * End an array dict entry
*
* @param iter_dict A valid DBusMessageIter returned from
* wpa_dbus_dict_open_write()
* @param iter_dict_entry A private DBusMessageIter returned from
- * wpa_dbus_dict_end_string_array()
+ * wpa_dbus_dict_begin_string_array() or
+ * wpa_dbus_dict_begin_array()
* @param iter_dict_val A private DBusMessageIter returned from
- * wpa_dbus_dict_end_string_array()
+ * wpa_dbus_dict_begin_string_array() or
+ * wpa_dbus_dict_begin_array()
* @param iter_array A DBusMessageIter returned from
- * wpa_dbus_dict_end_string_array()
+ * wpa_dbus_dict_begin_string_array() or
+ * wpa_dbus_dict_begin_array()
* @return TRUE on success, FALSE on failure
*
*/
-dbus_bool_t wpa_dbus_dict_end_string_array(DBusMessageIter *iter_dict,
- DBusMessageIter *iter_dict_entry,
- DBusMessageIter *iter_dict_val,
- DBusMessageIter *iter_array)
+dbus_bool_t wpa_dbus_dict_end_array(DBusMessageIter *iter_dict,
+ DBusMessageIter *iter_dict_entry,
+ DBusMessageIter *iter_dict_val,
+ DBusMessageIter *iter_array)
{
if (!iter_dict || !iter_dict_entry || !iter_dict_val || !iter_array)
return FALSE;
}
+/**
+ * Convenience function to add an wpabuf binary array to the dict.
+ *
+ * @param iter_dict A valid DBusMessageIter returned from
+ * wpa_dbus_dict_open_write()
+ * @param key The key of the dict item
+ * @param items The array of wpabuf structures
+ * @param num_items The number of strings in the array
+ * @return TRUE on success, FALSE on failure
+ *
+ */
+dbus_bool_t wpa_dbus_dict_append_wpabuf_array(DBusMessageIter *iter_dict,
+ const char *key,
+ const struct wpabuf **items,
+ const dbus_uint32_t num_items)
+{
+ DBusMessageIter iter_dict_entry, iter_dict_val, iter_array;
+ dbus_uint32_t i;
+
+ if (!key)
+ return FALSE;
+ if (!items && (num_items != 0))
+ return FALSE;
+
+ if (!wpa_dbus_dict_begin_array(iter_dict, key,
+ DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_TYPE_BYTE_AS_STRING,
+ &iter_dict_entry, &iter_dict_val,
+ &iter_array))
+ return FALSE;
+
+ for (i = 0; i < num_items; i++) {
+ if (!wpa_dbus_dict_bin_array_add_element(&iter_array,
+ wpabuf_head(items[i]),
+ wpabuf_len(items[i])))
+ return FALSE;
+ }
+
+ if (!wpa_dbus_dict_end_array(iter_dict, &iter_dict_entry,
+ &iter_dict_val, &iter_array))
+ return FALSE;
+
+ return TRUE;
+}
+
+
/*****************************************************/
/* Stuff for reading dicts */
/*****************************************************/
* @param iter A valid DBusMessageIter pointing to the start of the dict
* @param iter_dict (out) A DBusMessageIter to be passed to
* wpa_dbus_dict_read_next_entry()
+ * @error on failure a descriptive error
* @return TRUE on success, FALSE on failure
*
*/
dbus_bool_t wpa_dbus_dict_open_read(DBusMessageIter *iter,
- DBusMessageIter *iter_dict)
+ DBusMessageIter *iter_dict,
+ DBusError *error)
{
- if (!iter || !iter_dict)
+ if (!iter || !iter_dict) {
+ dbus_set_error_const(error, DBUS_ERROR_FAILED,
+ "[internal] missing message iterators");
return FALSE;
+ }
if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY ||
- dbus_message_iter_get_element_type(iter) != DBUS_TYPE_DICT_ENTRY)
+ dbus_message_iter_get_element_type(iter) != DBUS_TYPE_DICT_ENTRY) {
+ dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS,
+ "unexpected message argument types");
return FALSE;
+ }
dbus_message_iter_recurse(iter, iter_dict);
return TRUE;
#define BYTE_ARRAY_ITEM_SIZE (sizeof(char))
static dbus_bool_t _wpa_dbus_dict_entry_get_byte_array(
- DBusMessageIter *iter, int array_type,
- struct wpa_dbus_dict_entry *entry)
+ DBusMessageIter *iter, struct wpa_dbus_dict_entry *entry)
{
dbus_uint32_t count = 0;
dbus_bool_t success = FALSE;
}
+#define BIN_ARRAY_CHUNK_SIZE 10
+#define BIN_ARRAY_ITEM_SIZE (sizeof(struct wpabuf *))
+
+static dbus_bool_t _wpa_dbus_dict_entry_get_binarray(
+ DBusMessageIter *iter, struct wpa_dbus_dict_entry *entry)
+{
+ struct wpa_dbus_dict_entry tmpentry;
+ size_t buflen = 0;
+ int i;
+
+ if (dbus_message_iter_get_element_type(iter) != DBUS_TYPE_BYTE)
+ return FALSE;
+
+ entry->array_type = WPAS_DBUS_TYPE_BINARRAY;
+ entry->array_len = 0;
+ entry->binarray_value = NULL;
+
+ while (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_ARRAY) {
+ DBusMessageIter iter_array;
+
+ if (entry->array_len == buflen) {
+ struct wpabuf **newbuf;
+
+ buflen += BIN_ARRAY_CHUNK_SIZE;
+
+ newbuf = os_realloc(entry->binarray_value,
+ buflen * BIN_ARRAY_ITEM_SIZE);
+ if (!newbuf)
+ goto cleanup;
+ entry->binarray_value = newbuf;
+ }
+
+ dbus_message_iter_recurse(iter, &iter_array);
+ if (_wpa_dbus_dict_entry_get_byte_array(&iter_array, &tmpentry)
+ == FALSE)
+ goto cleanup;
+
+ entry->binarray_value[entry->array_len] =
+ wpabuf_alloc_ext_data((u8 *) tmpentry.bytearray_value,
+ tmpentry.array_len);
+ if (entry->binarray_value[entry->array_len] == NULL) {
+ wpa_dbus_dict_entry_clear(&tmpentry);
+ goto cleanup;
+ }
+ entry->array_len++;
+ dbus_message_iter_next(iter);
+ }
+
+ return TRUE;
+
+ cleanup:
+ for (i = 0; i < (int) entry->array_len; i++)
+ wpabuf_free(entry->binarray_value[i]);
+ os_free(entry->binarray_value);
+ entry->array_len = 0;
+ entry->binarray_value = NULL;
+ return FALSE;
+}
+
+
static dbus_bool_t _wpa_dbus_dict_entry_get_array(
DBusMessageIter *iter_dict_val, struct wpa_dbus_dict_entry *entry)
{
switch (array_type) {
case DBUS_TYPE_BYTE:
success = _wpa_dbus_dict_entry_get_byte_array(&iter_array,
- array_type,
entry);
break;
case DBUS_TYPE_STRING:
array_type,
entry);
break;
+ case DBUS_TYPE_ARRAY:
+ success = _wpa_dbus_dict_entry_get_binarray(&iter_array, entry);
default:
break;
}
os_free(entry->strarray_value[i]);
os_free(entry->strarray_value);
break;
+ case WPAS_DBUS_TYPE_BINARRAY:
+ for (i = 0; i < entry->array_len; i++)
+ wpabuf_free(entry->binarray_value[i]);
+ os_free(entry->binarray_value);
+ break;
}
break;
}
#ifndef DBUS_DICT_HELPERS_H
#define DBUS_DICT_HELPERS_H
+#include "wpabuf.h"
+
/*
* Adding a dict to a DBusMessage
*/
const char *value,
const dbus_uint32_t value_len);
-/* Manual construction and addition of string array elements */
+/* Manual construction and addition of array elements */
+dbus_bool_t wpa_dbus_dict_begin_array(DBusMessageIter *iter_dict,
+ const char *key, const char *type,
+ DBusMessageIter *iter_dict_entry,
+ DBusMessageIter *iter_dict_val,
+ DBusMessageIter *iter_array);
+
dbus_bool_t wpa_dbus_dict_begin_string_array(DBusMessageIter *iter_dict,
const char *key,
DBusMessageIter *iter_dict_entry,
dbus_bool_t wpa_dbus_dict_string_array_add_element(DBusMessageIter *iter_array,
const char *elem);
-dbus_bool_t wpa_dbus_dict_end_string_array(DBusMessageIter *iter_dict,
- DBusMessageIter *iter_dict_entry,
- DBusMessageIter *iter_dict_val,
- DBusMessageIter *iter_array);
+dbus_bool_t wpa_dbus_dict_bin_array_add_element(DBusMessageIter *iter_array,
+ const u8 *value,
+ size_t value_len);
+
+dbus_bool_t wpa_dbus_dict_end_array(DBusMessageIter *iter_dict,
+ DBusMessageIter *iter_dict_entry,
+ DBusMessageIter *iter_dict_val,
+ DBusMessageIter *iter_array);
+
+static inline dbus_bool_t
+wpa_dbus_dict_end_string_array(DBusMessageIter *iter_dict,
+ DBusMessageIter *iter_dict_entry,
+ DBusMessageIter *iter_dict_val,
+ DBusMessageIter *iter_array)
+{
+ return wpa_dbus_dict_end_array(iter_dict, iter_dict_entry,
+ iter_dict_val, iter_array);
+}
/* Convenience function to add a whole string list */
dbus_bool_t wpa_dbus_dict_append_string_array(DBusMessageIter *iter_dict,
const char **items,
const dbus_uint32_t num_items);
+dbus_bool_t wpa_dbus_dict_append_wpabuf_array(DBusMessageIter *iter_dict,
+ const char *key,
+ const struct wpabuf **items,
+ const dbus_uint32_t num_items);
+
/*
* Reading a dict from a DBusMessage
*/
+#define WPAS_DBUS_TYPE_BINARRAY (DBUS_NUMBER_OF_TYPES + 100)
+
struct wpa_dbus_dict_entry {
int type; /** the dbus type of the dict entry's value */
int array_type; /** the dbus type of the array elements if the dict
- entry value contains an array */
+ entry value contains an array, or the special
+ WPAS_DBUS_TYPE_BINARRAY */
const char *key; /** key of the dict entry */
/** Possible values of the property */
double double_value;
char *bytearray_value;
char **strarray_value;
+ struct wpabuf **binarray_value;
};
dbus_uint32_t array_len; /** length of the array if the dict entry's
value contains an array */
};
dbus_bool_t wpa_dbus_dict_open_read(DBusMessageIter *iter,
- DBusMessageIter *iter_dict);
+ DBusMessageIter *iter_dict,
+ DBusError *error);
dbus_bool_t wpa_dbus_dict_get_entry(DBusMessageIter *iter_dict,
struct wpa_dbus_dict_entry *entry);
#include "includes.h"
#include "common.h"
+#include "common/ieee802_11_defs.h"
#include "wps/wps.h"
#include "../config.h"
#include "../wpa_supplicant_i.h"
#include "../bss.h"
+#include "../wpas_glue.h"
#include "dbus_new_helpers.h"
#include "dbus_dict_helpers.h"
#include "dbus_new.h"
#include "dbus_new_handlers.h"
#include "dbus_common.h"
#include "dbus_common_i.h"
+#include "dbus_new_handlers_p2p.h"
+#include "p2p/p2p.h"
/**
{
struct wpas_dbus_priv *iface;
DBusMessage *msg;
- DBusMessageIter iter, iter_dict;
+ DBusMessageIter iter;
iface = wpa_s->global->dbus;
goto err;
if (properties) {
- if (!wpa_dbus_dict_open_write(&iter, &iter_dict))
- goto err;
-
- wpa_dbus_get_object_properties(iface, wpa_s->dbus_new_path,
- WPAS_DBUS_NEW_IFACE_INTERFACE,
- &iter_dict);
-
- if (!wpa_dbus_dict_close_write(&iter, &iter_dict))
+ if (!wpa_dbus_get_object_properties(
+ iface, wpa_s->dbus_new_path,
+ WPAS_DBUS_NEW_IFACE_INTERFACE, &iter))
goto err;
}
{
struct wpas_dbus_priv *iface;
DBusMessage *msg;
- DBusMessageIter iter, iter_dict;
+ DBusMessageIter iter;
iface = wpa_s->global->dbus;
goto err;
if (properties) {
- if (!wpa_dbus_dict_open_write(&iter, &iter_dict))
- goto err;
-
- wpa_dbus_get_object_properties(iface, bss_obj_path,
- WPAS_DBUS_NEW_IFACE_BSS,
- &iter_dict);
-
- if (!wpa_dbus_dict_close_write(&iter, &iter_dict))
+ if (!wpa_dbus_get_object_properties(iface, bss_obj_path,
+ WPAS_DBUS_NEW_IFACE_BSS,
+ &iter))
goto err;
}
{
struct wpas_dbus_priv *iface;
DBusMessage *msg;
- DBusMessageIter iter, iter_dict;
+ DBusMessageIter iter;
char net_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
iface = wpa_s->global->dbus;
goto err;
if (properties) {
- if (!wpa_dbus_dict_open_write(&iter, &iter_dict))
- goto err;
-
- wpa_dbus_get_object_properties(iface, net_obj_path,
- WPAS_DBUS_NEW_IFACE_NETWORK,
- &iter_dict);
-
- if (!wpa_dbus_dict_close_write(&iter, &iter_dict))
+ if (!wpa_dbus_get_object_properties(
+ iface, net_obj_path, WPAS_DBUS_NEW_IFACE_NETWORK,
+ &iter))
goto err;
}
/**
+ * wpas_dbus_signal_network_request - Indicate that additional information
+ * (EAP password, etc.) is required to complete the association to this SSID
+ * @wpa_s: %wpa_supplicant network interface data
+ * @rtype: The specific additional information required
+ * @default_text: Optional description of required information
+ *
+ * Request additional information or passwords to complete an association
+ * request.
+ */
+void wpas_dbus_signal_network_request(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid,
+ enum wpa_ctrl_req_type rtype,
+ const char *default_txt)
+{
+ struct wpas_dbus_priv *iface;
+ DBusMessage *msg;
+ DBusMessageIter iter;
+ char net_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
+ const char *field, *txt = NULL, *net_ptr;
+
+ iface = wpa_s->global->dbus;
+
+ /* Do nothing if the control interface is not turned on */
+ if (iface == NULL)
+ return;
+
+ field = wpa_supplicant_ctrl_req_to_string(rtype, default_txt, &txt);
+ if (field == NULL)
+ return;
+
+ msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+ WPAS_DBUS_NEW_IFACE_INTERFACE,
+ "NetworkRequest");
+ if (msg == NULL)
+ return;
+
+ os_snprintf(net_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%u",
+ wpa_s->dbus_new_path, ssid->id);
+ net_ptr = &net_obj_path[0];
+
+ dbus_message_iter_init_append(msg, &iter);
+ if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
+ &net_ptr))
+ goto err;
+ if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &field))
+ goto err;
+ if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &txt))
+ goto err;
+
+ dbus_connection_send(iface->con, msg, NULL);
+ dbus_message_unref(msg);
+ return;
+
+err:
+ wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+ dbus_message_unref(msg);
+}
+
+
+/**
* wpas_dbus_signal_network_enabled_changed - Signals Enabled property changes
* @wpa_s: %wpa_supplicant network interface data
* @ssid: configured network which Enabled property has changed
#endif /* CONFIG_WPS */
+void wpas_dbus_signal_certification(struct wpa_supplicant *wpa_s,
+ int depth, const char *subject,
+ const char *cert_hash,
+ const struct wpabuf *cert)
+{
+ struct wpas_dbus_priv *iface;
+ DBusMessage *msg;
+ DBusMessageIter iter, dict_iter;
+
+ iface = wpa_s->global->dbus;
+
+ /* Do nothing if the control interface is not turned on */
+ if (iface == NULL)
+ return;
+
+ msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+ WPAS_DBUS_NEW_IFACE_INTERFACE,
+ "Certification");
+ if (msg == NULL)
+ return;
+
+ dbus_message_iter_init_append(msg, &iter);
+ if (!wpa_dbus_dict_open_write(&iter, &dict_iter))
+ goto nomem;
+
+ if (!wpa_dbus_dict_append_uint32(&dict_iter, "depth", depth) ||
+ !wpa_dbus_dict_append_string(&dict_iter, "subject", subject))
+ goto nomem;
+
+ if (cert_hash &&
+ !wpa_dbus_dict_append_string(&dict_iter, "cert_hash", cert_hash))
+ goto nomem;
+
+ if (cert &&
+ !wpa_dbus_dict_append_byte_array(&dict_iter, "cert",
+ wpabuf_head(cert),
+ wpabuf_len(cert)))
+ goto nomem;
+
+ if (!wpa_dbus_dict_close_write(&iter, &dict_iter))
+ goto nomem;
+
+ dbus_connection_send(iface->con, msg, NULL);
+
+nomem:
+ dbus_message_unref(msg);
+}
+
+#ifdef CONFIG_P2P
/**
- * wpas_dbus_signal_prop_changed - Signals change of property
+ * wpas_dbus_signal_p2p_group_removed - Signals P2P group was removed
* @wpa_s: %wpa_supplicant network interface data
- * @property: indicates which property has changed
- *
- * Sends ProertyChanged signals with path, interface and arguments
- * depending on which property has changed.
+ * @role: role of this device (client or GO)
+ * Sends signal with i/f name and role as string arguments
*/
-void wpas_dbus_signal_prop_changed(struct wpa_supplicant *wpa_s,
- enum wpas_dbus_prop property)
+void wpas_dbus_signal_p2p_group_removed(struct wpa_supplicant *wpa_s,
+ const char *role)
{
- WPADBusPropertyAccessor getter;
- char *prop;
- if (wpa_s->dbus_new_path == NULL)
- return; /* Skip signal since D-Bus setup is not yet ready */
+ DBusMessage *msg;
+ DBusMessageIter iter;
+ struct wpas_dbus_priv *iface = wpa_s->global->dbus;
+ char *ifname = wpa_s->ifname;
- switch (property) {
- case WPAS_DBUS_PROP_AP_SCAN:
- getter = (WPADBusPropertyAccessor) wpas_dbus_getter_ap_scan;
- prop = "ApScan";
- break;
- case WPAS_DBUS_PROP_SCANNING:
- getter = (WPADBusPropertyAccessor) wpas_dbus_getter_scanning;
- prop = "Scanning";
- break;
- case WPAS_DBUS_PROP_STATE:
- getter = (WPADBusPropertyAccessor) wpas_dbus_getter_state;
- prop = "State";
- break;
- case WPAS_DBUS_PROP_CURRENT_BSS:
- getter = (WPADBusPropertyAccessor)
- wpas_dbus_getter_current_bss;
- prop = "CurrentBSS";
- break;
- case WPAS_DBUS_PROP_CURRENT_NETWORK:
- getter = (WPADBusPropertyAccessor)
- wpas_dbus_getter_current_network;
- prop = "CurrentNetwork";
- break;
- default:
- wpa_printf(MSG_ERROR, "dbus: %s: Unknown Property value %d",
- __func__, property);
+ /* Do nothing if the control interface is not turned on */
+ if (iface == NULL)
+ return;
+
+ msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+ WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ "GroupFinished");
+ if (msg == NULL)
return;
+
+ dbus_message_iter_init_append(msg, &iter);
+
+ if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &ifname)) {
+ wpa_printf(MSG_ERROR, "dbus: Failed to construct GroupFinished"
+ "signal -not enough memory for ifname ");
+ goto err;
}
- wpa_dbus_mark_property_changed(wpa_s->global->dbus,
- wpa_s->dbus_new_path,
- WPAS_DBUS_NEW_IFACE_INTERFACE, prop);
+ if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &role))
+ wpa_printf(MSG_ERROR, "dbus: Failed to construct GroupFinished"
+ "signal -not enough memory for role ");
+ else
+ dbus_connection_send(iface->con, msg, NULL);
+
+err:
+ dbus_message_unref(msg);
}
/**
- * wpas_dbus_bss_signal_prop_changed - Signals change of BSS property
- * @wpa_s: %wpa_supplicant network interface data
- * @property: indicates which property has changed
- * @id: unique BSS identifier
+ * wpas_dbus_signal_p2p_provision_discovery - Signals various PD events
*
- * Sends PropertyChanged signals with path, interface, and arguments depending
- * on which property has changed.
+ * @dev_addr - who sent the request or responded to our request.
+ * @request - Will be 1 if request, 0 for response.
+ * @status - valid only in case of response
+ * @config_methods - wps config methods
+ * @generated_pin - pin to be displayed in case of WPS_CONFIG_DISPLAY method
+ *
+ * Sends following provision discovery related events:
+ * ProvisionDiscoveryRequestDisplayPin
+ * ProvisionDiscoveryResponseDisplayPin
+ * ProvisionDiscoveryRequestEnterPin
+ * ProvisionDiscoveryResponseEnterPin
+ * ProvisionDiscoveryPBCRequest
+ * ProvisionDiscoveryPBCResponse
+ *
+ * TODO::
+ * ProvisionDiscoveryFailure (timeout case)
*/
-void wpas_dbus_bss_signal_prop_changed(struct wpa_supplicant *wpa_s,
- enum wpas_dbus_bss_prop property,
- unsigned int id)
+void wpas_dbus_signal_p2p_provision_discovery(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr, int request,
+ enum p2p_prov_disc_status status,
+ u16 config_methods,
+ unsigned int generated_pin)
{
- char path[WPAS_DBUS_OBJECT_PATH_MAX];
- char *prop;
+ DBusMessage *msg;
+ DBusMessageIter iter;
+ struct wpas_dbus_priv *iface;
+ char *_signal;
+ int add_pin = 0;
+ char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
+ int error_ret = 1;
+ char pin[9], *p_pin = NULL;
- switch (property) {
- case WPAS_DBUS_BSS_PROP_SIGNAL:
- prop = "Signal";
- break;
- case WPAS_DBUS_BSS_PROP_FREQ:
- prop = "Frequency";
- break;
- case WPAS_DBUS_BSS_PROP_MODE:
- prop = "Mode";
- break;
- case WPAS_DBUS_BSS_PROP_PRIVACY:
- prop = "Privacy";
- break;
- case WPAS_DBUS_BSS_PROP_RATES:
- prop = "Rates";
- break;
- case WPAS_DBUS_BSS_PROP_WPA:
- prop = "WPA";
- break;
- case WPAS_DBUS_BSS_PROP_RSN:
- prop = "RSN";
- break;
- case WPAS_DBUS_BSS_PROP_IES:
- prop = "IEs";
- break;
- default:
- wpa_printf(MSG_ERROR, "dbus: %s: Unknown Property value %d",
- __func__, property);
+ iface = wpa_s->global->dbus;
+
+ /* Do nothing if the control interface is not turned on */
+ if (iface == NULL)
return;
+
+ if (request || !status) {
+ if (config_methods & WPS_CONFIG_DISPLAY)
+ _signal = request ?
+ "ProvisionDiscoveryRequestDisplayPin" :
+ "ProvisionDiscoveryResponseEnterPin";
+ else if (config_methods & WPS_CONFIG_KEYPAD)
+ _signal = request ?
+ "ProvisionDiscoveryRequestEnterPin" :
+ "ProvisionDiscoveryResponseDisplayPin";
+ else if (config_methods & WPS_CONFIG_PUSHBUTTON)
+ _signal = request ? "ProvisionDiscoveryPBCRequest" :
+ "ProvisionDiscoveryPBCResponse";
+ else
+ return; /* Unknown or un-supported method */
+ } else if (!request && status)
+ /* Explicit check for failure response */
+ _signal = "ProvisionDiscoveryFailure";
+
+ add_pin = ((request && (config_methods & WPS_CONFIG_DISPLAY)) ||
+ (!request && !status &&
+ (config_methods & WPS_CONFIG_KEYPAD)));
+
+ if (add_pin) {
+ os_snprintf(pin, sizeof(pin), "%08d", generated_pin);
+ p_pin = pin;
}
- os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX,
- "%s/" WPAS_DBUS_NEW_BSSIDS_PART "/%u",
- wpa_s->dbus_new_path, id);
+ msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+ WPAS_DBUS_NEW_IFACE_P2PDEVICE, _signal);
+ if (msg == NULL)
+ return;
- wpa_dbus_mark_property_changed(wpa_s->global->dbus, path,
- WPAS_DBUS_NEW_IFACE_BSS, prop);
+ /* Check if this is a known peer */
+ if (!p2p_peer_known(wpa_s->global->p2p, dev_addr))
+ goto error;
+
+ os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/"
+ COMPACT_MACSTR,
+ wpa_s->dbus_new_path, MAC2STR(dev_addr));
+
+ path = peer_obj_path;
+
+ dbus_message_iter_init_append(msg, &iter);
+
+ if (!dbus_message_iter_append_basic(&iter,
+ DBUS_TYPE_OBJECT_PATH,
+ &path))
+ goto error;
+
+ if (!request && status)
+ /* Attach status to ProvisionDiscoveryFailure */
+ error_ret = !dbus_message_iter_append_basic(&iter,
+ DBUS_TYPE_INT32,
+ &status);
+ else
+ error_ret = (add_pin &&
+ !dbus_message_iter_append_basic(&iter,
+ DBUS_TYPE_STRING,
+ &p_pin));
+
+error:
+ if (!error_ret)
+ dbus_connection_send(iface->con, msg, NULL);
+ else
+ wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+
+ dbus_message_unref(msg);
}
-/**
- * wpas_dbus_signal_debug_level_changed - Signals change of debug param
- * @global: wpa_global structure
- *
- * Sends ProertyChanged signals informing that debug level has changed.
- */
-void wpas_dbus_signal_debug_level_changed(struct wpa_global *global)
+void wpas_dbus_signal_p2p_go_neg_req(struct wpa_supplicant *wpa_s,
+ const u8 *src, u16 dev_passwd_id)
{
- wpa_dbus_mark_property_changed(global->dbus, WPAS_DBUS_NEW_PATH,
- WPAS_DBUS_NEW_INTERFACE,
- "DebugLevel");
+ DBusMessage *msg;
+ DBusMessageIter iter;
+ struct wpas_dbus_priv *iface;
+ char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
+
+ iface = wpa_s->global->dbus;
+
+ /* Do nothing if the control interface is not turned on */
+ if (iface == NULL)
+ return;
+
+ os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR,
+ wpa_s->dbus_new_path, MAC2STR(src));
+ path = peer_obj_path;
+
+ msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+ WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ "GONegotiationRequest");
+ if (msg == NULL)
+ return;
+
+ dbus_message_iter_init_append(msg, &iter);
+
+ if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
+ &path) ||
+ !dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT16,
+ &dev_passwd_id))
+ wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+ else
+ dbus_connection_send(iface->con, msg, NULL);
+
+ dbus_message_unref(msg);
}
-/**
- * wpas_dbus_signal_debug_timestamp_changed - Signals change of debug param
- * @global: wpa_global structure
- *
- * Sends ProertyChanged signals informing that debug timestamp has changed.
- */
-void wpas_dbus_signal_debug_timestamp_changed(struct wpa_global *global)
+static int wpas_dbus_get_group_obj_path(struct wpa_supplicant *wpa_s,
+ const struct wpa_ssid *ssid,
+ char *group_obj_path)
{
- wpa_dbus_mark_property_changed(global->dbus, WPAS_DBUS_NEW_PATH,
- WPAS_DBUS_NEW_INTERFACE,
- "DebugTimestamp");
+ char group_name[3];
+
+ if (os_memcmp(ssid->ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN))
+ return -1;
+
+ memcpy(group_name, ssid->ssid + P2P_WILDCARD_SSID_LEN, 2);
+ group_name[2] = '\0';
+
+ os_snprintf(group_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s/" WPAS_DBUS_NEW_P2P_GROUPS_PART "/%s",
+ wpa_s->dbus_new_path, group_name);
+
+ return 0;
}
/**
- * wpas_dbus_signal_debug_show_keys_changed - Signals change of debug param
- * @global: wpa_global structure
+ * wpas_dbus_signal_p2p_group_started - Signals P2P group has
+ * started. Emitted when a group is successfully started
+ * irrespective of the role (client/GO) of the current device
*
- * Sends ProertyChanged signals informing that debug show_keys has changed.
+ * @wpa_s: %wpa_supplicant network interface data
+ * @ssid: SSID object
+ * @client: this device is P2P client
+ * @network_id: network id of the group started, use instead of ssid->id
+ * to account for persistent groups
*/
-void wpas_dbus_signal_debug_show_keys_changed(struct wpa_global *global)
+void wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s,
+ const struct wpa_ssid *ssid,
+ int client, int network_id)
{
- wpa_dbus_mark_property_changed(global->dbus, WPAS_DBUS_NEW_PATH,
- WPAS_DBUS_NEW_INTERFACE,
- "DebugShowKeys");
-}
+ DBusMessage *msg;
+ DBusMessageIter iter, dict_iter;
+ struct wpas_dbus_priv *iface;
+ char net_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
+ char group_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
+ iface = wpa_s->parent->global->dbus;
-static void wpas_dbus_register(struct wpa_dbus_object_desc *obj_desc,
- void *priv,
- WPADBusArgumentFreeFunction priv_free,
- const struct wpa_dbus_method_desc *methods,
- const struct wpa_dbus_property_desc *properties,
- const struct wpa_dbus_signal_desc *signals)
+ /* Do nothing if the control interface is not turned on */
+ if (iface == NULL)
+ return;
+
+ if (wpas_dbus_get_group_obj_path(wpa_s, ssid, group_obj_path) < 0)
+ return;
+
+ /* New interface has been created for this group */
+ msg = dbus_message_new_signal(wpa_s->parent->dbus_new_path,
+ WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ "GroupStarted");
+
+ if (msg == NULL)
+ return;
+
+ dbus_message_iter_init_append(msg, &iter);
+ if (!wpa_dbus_dict_open_write(&iter, &dict_iter))
+ goto nomem;
+
+ /*
+ * In case the device supports creating a separate interface the
+ * DBus client will need to know the object path for the interface
+ * object this group was created on, so include it here.
+ */
+ if (!wpa_dbus_dict_append_object_path(&dict_iter,
+ "interface_object",
+ wpa_s->dbus_new_path))
+ goto nomem;
+
+ if (!wpa_dbus_dict_append_string(&dict_iter, "role",
+ client ? "client" : "GO"))
+ goto nomem;
+
+ os_snprintf(net_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%u",
+ wpa_s->parent->dbus_new_path, network_id);
+
+ if (!wpa_dbus_dict_append_object_path(&dict_iter, "group_object",
+ group_obj_path) ||
+ !wpa_dbus_dict_append_object_path(&dict_iter, "network_object",
+ net_obj_path) ||
+ !wpa_dbus_dict_close_write(&iter, &dict_iter))
+ goto nomem;
+
+ dbus_connection_send(iface->con, msg, NULL);
+
+nomem:
+ dbus_message_unref(msg);
+}
+
+
+/**
+ *
+ * Method to emit GONeogtiation Success or Failure signals based
+ * on status.
+ * @status: Status of the GO neg request. 0 for success, other for errors.
+ */
+void wpas_dbus_signal_p2p_go_neg_resp(struct wpa_supplicant *wpa_s,
+ struct p2p_go_neg_results *res)
{
- int n;
+ DBusMessage *msg;
+ DBusMessageIter iter, dict_iter;
+ DBusMessageIter iter_dict_entry, iter_dict_val, iter_dict_array;
+ struct wpas_dbus_priv *iface;
+ char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
+ dbus_int32_t freqs[P2P_MAX_CHANNELS];
+ dbus_int32_t *f_array = freqs;
- obj_desc->user_data = priv;
- obj_desc->user_data_free_func = priv_free;
- obj_desc->methods = methods;
- obj_desc->properties = properties;
- obj_desc->signals = signals;
- for (n = 0; properties && properties->dbus_property; properties++)
- n++;
+ iface = wpa_s->global->dbus;
- obj_desc->prop_changed_flags = os_zalloc(n);
- if (!obj_desc->prop_changed_flags)
- wpa_printf(MSG_DEBUG, "dbus: %s: can't register handlers",
- __func__);
+ os_memset(freqs, 0, sizeof(freqs));
+ /* Do nothing if the control interface is not turned on */
+ if (iface == NULL)
+ return;
+
+ os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR,
+ wpa_s->dbus_new_path, MAC2STR(res->peer_device_addr));
+ path = peer_obj_path;
+
+ msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+ WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ res->status ? "GONegotiationFailure" :
+ "GONegotiationSuccess");
+ if (msg == NULL)
+ return;
+
+ dbus_message_iter_init_append(msg, &iter);
+ if (!wpa_dbus_dict_open_write(&iter, &dict_iter))
+ goto err;
+ if (!wpa_dbus_dict_append_object_path(&dict_iter, "peer_object",
+ path) ||
+ !wpa_dbus_dict_append_int32(&dict_iter, "status", res->status))
+ goto err;
+
+ if (!res->status) {
+ int i = 0;
+ int freq_list_num = 0;
+
+ if (res->role_go) {
+ if (!wpa_dbus_dict_append_byte_array(
+ &dict_iter, "passphrase",
+ (const char *) res->passphrase,
+ sizeof(res->passphrase)))
+ goto err;
+ }
+
+ if (!wpa_dbus_dict_append_string(&dict_iter, "role_go",
+ res->role_go ? "GO" :
+ "client") ||
+ !wpa_dbus_dict_append_int32(&dict_iter, "frequency",
+ res->freq) ||
+ !wpa_dbus_dict_append_byte_array(&dict_iter, "ssid",
+ (const char *) res->ssid,
+ res->ssid_len) ||
+ !wpa_dbus_dict_append_byte_array(&dict_iter,
+ "peer_device_addr",
+ (const char *)
+ res->peer_device_addr,
+ ETH_ALEN) ||
+ !wpa_dbus_dict_append_byte_array(&dict_iter,
+ "peer_interface_addr",
+ (const char *)
+ res->peer_interface_addr,
+ ETH_ALEN) ||
+ !wpa_dbus_dict_append_string(&dict_iter, "wps_method",
+ p2p_wps_method_text(
+ res->wps_method)))
+ goto err;
+
+ for (i = 0; i < P2P_MAX_CHANNELS; i++) {
+ if (res->freq_list[i]) {
+ freqs[i] = res->freq_list[i];
+ freq_list_num++;
+ }
+ }
+
+ if (!wpa_dbus_dict_begin_array(&dict_iter,
+ "frequency_list",
+ DBUS_TYPE_INT32_AS_STRING,
+ &iter_dict_entry,
+ &iter_dict_val,
+ &iter_dict_array))
+ goto err;
+
+ if (!dbus_message_iter_append_fixed_array(&iter_dict_array,
+ DBUS_TYPE_INT32,
+ &f_array,
+ freq_list_num))
+ goto err;
+
+ if (!wpa_dbus_dict_end_array(&dict_iter,
+ &iter_dict_entry,
+ &iter_dict_val,
+ &iter_dict_array))
+ goto err;
+
+ if (!wpa_dbus_dict_append_int32(&dict_iter, "persistent_group",
+ res->persistent_group) ||
+ !wpa_dbus_dict_append_uint32(&dict_iter,
+ "peer_config_timeout",
+ res->peer_config_timeout))
+ goto err;
+ }
+
+ if (!wpa_dbus_dict_close_write(&iter, &dict_iter))
+ goto err;
+
+ dbus_connection_send(iface->con, msg, NULL);
+err:
+ dbus_message_unref(msg);
}
-static const struct wpa_dbus_method_desc wpas_dbus_global_methods[] = {
- { "CreateInterface", WPAS_DBUS_NEW_INTERFACE,
- (WPADBusMethodHandler) &wpas_dbus_handler_create_interface,
+/**
+ *
+ * Method to emit Invitation Result signal based on status and
+ * bssid
+ * @status: Status of the Invite request. 0 for success, other
+ * for errors
+ * @bssid : Basic Service Set Identifier
+ */
+void wpas_dbus_signal_p2p_invitation_result(struct wpa_supplicant *wpa_s,
+ int status, const u8 *bssid)
+{
+ DBusMessage *msg;
+ DBusMessageIter iter, dict_iter;
+ struct wpas_dbus_priv *iface;
+
+ wpa_printf(MSG_INFO, "%s\n", __func__);
+
+ iface = wpa_s->global->dbus;
+ /* Do nothing if the control interface is not turned on */
+ if (iface == NULL)
+ return;
+
+ msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+ WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ "InvitationResult");
+
+ if (msg == NULL)
+ return;
+
+ dbus_message_iter_init_append(msg, &iter);
+ if (!wpa_dbus_dict_open_write(&iter, &dict_iter))
+ goto nomem;
+
+ if (!wpa_dbus_dict_append_int32(&dict_iter, "status", status))
+ goto nomem;
+ if (bssid) {
+ if (!wpa_dbus_dict_append_byte_array(&dict_iter, "BSSID",
+ (const char *) bssid,
+ ETH_ALEN))
+ goto nomem;
+ }
+ if (!wpa_dbus_dict_close_write(&iter, &dict_iter))
+ goto nomem;
+
+ dbus_connection_send(iface->con, msg, NULL);
+
+nomem:
+ dbus_message_unref(msg);
+}
+
+
+/**
+ *
+ * Method to emit a signal for a peer joining the group.
+ * The signal will carry path to the group member object
+ * constructed using p2p i/f addr used for connecting.
+ *
+ * @wpa_s: %wpa_supplicant network interface data
+ * @member_addr: addr (p2p i/f) of the peer joining the group
+ */
+void wpas_dbus_signal_p2p_peer_joined(struct wpa_supplicant *wpa_s,
+ const u8 *member)
+{
+ struct wpas_dbus_priv *iface;
+ DBusMessage *msg;
+ DBusMessageIter iter;
+ char groupmember_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
+
+ iface = wpa_s->global->dbus;
+
+ /* Do nothing if the control interface is not turned on */
+ if (iface == NULL)
+ return;
+
+ if (!wpa_s->dbus_groupobj_path)
+ return;
+
+ os_snprintf(groupmember_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s/" WPAS_DBUS_NEW_P2P_GROUPMEMBERS_PART "/"
+ COMPACT_MACSTR,
+ wpa_s->dbus_groupobj_path, MAC2STR(member));
+
+ msg = dbus_message_new_signal(wpa_s->dbus_groupobj_path,
+ WPAS_DBUS_NEW_IFACE_P2P_GROUP,
+ "PeerJoined");
+ if (msg == NULL)
+ return;
+
+ dbus_message_iter_init_append(msg, &iter);
+ path = groupmember_obj_path;
+ if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
+ &path))
+ goto err;
+
+ dbus_connection_send(iface->con, msg, NULL);
+
+ dbus_message_unref(msg);
+ return;
+
+err:
+ wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+ dbus_message_unref(msg);
+}
+
+
+/**
+ *
+ * Method to emit a signal for a peer disconnecting the group.
+ * The signal will carry path to the group member object
+ * constructed using p2p i/f addr used for connecting.
+ *
+ * @wpa_s: %wpa_supplicant network interface data
+ * @member_addr: addr (p2p i/f) of the peer joining the group
+ */
+void wpas_dbus_signal_p2p_peer_disconnected(struct wpa_supplicant *wpa_s,
+ const u8 *member)
+{
+ struct wpas_dbus_priv *iface;
+ DBusMessage *msg;
+ DBusMessageIter iter;
+ char groupmember_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
+
+ iface = wpa_s->global->dbus;
+
+ /* Do nothing if the control interface is not turned on */
+ if (iface == NULL)
+ return;
+
+ if (!wpa_s->dbus_groupobj_path)
+ return;
+
+ os_snprintf(groupmember_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s/" WPAS_DBUS_NEW_P2P_GROUPMEMBERS_PART "/"
+ COMPACT_MACSTR,
+ wpa_s->dbus_groupobj_path, MAC2STR(member));
+
+ msg = dbus_message_new_signal(wpa_s->dbus_groupobj_path,
+ WPAS_DBUS_NEW_IFACE_P2P_GROUP,
+ "PeerDisconnected");
+ if (msg == NULL)
+ return;
+
+ dbus_message_iter_init_append(msg, &iter);
+ path = groupmember_obj_path;
+ if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
+ &path))
+ goto err;
+
+ dbus_connection_send(iface->con, msg, NULL);
+
+ dbus_message_unref(msg);
+ return;
+
+err:
+ wpa_printf(MSG_ERROR, "dbus: Failed to construct PeerDisconnected "
+ "signal");
+ dbus_message_unref(msg);
+}
+
+
+/**
+ *
+ * Method to emit a signal for a service discovery request.
+ * The signal will carry station address, frequency, dialog token,
+ * update indicator and it tlvs
+ *
+ * @wpa_s: %wpa_supplicant network interface data
+ * @sa: station addr (p2p i/f) of the peer
+ * @dialog_token: service discovery request dialog token
+ * @update_indic: service discovery request update indicator
+ * @tlvs: service discovery request genrated byte array of tlvs
+ * @tlvs_len: service discovery request tlvs length
+ */
+void wpas_dbus_signal_p2p_sd_request(struct wpa_supplicant *wpa_s,
+ int freq, const u8 *sa, u8 dialog_token,
+ u16 update_indic, const u8 *tlvs,
+ size_t tlvs_len)
+{
+ DBusMessage *msg;
+ DBusMessageIter iter, dict_iter;
+ struct wpas_dbus_priv *iface;
+ char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
+ iface = wpa_s->global->dbus;
+
+ /* Do nothing if the control interface is not turned on */
+ if (iface == NULL)
+ return;
+
+ msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+ WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ "ServiceDiscoveryRequest");
+ if (msg == NULL)
+ return;
+
+ /* Check if this is a known peer */
+ if (!p2p_peer_known(wpa_s->global->p2p, sa))
+ goto error;
+
+ os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/"
+ COMPACT_MACSTR, wpa_s->dbus_new_path, MAC2STR(sa));
+
+ path = peer_obj_path;
+
+ dbus_message_iter_init_append(msg, &iter);
+ if (!wpa_dbus_dict_open_write(&iter, &dict_iter))
+ goto error;
+
+
+ if (!wpa_dbus_dict_append_object_path(&dict_iter, "peer_object",
+ path) ||
+ !wpa_dbus_dict_append_int32(&dict_iter, "frequency", freq) ||
+ !wpa_dbus_dict_append_int32(&dict_iter, "dialog_token",
+ dialog_token) ||
+ !wpa_dbus_dict_append_uint16(&dict_iter, "update_indicator",
+ update_indic) ||
+ !wpa_dbus_dict_append_byte_array(&dict_iter, "tlvs",
+ (const char *) tlvs,
+ tlvs_len) ||
+ !wpa_dbus_dict_close_write(&iter, &dict_iter))
+ goto error;
+
+ dbus_connection_send(iface->con, msg, NULL);
+ dbus_message_unref(msg);
+ return;
+error:
+ wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+ dbus_message_unref(msg);
+}
+
+
+/**
+ *
+ * Method to emit a signal for a service discovery response.
+ * The signal will carry station address, update indicator and it
+ * tlvs
+ *
+ * @wpa_s: %wpa_supplicant network interface data
+ * @sa: station addr (p2p i/f) of the peer
+ * @update_indic: service discovery request update indicator
+ * @tlvs: service discovery request genrated byte array of tlvs
+ * @tlvs_len: service discovery request tlvs length
+ */
+void wpas_dbus_signal_p2p_sd_response(struct wpa_supplicant *wpa_s,
+ const u8 *sa, u16 update_indic,
+ const u8 *tlvs, size_t tlvs_len)
+{
+ DBusMessage *msg;
+ DBusMessageIter iter, dict_iter;
+ struct wpas_dbus_priv *iface;
+ char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
+ iface = wpa_s->global->dbus;
+
+ /* Do nothing if the control interface is not turned on */
+ if (iface == NULL)
+ return;
+
+ msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+ WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ "ServiceDiscoveryResponse");
+ if (msg == NULL)
+ return;
+
+ /* Check if this is a known peer */
+ if (!p2p_peer_known(wpa_s->global->p2p, sa))
+ goto error;
+
+ os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/"
+ COMPACT_MACSTR, wpa_s->dbus_new_path, MAC2STR(sa));
+
+ path = peer_obj_path;
+
+ dbus_message_iter_init_append(msg, &iter);
+ if (!wpa_dbus_dict_open_write(&iter, &dict_iter))
+ goto error;
+
+ if (!wpa_dbus_dict_append_object_path(&dict_iter, "peer_object",
+ path) ||
+ !wpa_dbus_dict_append_uint16(&dict_iter, "update_indicator",
+ update_indic) ||
+ !wpa_dbus_dict_append_byte_array(&dict_iter, "tlvs",
+ (const char *) tlvs,
+ tlvs_len) ||
+ !wpa_dbus_dict_close_write(&iter, &dict_iter))
+ goto error;
+
+
+ dbus_connection_send(iface->con, msg, NULL);
+ dbus_message_unref(msg);
+ return;
+error:
+ wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+ dbus_message_unref(msg);
+}
+
+/**
+ * wpas_dbus_signal_persistent_group - Send a persistent group related
+ * event signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @id: new persistent group id
+ * @sig_name: signal name - PersistentGroupAdded, PersistentGroupRemoved
+ * @properties: determines if add second argument with object properties
+ *
+ * Notify listeners about an event related to persistent groups.
+ */
+static void wpas_dbus_signal_persistent_group(struct wpa_supplicant *wpa_s,
+ int id, const char *sig_name,
+ int properties)
+{
+ struct wpas_dbus_priv *iface;
+ DBusMessage *msg;
+ DBusMessageIter iter;
+ char pgrp_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
+
+ iface = wpa_s->global->dbus;
+
+ /* Do nothing if the control interface is not turned on */
+ if (iface == NULL)
+ return;
+
+ os_snprintf(pgrp_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s/" WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/%u",
+ wpa_s->dbus_new_path, id);
+
+ msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+ WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ sig_name);
+ if (msg == NULL)
+ return;
+
+ dbus_message_iter_init_append(msg, &iter);
+ path = pgrp_obj_path;
+ if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
+ &path))
+ goto err;
+
+ if (properties) {
+ if (!wpa_dbus_get_object_properties(
+ iface, pgrp_obj_path,
+ WPAS_DBUS_NEW_IFACE_PERSISTENT_GROUP, &iter))
+ goto err;
+ }
+
+ dbus_connection_send(iface->con, msg, NULL);
+
+ dbus_message_unref(msg);
+ return;
+
+err:
+ wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+ dbus_message_unref(msg);
+}
+
+
+/**
+ * wpas_dbus_signal_persistent_group_added - Send a persistent_group
+ * added signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @id: new persistent group id
+ *
+ * Notify listeners about addition of a new persistent group.
+ */
+static void wpas_dbus_signal_persistent_group_added(
+ struct wpa_supplicant *wpa_s, int id)
+{
+ wpas_dbus_signal_persistent_group(wpa_s, id, "PersistentGroupAdded",
+ TRUE);
+}
+
+
+/**
+ * wpas_dbus_signal_persistent_group_removed - Send a persistent_group
+ * removed signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @id: persistent group id
+ *
+ * Notify listeners about removal of a persistent group.
+ */
+static void wpas_dbus_signal_persistent_group_removed(
+ struct wpa_supplicant *wpa_s, int id)
+{
+ wpas_dbus_signal_persistent_group(wpa_s, id, "PersistentGroupRemoved",
+ FALSE);
+}
+
+
+/**
+ * wpas_dbus_signal_p2p_wps_failed - Signals WpsFailed event
+ * @wpa_s: %wpa_supplicant network interface data
+ *
+ * Sends Event dbus signal with name "fail" and dictionary containing
+ * "msg" field with fail message number (int32) as arguments
+ */
+void wpas_dbus_signal_p2p_wps_failed(struct wpa_supplicant *wpa_s,
+ struct wps_event_fail *fail)
+{
+
+ DBusMessage *msg;
+ DBusMessageIter iter, dict_iter;
+ struct wpas_dbus_priv *iface;
+ char *key = "fail";
+
+ iface = wpa_s->global->dbus;
+
+ /* Do nothing if the control interface is not turned on */
+ if (iface == NULL)
+ return;
+
+ msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+ WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ "WpsFailed");
+ if (msg == NULL)
+ return;
+
+ dbus_message_iter_init_append(msg, &iter);
+
+ if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &key) ||
+ !wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+ !wpa_dbus_dict_append_int32(&dict_iter, "msg", fail->msg) ||
+ !wpa_dbus_dict_append_int16(&dict_iter, "config_error",
+ fail->config_error) ||
+ !wpa_dbus_dict_close_write(&iter, &dict_iter))
+ wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+ else
+ dbus_connection_send(iface->con, msg, NULL);
+
+ dbus_message_unref(msg);
+}
+
+#endif /*CONFIG_P2P*/
+
+
+/**
+ * wpas_dbus_signal_prop_changed - Signals change of property
+ * @wpa_s: %wpa_supplicant network interface data
+ * @property: indicates which property has changed
+ *
+ * Sends PropertyChanged signals with path, interface and arguments
+ * depending on which property has changed.
+ */
+void wpas_dbus_signal_prop_changed(struct wpa_supplicant *wpa_s,
+ enum wpas_dbus_prop property)
+{
+ char *prop;
+
+ if (wpa_s->dbus_new_path == NULL)
+ return; /* Skip signal since D-Bus setup is not yet ready */
+
+ switch (property) {
+ case WPAS_DBUS_PROP_AP_SCAN:
+ prop = "ApScan";
+ break;
+ case WPAS_DBUS_PROP_SCANNING:
+ prop = "Scanning";
+ break;
+ case WPAS_DBUS_PROP_STATE:
+ prop = "State";
+ break;
+ case WPAS_DBUS_PROP_CURRENT_BSS:
+ prop = "CurrentBSS";
+ break;
+ case WPAS_DBUS_PROP_CURRENT_NETWORK:
+ prop = "CurrentNetwork";
+ break;
+ case WPAS_DBUS_PROP_BSSS:
+ prop = "BSSs";
+ break;
+ case WPAS_DBUS_PROP_CURRENT_AUTH_MODE:
+ prop = "CurrentAuthMode";
+ break;
+ default:
+ wpa_printf(MSG_ERROR, "dbus: %s: Unknown Property value %d",
+ __func__, property);
+ return;
+ }
+
+ wpa_dbus_mark_property_changed(wpa_s->global->dbus,
+ wpa_s->dbus_new_path,
+ WPAS_DBUS_NEW_IFACE_INTERFACE, prop);
+}
+
+
+/**
+ * wpas_dbus_bss_signal_prop_changed - Signals change of BSS property
+ * @wpa_s: %wpa_supplicant network interface data
+ * @property: indicates which property has changed
+ * @id: unique BSS identifier
+ *
+ * Sends PropertyChanged signals with path, interface, and arguments depending
+ * on which property has changed.
+ */
+void wpas_dbus_bss_signal_prop_changed(struct wpa_supplicant *wpa_s,
+ enum wpas_dbus_bss_prop property,
+ unsigned int id)
+{
+ char path[WPAS_DBUS_OBJECT_PATH_MAX];
+ char *prop;
+
+ switch (property) {
+ case WPAS_DBUS_BSS_PROP_SIGNAL:
+ prop = "Signal";
+ break;
+ case WPAS_DBUS_BSS_PROP_FREQ:
+ prop = "Frequency";
+ break;
+ case WPAS_DBUS_BSS_PROP_MODE:
+ prop = "Mode";
+ break;
+ case WPAS_DBUS_BSS_PROP_PRIVACY:
+ prop = "Privacy";
+ break;
+ case WPAS_DBUS_BSS_PROP_RATES:
+ prop = "Rates";
+ break;
+ case WPAS_DBUS_BSS_PROP_WPA:
+ prop = "WPA";
+ break;
+ case WPAS_DBUS_BSS_PROP_RSN:
+ prop = "RSN";
+ break;
+ case WPAS_DBUS_BSS_PROP_IES:
+ prop = "IEs";
+ break;
+ default:
+ wpa_printf(MSG_ERROR, "dbus: %s: Unknown Property value %d",
+ __func__, property);
+ return;
+ }
+
+ os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s/" WPAS_DBUS_NEW_BSSIDS_PART "/%u",
+ wpa_s->dbus_new_path, id);
+
+ wpa_dbus_mark_property_changed(wpa_s->global->dbus, path,
+ WPAS_DBUS_NEW_IFACE_BSS, prop);
+}
+
+
+/**
+ * wpas_dbus_signal_debug_level_changed - Signals change of debug param
+ * @global: wpa_global structure
+ *
+ * Sends PropertyChanged signals informing that debug level has changed.
+ */
+void wpas_dbus_signal_debug_level_changed(struct wpa_global *global)
+{
+ wpa_dbus_mark_property_changed(global->dbus, WPAS_DBUS_NEW_PATH,
+ WPAS_DBUS_NEW_INTERFACE,
+ "DebugLevel");
+}
+
+
+/**
+ * wpas_dbus_signal_debug_timestamp_changed - Signals change of debug param
+ * @global: wpa_global structure
+ *
+ * Sends PropertyChanged signals informing that debug timestamp has changed.
+ */
+void wpas_dbus_signal_debug_timestamp_changed(struct wpa_global *global)
+{
+ wpa_dbus_mark_property_changed(global->dbus, WPAS_DBUS_NEW_PATH,
+ WPAS_DBUS_NEW_INTERFACE,
+ "DebugTimestamp");
+}
+
+
+/**
+ * wpas_dbus_signal_debug_show_keys_changed - Signals change of debug param
+ * @global: wpa_global structure
+ *
+ * Sends PropertyChanged signals informing that debug show_keys has changed.
+ */
+void wpas_dbus_signal_debug_show_keys_changed(struct wpa_global *global)
+{
+ wpa_dbus_mark_property_changed(global->dbus, WPAS_DBUS_NEW_PATH,
+ WPAS_DBUS_NEW_INTERFACE,
+ "DebugShowKeys");
+}
+
+
+static void wpas_dbus_register(struct wpa_dbus_object_desc *obj_desc,
+ void *priv,
+ WPADBusArgumentFreeFunction priv_free,
+ const struct wpa_dbus_method_desc *methods,
+ const struct wpa_dbus_property_desc *properties,
+ const struct wpa_dbus_signal_desc *signals)
+{
+ int n;
+
+ obj_desc->user_data = priv;
+ obj_desc->user_data_free_func = priv_free;
+ obj_desc->methods = methods;
+ obj_desc->properties = properties;
+ obj_desc->signals = signals;
+
+ for (n = 0; properties && properties->dbus_property; properties++)
+ n++;
+
+ obj_desc->prop_changed_flags = os_zalloc(n);
+ if (!obj_desc->prop_changed_flags)
+ wpa_printf(MSG_DEBUG, "dbus: %s: can't register handlers",
+ __func__);
+}
+
+
+static const struct wpa_dbus_method_desc wpas_dbus_global_methods[] = {
+ { "CreateInterface", WPAS_DBUS_NEW_INTERFACE,
+ (WPADBusMethodHandler) &wpas_dbus_handler_create_interface,
+ {
+ { "args", "a{sv}", ARG_IN },
+ { "path", "o", ARG_OUT },
+ END_ARGS
+ }
+ },
+ { "RemoveInterface", WPAS_DBUS_NEW_INTERFACE,
+ (WPADBusMethodHandler) &wpas_dbus_handler_remove_interface,
+ {
+ { "path", "o", ARG_IN },
+ END_ARGS
+ }
+ },
+ { "GetInterface", WPAS_DBUS_NEW_INTERFACE,
+ (WPADBusMethodHandler) &wpas_dbus_handler_get_interface,
+ {
+ { "ifname", "s", ARG_IN },
+ { "path", "o", ARG_OUT },
+ END_ARGS
+ }
+ },
+ { NULL, NULL, NULL, { END_ARGS } }
+};
+
+static const struct wpa_dbus_property_desc wpas_dbus_global_properties[] = {
+ { "DebugLevel", WPAS_DBUS_NEW_INTERFACE, "s",
+ wpas_dbus_getter_debug_level,
+ wpas_dbus_setter_debug_level
+ },
+ { "DebugTimestamp", WPAS_DBUS_NEW_INTERFACE, "b",
+ wpas_dbus_getter_debug_timestamp,
+ wpas_dbus_setter_debug_timestamp
+ },
+ { "DebugShowKeys", WPAS_DBUS_NEW_INTERFACE, "b",
+ wpas_dbus_getter_debug_show_keys,
+ wpas_dbus_setter_debug_show_keys
+ },
+ { "Interfaces", WPAS_DBUS_NEW_INTERFACE, "ao",
+ wpas_dbus_getter_interfaces,
+ NULL
+ },
+ { "EapMethods", WPAS_DBUS_NEW_INTERFACE, "as",
+ wpas_dbus_getter_eap_methods,
+ NULL
+ },
+ { NULL, NULL, NULL, NULL, NULL }
+};
+
+static const struct wpa_dbus_signal_desc wpas_dbus_global_signals[] = {
+ { "InterfaceAdded", WPAS_DBUS_NEW_INTERFACE,
+ {
+ { "path", "o", ARG_OUT },
+ { "properties", "a{sv}", ARG_OUT },
+ END_ARGS
+ }
+ },
+ { "InterfaceRemoved", WPAS_DBUS_NEW_INTERFACE,
+ {
+ { "path", "o", ARG_OUT },
+ END_ARGS
+ }
+ },
+ { "NetworkRequest", WPAS_DBUS_NEW_IFACE_INTERFACE,
+ {
+ { "path", "o", ARG_OUT },
+ { "field", "s", ARG_OUT },
+ { "text", "s", ARG_OUT },
+ END_ARGS
+ }
+ },
+ /* Deprecated: use org.freedesktop.DBus.Properties.PropertiesChanged */
+ { "PropertiesChanged", WPAS_DBUS_NEW_INTERFACE,
+ {
+ { "properties", "a{sv}", ARG_OUT },
+ END_ARGS
+ }
+ },
+ { NULL, NULL, { END_ARGS } }
+};
+
+
+/**
+ * wpas_dbus_ctrl_iface_init - Initialize dbus control interface
+ * @global: Pointer to global data from wpa_supplicant_init()
+ * Returns: 0 on success or -1 on failure
+ *
+ * Initialize the dbus control interface for wpa_supplicantand and start
+ * receiving commands from external programs over the bus.
+ */
+int wpas_dbus_ctrl_iface_init(struct wpas_dbus_priv *priv)
+{
+ struct wpa_dbus_object_desc *obj_desc;
+ int ret;
+
+ obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc));
+ if (!obj_desc) {
+ wpa_printf(MSG_ERROR, "Not enough memory "
+ "to create object description");
+ return -1;
+ }
+
+ wpas_dbus_register(obj_desc, priv->global, NULL,
+ wpas_dbus_global_methods,
+ wpas_dbus_global_properties,
+ wpas_dbus_global_signals);
+
+ wpa_printf(MSG_DEBUG, "dbus: Register D-Bus object '%s'",
+ WPAS_DBUS_NEW_PATH);
+ ret = wpa_dbus_ctrl_iface_init(priv, WPAS_DBUS_NEW_PATH,
+ WPAS_DBUS_NEW_SERVICE,
+ obj_desc);
+ if (ret < 0)
+ free_dbus_object_desc(obj_desc);
+ else
+ priv->dbus_new_initialized = 1;
+
+ return ret;
+}
+
+
+/**
+ * wpas_dbus_ctrl_iface_deinit - Deinitialize dbus ctrl interface for
+ * wpa_supplicant
+ * @iface: Pointer to dbus private data from wpas_dbus_init()
+ *
+ * Deinitialize the dbus control interface that was initialized with
+ * wpas_dbus_ctrl_iface_init().
+ */
+void wpas_dbus_ctrl_iface_deinit(struct wpas_dbus_priv *iface)
+{
+ if (!iface->dbus_new_initialized)
+ return;
+ wpa_printf(MSG_DEBUG, "dbus: Unregister D-Bus object '%s'",
+ WPAS_DBUS_NEW_PATH);
+ dbus_connection_unregister_object_path(iface->con,
+ WPAS_DBUS_NEW_PATH);
+}
+
+
+static void wpa_dbus_free(void *ptr)
+{
+ os_free(ptr);
+}
+
+
+static const struct wpa_dbus_property_desc wpas_dbus_network_properties[] = {
+ { "Properties", WPAS_DBUS_NEW_IFACE_NETWORK, "a{sv}",
+ wpas_dbus_getter_network_properties,
+ wpas_dbus_setter_network_properties
+ },
+ { "Enabled", WPAS_DBUS_NEW_IFACE_NETWORK, "b",
+ wpas_dbus_getter_enabled,
+ wpas_dbus_setter_enabled
+ },
+ { NULL, NULL, NULL, NULL, NULL }
+};
+
+
+static const struct wpa_dbus_signal_desc wpas_dbus_network_signals[] = {
+ /* Deprecated: use org.freedesktop.DBus.Properties.PropertiesChanged */
+ { "PropertiesChanged", WPAS_DBUS_NEW_IFACE_NETWORK,
+ {
+ { "properties", "a{sv}", ARG_OUT },
+ END_ARGS
+ }
+ },
+ { NULL, NULL, { END_ARGS } }
+};
+
+
+/**
+ * wpas_dbus_register_network - Register a configured network with dbus
+ * @wpa_s: wpa_supplicant interface structure
+ * @ssid: network configuration data
+ * Returns: 0 on success, -1 on failure
+ *
+ * Registers network representing object with dbus
+ */
+int wpas_dbus_register_network(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+ struct wpas_dbus_priv *ctrl_iface;
+ struct wpa_dbus_object_desc *obj_desc;
+ struct network_handler_args *arg;
+ char net_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
+
+#ifdef CONFIG_P2P
+ /*
+ * If it is a persistent group register it as such.
+ * This is to handle cases where an interface is being initialized
+ * with a list of networks read from config.
+ */
+ if (network_is_persistent_group(ssid))
+ return wpas_dbus_register_persistent_group(wpa_s, ssid);
+#endif /* CONFIG_P2P */
+
+ /* Do nothing if the control interface is not turned on */
+ if (wpa_s == NULL || wpa_s->global == NULL)
+ return 0;
+ ctrl_iface = wpa_s->global->dbus;
+ if (ctrl_iface == NULL)
+ return 0;
+
+ os_snprintf(net_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%u",
+ wpa_s->dbus_new_path, ssid->id);
+
+ wpa_printf(MSG_DEBUG, "dbus: Register network object '%s'",
+ net_obj_path);
+ obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc));
+ if (!obj_desc) {
+ wpa_printf(MSG_ERROR, "Not enough memory "
+ "to create object description");
+ goto err;
+ }
+
+ /* allocate memory for handlers arguments */
+ arg = os_zalloc(sizeof(struct network_handler_args));
+ if (!arg) {
+ wpa_printf(MSG_ERROR, "Not enough memory "
+ "to create arguments for method");
+ goto err;
+ }
+
+ arg->wpa_s = wpa_s;
+ arg->ssid = ssid;
+
+ wpas_dbus_register(obj_desc, arg, wpa_dbus_free, NULL,
+ wpas_dbus_network_properties,
+ wpas_dbus_network_signals);
+
+ if (wpa_dbus_register_object_per_iface(ctrl_iface, net_obj_path,
+ wpa_s->ifname, obj_desc))
+ goto err;
+
+ wpas_dbus_signal_network_added(wpa_s, ssid->id);
+
+ return 0;
+
+err:
+ free_dbus_object_desc(obj_desc);
+ return -1;
+}
+
+
+/**
+ * wpas_dbus_unregister_network - Unregister a configured network from dbus
+ * @wpa_s: wpa_supplicant interface structure
+ * @nid: network id
+ * Returns: 0 on success, -1 on failure
+ *
+ * Unregisters network representing object from dbus
+ */
+int wpas_dbus_unregister_network(struct wpa_supplicant *wpa_s, int nid)
+{
+ struct wpas_dbus_priv *ctrl_iface;
+ char net_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
+ int ret;
+ struct wpa_ssid *ssid;
+
+ ssid = wpa_config_get_network(wpa_s->conf, nid);
+
+#ifdef CONFIG_P2P
+ /* If it is a persistent group unregister it as such */
+ if (ssid && network_is_persistent_group(ssid))
+ return wpas_dbus_unregister_persistent_group(wpa_s, nid);
+#endif /* CONFIG_P2P */
+
+ /* Do nothing if the control interface is not turned on */
+ if (wpa_s->global == NULL || wpa_s->dbus_new_path == NULL)
+ return 0;
+ ctrl_iface = wpa_s->global->dbus;
+ if (ctrl_iface == NULL)
+ return 0;
+
+ os_snprintf(net_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%u",
+ wpa_s->dbus_new_path, nid);
+
+ wpa_printf(MSG_DEBUG, "dbus: Unregister network object '%s'",
+ net_obj_path);
+ ret = wpa_dbus_unregister_object_per_iface(ctrl_iface, net_obj_path);
+
+ if (!ret)
+ wpas_dbus_signal_network_removed(wpa_s, nid);
+
+ return ret;
+}
+
+
+static const struct wpa_dbus_property_desc wpas_dbus_bss_properties[] = {
+ { "SSID", WPAS_DBUS_NEW_IFACE_BSS, "ay",
+ wpas_dbus_getter_bss_ssid,
+ NULL
+ },
+ { "BSSID", WPAS_DBUS_NEW_IFACE_BSS, "ay",
+ wpas_dbus_getter_bss_bssid,
+ NULL
+ },
+ { "Privacy", WPAS_DBUS_NEW_IFACE_BSS, "b",
+ wpas_dbus_getter_bss_privacy,
+ NULL
+ },
+ { "Mode", WPAS_DBUS_NEW_IFACE_BSS, "s",
+ wpas_dbus_getter_bss_mode,
+ NULL
+ },
+ { "Signal", WPAS_DBUS_NEW_IFACE_BSS, "n",
+ wpas_dbus_getter_bss_signal,
+ NULL
+ },
+ { "Frequency", WPAS_DBUS_NEW_IFACE_BSS, "q",
+ wpas_dbus_getter_bss_frequency,
+ NULL
+ },
+ { "Rates", WPAS_DBUS_NEW_IFACE_BSS, "au",
+ wpas_dbus_getter_bss_rates,
+ NULL
+ },
+ { "WPA", WPAS_DBUS_NEW_IFACE_BSS, "a{sv}",
+ wpas_dbus_getter_bss_wpa,
+ NULL
+ },
+ { "RSN", WPAS_DBUS_NEW_IFACE_BSS, "a{sv}",
+ wpas_dbus_getter_bss_rsn,
+ NULL
+ },
+ { "IEs", WPAS_DBUS_NEW_IFACE_BSS, "ay",
+ wpas_dbus_getter_bss_ies,
+ NULL
+ },
+ { NULL, NULL, NULL, NULL, NULL }
+};
+
+
+static const struct wpa_dbus_signal_desc wpas_dbus_bss_signals[] = {
+ /* Deprecated: use org.freedesktop.DBus.Properties.PropertiesChanged */
+ { "PropertiesChanged", WPAS_DBUS_NEW_IFACE_BSS,
+ {
+ { "properties", "a{sv}", ARG_OUT },
+ END_ARGS
+ }
+ },
+ { NULL, NULL, { END_ARGS } }
+};
+
+
+/**
+ * wpas_dbus_unregister_bss - Unregister a scanned BSS from dbus
+ * @wpa_s: wpa_supplicant interface structure
+ * @bssid: scanned network bssid
+ * @id: unique BSS identifier
+ * Returns: 0 on success, -1 on failure
+ *
+ * Unregisters BSS representing object from dbus
+ */
+int wpas_dbus_unregister_bss(struct wpa_supplicant *wpa_s,
+ u8 bssid[ETH_ALEN], unsigned int id)
+{
+ struct wpas_dbus_priv *ctrl_iface;
+ char bss_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
+
+ /* Do nothing if the control interface is not turned on */
+ if (wpa_s == NULL || wpa_s->global == NULL)
+ return 0;
+ ctrl_iface = wpa_s->global->dbus;
+ if (ctrl_iface == NULL)
+ return 0;
+
+ os_snprintf(bss_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s/" WPAS_DBUS_NEW_BSSIDS_PART "/%u",
+ wpa_s->dbus_new_path, id);
+
+ wpa_printf(MSG_DEBUG, "dbus: Unregister BSS object '%s'",
+ bss_obj_path);
+ if (wpa_dbus_unregister_object_per_iface(ctrl_iface, bss_obj_path)) {
+ wpa_printf(MSG_ERROR, "dbus: Cannot unregister BSS object %s",
+ bss_obj_path);
+ return -1;
+ }
+
+ wpas_dbus_signal_bss_removed(wpa_s, bss_obj_path);
+ wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_BSSS);
+
+ return 0;
+}
+
+
+/**
+ * wpas_dbus_register_bss - Register a scanned BSS with dbus
+ * @wpa_s: wpa_supplicant interface structure
+ * @bssid: scanned network bssid
+ * @id: unique BSS identifier
+ * Returns: 0 on success, -1 on failure
+ *
+ * Registers BSS representing object with dbus
+ */
+int wpas_dbus_register_bss(struct wpa_supplicant *wpa_s,
+ u8 bssid[ETH_ALEN], unsigned int id)
+{
+ struct wpas_dbus_priv *ctrl_iface;
+ struct wpa_dbus_object_desc *obj_desc;
+ char bss_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
+ struct bss_handler_args *arg;
+
+ /* Do nothing if the control interface is not turned on */
+ if (wpa_s == NULL || wpa_s->global == NULL)
+ return 0;
+ ctrl_iface = wpa_s->global->dbus;
+ if (ctrl_iface == NULL)
+ return 0;
+
+ os_snprintf(bss_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s/" WPAS_DBUS_NEW_BSSIDS_PART "/%u",
+ wpa_s->dbus_new_path, id);
+
+ obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc));
+ if (!obj_desc) {
+ wpa_printf(MSG_ERROR, "Not enough memory "
+ "to create object description");
+ goto err;
+ }
+
+ arg = os_zalloc(sizeof(struct bss_handler_args));
+ if (!arg) {
+ wpa_printf(MSG_ERROR, "Not enough memory "
+ "to create arguments for handler");
+ goto err;
+ }
+ arg->wpa_s = wpa_s;
+ arg->id = id;
+
+ wpas_dbus_register(obj_desc, arg, wpa_dbus_free, NULL,
+ wpas_dbus_bss_properties,
+ wpas_dbus_bss_signals);
+
+ wpa_printf(MSG_DEBUG, "dbus: Register BSS object '%s'",
+ bss_obj_path);
+ if (wpa_dbus_register_object_per_iface(ctrl_iface, bss_obj_path,
+ wpa_s->ifname, obj_desc)) {
+ wpa_printf(MSG_ERROR,
+ "Cannot register BSSID dbus object %s.",
+ bss_obj_path);
+ goto err;
+ }
+
+ wpas_dbus_signal_bss_added(wpa_s, bss_obj_path);
+ wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_BSSS);
+
+ return 0;
+
+err:
+ free_dbus_object_desc(obj_desc);
+ return -1;
+}
+
+
+static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = {
+ { "Scan", WPAS_DBUS_NEW_IFACE_INTERFACE,
+ (WPADBusMethodHandler) &wpas_dbus_handler_scan,
+ {
+ { "args", "a{sv}", ARG_IN },
+ END_ARGS
+ }
+ },
+ { "Disconnect", WPAS_DBUS_NEW_IFACE_INTERFACE,
+ (WPADBusMethodHandler) &wpas_dbus_handler_disconnect,
+ {
+ END_ARGS
+ }
+ },
+ { "AddNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE,
+ (WPADBusMethodHandler) &wpas_dbus_handler_add_network,
+ {
+ { "args", "a{sv}", ARG_IN },
+ { "path", "o", ARG_OUT },
+ END_ARGS
+ }
+ },
+ { "RemoveNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE,
+ (WPADBusMethodHandler) &wpas_dbus_handler_remove_network,
+ {
+ { "path", "o", ARG_IN },
+ END_ARGS
+ }
+ },
+ { "RemoveAllNetworks", WPAS_DBUS_NEW_IFACE_INTERFACE,
+ (WPADBusMethodHandler) &wpas_dbus_handler_remove_all_networks,
+ {
+ END_ARGS
+ }
+ },
+ { "SelectNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE,
+ (WPADBusMethodHandler) &wpas_dbus_handler_select_network,
+ {
+ { "path", "o", ARG_IN },
+ END_ARGS
+ }
+ },
+ { "NetworkReply", WPAS_DBUS_NEW_IFACE_INTERFACE,
+ (WPADBusMethodHandler) &wpas_dbus_handler_network_reply,
+ {
+ { "path", "o", ARG_IN },
+ { "field", "s", ARG_IN },
+ { "value", "s", ARG_IN },
+ END_ARGS
+ }
+ },
+ { "AddBlob", WPAS_DBUS_NEW_IFACE_INTERFACE,
+ (WPADBusMethodHandler) &wpas_dbus_handler_add_blob,
+ {
+ { "name", "s", ARG_IN },
+ { "data", "ay", ARG_IN },
+ END_ARGS
+ }
+ },
+ { "GetBlob", WPAS_DBUS_NEW_IFACE_INTERFACE,
+ (WPADBusMethodHandler) &wpas_dbus_handler_get_blob,
+ {
+ { "name", "s", ARG_IN },
+ { "data", "ay", ARG_OUT },
+ END_ARGS
+ }
+ },
+ { "RemoveBlob", WPAS_DBUS_NEW_IFACE_INTERFACE,
+ (WPADBusMethodHandler) &wpas_dbus_handler_remove_blob,
+ {
+ { "name", "s", ARG_IN },
+ END_ARGS
+ }
+ },
+#ifdef CONFIG_WPS
+ { "Start", WPAS_DBUS_NEW_IFACE_WPS,
+ (WPADBusMethodHandler) &wpas_dbus_handler_wps_start,
+ {
+ { "args", "a{sv}", ARG_IN },
+ { "output", "a{sv}", ARG_OUT },
+ END_ARGS
+ }
+ },
+#endif /* CONFIG_WPS */
+#ifdef CONFIG_P2P
+ { "Find", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ (WPADBusMethodHandler)wpas_dbus_handler_p2p_find,
+ {
+ { "args", "a{sv}", ARG_IN },
+ END_ARGS
+ }
+ },
+ { "StopFind", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ (WPADBusMethodHandler)wpas_dbus_handler_p2p_stop_find,
+ {
+ END_ARGS
+ }
+ },
+ { "Listen", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ (WPADBusMethodHandler)wpas_dbus_handler_p2p_listen,
+ {
+ { "timeout", "i", ARG_IN },
+ END_ARGS
+ }
+ },
+ { "ExtendedListen", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ (WPADBusMethodHandler)wpas_dbus_handler_p2p_extendedlisten,
+ {
+ { "args", "a{sv}", ARG_IN },
+ END_ARGS
+ }
+ },
+ { "PresenceRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ (WPADBusMethodHandler)wpas_dbus_handler_p2p_presence_request,
+ {
+ { "args", "a{sv}", ARG_IN },
+ END_ARGS
+ }
+ },
+ { "ProvisionDiscoveryRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ (WPADBusMethodHandler)wpas_dbus_handler_p2p_prov_disc_req,
+ {
+ { "peer", "o", ARG_IN },
+ { "config_method", "s", ARG_IN },
+ END_ARGS
+ }
+ },
+ { "Connect", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ (WPADBusMethodHandler)wpas_dbus_handler_p2p_connect,
+ {
+ { "args", "a{sv}", ARG_IN },
+ { "generated_pin", "s", ARG_OUT },
+ END_ARGS
+ }
+ },
+ { "GroupAdd", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ (WPADBusMethodHandler)wpas_dbus_handler_p2p_group_add,
+ {
+ { "args", "a{sv}", ARG_IN },
+ END_ARGS
+ }
+ },
+ { "Invite", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ (WPADBusMethodHandler)wpas_dbus_handler_p2p_invite,
+ {
+ { "args", "a{sv}", ARG_IN },
+ END_ARGS
+ }
+ },
+ { "Disconnect", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ (WPADBusMethodHandler)wpas_dbus_handler_p2p_disconnect,
+ {
+ END_ARGS
+ }
+ },
+ { "RejectPeer", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ (WPADBusMethodHandler)wpas_dbus_handler_p2p_rejectpeer,
+ {
+ { "peer", "o", ARG_IN },
+ END_ARGS
+ }
+ },
+ { "Flush", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ (WPADBusMethodHandler)wpas_dbus_handler_p2p_flush,
+ {
+ END_ARGS
+ }
+ },
+ { "AddService", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ (WPADBusMethodHandler)wpas_dbus_handler_p2p_add_service,
+ {
+ { "args", "a{sv}", ARG_IN },
+ END_ARGS
+ }
+ },
+ { "DeleteService", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ (WPADBusMethodHandler)wpas_dbus_handler_p2p_delete_service,
+ {
+ { "args", "a{sv}", ARG_IN },
+ END_ARGS
+ }
+ },
+ { "FlushService", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ (WPADBusMethodHandler)wpas_dbus_handler_p2p_flush_service,
+ {
+ END_ARGS
+ }
+ },
+ { "ServiceDiscoveryRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ (WPADBusMethodHandler)wpas_dbus_handler_p2p_service_sd_req,
+ {
+ { "args", "a{sv}", ARG_IN },
+ END_ARGS
+ }
+ },
+ { "ServiceDiscoveryResponse", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ (WPADBusMethodHandler)wpas_dbus_handler_p2p_service_sd_res,
+ {
+ { "args", "a{sv}", ARG_IN },
+ END_ARGS
+ }
+ },
+ { "ServiceDiscoveryCancelRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ (WPADBusMethodHandler)wpas_dbus_handler_p2p_service_sd_cancel_req,
+ {
+ { "args", "t", ARG_IN },
+ END_ARGS
+ }
+ },
+ { "ServiceUpdate", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ (WPADBusMethodHandler)wpas_dbus_handler_p2p_service_update,
+ {
+ END_ARGS
+ }
+ },
+ { "ServiceDiscoveryExternal", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ (WPADBusMethodHandler)wpas_dbus_handler_p2p_serv_disc_external,
+ {
+ { "arg", "i", ARG_IN },
+ END_ARGS
+ }
+ },
+ { "ServiceDiscoveryExternal", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ (WPADBusMethodHandler)wpas_dbus_handler_p2p_serv_disc_external,
+ {
+ { "arg", "i", ARG_IN },
+ END_ARGS
+ }
+ },
+ { "AddPersistentGroup", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ (WPADBusMethodHandler) wpas_dbus_handler_add_persistent_group,
+ {
+ { "args", "a{sv}", ARG_IN },
+ { "path", "o", ARG_OUT },
+ END_ARGS
+ }
+ },
+ { "RemovePersistentGroup", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ (WPADBusMethodHandler) wpas_dbus_handler_remove_persistent_group,
+ {
+ { "path", "o", ARG_IN },
+ END_ARGS
+ }
+ },
+ { "RemoveAllPersistentGroups", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ (WPADBusMethodHandler)
+ wpas_dbus_handler_remove_all_persistent_groups,
+ {
+ END_ARGS
+ }
+ },
+#endif /* CONFIG_P2P */
+ { "FlushBSS", WPAS_DBUS_NEW_IFACE_INTERFACE,
+ (WPADBusMethodHandler) &wpas_dbus_handler_flush_bss,
+ {
+ { "age", "u", ARG_IN },
+ END_ARGS
+ }
+ },
+ { NULL, NULL, NULL, { END_ARGS } }
+};
+
+static const struct wpa_dbus_property_desc wpas_dbus_interface_properties[] = {
+ { "Capabilities", WPAS_DBUS_NEW_IFACE_INTERFACE, "a{sv}",
+ wpas_dbus_getter_capabilities,
+ NULL
+ },
+ { "State", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
+ wpas_dbus_getter_state,
+ NULL
+ },
+ { "Scanning", WPAS_DBUS_NEW_IFACE_INTERFACE, "b",
+ wpas_dbus_getter_scanning,
+ NULL
+ },
+ { "ApScan", WPAS_DBUS_NEW_IFACE_INTERFACE, "u",
+ wpas_dbus_getter_ap_scan,
+ wpas_dbus_setter_ap_scan
+ },
+ { "BSSExpireAge", WPAS_DBUS_NEW_IFACE_INTERFACE, "u",
+ wpas_dbus_getter_bss_expire_age,
+ wpas_dbus_setter_bss_expire_age
+ },
+ { "BSSExpireCount", WPAS_DBUS_NEW_IFACE_INTERFACE, "u",
+ wpas_dbus_getter_bss_expire_count,
+ wpas_dbus_setter_bss_expire_count
+ },
+ { "Country", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
+ wpas_dbus_getter_country,
+ wpas_dbus_setter_country
+ },
+ { "Ifname", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
+ wpas_dbus_getter_ifname,
+ NULL
+ },
+ { "Driver", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
+ wpas_dbus_getter_driver,
+ NULL
+ },
+ { "BridgeIfname", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
+ wpas_dbus_getter_bridge_ifname,
+ NULL
+ },
+ { "CurrentBSS", WPAS_DBUS_NEW_IFACE_INTERFACE, "o",
+ wpas_dbus_getter_current_bss,
+ NULL
+ },
+ { "CurrentNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE, "o",
+ wpas_dbus_getter_current_network,
+ NULL
+ },
+ { "CurrentAuthMode", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
+ wpas_dbus_getter_current_auth_mode,
+ NULL
+ },
+ { "Blobs", WPAS_DBUS_NEW_IFACE_INTERFACE, "a{say}",
+ wpas_dbus_getter_blobs,
+ NULL
+ },
+ { "BSSs", WPAS_DBUS_NEW_IFACE_INTERFACE, "ao",
+ wpas_dbus_getter_bsss,
+ NULL
+ },
+ { "Networks", WPAS_DBUS_NEW_IFACE_INTERFACE, "ao",
+ wpas_dbus_getter_networks,
+ NULL
+ },
+ { "FastReauth", WPAS_DBUS_NEW_IFACE_INTERFACE, "b",
+ wpas_dbus_getter_fast_reauth,
+ wpas_dbus_setter_fast_reauth
+ },
+#ifdef CONFIG_WPS
+ { "ProcessCredentials", WPAS_DBUS_NEW_IFACE_WPS, "b",
+ wpas_dbus_getter_process_credentials,
+ wpas_dbus_setter_process_credentials
+ },
+#endif /* CONFIG_WPS */
+#ifdef CONFIG_P2P
+ { "P2PDeviceConfig", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "a{sv}",
+ wpas_dbus_getter_p2p_device_config,
+ wpas_dbus_setter_p2p_device_config
+ },
+ { "Peers", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "ao",
+ wpas_dbus_getter_p2p_peers,
+ NULL
+ },
+ { "Role", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "s",
+ wpas_dbus_getter_p2p_role,
+ NULL
+ },
+ { "Group", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "o",
+ wpas_dbus_getter_p2p_group,
+ NULL
+ },
+ { "PeerGO", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "o",
+ wpas_dbus_getter_p2p_peergo,
+ NULL
+ },
+ { "PersistentGroups", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "ao",
+ wpas_dbus_getter_persistent_groups,
+ NULL
+ },
+#endif /* CONFIG_P2P */
+ { NULL, NULL, NULL, NULL, NULL }
+};
+
+static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = {
+ { "ScanDone", WPAS_DBUS_NEW_IFACE_INTERFACE,
+ {
+ { "success", "b", ARG_OUT },
+ END_ARGS
+ }
+ },
+ { "BSSAdded", WPAS_DBUS_NEW_IFACE_INTERFACE,
+ {
+ { "path", "o", ARG_OUT },
+ { "properties", "a{sv}", ARG_OUT },
+ END_ARGS
+ }
+ },
+ { "BSSRemoved", WPAS_DBUS_NEW_IFACE_INTERFACE,
+ {
+ { "path", "o", ARG_OUT },
+ END_ARGS
+ }
+ },
+ { "BlobAdded", WPAS_DBUS_NEW_IFACE_INTERFACE,
+ {
+ { "name", "s", ARG_OUT },
+ END_ARGS
+ }
+ },
+ { "BlobRemoved", WPAS_DBUS_NEW_IFACE_INTERFACE,
+ {
+ { "name", "s", ARG_OUT },
+ END_ARGS
+ }
+ },
+ { "NetworkAdded", WPAS_DBUS_NEW_IFACE_INTERFACE,
+ {
+ { "path", "o", ARG_OUT },
+ { "properties", "a{sv}", ARG_OUT },
+ END_ARGS
+ }
+ },
+ { "NetworkRemoved", WPAS_DBUS_NEW_IFACE_INTERFACE,
+ {
+ { "path", "o", ARG_OUT },
+ END_ARGS
+ }
+ },
+ { "NetworkSelected", WPAS_DBUS_NEW_IFACE_INTERFACE,
+ {
+ { "path", "o", ARG_OUT },
+ END_ARGS
+ }
+ },
+ /* Deprecated: use org.freedesktop.DBus.Properties.PropertiesChanged */
+ { "PropertiesChanged", WPAS_DBUS_NEW_IFACE_INTERFACE,
+ {
+ { "properties", "a{sv}", ARG_OUT },
+ END_ARGS
+ }
+ },
+#ifdef CONFIG_WPS
+ { "Event", WPAS_DBUS_NEW_IFACE_WPS,
+ {
+ { "name", "s", ARG_OUT },
+ { "args", "a{sv}", ARG_OUT },
+ END_ARGS
+ }
+ },
+ { "Credentials", WPAS_DBUS_NEW_IFACE_WPS,
+ {
+ { "credentials", "a{sv}", ARG_OUT },
+ END_ARGS
+ }
+ },
+ /* Deprecated: use org.freedesktop.DBus.Properties.PropertiesChanged */
+ { "PropertiesChanged", WPAS_DBUS_NEW_IFACE_WPS,
{
- { "args", "a{sv}", ARG_IN },
- { "path", "o", ARG_OUT },
+ { "properties", "a{sv}", ARG_OUT },
END_ARGS
}
},
- { "RemoveInterface", WPAS_DBUS_NEW_INTERFACE,
- (WPADBusMethodHandler) &wpas_dbus_handler_remove_interface,
+#endif /* CONFIG_WPS */
+#ifdef CONFIG_P2P
+ { "P2PStateChanged", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
{
- { "path", "o", ARG_IN },
+ { "states", "a{ss}", ARG_OUT },
END_ARGS
}
},
- { "GetInterface", WPAS_DBUS_NEW_INTERFACE,
- (WPADBusMethodHandler) &wpas_dbus_handler_get_interface,
+ { "DeviceFound", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
{
- { "ifname", "s", ARG_IN },
{ "path", "o", ARG_OUT },
+ { "properties", "a{sv}", ARG_OUT },
END_ARGS
}
},
- { NULL, NULL, NULL, { END_ARGS } }
-};
-
-static const struct wpa_dbus_property_desc wpas_dbus_global_properties[] = {
- { "DebugLevel", WPAS_DBUS_NEW_INTERFACE, "s",
- (WPADBusPropertyAccessor) wpas_dbus_getter_debug_level,
- (WPADBusPropertyAccessor) wpas_dbus_setter_debug_level,
- RW
+ { "DeviceLost", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ {
+ { "path", "o", ARG_OUT },
+ END_ARGS
+ }
},
- { "DebugTimestamp", WPAS_DBUS_NEW_INTERFACE, "b",
- (WPADBusPropertyAccessor) wpas_dbus_getter_debug_timestamp,
- (WPADBusPropertyAccessor) wpas_dbus_setter_debug_timestamp,
- RW
+ { "ProvisionDiscoveryRequestDisplayPin", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ {
+ { "peer_object", "o", ARG_OUT },
+ { "pin", "s", ARG_OUT },
+ END_ARGS
+ }
},
- { "DebugShowKeys", WPAS_DBUS_NEW_INTERFACE, "b",
- (WPADBusPropertyAccessor) wpas_dbus_getter_debug_show_keys,
- (WPADBusPropertyAccessor) wpas_dbus_setter_debug_show_keys,
- RW
+ { "ProvisionDiscoveryResponseDisplayPin", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ {
+ { "peer_object", "o", ARG_OUT },
+ { "pin", "s", ARG_OUT },
+ END_ARGS
+ }
},
- { "Interfaces", WPAS_DBUS_NEW_INTERFACE, "ao",
- (WPADBusPropertyAccessor) &wpas_dbus_getter_interfaces,
- NULL,
- R
+ { "ProvisionDiscoveryRequestEnterPin", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ {
+ { "peer_object", "o", ARG_OUT },
+ END_ARGS
+ }
},
- { "EapMethods", WPAS_DBUS_NEW_INTERFACE, "as",
- (WPADBusPropertyAccessor) wpas_dbus_getter_eap_methods,
- NULL,
- R
+ { "ProvisionDiscoveryResponseEnterPin", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ {
+ { "peer_object", "o", ARG_OUT },
+ END_ARGS
+ }
},
- { NULL, NULL, NULL, NULL, NULL, 0 }
-};
-
-static const struct wpa_dbus_signal_desc wpas_dbus_global_signals[] = {
- { "InterfaceAdded", WPAS_DBUS_NEW_INTERFACE,
+ { "ProvisionDiscoveryPBCRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ {
+ { "peer_object", "o", ARG_OUT },
+ END_ARGS
+ }
+ },
+ { "ProvisionDiscoveryPBCResponse", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ {
+ { "peer_object", "o", ARG_OUT },
+ END_ARGS
+ }
+ },
+ { "ProvisionDiscoveryFailure", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ {
+ { "peer_object", "o", ARG_OUT },
+ { "status", "i", ARG_OUT },
+ END_ARGS
+ }
+ },
+ { "GroupStarted", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
{
- { "path", "o", ARG_OUT },
{ "properties", "a{sv}", ARG_OUT },
END_ARGS
}
},
- { "InterfaceRemoved", WPAS_DBUS_NEW_INTERFACE,
+ { "GONegotiationSuccess", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ {
+ END_ARGS
+ }
+ },
+ { "GONegotiationFailure", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ {
+ { "status", "i", ARG_OUT },
+ END_ARGS
+ }
+ },
+ { "GONegotiationRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
{
{ "path", "o", ARG_OUT },
+ { "dev_passwd_id", "i", ARG_OUT },
END_ARGS
}
},
- { "PropertiesChanged", WPAS_DBUS_NEW_INTERFACE,
+ { "InvitationResult", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
{
- { "properties", "a{sv}", ARG_OUT },
+ { "invite_result", "a{sv}", ARG_OUT },
END_ARGS
}
},
- { NULL, NULL, { END_ARGS } }
-};
-
-
-/**
- * wpas_dbus_ctrl_iface_init - Initialize dbus control interface
- * @global: Pointer to global data from wpa_supplicant_init()
- * Returns: 0 on success or -1 on failure
- *
- * Initialize the dbus control interface for wpa_supplicantand and start
- * receiving commands from external programs over the bus.
- */
-int wpas_dbus_ctrl_iface_init(struct wpas_dbus_priv *priv)
-{
- struct wpa_dbus_object_desc *obj_desc;
- int ret;
-
- obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc));
- if (!obj_desc) {
- wpa_printf(MSG_ERROR, "Not enough memory "
- "to create object description");
- return -1;
- }
-
- wpas_dbus_register(obj_desc, priv->global, NULL,
- wpas_dbus_global_methods,
- wpas_dbus_global_properties,
- wpas_dbus_global_signals);
-
- wpa_printf(MSG_DEBUG, "dbus: Register D-Bus object '%s'",
- WPAS_DBUS_NEW_PATH);
- ret = wpa_dbus_ctrl_iface_init(priv, WPAS_DBUS_NEW_PATH,
- WPAS_DBUS_NEW_SERVICE,
- obj_desc);
- if (ret < 0)
- free_dbus_object_desc(obj_desc);
- else
- priv->dbus_new_initialized = 1;
-
- return ret;
-}
-
-
-/**
- * wpas_dbus_ctrl_iface_deinit - Deinitialize dbus ctrl interface for
- * wpa_supplicant
- * @iface: Pointer to dbus private data from wpas_dbus_init()
- *
- * Deinitialize the dbus control interface that was initialized with
- * wpas_dbus_ctrl_iface_init().
- */
-void wpas_dbus_ctrl_iface_deinit(struct wpas_dbus_priv *iface)
-{
- if (!iface->dbus_new_initialized)
- return;
- wpa_printf(MSG_DEBUG, "dbus: Unregister D-Bus object '%s'",
- WPAS_DBUS_NEW_PATH);
- dbus_connection_unregister_object_path(iface->con,
- WPAS_DBUS_NEW_PATH);
-}
-
-
-static void wpa_dbus_free(void *ptr)
-{
- os_free(ptr);
-}
-
-
-static const struct wpa_dbus_property_desc wpas_dbus_network_properties[] = {
- { "Properties", WPAS_DBUS_NEW_IFACE_NETWORK, "a{sv}",
- (WPADBusPropertyAccessor) wpas_dbus_getter_network_properties,
- (WPADBusPropertyAccessor) wpas_dbus_setter_network_properties,
- RW
+ { "GroupFinished", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ {
+ { "ifname", "s", ARG_OUT },
+ { "role", "s", ARG_OUT },
+ END_ARGS
+ }
},
- { "Enabled", WPAS_DBUS_NEW_IFACE_NETWORK, "b",
- (WPADBusPropertyAccessor) wpas_dbus_getter_enabled,
- (WPADBusPropertyAccessor) wpas_dbus_setter_enabled,
- RW
+ { "ServiceDiscoveryRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ {
+ { "sd_request", "a{sv}", ARG_OUT },
+ END_ARGS
+ }
},
- { NULL, NULL, NULL, NULL, NULL, 0 }
-};
-
-
-static const struct wpa_dbus_signal_desc wpas_dbus_network_signals[] = {
- { "PropertiesChanged", WPAS_DBUS_NEW_IFACE_NETWORK,
+ { "ServiceDiscoveryResponse", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ {
+ { "sd_response", "a{sv}", ARG_OUT },
+ END_ARGS
+ }
+ },
+ { "PersistentGroupAdded", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
{
+ { "path", "o", ARG_OUT },
{ "properties", "a{sv}", ARG_OUT },
END_ARGS
}
},
+ { "PersistentGroupRemoved", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ {
+ { "path", "o", ARG_OUT },
+ END_ARGS
+ }
+ },
+ { "WpsFailed", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ {
+ { "name", "s", ARG_OUT },
+ { "args", "a{sv}", ARG_OUT },
+ END_ARGS
+ }
+ },
+#endif /* CONFIG_P2P */
+ { "Certification", WPAS_DBUS_NEW_IFACE_INTERFACE,
+ {
+ { "certification", "a{sv}", ARG_OUT },
+ END_ARGS
+ }
+ },
{ NULL, NULL, { END_ARGS } }
};
-/**
- * wpas_dbus_register_network - Register a configured network with dbus
- * @wpa_s: wpa_supplicant interface structure
- * @ssid: network configuration data
- * Returns: 0 on success, -1 on failure
- *
- * Registers network representing object with dbus
- */
-int wpas_dbus_register_network(struct wpa_supplicant *wpa_s,
- struct wpa_ssid *ssid)
+int wpas_dbus_register_interface(struct wpa_supplicant *wpa_s)
{
- struct wpas_dbus_priv *ctrl_iface;
- struct wpa_dbus_object_desc *obj_desc;
- struct network_handler_args *arg;
- char net_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
+
+ struct wpa_dbus_object_desc *obj_desc = NULL;
+ struct wpas_dbus_priv *ctrl_iface = wpa_s->global->dbus;
+ int next;
/* Do nothing if the control interface is not turned on */
- if (wpa_s == NULL || wpa_s->global == NULL)
- return 0;
- ctrl_iface = wpa_s->global->dbus;
if (ctrl_iface == NULL)
return 0;
- os_snprintf(net_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
- "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%u",
- wpa_s->dbus_new_path, ssid->id);
+ /* Create and set the interface's object path */
+ wpa_s->dbus_new_path = os_zalloc(WPAS_DBUS_OBJECT_PATH_MAX);
+ if (wpa_s->dbus_new_path == NULL)
+ return -1;
+ next = ctrl_iface->next_objid++;
+ os_snprintf(wpa_s->dbus_new_path, WPAS_DBUS_OBJECT_PATH_MAX,
+ WPAS_DBUS_NEW_PATH_INTERFACES "/%u",
+ next);
- wpa_printf(MSG_DEBUG, "dbus: Register network object '%s'",
- net_obj_path);
obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc));
if (!obj_desc) {
wpa_printf(MSG_ERROR, "Not enough memory "
goto err;
}
- /* allocate memory for handlers arguments */
- arg = os_zalloc(sizeof(struct network_handler_args));
- if (!arg) {
- wpa_printf(MSG_ERROR, "Not enough memory "
- "to create arguments for method");
- goto err;
- }
-
- arg->wpa_s = wpa_s;
- arg->ssid = ssid;
-
- wpas_dbus_register(obj_desc, arg, wpa_dbus_free, NULL,
- wpas_dbus_network_properties,
- wpas_dbus_network_signals);
+ wpas_dbus_register(obj_desc, wpa_s, NULL, wpas_dbus_interface_methods,
+ wpas_dbus_interface_properties,
+ wpas_dbus_interface_signals);
- if (wpa_dbus_register_object_per_iface(ctrl_iface, net_obj_path,
+ wpa_printf(MSG_DEBUG, "dbus: Register interface object '%s'",
+ wpa_s->dbus_new_path);
+ if (wpa_dbus_register_object_per_iface(ctrl_iface,
+ wpa_s->dbus_new_path,
wpa_s->ifname, obj_desc))
goto err;
- wpas_dbus_signal_network_added(wpa_s, ssid->id);
+ wpas_dbus_signal_interface_added(wpa_s);
return 0;
err:
+ os_free(wpa_s->dbus_new_path);
+ wpa_s->dbus_new_path = NULL;
free_dbus_object_desc(obj_desc);
return -1;
}
-/**
- * wpas_dbus_unregister_network - Unregister a configured network from dbus
- * @wpa_s: wpa_supplicant interface structure
- * @nid: network id
- * Returns: 0 on success, -1 on failure
- *
- * Unregisters network representing object from dbus
- */
-int wpas_dbus_unregister_network(struct wpa_supplicant *wpa_s, int nid)
+int wpas_dbus_unregister_interface(struct wpa_supplicant *wpa_s)
{
struct wpas_dbus_priv *ctrl_iface;
- char net_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
- int ret;
/* Do nothing if the control interface is not turned on */
- if (wpa_s == NULL || wpa_s->global == NULL ||
- wpa_s->dbus_new_path == NULL)
+ if (wpa_s == NULL || wpa_s->global == NULL)
return 0;
ctrl_iface = wpa_s->global->dbus;
if (ctrl_iface == NULL)
return 0;
- os_snprintf(net_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
- "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%u",
- wpa_s->dbus_new_path, nid);
+ wpa_printf(MSG_DEBUG, "dbus: Unregister interface object '%s'",
+ wpa_s->dbus_new_path);
+ if (wpa_dbus_unregister_object_per_iface(ctrl_iface,
+ wpa_s->dbus_new_path))
+ return -1;
- wpa_printf(MSG_DEBUG, "dbus: Unregister network object '%s'",
- net_obj_path);
- ret = wpa_dbus_unregister_object_per_iface(ctrl_iface, net_obj_path);
+ wpas_dbus_signal_interface_removed(wpa_s);
- if (!ret)
- wpas_dbus_signal_network_removed(wpa_s, nid);
+ os_free(wpa_s->dbus_new_path);
+ wpa_s->dbus_new_path = NULL;
- return ret;
+ return 0;
}
+#ifdef CONFIG_P2P
-static const struct wpa_dbus_property_desc wpas_dbus_bss_properties[] = {
- { "SSID", WPAS_DBUS_NEW_IFACE_BSS, "ay",
- (WPADBusPropertyAccessor) wpas_dbus_getter_bss_ssid,
- NULL,
- R
+static const struct wpa_dbus_property_desc wpas_dbus_p2p_peer_properties[] = {
+ { "DeviceName", WPAS_DBUS_NEW_IFACE_P2P_PEER, "s",
+ wpas_dbus_getter_p2p_peer_device_name,
+ NULL
},
- { "BSSID", WPAS_DBUS_NEW_IFACE_BSS, "ay",
- (WPADBusPropertyAccessor) wpas_dbus_getter_bss_bssid,
- NULL,
- R
- },
- { "Privacy", WPAS_DBUS_NEW_IFACE_BSS, "b",
- (WPADBusPropertyAccessor) wpas_dbus_getter_bss_privacy,
- NULL,
- R
+ { "PrimaryDeviceType", WPAS_DBUS_NEW_IFACE_P2P_PEER, "ay",
+ wpas_dbus_getter_p2p_peer_primary_device_type,
+ NULL
},
- { "Mode", WPAS_DBUS_NEW_IFACE_BSS, "s",
- (WPADBusPropertyAccessor) wpas_dbus_getter_bss_mode,
- NULL,
- R
+ { "config_method", WPAS_DBUS_NEW_IFACE_P2P_PEER, "q",
+ wpas_dbus_getter_p2p_peer_config_method,
+ NULL
},
- { "Signal", WPAS_DBUS_NEW_IFACE_BSS, "n",
- (WPADBusPropertyAccessor) wpas_dbus_getter_bss_signal,
- NULL,
- R
+ { "level", WPAS_DBUS_NEW_IFACE_P2P_PEER, "i",
+ wpas_dbus_getter_p2p_peer_level,
+ NULL
},
- { "Frequency", WPAS_DBUS_NEW_IFACE_BSS, "q",
- (WPADBusPropertyAccessor) wpas_dbus_getter_bss_frequency,
- NULL,
- R
+ { "devicecapability", WPAS_DBUS_NEW_IFACE_P2P_PEER, "y",
+ wpas_dbus_getter_p2p_peer_device_capability,
+ NULL
},
- { "Rates", WPAS_DBUS_NEW_IFACE_BSS, "au",
- (WPADBusPropertyAccessor) wpas_dbus_getter_bss_rates,
- NULL,
- R
+ { "groupcapability", WPAS_DBUS_NEW_IFACE_P2P_PEER, "y",
+ wpas_dbus_getter_p2p_peer_group_capability,
+ NULL
},
- { "WPA", WPAS_DBUS_NEW_IFACE_BSS, "a{sv}",
- (WPADBusPropertyAccessor) wpas_dbus_getter_bss_wpa,
- NULL,
- R
+ { "SecondaryDeviceTypes", WPAS_DBUS_NEW_IFACE_P2P_PEER, "aay",
+ wpas_dbus_getter_p2p_peer_secondary_device_types,
+ NULL
},
- { "RSN", WPAS_DBUS_NEW_IFACE_BSS, "a{sv}",
- (WPADBusPropertyAccessor) wpas_dbus_getter_bss_rsn,
- NULL,
- R
+ { "VendorExtension", WPAS_DBUS_NEW_IFACE_P2P_PEER, "aay",
+ wpas_dbus_getter_p2p_peer_vendor_extension,
+ NULL
},
- { "IEs", WPAS_DBUS_NEW_IFACE_BSS, "ay",
- (WPADBusPropertyAccessor) wpas_dbus_getter_bss_ies,
- NULL,
- R
+ { "IEs", WPAS_DBUS_NEW_IFACE_P2P_PEER, "ay",
+ wpas_dbus_getter_p2p_peer_ies,
+ NULL
},
- { NULL, NULL, NULL, NULL, NULL, 0 }
+ { NULL, NULL, NULL, NULL, NULL }
};
+static const struct wpa_dbus_signal_desc wpas_dbus_p2p_peer_signals[] = {
-static const struct wpa_dbus_signal_desc wpas_dbus_bss_signals[] = {
- { "PropertiesChanged", WPAS_DBUS_NEW_IFACE_BSS,
- {
- { "properties", "a{sv}", ARG_OUT },
- END_ARGS
- }
- },
{ NULL, NULL, { END_ARGS } }
};
-
/**
- * wpas_dbus_unregister_bss - Unregister a scanned BSS from dbus
- * @wpa_s: wpa_supplicant interface structure
- * @bssid: scanned network bssid
- * @id: unique BSS identifier
- * Returns: 0 on success, -1 on failure
+ * wpas_dbus_signal_peer - Send a peer related event signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @dev: peer device object
+ * @interface: name of the interface emitting this signal.
+ * In case of peer objects, it would be emitted by either
+ * the "interface object" or by "peer objects"
+ * @sig_name: signal name - DeviceFound
*
- * Unregisters BSS representing object from dbus
+ * Notify listeners about event related with newly found p2p peer device
*/
-int wpas_dbus_unregister_bss(struct wpa_supplicant *wpa_s,
- u8 bssid[ETH_ALEN], unsigned int id)
+static void wpas_dbus_signal_peer(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr, const char *interface,
+ const char *sig_name)
{
- struct wpas_dbus_priv *ctrl_iface;
- char bss_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
+ struct wpas_dbus_priv *iface;
+ DBusMessage *msg;
+ DBusMessageIter iter;
+ char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
+
+ iface = wpa_s->global->dbus;
/* Do nothing if the control interface is not turned on */
- if (wpa_s == NULL || wpa_s->global == NULL)
- return 0;
- ctrl_iface = wpa_s->global->dbus;
- if (ctrl_iface == NULL)
- return 0;
+ if (iface == NULL)
+ return;
- os_snprintf(bss_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
- "%s/" WPAS_DBUS_NEW_BSSIDS_PART "/%u",
- wpa_s->dbus_new_path, id);
+ os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR,
+ wpa_s->dbus_new_path, MAC2STR(dev_addr));
- wpa_printf(MSG_DEBUG, "dbus: Unregister BSS object '%s'",
- bss_obj_path);
- if (wpa_dbus_unregister_object_per_iface(ctrl_iface, bss_obj_path)) {
- wpa_printf(MSG_ERROR, "dbus: Cannot unregister BSS object %s",
- bss_obj_path);
- return -1;
- }
+ msg = dbus_message_new_signal(wpa_s->dbus_new_path, interface,
+ sig_name);
+ if (msg == NULL)
+ return;
- wpas_dbus_signal_bss_removed(wpa_s, bss_obj_path);
+ dbus_message_iter_init_append(msg, &iter);
+ path = peer_obj_path;
+ if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
+ &path))
+ goto err;
- return 0;
+ dbus_connection_send(iface->con, msg, NULL);
+
+ dbus_message_unref(msg);
+ return;
+
+err:
+ wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+ dbus_message_unref(msg);
}
/**
- * wpas_dbus_register_bss - Register a scanned BSS with dbus
+ * wpas_dbus_signal_peer_found - Send a peer found signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @dev: peer device object
+ *
+ * Notify listeners about find a p2p peer device found
+ */
+void wpas_dbus_signal_peer_device_found(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr)
+{
+ wpas_dbus_signal_peer(wpa_s, dev_addr,
+ WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ "DeviceFound");
+}
+
+/**
+ * wpas_dbus_signal_peer_lost - Send a peer lost signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @dev: peer device object
+ *
+ * Notify listeners about lost a p2p peer device
+ */
+void wpas_dbus_signal_peer_device_lost(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr)
+{
+ wpas_dbus_signal_peer(wpa_s, dev_addr,
+ WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ "DeviceLost");
+}
+
+/**
+ * wpas_dbus_register_peer - Register a discovered peer object with dbus
* @wpa_s: wpa_supplicant interface structure
- * @bssid: scanned network bssid
- * @id: unique BSS identifier
+ * @ssid: network configuration data
* Returns: 0 on success, -1 on failure
*
- * Registers BSS representing object with dbus
+ * Registers network representing object with dbus
*/
-int wpas_dbus_register_bss(struct wpa_supplicant *wpa_s,
- u8 bssid[ETH_ALEN], unsigned int id)
+int wpas_dbus_register_peer(struct wpa_supplicant *wpa_s, const u8 *dev_addr)
{
struct wpas_dbus_priv *ctrl_iface;
struct wpa_dbus_object_desc *obj_desc;
- char bss_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
- struct bss_handler_args *arg;
+ struct peer_handler_args *arg;
+ char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
/* Do nothing if the control interface is not turned on */
if (wpa_s == NULL || wpa_s->global == NULL)
return 0;
+
ctrl_iface = wpa_s->global->dbus;
if (ctrl_iface == NULL)
return 0;
- os_snprintf(bss_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
- "%s/" WPAS_DBUS_NEW_BSSIDS_PART "/%u",
- wpa_s->dbus_new_path, id);
+ os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR,
+ wpa_s->dbus_new_path, MAC2STR(dev_addr));
+ wpa_printf(MSG_INFO, "dbus: Register peer object '%s'",
+ peer_obj_path);
obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc));
if (!obj_desc) {
wpa_printf(MSG_ERROR, "Not enough memory "
goto err;
}
- arg = os_zalloc(sizeof(struct bss_handler_args));
+ /* allocate memory for handlers arguments */
+ arg = os_zalloc(sizeof(struct peer_handler_args));
if (!arg) {
wpa_printf(MSG_ERROR, "Not enough memory "
- "to create arguments for handler");
+ "to create arguments for method");
goto err;
}
+
arg->wpa_s = wpa_s;
- arg->id = id;
+ os_memcpy(arg->p2p_device_addr, dev_addr, ETH_ALEN);
- wpas_dbus_register(obj_desc, arg, wpa_dbus_free, NULL,
- wpas_dbus_bss_properties,
- wpas_dbus_bss_signals);
+ wpas_dbus_register(obj_desc, arg, wpa_dbus_free,
+ NULL,
+ wpas_dbus_p2p_peer_properties,
+ wpas_dbus_p2p_peer_signals);
- wpa_printf(MSG_DEBUG, "dbus: Register BSS object '%s'",
- bss_obj_path);
- if (wpa_dbus_register_object_per_iface(ctrl_iface, bss_obj_path,
- wpa_s->ifname, obj_desc)) {
- wpa_printf(MSG_ERROR,
- "Cannot register BSSID dbus object %s.",
- bss_obj_path);
+ if (wpa_dbus_register_object_per_iface(ctrl_iface, peer_obj_path,
+ wpa_s->ifname, obj_desc))
goto err;
- }
-
- wpas_dbus_signal_bss_added(wpa_s, bss_obj_path);
return 0;
return -1;
}
+/**
+ * wpas_dbus_unregister_peer - Unregister a peer object with dbus
+ * @wpa_s: wpa_supplicant interface structure
+ * @dev_addr: p2p device addr
+ * Returns: 0 on success, -1 on failure
+ *
+ * Registers network representing object with dbus
+ */
+int wpas_dbus_unregister_peer(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr)
+{
+ struct wpas_dbus_priv *ctrl_iface;
+ char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
+ int ret;
+
+ /* Do nothing if the control interface is not turned on */
+ if (wpa_s == NULL || wpa_s->global == NULL ||
+ wpa_s->dbus_new_path == NULL)
+ return 0;
+ ctrl_iface = wpa_s->global->dbus;
+ if (ctrl_iface == NULL)
+ return 0;
-static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = {
- { "Scan", WPAS_DBUS_NEW_IFACE_INTERFACE,
- (WPADBusMethodHandler) &wpas_dbus_handler_scan,
- {
- { "args", "a{sv}", ARG_IN },
- END_ARGS
- }
- },
- { "Disconnect", WPAS_DBUS_NEW_IFACE_INTERFACE,
- (WPADBusMethodHandler) &wpas_dbus_handler_disconnect,
- {
- END_ARGS
- }
- },
- { "AddNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE,
- (WPADBusMethodHandler) &wpas_dbus_handler_add_network,
- {
- { "args", "a{sv}", ARG_IN },
- { "path", "o", ARG_OUT },
- END_ARGS
- }
- },
- { "RemoveNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE,
- (WPADBusMethodHandler) &wpas_dbus_handler_remove_network,
- {
- { "path", "o", ARG_IN },
- END_ARGS
- }
- },
- { "SelectNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE,
- (WPADBusMethodHandler) &wpas_dbus_handler_select_network,
- {
- { "path", "o", ARG_IN },
- END_ARGS
- }
- },
- { "AddBlob", WPAS_DBUS_NEW_IFACE_INTERFACE,
- (WPADBusMethodHandler) &wpas_dbus_handler_add_blob,
- {
- { "name", "s", ARG_IN },
- { "data", "ay", ARG_IN },
- END_ARGS
- }
- },
- { "GetBlob", WPAS_DBUS_NEW_IFACE_INTERFACE,
- (WPADBusMethodHandler) &wpas_dbus_handler_get_blob,
- {
- { "name", "s", ARG_IN },
- { "data", "ay", ARG_OUT },
- END_ARGS
- }
- },
- { "RemoveBlob", WPAS_DBUS_NEW_IFACE_INTERFACE,
- (WPADBusMethodHandler) &wpas_dbus_handler_remove_blob,
- {
- { "name", "s", ARG_IN },
- END_ARGS
- }
- },
-#ifdef CONFIG_WPS
- { "Start", WPAS_DBUS_NEW_IFACE_WPS,
- (WPADBusMethodHandler) &wpas_dbus_handler_wps_start,
- {
- { "args", "a{sv}", ARG_IN },
- { "output", "a{sv}", ARG_OUT },
- END_ARGS
- }
- },
-#endif /* CONFIG_WPS */
- { NULL, NULL, NULL, { END_ARGS } }
-};
+ os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR,
+ wpa_s->dbus_new_path, MAC2STR(dev_addr));
-static const struct wpa_dbus_property_desc wpas_dbus_interface_properties[] = {
- { "Capabilities", WPAS_DBUS_NEW_IFACE_INTERFACE, "a{sv}",
- (WPADBusPropertyAccessor) wpas_dbus_getter_capabilities,
- NULL, R
- },
- { "State", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
- (WPADBusPropertyAccessor) wpas_dbus_getter_state,
- NULL, R
- },
- { "Scanning", WPAS_DBUS_NEW_IFACE_INTERFACE, "b",
- (WPADBusPropertyAccessor) wpas_dbus_getter_scanning,
- NULL, R
- },
- { "ApScan", WPAS_DBUS_NEW_IFACE_INTERFACE, "u",
- (WPADBusPropertyAccessor) wpas_dbus_getter_ap_scan,
- (WPADBusPropertyAccessor) wpas_dbus_setter_ap_scan,
- RW
- },
- { "Ifname", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
- (WPADBusPropertyAccessor) wpas_dbus_getter_ifname,
- NULL, R
- },
- { "Driver", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
- (WPADBusPropertyAccessor) wpas_dbus_getter_driver,
- NULL, R
- },
- { "BridgeIfname", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
- (WPADBusPropertyAccessor) wpas_dbus_getter_bridge_ifname,
- NULL, R
- },
- { "CurrentBSS", WPAS_DBUS_NEW_IFACE_INTERFACE, "o",
- (WPADBusPropertyAccessor) wpas_dbus_getter_current_bss,
- NULL, R
- },
- { "CurrentNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE, "o",
- (WPADBusPropertyAccessor) wpas_dbus_getter_current_network,
- NULL, R
- },
- { "Blobs", WPAS_DBUS_NEW_IFACE_INTERFACE, "a{say}",
- (WPADBusPropertyAccessor) wpas_dbus_getter_blobs,
- NULL, R
- },
- { "BSSs", WPAS_DBUS_NEW_IFACE_INTERFACE, "ao",
- (WPADBusPropertyAccessor) wpas_dbus_getter_bsss,
- NULL, R
- },
- { "Networks", WPAS_DBUS_NEW_IFACE_INTERFACE, "ao",
- (WPADBusPropertyAccessor) wpas_dbus_getter_networks,
- NULL, R
- },
-#ifdef CONFIG_WPS
- { "ProcessCredentials", WPAS_DBUS_NEW_IFACE_WPS, "b",
- (WPADBusPropertyAccessor) wpas_dbus_getter_process_credentials,
- (WPADBusPropertyAccessor) wpas_dbus_setter_process_credentials,
- RW
- },
-#endif /* CONFIG_WPS */
- { NULL, NULL, NULL, NULL, NULL, 0 }
-};
+ wpa_printf(MSG_INFO, "dbus: Unregister peer object '%s'",
+ peer_obj_path);
+ ret = wpa_dbus_unregister_object_per_iface(ctrl_iface, peer_obj_path);
-static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = {
- { "ScanDone", WPAS_DBUS_NEW_IFACE_INTERFACE,
- {
- { "success", "b", ARG_OUT },
- END_ARGS
- }
- },
- { "BSSAdded", WPAS_DBUS_NEW_IFACE_INTERFACE,
- {
- { "path", "o", ARG_OUT },
- { "properties", "a{sv}", ARG_OUT },
- END_ARGS
- }
+ return ret;
+}
+
+
+static const struct wpa_dbus_property_desc wpas_dbus_p2p_group_properties[] = {
+ { "Members", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "ao",
+ wpas_dbus_getter_p2p_group_members,
+ NULL
},
- { "BSSRemoved", WPAS_DBUS_NEW_IFACE_INTERFACE,
- {
- { "path", "o", ARG_OUT },
- END_ARGS
- }
+ { "Group", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "o",
+ wpas_dbus_getter_p2p_group,
+ NULL
},
- { "BlobAdded", WPAS_DBUS_NEW_IFACE_INTERFACE,
- {
- { "name", "s", ARG_OUT },
- END_ARGS
- }
+ { "Role", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "s",
+ wpas_dbus_getter_p2p_role,
+ NULL
},
- { "BlobRemoved", WPAS_DBUS_NEW_IFACE_INTERFACE,
- {
- { "name", "s", ARG_OUT },
- END_ARGS
- }
+ { "SSID", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "ay",
+ wpas_dbus_getter_p2p_group_ssid,
+ NULL
},
- { "NetworkAdded", WPAS_DBUS_NEW_IFACE_INTERFACE,
- {
- { "path", "o", ARG_OUT },
- { "properties", "a{sv}", ARG_OUT },
- END_ARGS
- }
+ { "BSSID", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "ay",
+ wpas_dbus_getter_p2p_group_bssid,
+ NULL
},
- { "NetworkRemoved", WPAS_DBUS_NEW_IFACE_INTERFACE,
- {
- { "path", "o", ARG_OUT },
- END_ARGS
- }
+ { "Frequency", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "q",
+ wpas_dbus_getter_p2p_group_frequency,
+ NULL
},
- { "NetworkSelected", WPAS_DBUS_NEW_IFACE_INTERFACE,
- {
- { "path", "o", ARG_OUT },
- END_ARGS
- }
+ { "Passphrase", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "s",
+ wpas_dbus_getter_p2p_group_passphrase,
+ NULL
},
- { "PropertiesChanged", WPAS_DBUS_NEW_IFACE_INTERFACE,
- {
- { "properties", "a{sv}", ARG_OUT },
- END_ARGS
- }
+ { "PSK", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "ay",
+ wpas_dbus_getter_p2p_group_psk,
+ NULL
},
-#ifdef CONFIG_WPS
- { "Event", WPAS_DBUS_NEW_IFACE_WPS,
- {
- { "name", "s", ARG_OUT },
- { "args", "a{sv}", ARG_OUT },
- END_ARGS
- }
+ { "WPSVendorExtensions", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "aay",
+ wpas_dbus_getter_p2p_group_vendor_ext,
+ wpas_dbus_setter_p2p_group_vendor_ext
},
- { "Credentials", WPAS_DBUS_NEW_IFACE_WPS,
+ { NULL, NULL, NULL, NULL, NULL }
+};
+
+static const struct wpa_dbus_signal_desc wpas_dbus_p2p_group_signals[] = {
+ { "PeerJoined", WPAS_DBUS_NEW_IFACE_P2P_GROUP,
{
- { "credentials", "a{sv}", ARG_OUT },
+ { "peer", "o", ARG_OUT },
END_ARGS
}
},
- { "PropertiesChanged", WPAS_DBUS_NEW_IFACE_WPS,
+ { "PeerDisconnected", WPAS_DBUS_NEW_IFACE_P2P_GROUP,
{
- { "properties", "a{sv}", ARG_OUT },
+ { "peer", "o", ARG_OUT },
END_ARGS
}
},
-#endif /* CONFIG_WPS */
{ NULL, NULL, { END_ARGS } }
};
+/**
+ * wpas_dbus_register_p2p_group - Register a p2p group object with dbus
+ * @wpa_s: wpa_supplicant interface structure
+ * @ssid: SSID struct
+ * Returns: 0 on success, -1 on failure
+ *
+ * Registers p2p group representing object with dbus
+ */
+void wpas_dbus_register_p2p_group(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+ struct wpas_dbus_priv *ctrl_iface;
+ struct wpa_dbus_object_desc *obj_desc;
+ char group_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
-int wpas_dbus_register_interface(struct wpa_supplicant *wpa_s)
+ /* Do nothing if the control interface is not turned on */
+ if (wpa_s == NULL || wpa_s->global == NULL)
+ return;
+
+ ctrl_iface = wpa_s->global->dbus;
+ if (ctrl_iface == NULL)
+ return;
+
+ if (wpa_s->dbus_groupobj_path) {
+ wpa_printf(MSG_INFO, "%s: Group object '%s' already exists",
+ __func__, wpa_s->dbus_groupobj_path);
+ return;
+ }
+
+ if (wpas_dbus_get_group_obj_path(wpa_s, ssid, group_obj_path) < 0)
+ return;
+
+ wpa_s->dbus_groupobj_path = os_strdup(group_obj_path);
+ if (wpa_s->dbus_groupobj_path == NULL)
+ return;
+
+ wpa_printf(MSG_INFO, "dbus: Register group object '%s'",
+ group_obj_path);
+ obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc));
+ if (!obj_desc) {
+ wpa_printf(MSG_ERROR, "Not enough memory "
+ "to create object description");
+ goto err;
+ }
+
+ wpas_dbus_register(obj_desc, wpa_s, NULL, NULL,
+ wpas_dbus_p2p_group_properties,
+ wpas_dbus_p2p_group_signals);
+
+ if (wpa_dbus_register_object_per_iface(ctrl_iface, group_obj_path,
+ wpa_s->ifname, obj_desc))
+ goto err;
+
+ return;
+
+err:
+ if (wpa_s->dbus_groupobj_path) {
+ os_free(wpa_s->dbus_groupobj_path);
+ wpa_s->dbus_groupobj_path = NULL;
+ }
+
+ free_dbus_object_desc(obj_desc);
+}
+
+/**
+ * wpas_dbus_unregister_p2p_group - Unregister a p2p group object from dbus
+ * @wpa_s: wpa_supplicant interface structure
+ * @ssid: network name of the p2p group started
+ */
+void wpas_dbus_unregister_p2p_group(struct wpa_supplicant *wpa_s,
+ const struct wpa_ssid *ssid)
{
+ struct wpas_dbus_priv *ctrl_iface;
+
+ /* Do nothing if the control interface is not turned on */
+ if (wpa_s == NULL || wpa_s->global == NULL)
+ return;
+
+ ctrl_iface = wpa_s->global->dbus;
+ if (ctrl_iface == NULL)
+ return;
+
+ if (!wpa_s->dbus_groupobj_path) {
+ wpa_printf(MSG_DEBUG,
+ "%s: Group object '%s' already unregistered",
+ __func__, wpa_s->dbus_groupobj_path);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "dbus: Unregister group object '%s'",
+ wpa_s->dbus_groupobj_path);
+
+ wpa_dbus_unregister_object_per_iface(ctrl_iface,
+ wpa_s->dbus_groupobj_path);
+ os_free(wpa_s->dbus_groupobj_path);
+ wpa_s->dbus_groupobj_path = NULL;
+}
+
+static const struct wpa_dbus_property_desc
+wpas_dbus_p2p_groupmember_properties[] = {
+ { NULL, NULL, NULL, NULL, NULL }
+};
+
+/**
+ * wpas_dbus_register_p2p_groupmember - Register a p2p groupmember
+ * object with dbus
+ * @wpa_s: wpa_supplicant interface structure
+ * @p2p_if_addr: i/f addr of the device joining this group
+ *
+ * Registers p2p groupmember representing object with dbus
+ */
+void wpas_dbus_register_p2p_groupmember(struct wpa_supplicant *wpa_s,
+ const u8 *p2p_if_addr)
+{
+ struct wpas_dbus_priv *ctrl_iface;
struct wpa_dbus_object_desc *obj_desc = NULL;
- struct wpas_dbus_priv *ctrl_iface = wpa_s->global->dbus;
- int next;
+ struct groupmember_handler_args *arg;
+ char groupmember_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
/* Do nothing if the control interface is not turned on */
+ if (wpa_s == NULL || wpa_s->global == NULL)
+ return;
+
+ ctrl_iface = wpa_s->global->dbus;
if (ctrl_iface == NULL)
- return 0;
+ return;
- /* Create and set the interface's object path */
- wpa_s->dbus_new_path = os_zalloc(WPAS_DBUS_OBJECT_PATH_MAX);
- if (wpa_s->dbus_new_path == NULL)
- return -1;
- next = ctrl_iface->next_objid++;
- os_snprintf(wpa_s->dbus_new_path, WPAS_DBUS_OBJECT_PATH_MAX,
- WPAS_DBUS_NEW_PATH_INTERFACES "/%u",
- next);
+ if (!wpa_s->dbus_groupobj_path)
+ return;
+
+ os_snprintf(groupmember_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s/" WPAS_DBUS_NEW_P2P_GROUPMEMBERS_PART "/" COMPACT_MACSTR,
+ wpa_s->dbus_groupobj_path, MAC2STR(p2p_if_addr));
obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc));
if (!obj_desc) {
goto err;
}
- wpas_dbus_register(obj_desc, wpa_s, NULL, wpas_dbus_interface_methods,
- wpas_dbus_interface_properties,
- wpas_dbus_interface_signals);
+ /* allocate memory for handlers arguments */
+ arg = os_zalloc(sizeof(struct groupmember_handler_args));
+ if (!arg) {
+ wpa_printf(MSG_ERROR, "Not enough memory "
+ "to create arguments for method");
+ goto err;
+ }
- wpa_printf(MSG_DEBUG, "dbus: Register interface object '%s'",
- wpa_s->dbus_new_path);
- if (wpa_dbus_register_object_per_iface(ctrl_iface,
- wpa_s->dbus_new_path,
+ arg->wpa_s = wpa_s;
+ os_memcpy(arg->member_addr, p2p_if_addr, ETH_ALEN);
+
+ wpas_dbus_register(obj_desc, arg, wpa_dbus_free, NULL,
+ wpas_dbus_p2p_groupmember_properties, NULL);
+
+ if (wpa_dbus_register_object_per_iface(ctrl_iface, groupmember_obj_path,
wpa_s->ifname, obj_desc))
goto err;
- wpas_dbus_signal_interface_added(wpa_s);
+ wpa_printf(MSG_INFO,
+ "dbus: Registered group member object '%s' successfully",
+ groupmember_obj_path);
+ return;
+
+err:
+ free_dbus_object_desc(obj_desc);
+}
+
+/**
+ * wpas_dbus_unregister_p2p_groupmember - Unregister a p2p groupmember
+ * object with dbus
+ * @wpa_s: wpa_supplicant interface structure
+ * @p2p_if_addr: i/f addr of the device joining this group
+ *
+ * Unregisters p2p groupmember representing object with dbus
+ */
+void wpas_dbus_unregister_p2p_groupmember(struct wpa_supplicant *wpa_s,
+ const u8 *p2p_if_addr)
+{
+ struct wpas_dbus_priv *ctrl_iface;
+ char groupmember_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
+
+ /* Do nothing if the control interface is not turned on */
+ if (wpa_s == NULL || wpa_s->global == NULL)
+ return;
+
+ ctrl_iface = wpa_s->global->dbus;
+ if (ctrl_iface == NULL)
+ return;
+
+ if (!wpa_s->dbus_groupobj_path)
+ return;
+
+ os_snprintf(groupmember_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s/" WPAS_DBUS_NEW_P2P_GROUPMEMBERS_PART "/" COMPACT_MACSTR,
+ wpa_s->dbus_groupobj_path, MAC2STR(p2p_if_addr));
+
+ wpa_dbus_unregister_object_per_iface(ctrl_iface, groupmember_obj_path);
+}
+
+
+static const struct wpa_dbus_property_desc
+ wpas_dbus_persistent_group_properties[] = {
+ { "Properties", WPAS_DBUS_NEW_IFACE_PERSISTENT_GROUP, "a{sv}",
+ wpas_dbus_getter_persistent_group_properties,
+ wpas_dbus_setter_persistent_group_properties
+ },
+ { NULL, NULL, NULL, NULL, NULL }
+};
+
+/* No signals intended for persistent group objects */
+
+/**
+ * wpas_dbus_register_persistent_group - Register a configured(saved)
+ * persistent group with dbus
+ * @wpa_s: wpa_supplicant interface structure
+ * @ssid: persistent group (still represented as a network within wpa)
+ * configuration data
+ * Returns: 0 on success, -1 on failure
+ *
+ * Registers a persistent group representing object with dbus.
+ */
+int wpas_dbus_register_persistent_group(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+ struct wpas_dbus_priv *ctrl_iface;
+ struct wpa_dbus_object_desc *obj_desc;
+ struct network_handler_args *arg;
+ char pgrp_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
+
+ /* Do nothing if the control interface is not turned on */
+ if (wpa_s == NULL || wpa_s->global == NULL)
+ return 0;
+
+ /* Make sure ssid is a persistent group */
+ if (ssid->disabled != 2 && !ssid->p2p_persistent_group)
+ return -1; /* should we return w/o complaining? */
+
+ ctrl_iface = wpa_s->global->dbus;
+ if (ctrl_iface == NULL)
+ return 0;
+
+ /*
+ * Intentionally not coming up with different numbering scheme
+ * for persistent groups.
+ */
+ os_snprintf(pgrp_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s/" WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/%u",
+ wpa_s->dbus_new_path, ssid->id);
+
+ wpa_printf(MSG_DEBUG, "dbus: Register persistent group object '%s'",
+ pgrp_obj_path);
+ obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc));
+ if (!obj_desc) {
+ wpa_printf(MSG_ERROR, "dbus: Not enough memory to create "
+ "object description");
+ goto err;
+ }
+
+ /*
+ * Reusing the same context structure as that for networks
+ * since these are represented using same data structure.
+ */
+ /* allocate memory for handlers arguments */
+ arg = os_zalloc(sizeof(struct network_handler_args));
+ if (!arg) {
+ wpa_printf(MSG_ERROR, "dbus: Not enough memory to create "
+ "arguments for method");
+ goto err;
+ }
+
+ arg->wpa_s = wpa_s;
+ arg->ssid = ssid;
+
+ wpas_dbus_register(obj_desc, arg, wpa_dbus_free, NULL,
+ wpas_dbus_persistent_group_properties,
+ NULL);
+
+ if (wpa_dbus_register_object_per_iface(ctrl_iface, pgrp_obj_path,
+ wpa_s->ifname, obj_desc))
+ goto err;
+
+ wpas_dbus_signal_persistent_group_added(wpa_s, ssid->id);
return 0;
err:
- os_free(wpa_s->dbus_new_path);
- wpa_s->dbus_new_path = NULL;
free_dbus_object_desc(obj_desc);
return -1;
}
-int wpas_dbus_unregister_interface(struct wpa_supplicant *wpa_s)
+/**
+ * wpas_dbus_unregister_persistent_group - Unregister a persistent_group
+ * from dbus
+ * @wpa_s: wpa_supplicant interface structure
+ * @nid: network id
+ * Returns: 0 on success, -1 on failure
+ *
+ * Unregisters persistent group representing object from dbus
+ *
+ * NOTE: There is a slight issue with the semantics here. While the
+ * implementation simply means the persistent group is unloaded from memory,
+ * it should not get interpreted as the group is actually being erased/removed
+ * from persistent storage as well.
+ */
+int wpas_dbus_unregister_persistent_group(struct wpa_supplicant *wpa_s,
+ int nid)
{
struct wpas_dbus_priv *ctrl_iface;
+ char pgrp_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
+ int ret;
/* Do nothing if the control interface is not turned on */
- if (wpa_s == NULL || wpa_s->global == NULL)
+ if (wpa_s == NULL || wpa_s->global == NULL ||
+ wpa_s->dbus_new_path == NULL)
return 0;
ctrl_iface = wpa_s->global->dbus;
if (ctrl_iface == NULL)
return 0;
- wpa_printf(MSG_DEBUG, "dbus: Unregister interface object '%s'",
- wpa_s->dbus_new_path);
- if (wpa_dbus_unregister_object_per_iface(ctrl_iface,
- wpa_s->dbus_new_path))
- return -1;
+ os_snprintf(pgrp_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s/" WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/%u",
+ wpa_s->dbus_new_path, nid);
- wpas_dbus_signal_interface_removed(wpa_s);
+ wpa_printf(MSG_DEBUG, "dbus: Unregister persistent group object '%s'",
+ pgrp_obj_path);
+ ret = wpa_dbus_unregister_object_per_iface(ctrl_iface, pgrp_obj_path);
- os_free(wpa_s->dbus_new_path);
- wpa_s->dbus_new_path = NULL;
+ if (!ret)
+ wpas_dbus_signal_persistent_group_removed(wpa_s, nid);
- return 0;
+ return ret;
}
+
+#endif /* CONFIG_P2P */
#ifndef CTRL_IFACE_DBUS_NEW_H
#define CTRL_IFACE_DBUS_NEW_H
+#include "common/defs.h"
+#include "p2p/p2p.h"
+
struct wpa_global;
struct wpa_supplicant;
struct wpa_ssid;
struct wps_event_m2d;
struct wps_event_fail;
struct wps_credential;
-enum wpa_states;
enum wpas_dbus_prop {
WPAS_DBUS_PROP_AP_SCAN,
WPAS_DBUS_PROP_STATE,
WPAS_DBUS_PROP_CURRENT_BSS,
WPAS_DBUS_PROP_CURRENT_NETWORK,
+ WPAS_DBUS_PROP_CURRENT_AUTH_MODE,
+ WPAS_DBUS_PROP_BSSS,
};
enum wpas_dbus_bss_prop {
#define WPAS_DBUS_NEW_BSSIDS_PART "BSSs"
#define WPAS_DBUS_NEW_IFACE_BSS WPAS_DBUS_NEW_INTERFACE ".BSS"
+#define WPAS_DBUS_NEW_IFACE_P2PDEVICE \
+ WPAS_DBUS_NEW_IFACE_INTERFACE ".P2PDevice"
+
+/*
+ * Groups correspond to P2P groups where this device is a GO (owner)
+ */
+#define WPAS_DBUS_NEW_P2P_GROUPS_PART "Groups"
+#define WPAS_DBUS_NEW_IFACE_P2P_GROUP WPAS_DBUS_NEW_INTERFACE ".Group"
+
+/*
+ * Different dbus object for persistent groups so they do not get confused
+ * with regular (configured) network objects.
+ */
+#define WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "PersistentGroups"
+#define WPAS_DBUS_NEW_IFACE_PERSISTENT_GROUP \
+ WPAS_DBUS_NEW_INTERFACE ".PersistentGroup"
+
+#define WPAS_DBUS_NEW_P2P_PEERS_PART "Peers"
+#define WPAS_DBUS_NEW_IFACE_P2P_PEER WPAS_DBUS_NEW_INTERFACE ".Peer"
+
+#define WPAS_DBUS_NEW_P2P_GROUPMEMBERS_PART "Members"
+#define WPAS_DBUS_NEW_IFACE_P2P_GROUPMEMBER \
+ WPAS_DBUS_NEW_INTERFACE ".GroupMember"
/* Errors */
#define WPAS_DBUS_ERROR_UNKNOWN_ERROR \
#define WPAS_DBUS_ERROR_NETWORK_UNKNOWN \
WPAS_DBUS_NEW_INTERFACE ".NetworkUnknown"
+#define WPAS_DBUS_ERROR_CONNECT_CHANNEL_UNAVAILABLE \
+ WPAS_DBUS_NEW_INTERFACE ".ConnectChannelUnavailable"
+#define WPAS_DBUS_ERROR_CONNECT_CHANNEL_UNSUPPORTED \
+ WPAS_DBUS_NEW_INTERFACE ".ConnectChannelUnsupported"
+#define WPAS_DBUS_ERROR_CONNECT_UNSPECIFIED_ERROR \
+ WPAS_DBUS_NEW_INTERFACE ".ConnectUnspecifiedError"
+
#define WPAS_DBUS_ERROR_BLOB_EXISTS \
WPAS_DBUS_NEW_INTERFACE ".BlobExists"
#define WPAS_DBUS_ERROR_BLOB_UNKNOWN \
void wpas_dbus_signal_network_enabled_changed(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid);
void wpas_dbus_signal_network_selected(struct wpa_supplicant *wpa_s, int id);
+void wpas_dbus_signal_network_request(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid,
+ enum wpa_ctrl_req_type rtype,
+ const char *default_text);
void wpas_dbus_signal_scan_done(struct wpa_supplicant *wpa_s, int success);
void wpas_dbus_signal_wps_cred(struct wpa_supplicant *wpa_s,
const struct wps_credential *cred);
void wpas_dbus_signal_debug_timestamp_changed(struct wpa_global *global);
void wpas_dbus_signal_debug_show_keys_changed(struct wpa_global *global);
+int wpas_dbus_register_peer(struct wpa_supplicant *wpa_s, const u8 *dev_addr);
+void wpas_dbus_signal_peer_device_found(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr);
+int wpas_dbus_unregister_peer(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr);
+void wpas_dbus_signal_peer_device_lost(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr);
+void wpas_dbus_signal_p2p_group_removed(struct wpa_supplicant *wpa_s,
+ const char *role);
+void wpas_dbus_signal_p2p_provision_discovery(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr, int request,
+ enum p2p_prov_disc_status status,
+ u16 config_methods,
+ unsigned int generated_pin);
+void wpas_dbus_signal_p2p_go_neg_req(struct wpa_supplicant *wpa_s,
+ const u8 *src, u16 dev_passwd_id);
+void wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s,
+ const struct wpa_ssid *ssid,
+ int client, int network_id);
+void wpas_dbus_register_p2p_group(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid);
+void wpas_dbus_signal_p2p_go_neg_resp(struct wpa_supplicant *wpa_s,
+ struct p2p_go_neg_results *res);
+void wpas_dbus_unregister_p2p_group(struct wpa_supplicant *wpa_s,
+ const struct wpa_ssid *ssid);
+int wpas_dbus_register_persistent_group(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid);
+int wpas_dbus_unregister_persistent_group(struct wpa_supplicant *wpa_s,
+ int nid);
+void wpas_dbus_signal_p2p_invitation_result(struct wpa_supplicant *wpa_s,
+ int status, const u8 *bssid);
+void wpas_dbus_register_p2p_groupmember(struct wpa_supplicant *wpa_s,
+ const u8 *p2p_if_addr);
+void wpas_dbus_unregister_p2p_groupmember(struct wpa_supplicant *wpa_s,
+ const u8 *p2p_if_addr);
+void wpas_dbus_signal_p2p_peer_disconnected(struct wpa_supplicant *wpa_s,
+ const u8 *member);
+void wpas_dbus_signal_p2p_sd_request(struct wpa_supplicant *wpa_s,
+ int freq, const u8 *sa, u8 dialog_token,
+ u16 update_indic, const u8 *tlvs,
+ size_t tlvs_len);
+void wpas_dbus_signal_p2p_sd_response(struct wpa_supplicant *wpa_s,
+ const u8 *sa, u16 update_indic,
+ const u8 *tlvs, size_t tlvs_len);
+void wpas_dbus_signal_p2p_peer_joined(struct wpa_supplicant *wpa_s,
+ const u8 *member);
+void wpas_dbus_signal_p2p_wps_failed(struct wpa_supplicant *wpa_s,
+ struct wps_event_fail *fail);
+void wpas_dbus_signal_certification(struct wpa_supplicant *wpa_s,
+ int depth, const char *subject,
+ const char *cert_hash,
+ const struct wpabuf *cert);
+
#else /* CONFIG_CTRL_IFACE_DBUS_NEW */
static inline int wpas_dbus_register_interface(struct wpa_supplicant *wpa_s)
{
}
+static inline void wpas_dbus_signal_network_request(
+ struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+ enum wpa_ctrl_req_type rtype, const char *default_txt)
+{
+}
+
static inline void wpas_dbus_signal_scan_done(struct wpa_supplicant *wpa_s,
int success)
{
{
}
+static inline int wpas_dbus_register_peer(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr)
+{
+ return 0;
+}
+
+static inline int wpas_dbus_unregister_peer(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr)
+{
+ return 0;
+}
+
+static inline void
+wpas_dbus_signal_p2p_group_removed(struct wpa_supplicant *wpa_s,
+ const char *role)
+{
+}
+
+static inline void
+wpas_dbus_signal_p2p_provision_discovery(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr, int request,
+ enum p2p_prov_disc_status status,
+ u16 config_methods,
+ unsigned int generated_pin)
+{
+}
+
+static inline void wpas_dbus_signal_p2p_go_neg_req(
+ struct wpa_supplicant *wpa_s,
+ const u8 *src,
+ u16 dev_passwd_id)
+{
+}
+
+static inline void
+wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s,
+ const struct wpa_ssid *ssid,
+ int client, int network_id)
+{
+}
+
+static inline void
+wpas_dbus_register_p2p_group(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+}
+
+static inline int wpas_dbus_register_persistent_group(
+ struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
+{
+ return 0;
+}
+
+static inline int wpas_dbus_unregister_persistent_group(
+ struct wpa_supplicant *wpa_s, int nid)
+{
+ return 0;
+}
+
+static inline void
+wpas_dbus_signal_p2p_go_neg_resp(struct wpa_supplicant *wpa_s,
+ struct p2p_go_neg_results *res)
+{
+}
+
+static inline void
+wpas_dbus_unregister_p2p_group(struct wpa_supplicant *wpa_s,
+ const struct wpa_ssid *ssid)
+{
+}
+
+static inline void wpas_dbus_signal_p2p_invitation_result(
+ struct wpa_supplicant *wpa_s, int status,
+ const u8 *bssid)
+{
+}
+
+static inline void
+wpas_dbus_register_p2p_groupmember(struct wpa_supplicant *wpa_s,
+ const u8 *p2p_if_addr)
+{
+}
+
+static inline void
+wpas_dbus_signal_p2p_sd_request(struct wpa_supplicant *wpa_s, int freq,
+ const u8 *sa, u8 dialog_token, u16 update_indic,
+ const u8 *tlvs, size_t tlvs_len)
+{
+}
+
+static inline void
+wpas_dbus_signal_p2p_sd_response(struct wpa_supplicant *wpa_s,
+ const u8 *sa, u16 update_indic,
+ const u8 *tlvs, size_t tlvs_len)
+{
+}
+
+static inline void
+wpas_dbus_unregister_p2p_groupmember(struct wpa_supplicant *wpa_s,
+ const u8 *p2p_if_addr)
+{
+}
+
+static inline void
+wpas_dbus_signal_p2p_peer_joined(struct wpa_supplicant *wpa_s,
+ const u8 *member)
+{
+}
+
+static inline void
+wpas_dbus_signal_peer_device_found(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr)
+{
+}
+
+static inline void
+wpas_dbus_signal_peer_device_lost(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr)
+{
+}
+
+static inline void
+wpas_dbus_signal_p2p_peer_disconnected(struct wpa_supplicant *wpa_s,
+ const u8 *member)
+{
+}
+
+static inline void
+wpas_dbus_signal_p2p_wps_failed(struct wpa_supplicant *wpa_s,
+ struct wps_event_fail *fail)
+{
+}
+
+static inline void wpas_dbus_signal_certification(struct wpa_supplicant *wpa_s,
+ int depth,
+ const char *subject,
+ const char *cert_hash,
+ const struct wpabuf *cert)
+{
+}
+
#endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
#endif /* CTRL_IFACE_DBUS_H_NEW */
#include "../wpas_glue.h"
#include "../bss.h"
#include "../scan.h"
+#include "../ctrl_iface.h"
#include "dbus_new_helpers.h"
#include "dbus_new.h"
#include "dbus_new_handlers.h"
extern int wpa_debug_timestamp;
static const char *debug_strings[] = {
- "msgdump", "debug", "info", "warning", "error", NULL
+ "excessive", "msgdump", "debug", "info", "warning", "error", NULL
};
/**
- * wpas_dbus_new_decompose_object_path - Decompose an interface object path into parts
- * @path: The dbus object path
- * @network: (out) the configured network this object path refers to, if any
- * @bssid: (out) the scanned bssid this object path refers to, if any
- * Returns: The object path of the network interface this path refers to
- *
- * For a given object path, decomposes the object path into object id, network,
- * and BSSID parts, if those parts exist.
- */
-static char * wpas_dbus_new_decompose_object_path(const char *path,
- char **network,
- char **bssid)
-{
- const unsigned int dev_path_prefix_len =
- strlen(WPAS_DBUS_NEW_PATH_INTERFACES "/");
- char *obj_path_only;
- char *next_sep;
-
- /* Be a bit paranoid about path */
- if (!path || os_strncmp(path, WPAS_DBUS_NEW_PATH_INTERFACES "/",
- dev_path_prefix_len))
- return NULL;
-
- /* Ensure there's something at the end of the path */
- if ((path + dev_path_prefix_len)[0] == '\0')
- return NULL;
-
- obj_path_only = os_strdup(path);
- if (obj_path_only == NULL)
- return NULL;
-
- next_sep = os_strchr(obj_path_only + dev_path_prefix_len, '/');
- if (next_sep != NULL) {
- const char *net_part = os_strstr(
- next_sep, WPAS_DBUS_NEW_NETWORKS_PART "/");
- const char *bssid_part = os_strstr(
- next_sep, WPAS_DBUS_NEW_BSSIDS_PART "/");
-
- if (network && net_part) {
- /* Deal with a request for a configured network */
- const char *net_name = net_part +
- os_strlen(WPAS_DBUS_NEW_NETWORKS_PART "/");
- *network = NULL;
- if (os_strlen(net_name))
- *network = os_strdup(net_name);
- } else if (bssid && bssid_part) {
- /* Deal with a request for a scanned BSSID */
- const char *bssid_name = bssid_part +
- os_strlen(WPAS_DBUS_NEW_BSSIDS_PART "/");
- if (strlen(bssid_name))
- *bssid = os_strdup(bssid_name);
- else
- *bssid = NULL;
- }
-
- /* Cut off interface object path before "/" */
- *next_sep = '\0';
- }
-
- return obj_path_only;
-}
-
-
-/**
* wpas_dbus_error_unknown_error - Return a new InvalidArgs error message
* @message: Pointer to incoming dbus message this error refers to
* @arg: Optional string appended to error message
DBusMessage * wpas_dbus_error_unknown_error(DBusMessage *message,
const char *arg)
{
+ /*
+ * This function can be called as a result of a failure
+ * within internal getter calls, which will call this function
+ * with a NULL message parameter. However, dbus_message_new_error
+ * looks very unkindly (i.e, abort()) on a NULL message, so
+ * in this case, we should not call it.
+ */
+ if (message == NULL) {
+ wpa_printf(MSG_INFO, "dbus: wpas_dbus_error_unknown_error "
+ "called with NULL message (arg=%s)",
+ arg ? arg : "N/A");
+ return NULL;
+ }
+
return dbus_message_new_error(message, WPAS_DBUS_ERROR_UNKNOWN_ERROR,
arg);
}
/**
* set_network_properties - Set properties of a configured network
- * @message: Pointer to incoming dbus message
* @wpa_s: wpa_supplicant structure for a network interface
* @ssid: wpa_ssid structure for a configured network
* @iter: DBus message iterator containing dictionary of network
* properties to set.
- * Returns: NULL when succeed or DBus error on failure
+ * @error: On failure, an error describing the failure
+ * Returns: TRUE if the request succeeds, FALSE if it failed
*
* Sets network configuration with parameters given id DBus dictionary
*/
-static DBusMessage * set_network_properties(DBusMessage *message,
- struct wpa_supplicant *wpa_s,
- struct wpa_ssid *ssid,
- DBusMessageIter *iter)
+dbus_bool_t set_network_properties(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid,
+ DBusMessageIter *iter,
+ DBusError *error)
{
-
struct wpa_dbus_dict_entry entry = { .type = DBUS_TYPE_STRING };
- DBusMessage *reply = NULL;
DBusMessageIter iter_dict;
+ char *value = NULL;
- if (!wpa_dbus_dict_open_read(iter, &iter_dict))
- return wpas_dbus_error_invalid_args(message, NULL);
+ if (!wpa_dbus_dict_open_read(iter, &iter_dict, error))
+ return FALSE;
while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
- char *value = NULL;
size_t size = 50;
int ret;
- if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) {
- reply = wpas_dbus_error_invalid_args(message, NULL);
- break;
- }
+
+ if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+ goto error;
+
+ value = NULL;
if (entry.type == DBUS_TYPE_ARRAY &&
entry.array_type == DBUS_TYPE_BYTE) {
if (entry.array_len <= 0)
os_free(value);
wpa_dbus_dict_entry_clear(&entry);
- continue;
-
- error:
- os_free(value);
- reply = wpas_dbus_error_invalid_args(message, entry.key);
- wpa_dbus_dict_entry_clear(&entry);
- break;
}
- return reply;
+ return TRUE;
+
+error:
+ os_free(value);
+ wpa_dbus_dict_entry_clear(&entry);
+ dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS,
+ "invalid message format");
+ return FALSE;
}
/**
* wpas_dbus_simple_property_getter - Get basic type property
- * @message: Pointer to incoming dbus message
+ * @iter: Message iter to use when appending arguments
* @type: DBus type of property (must be basic type)
* @val: pointer to place holding property value
- * Returns: The DBus message containing response for Properties.Get call
- * or DBus error message if error occurred.
+ * @error: On failure an error describing the failure
+ * Returns: TRUE if the request was successful, FALSE if it failed
*
* Generic getter for basic type properties. Type is required to be basic.
*/
-DBusMessage * wpas_dbus_simple_property_getter(DBusMessage *message,
- const int type, const void *val)
+dbus_bool_t wpas_dbus_simple_property_getter(DBusMessageIter *iter,
+ const int type,
+ const void *val,
+ DBusError *error)
{
- DBusMessage *reply = NULL;
- DBusMessageIter iter, variant_iter;
+ DBusMessageIter variant_iter;
if (!dbus_type_is_basic(type)) {
- wpa_printf(MSG_ERROR, "dbus: wpas_dbus_simple_property_getter:"
- " given type is not basic");
- return wpas_dbus_error_unknown_error(message, NULL);
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "%s: given type is not basic", __func__);
+ return FALSE;
}
- if (message == NULL)
- reply = dbus_message_new(DBUS_MESSAGE_TYPE_SIGNAL);
- else
- reply = dbus_message_new_method_return(message);
-
- if (reply != NULL) {
- dbus_message_iter_init_append(reply, &iter);
- if (!dbus_message_iter_open_container(
- &iter, DBUS_TYPE_VARIANT,
- wpa_dbus_type_as_string(type), &variant_iter) ||
- !dbus_message_iter_append_basic(&variant_iter, type,
- val) ||
- !dbus_message_iter_close_container(&iter, &variant_iter)) {
- wpa_printf(MSG_ERROR, "dbus: "
- "wpas_dbus_simple_property_getter: out of "
- "memory to put property value into "
- "message");
- dbus_message_unref(reply);
- reply = dbus_message_new_error(message,
- DBUS_ERROR_NO_MEMORY,
- NULL);
- }
- } else {
- wpa_printf(MSG_ERROR, "dbus: wpas_dbus_simple_property_getter:"
- " out of memory to return property value");
- reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
- NULL);
- }
+ if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+ wpa_dbus_type_as_string(type),
+ &variant_iter))
+ goto error;
- return reply;
+ if (!dbus_message_iter_append_basic(&variant_iter, type, val))
+ goto error;
+
+ if (!dbus_message_iter_close_container(iter, &variant_iter))
+ goto error;
+
+ return TRUE;
+
+error:
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "%s: error constructing reply", __func__);
+ return FALSE;
}
* @message: Pointer to incoming dbus message
* @type: DBus type of property (must be basic type)
* @val: pointer to place where value being set will be stored
- * Returns: NULL or DBus error message if error occurred.
+ * Returns: TRUE if the request was successful, FALSE if it failed
*
* Generic setter for basic type properties. Type is required to be basic.
*/
-DBusMessage * wpas_dbus_simple_property_setter(DBusMessage *message,
- const int type, void *val)
+dbus_bool_t wpas_dbus_simple_property_setter(DBusMessageIter *iter,
+ DBusError *error,
+ const int type, void *val)
{
- DBusMessageIter iter, variant_iter;
+ DBusMessageIter variant_iter;
if (!dbus_type_is_basic(type)) {
- wpa_printf(MSG_ERROR, "dbus: wpas_dbus_simple_property_setter:"
- " given type is not basic");
- return wpas_dbus_error_unknown_error(message, NULL);
- }
-
- if (!dbus_message_iter_init(message, &iter)) {
- wpa_printf(MSG_ERROR, "dbus: wpas_dbus_simple_property_setter:"
- " out of memory to return scanning state");
- return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
- NULL);
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "%s: given type is not basic", __func__);
+ return FALSE;
}
- /* omit first and second argument and get value from third */
- dbus_message_iter_next(&iter);
- dbus_message_iter_next(&iter);
- dbus_message_iter_recurse(&iter, &variant_iter);
-
+ /* Look at the new value */
+ dbus_message_iter_recurse(iter, &variant_iter);
if (dbus_message_iter_get_arg_type(&variant_iter) != type) {
- wpa_printf(MSG_DEBUG, "dbus: wpas_dbus_simple_property_setter:"
- " wrong property type");
- return wpas_dbus_error_invalid_args(message,
- "wrong property type");
+ dbus_set_error_const(error, DBUS_ERROR_FAILED,
+ "wrong property type");
+ return FALSE;
}
dbus_message_iter_get_basic(&variant_iter, val);
- return NULL;
+ return TRUE;
}
/**
* wpas_dbus_simple_array_property_getter - Get array type property
- * @message: Pointer to incoming dbus message
+ * @iter: Pointer to incoming dbus message iterator
* @type: DBus type of property array elements (must be basic type)
* @array: pointer to array of elements to put into response message
* @array_len: length of above array
- * Returns: The DBus message containing response for Properties.Get call
- * or DBus error message if error occurred.
+ * @error: a pointer to an error to fill on failure
+ * Returns: TRUE if the request succeeded, FALSE if it failed
*
* Generic getter for array type properties. Array elements type is
* required to be basic.
*/
-DBusMessage * wpas_dbus_simple_array_property_getter(DBusMessage *message,
- const int type,
- const void *array,
- size_t array_len)
+dbus_bool_t wpas_dbus_simple_array_property_getter(DBusMessageIter *iter,
+ const int type,
+ const void *array,
+ size_t array_len,
+ DBusError *error)
{
- DBusMessage *reply = NULL;
- DBusMessageIter iter, variant_iter, array_iter;
+ DBusMessageIter variant_iter, array_iter;
char type_str[] = "a?"; /* ? will be replaced with subtype letter; */
const char *sub_type_str;
size_t element_size, i;
if (!dbus_type_is_basic(type)) {
- wpa_printf(MSG_ERROR, "dbus: "
- "wpas_dbus_simple_array_property_getter: given "
- "type is not basic");
- return wpas_dbus_error_unknown_error(message, NULL);
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "%s: given type is not basic", __func__);
+ return FALSE;
}
sub_type_str = wpa_dbus_type_as_string(type);
type_str[1] = sub_type_str[0];
- if (message == NULL)
- reply = dbus_message_new(DBUS_MESSAGE_TYPE_SIGNAL);
- else
- reply = dbus_message_new_method_return(message);
- if (reply == NULL) {
- wpa_printf(MSG_ERROR, "dbus: "
- "wpas_dbus_simple_array_property_getter: out of "
- "memory to create return message");
- return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
- NULL);
+ if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+ type_str, &variant_iter)) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "%s: failed to construct message 1", __func__);
+ return FALSE;
}
- dbus_message_iter_init_append(reply, &iter);
-
- if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
- type_str, &variant_iter) ||
- !dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY,
+ if (!dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY,
sub_type_str, &array_iter)) {
- wpa_printf(MSG_ERROR, "dbus: "
- "wpas_dbus_simple_array_property_getter: out of "
- "memory to open container");
- dbus_message_unref(reply);
- return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
- NULL);
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "%s: failed to construct message 2", __func__);
+ return FALSE;
}
switch(type) {
element_size = sizeof(char *);
break;
default:
- wpa_printf(MSG_ERROR, "dbus: "
- "wpas_dbus_simple_array_property_getter: "
- "fatal: unknown element type");
- element_size = 1;
- break;
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "%s: unknown element type %d", __func__, type);
+ return FALSE;
}
for (i = 0; i < array_len; i++) {
array + i * element_size);
}
- if (!dbus_message_iter_close_container(&variant_iter, &array_iter) ||
- !dbus_message_iter_close_container(&iter, &variant_iter)) {
- wpa_printf(MSG_ERROR, "dbus: "
- "wpas_dbus_simple_array_property_getter: out of "
- "memory to close container");
- dbus_message_unref(reply);
- return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
- NULL);
+ if (!dbus_message_iter_close_container(&variant_iter, &array_iter)) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "%s: failed to construct message 3", __func__);
+ return FALSE;
}
- return reply;
+ if (!dbus_message_iter_close_container(iter, &variant_iter)) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "%s: failed to construct message 4", __func__);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/**
+ * wpas_dbus_simple_array_array_property_getter - Get array array type property
+ * @iter: Pointer to incoming dbus message iterator
+ * @type: DBus type of property array elements (must be basic type)
+ * @array: pointer to array of elements to put into response message
+ * @array_len: length of above array
+ * @error: a pointer to an error to fill on failure
+ * Returns: TRUE if the request succeeded, FALSE if it failed
+ *
+ * Generic getter for array type properties. Array elements type is
+ * required to be basic.
+ */
+dbus_bool_t wpas_dbus_simple_array_array_property_getter(DBusMessageIter *iter,
+ const int type,
+ struct wpabuf **array,
+ size_t array_len,
+ DBusError *error)
+{
+ DBusMessageIter variant_iter, array_iter;
+ char type_str[] = "aa?";
+ char inner_type_str[] = "a?";
+ const char *sub_type_str;
+ size_t i;
+
+ if (!dbus_type_is_basic(type)) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "%s: given type is not basic", __func__);
+ return FALSE;
+ }
+
+ sub_type_str = wpa_dbus_type_as_string(type);
+ type_str[2] = sub_type_str[0];
+ inner_type_str[1] = sub_type_str[0];
+
+ if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+ type_str, &variant_iter)) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "%s: failed to construct message 1", __func__);
+ return FALSE;
+ }
+ if (!dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY,
+ inner_type_str, &array_iter)) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "%s: failed to construct message 2", __func__);
+ return FALSE;
+ }
+
+ for (i = 0; i < array_len; i++) {
+ wpa_dbus_dict_bin_array_add_element(&array_iter,
+ wpabuf_head(array[i]),
+ wpabuf_len(array[i]));
+
+ }
+
+ if (!dbus_message_iter_close_container(&variant_iter, &array_iter)) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "%s: failed to close message 2", __func__);
+ return FALSE;
+ }
+
+ if (!dbus_message_iter_close_container(iter, &variant_iter)) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "%s: failed to close message 1", __func__);
+ return FALSE;
+ }
+
+ return TRUE;
}
struct wpa_dbus_dict_entry entry;
char *driver = NULL;
char *ifname = NULL;
+ char *confname = NULL;
char *bridge_ifname = NULL;
dbus_message_iter_init(message, &iter);
- if (!wpa_dbus_dict_open_read(&iter, &iter_dict))
+ if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
goto error;
while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
wpa_dbus_dict_entry_clear(&entry);
if (ifname == NULL)
goto error;
+ } else if (!strcmp(entry.key, "ConfigFile") &&
+ (entry.type == DBUS_TYPE_STRING)) {
+ confname = os_strdup(entry.str_value);
+ wpa_dbus_dict_entry_clear(&entry);
+ if (confname == NULL)
+ goto error;
} else if (!strcmp(entry.key, "BridgeIfname") &&
(entry.type == DBUS_TYPE_STRING)) {
bridge_ifname = os_strdup(entry.str_value);
os_memset(&iface, 0, sizeof(iface));
iface.driver = driver;
iface.ifname = ifname;
+ iface.confname = confname;
iface.bridge_ifname = bridge_ifname;
/* Otherwise, have wpa_supplicant attach to it. */
if ((wpa_s = wpa_supplicant_add_iface(global, &iface))) {
/**
* wpas_dbus_getter_debug_level - Get debug level
- * @message: Pointer to incoming dbus message
- * @global: %wpa_supplicant global data structure
- * Returns: DBus message with value of debug level
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "DebugLevel" property.
*/
-DBusMessage * wpas_dbus_getter_debug_level(DBusMessage *message,
- struct wpa_global *global)
+dbus_bool_t wpas_dbus_getter_debug_level(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
{
const char *str;
int idx = wpa_debug_level;
+
if (idx < 0)
idx = 0;
- if (idx > 4)
- idx = 4;
+ if (idx > 5)
+ idx = 5;
str = debug_strings[idx];
- return wpas_dbus_simple_property_getter(message, DBUS_TYPE_STRING,
- &str);
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
+ &str, error);
}
/**
* wpas_dbus_getter_debug_timestamp - Get debug timestamp
- * @message: Pointer to incoming dbus message
- * @global: %wpa_supplicant global data structure
- * Returns: DBus message with value of debug timestamp
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "DebugTimestamp" property.
*/
-DBusMessage * wpas_dbus_getter_debug_timestamp(DBusMessage *message,
- struct wpa_global *global)
+dbus_bool_t wpas_dbus_getter_debug_timestamp(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
{
- return wpas_dbus_simple_property_getter(message, DBUS_TYPE_BOOLEAN,
- &wpa_debug_timestamp);
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN,
+ &wpa_debug_timestamp, error);
}
/**
* wpas_dbus_getter_debug_show_keys - Get debug show keys
- * @message: Pointer to incoming dbus message
- * @global: %wpa_supplicant global data structure
- * Returns: DBus message with value of debug show_keys
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "DebugShowKeys" property.
*/
-DBusMessage * wpas_dbus_getter_debug_show_keys(DBusMessage *message,
- struct wpa_global *global)
+dbus_bool_t wpas_dbus_getter_debug_show_keys(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
{
- return wpas_dbus_simple_property_getter(message, DBUS_TYPE_BOOLEAN,
- &wpa_debug_show_keys);
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN,
+ &wpa_debug_show_keys, error);
}
/**
* wpas_dbus_setter_debug_level - Set debug level
- * @message: Pointer to incoming dbus message
- * @global: %wpa_supplicant global data structure
- * Returns: %NULL or DBus error message
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Setter for "DebugLevel" property.
*/
-DBusMessage * wpas_dbus_setter_debug_level(DBusMessage *message,
- struct wpa_global *global)
+dbus_bool_t wpas_dbus_setter_debug_level(DBusMessageIter *iter,
+ DBusError *error, void *user_data)
{
- DBusMessage *reply;
+ struct wpa_global *global = user_data;
const char *str = NULL;
int i, val = -1;
- reply = wpas_dbus_simple_property_setter(message, DBUS_TYPE_STRING,
- &str);
- if (reply)
- return reply;
+ if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_STRING,
+ &str))
+ return FALSE;
for (i = 0; debug_strings[i]; i++)
if (os_strcmp(debug_strings[i], str) == 0) {
if (val < 0 ||
wpa_supplicant_set_debug_params(global, val, wpa_debug_timestamp,
wpa_debug_show_keys)) {
- dbus_message_unref(reply);
- return wpas_dbus_error_invalid_args(
- message, "Wrong debug level value");
+ dbus_set_error_const(error, DBUS_ERROR_FAILED, "wrong debug "
+ "level value");
+ return FALSE;
}
- return NULL;
+ return TRUE;
}
/**
* wpas_dbus_setter_debug_timestamp - Set debug timestamp
- * @message: Pointer to incoming dbus message
- * @global: %wpa_supplicant global data structure
- * Returns: %NULL or DBus error message
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Setter for "DebugTimestamp" property.
*/
-DBusMessage * wpas_dbus_setter_debug_timestamp(DBusMessage *message,
- struct wpa_global *global)
+dbus_bool_t wpas_dbus_setter_debug_timestamp(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
{
- DBusMessage *reply;
+ struct wpa_global *global = user_data;
dbus_bool_t val;
- reply = wpas_dbus_simple_property_setter(message, DBUS_TYPE_BOOLEAN,
- &val);
- if (reply)
- return reply;
+ if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_BOOLEAN,
+ &val))
+ return FALSE;
wpa_supplicant_set_debug_params(global, wpa_debug_level, val ? 1 : 0,
wpa_debug_show_keys);
-
- return NULL;
+ return TRUE;
}
/**
* wpas_dbus_setter_debug_show_keys - Set debug show keys
- * @message: Pointer to incoming dbus message
- * @global: %wpa_supplicant global data structure
- * Returns: %NULL or DBus error message
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Setter for "DebugShowKeys" property.
*/
-DBusMessage * wpas_dbus_setter_debug_show_keys(DBusMessage *message,
- struct wpa_global *global)
+dbus_bool_t wpas_dbus_setter_debug_show_keys(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
{
- DBusMessage *reply;
+ struct wpa_global *global = user_data;
dbus_bool_t val;
- reply = wpas_dbus_simple_property_setter(message, DBUS_TYPE_BOOLEAN,
- &val);
- if (reply)
- return reply;
+ if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_BOOLEAN,
+ &val))
+ return FALSE;
wpa_supplicant_set_debug_params(global, wpa_debug_level,
wpa_debug_timestamp,
val ? 1 : 0);
-
- return NULL;
+ return TRUE;
}
/**
* wpas_dbus_getter_interfaces - Request registered interfaces list
- * @message: Pointer to incoming dbus message
- * @global: %wpa_supplicant global data structure
- * Returns: The object paths array containing registered interfaces
- * objects paths or DBus error on failure
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "Interfaces" property. Handles requests
* by dbus clients to return list of registered interfaces objects
* paths
*/
-DBusMessage * wpas_dbus_getter_interfaces(DBusMessage *message,
- struct wpa_global *global)
+dbus_bool_t wpas_dbus_getter_interfaces(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
{
- DBusMessage *reply = NULL;
+ struct wpa_global *global = user_data;
struct wpa_supplicant *wpa_s;
const char **paths;
unsigned int i = 0, num = 0;
+ dbus_bool_t success;
for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next)
num++;
paths = os_zalloc(num * sizeof(char*));
if (!paths) {
- return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
- NULL);
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+ return FALSE;
}
for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next)
- paths[i] = wpa_s->dbus_new_path;
+ paths[i++] = wpa_s->dbus_new_path;
- reply = wpas_dbus_simple_array_property_getter(message,
- DBUS_TYPE_OBJECT_PATH,
- paths, num);
+ success = wpas_dbus_simple_array_property_getter(iter,
+ DBUS_TYPE_OBJECT_PATH,
+ paths, num, error);
os_free(paths);
- return reply;
+ return success;
}
/**
* wpas_dbus_getter_eap_methods - Request supported EAP methods list
- * @message: Pointer to incoming dbus message
- * @nothing: not used argument. may be NULL or anything else
- * Returns: The object paths array containing supported EAP methods
- * represented by strings or DBus error on failure
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "EapMethods" property. Handles requests
* by dbus clients to return list of strings with supported EAP methods
*/
-DBusMessage * wpas_dbus_getter_eap_methods(DBusMessage *message, void *nothing)
+dbus_bool_t wpas_dbus_getter_eap_methods(DBusMessageIter *iter,
+ DBusError *error, void *user_data)
{
- DBusMessage *reply = NULL;
char **eap_methods;
size_t num_items = 0;
+ dbus_bool_t success;
eap_methods = eap_get_names_as_string_array(&num_items);
if (!eap_methods) {
- return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
- NULL);
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+ return FALSE;
}
- reply = wpas_dbus_simple_array_property_getter(message,
- DBUS_TYPE_STRING,
- eap_methods, num_items);
+ success = wpas_dbus_simple_array_property_getter(iter,
+ DBUS_TYPE_STRING,
+ eap_methods,
+ num_items, error);
while (num_items)
os_free(eap_methods[--num_items]);
os_free(eap_methods);
- return reply;
+ return success;
}
dbus_message_iter_recurse(&array_iter, &sub_array_iter);
dbus_message_iter_get_fixed_array(&sub_array_iter, &val, &len);
- if (len == 0) {
- dbus_message_iter_next(&array_iter);
- continue;
- }
- ssid = os_malloc(len);
- if (ssid == NULL) {
- wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: "
- "out of memory. Cannot allocate memory for "
- "SSID");
- *reply = dbus_message_new_error(
- message, DBUS_ERROR_NO_MEMORY, NULL);
+ if (len > MAX_SSID_LEN) {
+ wpa_printf(MSG_DEBUG,
+ "wpas_dbus_handler_scan[dbus]: "
+ "SSID too long (len=%d max_len=%d)",
+ len, MAX_SSID_LEN);
+ *reply = wpas_dbus_error_invalid_args(
+ message, "Invalid SSID: too long");
return -1;
}
- os_memcpy(ssid, val, len);
+
+ if (len != 0) {
+ ssid = os_malloc(len);
+ if (ssid == NULL) {
+ wpa_printf(MSG_DEBUG,
+ "wpas_dbus_handler_scan[dbus]: "
+ "out of memory. Cannot allocate "
+ "memory for SSID");
+ *reply = dbus_message_new_error(
+ message, DBUS_ERROR_NO_MEMORY, NULL);
+ return -1;
+ }
+ os_memcpy(ssid, val, len);
+ } else {
+ /* Allow zero-length SSIDs */
+ ssid = NULL;
+ }
+
ssids[ssids_num].ssid = ssid;
ssids[ssids_num].ssid_len = len;
"passive scan");
goto out;
} else if (params.freqs && params.freqs[0]) {
- /* wildcard ssid */
- params.num_ssids++;
wpa_supplicant_trigger_scan(wpa_s, ¶ms);
} else {
wpa_s->scan_req = 2;
wpa_supplicant_req_scan(wpa_s, 0, 0);
}
} else if (!os_strcmp(type, "active")) {
+ if (!params.num_ssids) {
+ /* Add wildcard ssid */
+ params.num_ssids++;
+ }
wpa_supplicant_trigger_scan(wpa_s, ¶ms);
} else {
wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: "
DBusMessageIter iter;
struct wpa_ssid *ssid = NULL;
char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *path = path_buf;
+ DBusError error;
dbus_message_iter_init(message, &iter);
ssid->disabled = 1;
wpa_config_set_network_defaults(ssid);
- reply = set_network_properties(message, wpa_s, ssid, &iter);
- if (reply) {
+ dbus_error_init(&error);
+ if (!set_network_properties(wpa_s, ssid, &iter, &error)) {
wpa_printf(MSG_DEBUG, "wpas_dbus_handler_add_network[dbus]:"
"control interface couldn't set network "
"properties");
+ reply = wpas_dbus_reply_new_from_error(message, &error,
+ DBUS_ERROR_INVALID_ARGS,
+ "Failed to add network");
+ dbus_error_free(&error);
goto err;
}
/* Extract the network ID and ensure the network */
/* is actually a child of this interface */
- iface = wpas_dbus_new_decompose_object_path(op, &net_id, NULL);
+ iface = wpas_dbus_new_decompose_object_path(op, 0, &net_id, NULL);
if (iface == NULL || os_strcmp(iface, wpa_s->dbus_new_path) != 0) {
reply = wpas_dbus_error_invalid_args(message, op);
goto out;
}
+static void remove_network(void *arg, struct wpa_ssid *ssid)
+{
+ struct wpa_supplicant *wpa_s = arg;
+
+ wpas_notify_network_removed(wpa_s, ssid);
+
+ if (wpa_config_remove_network(wpa_s->conf, ssid->id) < 0) {
+ wpa_printf(MSG_ERROR,
+ "wpas_dbus_handler_remove_all_networks[dbus]: "
+ "error occurred when removing network %d",
+ ssid->id);
+ return;
+ }
+
+ if (ssid == wpa_s->current_ssid)
+ wpa_supplicant_disassociate(wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+}
+
+
+/**
+ * wpas_dbus_handler_remove_all_networks - Remove all configured networks
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL on success or dbus error on failure
+ *
+ * Handler function for "RemoveAllNetworks" method call of a network interface.
+ */
+DBusMessage * wpas_dbus_handler_remove_all_networks(
+ DBusMessage *message, struct wpa_supplicant *wpa_s)
+{
+ /* NB: could check for failure and return an error */
+ wpa_config_foreach_network(wpa_s->conf, remove_network, wpa_s);
+ return NULL;
+}
+
+
/**
* wpas_dbus_handler_select_network - Attempt association with a network
* @message: Pointer to incoming dbus message
/* Extract the network ID and ensure the network */
/* is actually a child of this interface */
- iface = wpas_dbus_new_decompose_object_path(op, &net_id, NULL);
+ iface = wpas_dbus_new_decompose_object_path(op, 0, &net_id, NULL);
if (iface == NULL || os_strcmp(iface, wpa_s->dbus_new_path) != 0) {
reply = wpas_dbus_error_invalid_args(message, op);
goto out;
/**
+ * wpas_dbus_handler_network_reply - Reply to a NetworkRequest signal
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL on success or dbus error on failure
+ *
+ * Handler function for "NetworkReply" method call of network interface.
+ */
+DBusMessage * wpas_dbus_handler_network_reply(DBusMessage *message,
+ struct wpa_supplicant *wpa_s)
+{
+#ifdef IEEE8021X_EAPOL
+ DBusMessage *reply = NULL;
+ const char *op, *field, *value;
+ char *iface = NULL, *net_id = NULL;
+ int id;
+ struct wpa_ssid *ssid;
+
+ if (!dbus_message_get_args(message, NULL,
+ DBUS_TYPE_OBJECT_PATH, &op,
+ DBUS_TYPE_STRING, &field,
+ DBUS_TYPE_STRING, &value,
+ DBUS_TYPE_INVALID))
+ return wpas_dbus_error_invalid_args(message, NULL);
+
+ /* Extract the network ID and ensure the network */
+ /* is actually a child of this interface */
+ iface = wpas_dbus_new_decompose_object_path(op, 0, &net_id, NULL);
+ if (iface == NULL || os_strcmp(iface, wpa_s->dbus_new_path) != 0) {
+ reply = wpas_dbus_error_invalid_args(message, op);
+ goto out;
+ }
+
+ id = strtoul(net_id, NULL, 10);
+ if (errno == EINVAL) {
+ reply = wpas_dbus_error_invalid_args(message, net_id);
+ goto out;
+ }
+
+ ssid = wpa_config_get_network(wpa_s->conf, id);
+ if (ssid == NULL) {
+ reply = wpas_dbus_error_network_unknown(message);
+ goto out;
+ }
+
+ if (wpa_supplicant_ctrl_iface_ctrl_rsp_handle(wpa_s, ssid,
+ field, value) < 0)
+ reply = wpas_dbus_error_invalid_args(message, field);
+ else {
+ /* Tell EAP to retry immediately */
+ eapol_sm_notify_ctrl_response(wpa_s->eapol);
+ }
+
+out:
+ os_free(iface);
+ os_free(net_id);
+ return reply;
+#else /* IEEE8021X_EAPOL */
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: 802.1X not included");
+ return wpas_dbus_error_unknown_error(message, "802.1X not included");
+#endif /* IEEE8021X_EAPOL */
+}
+
+
+/**
* wpas_dbus_handler_add_blob - Store named binary blob (ie, for certificates)
* @message: Pointer to incoming dbus message
* @wpa_s: %wpa_supplicant data structure
}
-
-/**
- * wpas_dbus_getter_capabilities - Return interface capabilities
+/*
+ * wpas_dbus_handler_flush_bss - Flush the BSS cache
* @message: Pointer to incoming dbus message
* @wpa_s: wpa_supplicant structure for a network interface
- * Returns: A dbus message containing a dict of strings
+ * Returns: NULL
*
- * Getter for "Capabilities" property of an interface.
+ * Handler function for "FlushBSS" method call of network interface.
*/
-DBusMessage * wpas_dbus_getter_capabilities(DBusMessage *message,
- struct wpa_supplicant *wpa_s)
+DBusMessage * wpas_dbus_handler_flush_bss(DBusMessage *message,
+ struct wpa_supplicant *wpa_s)
{
- DBusMessage *reply = NULL;
- struct wpa_driver_capa capa;
- int res;
- DBusMessageIter iter, iter_dict;
- DBusMessageIter iter_dict_entry, iter_dict_val, iter_array,
- variant_iter;
- const char *scans[] = { "active", "passive", "ssid" };
- const char *modes[] = { "infrastructure", "ad-hoc", "ap" };
- int n = sizeof(modes) / sizeof(char *);
+ dbus_uint32_t age;
+
+ dbus_message_get_args(message, NULL, DBUS_TYPE_UINT32, &age,
+ DBUS_TYPE_INVALID);
- if (message == NULL)
- reply = dbus_message_new(DBUS_MESSAGE_TYPE_SIGNAL);
+ if (age == 0)
+ wpa_bss_flush(wpa_s);
else
- reply = dbus_message_new_method_return(message);
- if (!reply)
- goto nomem;
+ wpa_bss_flush_by_age(wpa_s, age);
- dbus_message_iter_init_append(reply, &iter);
- if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
- "a{sv}", &variant_iter))
+ return NULL;
+}
+
+
+/**
+ * wpas_dbus_getter_capabilities - Return interface capabilities
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "Capabilities" property of an interface.
+ */
+dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter,
+ DBusError *error, void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ struct wpa_driver_capa capa;
+ int res;
+ DBusMessageIter iter_dict, iter_dict_entry, iter_dict_val, iter_array,
+ variant_iter;
+ const char *scans[] = { "active", "passive", "ssid" };
+
+ if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+ "a{sv}", &variant_iter))
goto nomem;
if (!wpa_dbus_dict_open_write(&variant_iter, &iter_dict))
goto nomem;
/***** Modes */
- if (res < 0 || !(capa.flags & WPA_DRIVER_FLAGS_AP))
- n--; /* exclude ap mode if it is not supported by the driver */
- if (!wpa_dbus_dict_append_string_array(&iter_dict, "Modes", modes, n))
+ if (!wpa_dbus_dict_begin_string_array(&iter_dict, "Modes",
+ &iter_dict_entry,
+ &iter_dict_val,
+ &iter_array))
+ goto nomem;
+
+ if (!wpa_dbus_dict_string_array_add_element(
+ &iter_array, "infrastructure"))
+ goto nomem;
+
+ if (!wpa_dbus_dict_string_array_add_element(
+ &iter_array, "ad-hoc"))
goto nomem;
+ if (res >= 0) {
+ if (capa.flags & (WPA_DRIVER_FLAGS_AP)) {
+ if (!wpa_dbus_dict_string_array_add_element(
+ &iter_array, "ap"))
+ goto nomem;
+ }
+
+ if (capa.flags & (WPA_DRIVER_FLAGS_P2P_CAPABLE)) {
+ if (!wpa_dbus_dict_string_array_add_element(
+ &iter_array, "p2p"))
+ goto nomem;
+ }
+ }
+
+ if (!wpa_dbus_dict_end_string_array(&iter_dict,
+ &iter_dict_entry,
+ &iter_dict_val,
+ &iter_array))
+ goto nomem;
+ /***** Modes end */
+
+ if (res >= 0) {
+ dbus_int32_t max_scan_ssid = capa.max_scan_ssids;
+
+ if (!wpa_dbus_dict_append_int32(&iter_dict, "MaxScanSSID",
+ max_scan_ssid))
+ goto nomem;
+ }
+
if (!wpa_dbus_dict_close_write(&variant_iter, &iter_dict))
goto nomem;
- if (!dbus_message_iter_close_container(&iter, &variant_iter))
+ if (!dbus_message_iter_close_container(iter, &variant_iter))
goto nomem;
- return reply;
+ return TRUE;
nomem:
- if (reply)
- dbus_message_unref(reply);
-
- return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, NULL);
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+ return FALSE;
}
/**
* wpas_dbus_getter_state - Get interface state
- * @message: Pointer to incoming dbus message
- * @wpa_s: wpa_supplicant structure for a network interface
- * Returns: A dbus message containing a STRING representing the current
- * interface state
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "State" property.
*/
-DBusMessage * wpas_dbus_getter_state(DBusMessage *message,
- struct wpa_supplicant *wpa_s)
+dbus_bool_t wpas_dbus_getter_state(DBusMessageIter *iter, DBusError *error,
+ void *user_data)
{
- DBusMessage *reply = NULL;
+ struct wpa_supplicant *wpa_s = user_data;
const char *str_state;
char *state_ls, *tmp;
+ dbus_bool_t success = FALSE;
str_state = wpa_supplicant_state_txt(wpa_s->wpa_state);
*/
state_ls = tmp = os_strdup(str_state);
if (!tmp) {
- return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
- NULL);
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+ return FALSE;
}
while (*tmp) {
*tmp = tolower(*tmp);
tmp++;
}
- reply = wpas_dbus_simple_property_getter(message, DBUS_TYPE_STRING,
- &state_ls);
+ success = wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
+ &state_ls, error);
os_free(state_ls);
- return reply;
+ return success;
}
/**
* wpas_dbus_new_iface_get_scanning - Get interface scanning state
- * @message: Pointer to incoming dbus message
- * @wpa_s: wpa_supplicant structure for a network interface
- * Returns: A dbus message containing whether the interface is scanning
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "scanning" property.
*/
-DBusMessage * wpas_dbus_getter_scanning(DBusMessage *message,
- struct wpa_supplicant *wpa_s)
+dbus_bool_t wpas_dbus_getter_scanning(DBusMessageIter *iter, DBusError *error,
+ void *user_data)
{
+ struct wpa_supplicant *wpa_s = user_data;
dbus_bool_t scanning = wpa_s->scanning ? TRUE : FALSE;
- return wpas_dbus_simple_property_getter(message, DBUS_TYPE_BOOLEAN,
- &scanning);
+
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN,
+ &scanning, error);
}
/**
* wpas_dbus_getter_ap_scan - Control roaming mode
- * @message: Pointer to incoming dbus message
- * @wpa_s: wpa_supplicant structure for a network interface
- * Returns: A message containong value of ap_scan variable
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter function for "ApScan" property.
*/
-DBusMessage * wpas_dbus_getter_ap_scan(DBusMessage *message,
- struct wpa_supplicant *wpa_s)
+dbus_bool_t wpas_dbus_getter_ap_scan(DBusMessageIter *iter, DBusError *error,
+ void *user_data)
{
+ struct wpa_supplicant *wpa_s = user_data;
dbus_uint32_t ap_scan = wpa_s->conf->ap_scan;
- return wpas_dbus_simple_property_getter(message, DBUS_TYPE_UINT32,
- &ap_scan);
+
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT32,
+ &ap_scan, error);
}
/**
* wpas_dbus_setter_ap_scan - Control roaming mode
- * @message: Pointer to incoming dbus message
- * @wpa_s: wpa_supplicant structure for a network interface
- * Returns: NULL
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Setter function for "ApScan" property.
*/
-DBusMessage * wpas_dbus_setter_ap_scan(DBusMessage *message,
- struct wpa_supplicant *wpa_s)
+dbus_bool_t wpas_dbus_setter_ap_scan(DBusMessageIter *iter, DBusError *error,
+ void *user_data)
{
- DBusMessage *reply = NULL;
+ struct wpa_supplicant *wpa_s = user_data;
dbus_uint32_t ap_scan;
- reply = wpas_dbus_simple_property_setter(message, DBUS_TYPE_UINT32,
- &ap_scan);
- if (reply)
- return reply;
+ if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_UINT32,
+ &ap_scan))
+ return FALSE;
if (wpa_supplicant_set_ap_scan(wpa_s, ap_scan)) {
- return wpas_dbus_error_invalid_args(
- message, "ap_scan must equal 0, 1 or 2");
+ dbus_set_error_const(error, DBUS_ERROR_FAILED,
+ "ap_scan must be 0, 1, or 2");
+ return FALSE;
}
- return NULL;
+ return TRUE;
+}
+
+
+/**
+ * wpas_dbus_getter_fast_reauth - Control fast
+ * reauthentication (TLS session resumption)
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter function for "FastReauth" property.
+ */
+dbus_bool_t wpas_dbus_getter_fast_reauth(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ dbus_bool_t fast_reauth = wpa_s->conf->fast_reauth ? TRUE : FALSE;
+
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN,
+ &fast_reauth, error);
+}
+
+
+/**
+ * wpas_dbus_setter_fast_reauth - Control fast
+ * reauthentication (TLS session resumption)
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Setter function for "FastReauth" property.
+ */
+dbus_bool_t wpas_dbus_setter_fast_reauth(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ dbus_bool_t fast_reauth;
+
+ if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_BOOLEAN,
+ &fast_reauth))
+ return FALSE;
+
+ wpa_s->conf->fast_reauth = fast_reauth;
+ return TRUE;
+}
+
+
+/**
+ * wpas_dbus_getter_bss_expire_age - Get BSS entry expiration age
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter function for "BSSExpireAge" property.
+ */
+dbus_bool_t wpas_dbus_getter_bss_expire_age(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ dbus_uint32_t expire_age = wpa_s->conf->bss_expiration_age;
+
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT32,
+ &expire_age, error);
+}
+
+
+/**
+ * wpas_dbus_setter_bss_expire_age - Control BSS entry expiration age
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Setter function for "BSSExpireAge" property.
+ */
+dbus_bool_t wpas_dbus_setter_bss_expire_age(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ dbus_uint32_t expire_age;
+
+ if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_UINT32,
+ &expire_age))
+ return FALSE;
+
+ if (wpa_supplicant_set_bss_expiration_age(wpa_s, expire_age)) {
+ dbus_set_error_const(error, DBUS_ERROR_FAILED,
+ "BSSExpireAge must be >= 10");
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+/**
+ * wpas_dbus_getter_bss_expire_count - Get BSS entry expiration scan count
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter function for "BSSExpireCount" property.
+ */
+dbus_bool_t wpas_dbus_getter_bss_expire_count(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ dbus_uint32_t expire_count = wpa_s->conf->bss_expiration_age;
+
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT32,
+ &expire_count, error);
+}
+
+
+/**
+ * wpas_dbus_setter_bss_expire_count - Control BSS entry expiration scan count
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Setter function for "BSSExpireCount" property.
+ */
+dbus_bool_t wpas_dbus_setter_bss_expire_count(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ dbus_uint32_t expire_count;
+
+ if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_UINT32,
+ &expire_count))
+ return FALSE;
+
+ if (wpa_supplicant_set_bss_expiration_count(wpa_s, expire_count)) {
+ dbus_set_error_const(error, DBUS_ERROR_FAILED,
+ "BSSExpireCount must be > 0");
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+/**
+ * wpas_dbus_getter_country - Control country code
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter function for "Country" property.
+ */
+dbus_bool_t wpas_dbus_getter_country(DBusMessageIter *iter, DBusError *error,
+ void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ char country[3];
+ char *str = country;
+
+ country[0] = wpa_s->conf->country[0];
+ country[1] = wpa_s->conf->country[1];
+ country[2] = '\0';
+
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
+ &str, error);
+}
+
+
+/**
+ * wpas_dbus_setter_country - Control country code
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Setter function for "Country" property.
+ */
+dbus_bool_t wpas_dbus_setter_country(DBusMessageIter *iter, DBusError *error,
+ void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ const char *country;
+
+ if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_STRING,
+ &country))
+ return FALSE;
+
+ if (!country[0] || !country[1]) {
+ dbus_set_error_const(error, DBUS_ERROR_FAILED,
+ "invalid country code");
+ return FALSE;
+ }
+
+ if (wpa_s->drv_priv != NULL && wpa_drv_set_country(wpa_s, country)) {
+ wpa_printf(MSG_DEBUG, "Failed to set country");
+ dbus_set_error_const(error, DBUS_ERROR_FAILED,
+ "failed to set country code");
+ return FALSE;
+ }
+
+ wpa_s->conf->country[0] = country[0];
+ wpa_s->conf->country[1] = country[1];
+ return TRUE;
}
/**
* wpas_dbus_getter_ifname - Get interface name
- * @message: Pointer to incoming dbus message
- * @wpa_s: wpa_supplicant structure for a network interface
- * Returns: A dbus message containing a name of network interface
- * associated with with wpa_s
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "Ifname" property.
*/
-DBusMessage * wpas_dbus_getter_ifname(DBusMessage *message,
- struct wpa_supplicant *wpa_s)
+dbus_bool_t wpas_dbus_getter_ifname(DBusMessageIter *iter, DBusError *error,
+ void *user_data)
{
+ struct wpa_supplicant *wpa_s = user_data;
const char *ifname = wpa_s->ifname;
- return wpas_dbus_simple_property_getter(message, DBUS_TYPE_STRING,
- &ifname);
+
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
+ &ifname, error);
}
/**
* wpas_dbus_getter_driver - Get interface name
- * @message: Pointer to incoming dbus message
- * @wpa_s: wpa_supplicant structure for a network interface
- * Returns: A dbus message containing a name of network interface
- * driver associated with with wpa_s
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "Driver" property.
*/
-DBusMessage * wpas_dbus_getter_driver(DBusMessage *message,
- struct wpa_supplicant *wpa_s)
+dbus_bool_t wpas_dbus_getter_driver(DBusMessageIter *iter, DBusError *error,
+ void *user_data)
{
+ struct wpa_supplicant *wpa_s = user_data;
const char *driver;
if (wpa_s->driver == NULL || wpa_s->driver->name == NULL) {
wpa_printf(MSG_DEBUG, "wpas_dbus_getter_driver[dbus]: "
"wpa_s has no driver set");
- return wpas_dbus_error_unknown_error(message, NULL);
+ dbus_set_error(error, DBUS_ERROR_FAILED, "%s: no driver set",
+ __func__);
+ return FALSE;
}
driver = wpa_s->driver->name;
- return wpas_dbus_simple_property_getter(message, DBUS_TYPE_STRING,
- &driver);
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
+ &driver, error);
}
/**
* wpas_dbus_getter_current_bss - Get current bss object path
- * @message: Pointer to incoming dbus message
- * @wpa_s: wpa_supplicant structure for a network interface
- * Returns: A dbus message containing a DBus object path to
- * current BSS
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "CurrentBSS" property.
*/
-DBusMessage * wpas_dbus_getter_current_bss(DBusMessage *message,
- struct wpa_supplicant *wpa_s)
+dbus_bool_t wpas_dbus_getter_current_bss(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
{
- DBusMessage *reply;
+ struct wpa_supplicant *wpa_s = user_data;
char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *bss_obj_path = path_buf;
if (wpa_s->current_bss)
else
os_snprintf(bss_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "/");
- reply = wpas_dbus_simple_property_getter(message,
- DBUS_TYPE_OBJECT_PATH,
- &bss_obj_path);
-
- return reply;
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_OBJECT_PATH,
+ &bss_obj_path, error);
}
/**
* wpas_dbus_getter_current_network - Get current network object path
- * @message: Pointer to incoming dbus message
- * @wpa_s: wpa_supplicant structure for a network interface
- * Returns: A dbus message containing a DBus object path to
- * current network
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "CurrentNetwork" property.
*/
-DBusMessage * wpas_dbus_getter_current_network(DBusMessage *message,
- struct wpa_supplicant *wpa_s)
+dbus_bool_t wpas_dbus_getter_current_network(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
{
- DBusMessage *reply;
+ struct wpa_supplicant *wpa_s = user_data;
char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *net_obj_path = path_buf;
if (wpa_s->current_ssid)
else
os_snprintf(net_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "/");
- reply = wpas_dbus_simple_property_getter(message,
- DBUS_TYPE_OBJECT_PATH,
- &net_obj_path);
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_OBJECT_PATH,
+ &net_obj_path, error);
+}
- return reply;
+
+/**
+ * wpas_dbus_getter_current_auth_mode - Get current authentication type
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "CurrentAuthMode" property.
+ */
+dbus_bool_t wpas_dbus_getter_current_auth_mode(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ const char *eap_mode;
+ const char *auth_mode;
+ char eap_mode_buf[WPAS_DBUS_AUTH_MODE_MAX];
+
+ if (wpa_s->wpa_state != WPA_COMPLETED) {
+ auth_mode = "INACTIVE";
+ } else if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X ||
+ wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
+ eap_mode = wpa_supplicant_get_eap_mode(wpa_s);
+ os_snprintf(eap_mode_buf, WPAS_DBUS_AUTH_MODE_MAX,
+ "EAP-%s", eap_mode);
+ auth_mode = eap_mode_buf;
+
+ } else {
+ auth_mode = wpa_key_mgmt_txt(wpa_s->key_mgmt,
+ wpa_s->current_ssid->proto);
+ }
+
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
+ &auth_mode, error);
}
/**
* wpas_dbus_getter_bridge_ifname - Get interface name
- * @message: Pointer to incoming dbus message
- * @wpa_s: wpa_supplicant structure for a network interface
- * Returns: A dbus message containing a name of bridge network
- * interface associated with with wpa_s
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "BridgeIfname" property.
*/
-DBusMessage * wpas_dbus_getter_bridge_ifname(DBusMessage *message,
- struct wpa_supplicant *wpa_s)
+dbus_bool_t wpas_dbus_getter_bridge_ifname(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
{
- const char *bridge_ifname = NULL;
+ struct wpa_supplicant *wpa_s = user_data;
+ const char *bridge_ifname;
- bridge_ifname = wpa_s->bridge_ifname;
- if (bridge_ifname == NULL) {
- wpa_printf(MSG_ERROR, "wpas_dbus_getter_bridge_ifname[dbus]: "
- "wpa_s has no bridge interface name set");
- return wpas_dbus_error_unknown_error(message, NULL);
- }
-
- return wpas_dbus_simple_property_getter(message, DBUS_TYPE_STRING,
- &bridge_ifname);
+ bridge_ifname = wpa_s->bridge_ifname ? wpa_s->bridge_ifname : "";
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
+ &bridge_ifname, error);
}
/**
* wpas_dbus_getter_bsss - Get array of BSSs objects
- * @message: Pointer to incoming dbus message
- * @wpa_s: wpa_supplicant structure for a network interface
- * Returns: a dbus message containing an array of all known BSS objects
- * dbus paths
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "BSSs" property.
*/
-DBusMessage * wpas_dbus_getter_bsss(DBusMessage *message,
- struct wpa_supplicant *wpa_s)
+dbus_bool_t wpas_dbus_getter_bsss(DBusMessageIter *iter, DBusError *error,
+ void *user_data)
{
- DBusMessage *reply = NULL;
+ struct wpa_supplicant *wpa_s = user_data;
struct wpa_bss *bss;
char **paths;
unsigned int i = 0;
+ dbus_bool_t success = FALSE;
paths = os_zalloc(wpa_s->num_bss * sizeof(char *));
if (!paths) {
- return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
- NULL);
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+ return FALSE;
}
/* Loop through scan results and append each result's object path */
dl_list_for_each(bss, &wpa_s->bss_id, struct wpa_bss, list_id) {
paths[i] = os_zalloc(WPAS_DBUS_OBJECT_PATH_MAX);
if (paths[i] == NULL) {
- reply = dbus_message_new_error(message,
- DBUS_ERROR_NO_MEMORY,
- NULL);
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY,
+ "no memory");
goto out;
}
/* Construct the object path for this BSS. */
wpa_s->dbus_new_path, bss->id);
}
- reply = wpas_dbus_simple_array_property_getter(message,
- DBUS_TYPE_OBJECT_PATH,
- paths, wpa_s->num_bss);
+ success = wpas_dbus_simple_array_property_getter(iter,
+ DBUS_TYPE_OBJECT_PATH,
+ paths, wpa_s->num_bss,
+ error);
out:
while (i)
os_free(paths[--i]);
os_free(paths);
- return reply;
+ return success;
}
/**
* wpas_dbus_getter_networks - Get array of networks objects
- * @message: Pointer to incoming dbus message
- * @wpa_s: wpa_supplicant structure for a network interface
- * Returns: a dbus message containing an array of all configured
- * networks dbus object paths.
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "Networks" property.
*/
-DBusMessage * wpas_dbus_getter_networks(DBusMessage *message,
- struct wpa_supplicant *wpa_s)
+dbus_bool_t wpas_dbus_getter_networks(DBusMessageIter *iter, DBusError *error,
+ void *user_data)
{
- DBusMessage *reply = NULL;
+ struct wpa_supplicant *wpa_s = user_data;
struct wpa_ssid *ssid;
char **paths;
unsigned int i = 0, num = 0;
+ dbus_bool_t success = FALSE;
if (wpa_s->conf == NULL) {
- wpa_printf(MSG_ERROR, "wpas_dbus_getter_networks[dbus]: "
- "An error occurred getting networks list.");
- return wpas_dbus_error_unknown_error(message, NULL);
+ wpa_printf(MSG_ERROR, "%s[dbus]: An error occurred getting "
+ "networks list.", __func__);
+ dbus_set_error(error, DBUS_ERROR_FAILED, "%s: an error "
+ "occurred getting the networks list", __func__);
+ return FALSE;
}
for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next)
- num++;
+ if (!network_is_persistent_group(ssid))
+ num++;
paths = os_zalloc(num * sizeof(char *));
if (!paths) {
- return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
- NULL);
+ dbus_set_error(error, DBUS_ERROR_NO_MEMORY, "no memory");
+ return FALSE;
}
/* Loop through configured networks and append object path of each */
for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+ if (network_is_persistent_group(ssid))
+ continue;
paths[i] = os_zalloc(WPAS_DBUS_OBJECT_PATH_MAX);
if (paths[i] == NULL) {
- reply = dbus_message_new_error(message,
- DBUS_ERROR_NO_MEMORY,
- NULL);
+ dbus_set_error(error, DBUS_ERROR_NO_MEMORY, "no memory");
goto out;
}
wpa_s->dbus_new_path, ssid->id);
}
- reply = wpas_dbus_simple_array_property_getter(message,
- DBUS_TYPE_OBJECT_PATH,
- paths, num);
+ success = wpas_dbus_simple_array_property_getter(iter,
+ DBUS_TYPE_OBJECT_PATH,
+ paths, num, error);
out:
while (i)
os_free(paths[--i]);
os_free(paths);
- return reply;
+ return success;
}
/**
* wpas_dbus_getter_blobs - Get all blobs defined for this interface
- * @message: Pointer to incoming dbus message
- * @wpa_s: wpa_supplicant structure for a network interface
- * Returns: a dbus message containing a dictionary of pairs (blob_name, blob)
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "Blobs" property.
*/
-DBusMessage * wpas_dbus_getter_blobs(DBusMessage *message,
- struct wpa_supplicant *wpa_s)
+dbus_bool_t wpas_dbus_getter_blobs(DBusMessageIter *iter, DBusError *error,
+ void *user_data)
{
- DBusMessage *reply = NULL;
- DBusMessageIter iter, variant_iter, dict_iter, entry_iter, array_iter;
+ struct wpa_supplicant *wpa_s = user_data;
+ DBusMessageIter variant_iter, dict_iter, entry_iter, array_iter;
struct wpa_config_blob *blob;
- if (message == NULL)
- reply = dbus_message_new(DBUS_MESSAGE_TYPE_SIGNAL);
- else
- reply = dbus_message_new_method_return(message);
- if (!reply)
- return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
- NULL);
-
- dbus_message_iter_init_append(reply, &iter);
-
- if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
+ if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
"a{say}", &variant_iter) ||
!dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY,
"{say}", &dict_iter)) {
- dbus_message_unref(reply);
- return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
- NULL);
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+ return FALSE;
}
blob = wpa_s->conf->blobs;
&array_iter) ||
!dbus_message_iter_close_container(&dict_iter,
&entry_iter)) {
- dbus_message_unref(reply);
- return dbus_message_new_error(message,
- DBUS_ERROR_NO_MEMORY,
- NULL);
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY,
+ "no memory");
+ return FALSE;
}
blob = blob->next;
}
if (!dbus_message_iter_close_container(&variant_iter, &dict_iter) ||
- !dbus_message_iter_close_container(&iter, &variant_iter)) {
- dbus_message_unref(reply);
- return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
- NULL);
+ !dbus_message_iter_close_container(iter, &variant_iter)) {
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+ return FALSE;
}
- return reply;
+ return TRUE;
+}
+
+
+static struct wpa_bss * get_bss_helper(struct bss_handler_args *args,
+ DBusError *error, const char *func_name)
+{
+ struct wpa_bss *res = wpa_bss_get_id(args->wpa_s, args->id);
+
+ if (!res) {
+ wpa_printf(MSG_ERROR, "%s[dbus]: no bss with id %d found",
+ func_name, args->id);
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "%s: BSS %d not found",
+ func_name, args->id);
+ }
+
+ return res;
}
/**
* wpas_dbus_getter_bss_bssid - Return the BSSID of a BSS
- * @message: Pointer to incoming dbus message
- * @bss: a pair of interface describing structure and bss's id
- * Returns: a dbus message containing the bssid for the requested bss
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "BSSID" property.
*/
-DBusMessage * wpas_dbus_getter_bss_bssid(DBusMessage *message,
- struct bss_handler_args *bss)
+dbus_bool_t wpas_dbus_getter_bss_bssid(DBusMessageIter *iter, DBusError *error,
+ void *user_data)
{
- struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id);
+ struct bss_handler_args *args = user_data;
+ struct wpa_bss *res;
- if (!res) {
- wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_bssid[dbus]: no "
- "bss with id %d found", bss->id);
- return NULL;
- }
+ res = get_bss_helper(args, error, __func__);
+ if (!res)
+ return FALSE;
- return wpas_dbus_simple_array_property_getter(message, DBUS_TYPE_BYTE,
- res->bssid, ETH_ALEN);
+ return wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE,
+ res->bssid, ETH_ALEN,
+ error);
}
/**
* wpas_dbus_getter_bss_ssid - Return the SSID of a BSS
- * @message: Pointer to incoming dbus message
- * @bss: a pair of interface describing structure and bss's id
- * Returns: a dbus message containing the ssid for the requested bss
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "SSID" property.
*/
-DBusMessage * wpas_dbus_getter_bss_ssid(DBusMessage *message,
- struct bss_handler_args *bss)
+dbus_bool_t wpas_dbus_getter_bss_ssid(DBusMessageIter *iter, DBusError *error,
+ void *user_data)
{
- struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id);
+ struct bss_handler_args *args = user_data;
+ struct wpa_bss *res;
- if (!res) {
- wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_ssid[dbus]: no "
- "bss with id %d found", bss->id);
- return NULL;
- }
+ res = get_bss_helper(args, error, __func__);
+ if (!res)
+ return FALSE;
- return wpas_dbus_simple_array_property_getter(message, DBUS_TYPE_BYTE,
- res->ssid,
- res->ssid_len);
+ return wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE,
+ res->ssid, res->ssid_len,
+ error);
}
/**
* wpas_dbus_getter_bss_privacy - Return the privacy flag of a BSS
- * @message: Pointer to incoming dbus message
- * @bss: a pair of interface describing structure and bss's id
- * Returns: a dbus message containing the privacy flag value of requested bss
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "Privacy" property.
*/
-DBusMessage * wpas_dbus_getter_bss_privacy(DBusMessage *message,
- struct bss_handler_args *bss)
+dbus_bool_t wpas_dbus_getter_bss_privacy(DBusMessageIter *iter,
+ DBusError *error, void *user_data)
{
- struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id);
+ struct bss_handler_args *args = user_data;
+ struct wpa_bss *res;
dbus_bool_t privacy;
- if (!res) {
- wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_privacy[dbus]: no "
- "bss with id %d found", bss->id);
- return NULL;
- }
+ res = get_bss_helper(args, error, __func__);
+ if (!res)
+ return FALSE;
privacy = (res->caps & IEEE80211_CAP_PRIVACY) ? TRUE : FALSE;
- return wpas_dbus_simple_property_getter(message, DBUS_TYPE_BOOLEAN,
- &privacy);
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN,
+ &privacy, error);
}
/**
* wpas_dbus_getter_bss_mode - Return the mode of a BSS
- * @message: Pointer to incoming dbus message
- * @bss: a pair of interface describing structure and bss's id
- * Returns: a dbus message containing the mode of requested bss
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "Mode" property.
*/
-DBusMessage * wpas_dbus_getter_bss_mode(DBusMessage *message,
- struct bss_handler_args *bss)
+dbus_bool_t wpas_dbus_getter_bss_mode(DBusMessageIter *iter, DBusError *error,
+ void *user_data)
{
- struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id);
+ struct bss_handler_args *args = user_data;
+ struct wpa_bss *res;
const char *mode;
- if (!res) {
- wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_mode[dbus]: no "
- "bss with id %d found", bss->id);
- return NULL;
- }
+ res = get_bss_helper(args, error, __func__);
+ if (!res)
+ return FALSE;
if (res->caps & IEEE80211_CAP_IBSS)
mode = "ad-hoc";
else
mode = "infrastructure";
- return wpas_dbus_simple_property_getter(message, DBUS_TYPE_STRING,
- &mode);
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
+ &mode, error);
}
/**
* wpas_dbus_getter_bss_level - Return the signal strength of a BSS
- * @message: Pointer to incoming dbus message
- * @bss: a pair of interface describing structure and bss's id
- * Returns: a dbus message containing the signal strength of requested bss
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "Level" property.
*/
-DBusMessage * wpas_dbus_getter_bss_signal(DBusMessage *message,
- struct bss_handler_args *bss)
+dbus_bool_t wpas_dbus_getter_bss_signal(DBusMessageIter *iter,
+ DBusError *error, void *user_data)
{
- struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id);
+ struct bss_handler_args *args = user_data;
+ struct wpa_bss *res;
+ s16 level;
- if (!res) {
- wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_signal[dbus]: no "
- "bss with id %d found", bss->id);
- return NULL;
- }
+ res = get_bss_helper(args, error, __func__);
+ if (!res)
+ return FALSE;
- return wpas_dbus_simple_property_getter(message, DBUS_TYPE_INT16,
- &res->level);
+ level = (s16) res->level;
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_INT16,
+ &level, error);
}
/**
* wpas_dbus_getter_bss_frequency - Return the frequency of a BSS
- * @message: Pointer to incoming dbus message
- * @bss: a pair of interface describing structure and bss's id
- * Returns: a dbus message containing the frequency of requested bss
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "Frequency" property.
*/
-DBusMessage * wpas_dbus_getter_bss_frequency(DBusMessage *message,
- struct bss_handler_args *bss)
+dbus_bool_t wpas_dbus_getter_bss_frequency(DBusMessageIter *iter,
+ DBusError *error, void *user_data)
{
- struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id);
+ struct bss_handler_args *args = user_data;
+ struct wpa_bss *res;
+ u16 freq;
- if (!res) {
- wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_frequency[dbus]: "
- "no bss with id %d found", bss->id);
- return NULL;
- }
+ res = get_bss_helper(args, error, __func__);
+ if (!res)
+ return FALSE;
- return wpas_dbus_simple_property_getter(message, DBUS_TYPE_UINT16,
- &res->freq);
+ freq = (u16) res->freq;
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT16,
+ &freq, error);
}
/**
* wpas_dbus_getter_bss_rates - Return available bit rates of a BSS
- * @message: Pointer to incoming dbus message
- * @bss: a pair of interface describing structure and bss's id
- * Returns: a dbus message containing sorted array of bit rates
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "Rates" property.
*/
-DBusMessage * wpas_dbus_getter_bss_rates(DBusMessage *message,
- struct bss_handler_args *bss)
+dbus_bool_t wpas_dbus_getter_bss_rates(DBusMessageIter *iter,
+ DBusError *error, void *user_data)
{
- DBusMessage *reply;
- struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id);
+ struct bss_handler_args *args = user_data;
+ struct wpa_bss *res;
u8 *ie_rates = NULL;
u32 *real_rates;
int rates_num, i;
+ dbus_bool_t success = FALSE;
- if (!res) {
- wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_rates[dbus]: "
- "no bss with id %d found", bss->id);
- return NULL;
- }
+ res = get_bss_helper(args, error, __func__);
+ if (!res)
+ return FALSE;
rates_num = wpa_bss_get_bit_rates(res, &ie_rates);
if (rates_num < 0)
- return NULL;
+ return FALSE;
qsort(ie_rates, rates_num, 1, cmp_u8s_desc);
real_rates = os_malloc(sizeof(u32) * rates_num);
if (!real_rates) {
os_free(ie_rates);
- return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
- NULL);
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+ return FALSE;
}
for (i = 0; i < rates_num; i++)
real_rates[i] = ie_rates[i] * 500000;
- reply = wpas_dbus_simple_array_property_getter(message,
- DBUS_TYPE_UINT32,
- real_rates, rates_num);
+ success = wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_UINT32,
+ real_rates, rates_num,
+ error);
os_free(ie_rates);
os_free(real_rates);
- return reply;
+ return success;
}
-static DBusMessage * wpas_dbus_get_bss_security_prop(
- DBusMessage *message, struct wpa_ie_data *ie_data)
+static dbus_bool_t wpas_dbus_get_bss_security_prop(DBusMessageIter *iter,
+ struct wpa_ie_data *ie_data,
+ DBusError *error)
{
- DBusMessage *reply;
- DBusMessageIter iter, iter_dict, variant_iter;
+ DBusMessageIter iter_dict, variant_iter;
const char *group;
const char *pairwise[2]; /* max 2 pairwise ciphers is supported */
const char *key_mgmt[7]; /* max 7 key managements may be supported */
int n;
- if (message == NULL)
- reply = dbus_message_new(DBUS_MESSAGE_TYPE_SIGNAL);
- else
- reply = dbus_message_new_method_return(message);
- if (!reply)
- goto nomem;
-
- dbus_message_iter_init_append(reply, &iter);
- if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
+ if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
"a{sv}", &variant_iter))
goto nomem;
if (!wpa_dbus_dict_close_write(&variant_iter, &iter_dict))
goto nomem;
- if (!dbus_message_iter_close_container(&iter, &variant_iter))
+ if (!dbus_message_iter_close_container(iter, &variant_iter))
goto nomem;
- return reply;
+ return TRUE;
nomem:
- if (reply)
- dbus_message_unref(reply);
-
- return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, NULL);
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+ return FALSE;
}
/**
* wpas_dbus_getter_bss_wpa - Return the WPA options of a BSS
- * @message: Pointer to incoming dbus message
- * @bss: a pair of interface describing structure and bss's id
- * Returns: a dbus message containing the WPA options of requested bss
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "WPA" property.
*/
-DBusMessage * wpas_dbus_getter_bss_wpa(DBusMessage *message,
- struct bss_handler_args *bss)
+dbus_bool_t wpas_dbus_getter_bss_wpa(DBusMessageIter *iter, DBusError *error,
+ void *user_data)
{
- struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id);
+ struct bss_handler_args *args = user_data;
+ struct wpa_bss *res;
struct wpa_ie_data wpa_data;
const u8 *ie;
- if (!res) {
- wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_wpa[dbus]: no "
- "bss with id %d found", bss->id);
- return NULL;
- }
+ res = get_bss_helper(args, error, __func__);
+ if (!res)
+ return FALSE;
os_memset(&wpa_data, 0, sizeof(wpa_data));
ie = wpa_bss_get_vendor_ie(res, WPA_IE_VENDOR_TYPE);
if (ie) {
- if (wpa_parse_wpa_ie(ie, 2 + ie[1], &wpa_data) < 0)
- return wpas_dbus_error_unknown_error(message,
- "invalid WPA IE");
+ if (wpa_parse_wpa_ie(ie, 2 + ie[1], &wpa_data) < 0) {
+ dbus_set_error_const(error, DBUS_ERROR_FAILED,
+ "failed to parse WPA IE");
+ return FALSE;
+ }
}
- return wpas_dbus_get_bss_security_prop(message, &wpa_data);
+ return wpas_dbus_get_bss_security_prop(iter, &wpa_data, error);
}
/**
* wpas_dbus_getter_bss_rsn - Return the RSN options of a BSS
- * @message: Pointer to incoming dbus message
- * @bss: a pair of interface describing structure and bss's id
- * Returns: a dbus message containing the RSN options of requested bss
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "RSN" property.
*/
-DBusMessage * wpas_dbus_getter_bss_rsn(DBusMessage *message,
- struct bss_handler_args *bss)
+dbus_bool_t wpas_dbus_getter_bss_rsn(DBusMessageIter *iter, DBusError *error,
+ void *user_data)
{
- struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id);
+ struct bss_handler_args *args = user_data;
+ struct wpa_bss *res;
struct wpa_ie_data wpa_data;
const u8 *ie;
- if (!res) {
- wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_rsn[dbus]: no "
- "bss with id %d found", bss->id);
- return NULL;
- }
+ res = get_bss_helper(args, error, __func__);
+ if (!res)
+ return FALSE;
os_memset(&wpa_data, 0, sizeof(wpa_data));
ie = wpa_bss_get_ie(res, WLAN_EID_RSN);
if (ie) {
- if (wpa_parse_wpa_ie(ie, 2 + ie[1], &wpa_data) < 0)
- return wpas_dbus_error_unknown_error(message,
- "invalid RSN IE");
+ if (wpa_parse_wpa_ie(ie, 2 + ie[1], &wpa_data) < 0) {
+ dbus_set_error_const(error, DBUS_ERROR_FAILED,
+ "failed to parse RSN IE");
+ return FALSE;
+ }
}
- return wpas_dbus_get_bss_security_prop(message, &wpa_data);
+ return wpas_dbus_get_bss_security_prop(iter, &wpa_data, error);
}
/**
* wpas_dbus_getter_bss_ies - Return all IEs of a BSS
- * @message: Pointer to incoming dbus message
- * @bss: a pair of interface describing structure and bss's id
- * Returns: a dbus message containing IEs byte array
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "IEs" property.
*/
-DBusMessage * wpas_dbus_getter_bss_ies(DBusMessage *message,
- struct bss_handler_args *bss)
+dbus_bool_t wpas_dbus_getter_bss_ies(DBusMessageIter *iter, DBusError *error,
+ void *user_data)
{
- struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id);
+ struct bss_handler_args *args = user_data;
+ struct wpa_bss *res;
- if (!res) {
- wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_ies[dbus]: no "
- "bss with id %d found", bss->id);
- return NULL;
- }
+ res = get_bss_helper(args, error, __func__);
+ if (!res)
+ return FALSE;
- return wpas_dbus_simple_array_property_getter(message, DBUS_TYPE_BYTE,
- res + 1, res->ie_len);
+ return wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE,
+ res + 1, res->ie_len,
+ error);
}
/**
* wpas_dbus_getter_enabled - Check whether network is enabled or disabled
- * @message: Pointer to incoming dbus message
- * @wpas_dbus_setter_enabled: wpa_supplicant structure for a network interface
- * and wpa_ssid structure for a configured network
- * Returns: DBus message with boolean indicating state of configured network
- * or DBus error on failure
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "enabled" property of a configured network.
*/
-DBusMessage * wpas_dbus_getter_enabled(DBusMessage *message,
- struct network_handler_args *net)
+dbus_bool_t wpas_dbus_getter_enabled(DBusMessageIter *iter, DBusError *error,
+ void *user_data)
{
+ struct network_handler_args *net = user_data;
dbus_bool_t enabled = net->ssid->disabled ? FALSE : TRUE;
- return wpas_dbus_simple_property_getter(message, DBUS_TYPE_BOOLEAN,
- &enabled);
+
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN,
+ &enabled, error);
}
/**
* wpas_dbus_setter_enabled - Mark a configured network as enabled or disabled
- * @message: Pointer to incoming dbus message
- * @wpas_dbus_setter_enabled: wpa_supplicant structure for a network interface
- * and wpa_ssid structure for a configured network
- * Returns: NULL indicating success or DBus error on failure
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Setter for "Enabled" property of a configured network.
*/
-DBusMessage * wpas_dbus_setter_enabled(DBusMessage *message,
- struct network_handler_args *net)
+dbus_bool_t wpas_dbus_setter_enabled(DBusMessageIter *iter, DBusError *error,
+ void *user_data)
{
- DBusMessage *reply = NULL;
-
+ struct network_handler_args *net = user_data;
struct wpa_supplicant *wpa_s;
struct wpa_ssid *ssid;
-
dbus_bool_t enable;
- reply = wpas_dbus_simple_property_setter(message, DBUS_TYPE_BOOLEAN,
- &enable);
-
- if (reply)
- return reply;
+ if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_BOOLEAN,
+ &enable))
+ return FALSE;
wpa_s = net->wpa_s;
ssid = net->ssid;
else
wpa_supplicant_disable_network(wpa_s, ssid);
- return NULL;
+ return TRUE;
}
/**
* wpas_dbus_getter_network_properties - Get options for a configured network
- * @message: Pointer to incoming dbus message
- * @net: wpa_supplicant structure for a network interface and
- * wpa_ssid structure for a configured network
- * Returns: DBus message with network properties or DBus error on failure
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "Properties" property of a configured network.
*/
-DBusMessage * wpas_dbus_getter_network_properties(
- DBusMessage *message, struct network_handler_args *net)
+dbus_bool_t wpas_dbus_getter_network_properties(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
{
- DBusMessage *reply = NULL;
- DBusMessageIter iter, variant_iter, dict_iter;
+ struct network_handler_args *net = user_data;
+ DBusMessageIter variant_iter, dict_iter;
char **iterator;
- char **props = wpa_config_get_all(net->ssid, 0);
- if (!props)
- return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
- NULL);
+ char **props = wpa_config_get_all(net->ssid, 1);
+ dbus_bool_t success = FALSE;
- if (message == NULL)
- reply = dbus_message_new(DBUS_MESSAGE_TYPE_SIGNAL);
- else
- reply = dbus_message_new_method_return(message);
- if (!reply) {
- reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
- NULL);
- goto out;
+ if (!props) {
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+ return FALSE;
}
- dbus_message_iter_init_append(reply, &iter);
-
- if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
- "a{sv}", &variant_iter) ||
+ if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "a{sv}",
+ &variant_iter) ||
!wpa_dbus_dict_open_write(&variant_iter, &dict_iter)) {
- dbus_message_unref(reply);
- reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
- NULL);
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
goto out;
}
while (*iterator) {
if (!wpa_dbus_dict_append_string(&dict_iter, *iterator,
*(iterator + 1))) {
- dbus_message_unref(reply);
- reply = dbus_message_new_error(message,
- DBUS_ERROR_NO_MEMORY,
- NULL);
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY,
+ "no memory");
goto out;
}
iterator += 2;
if (!wpa_dbus_dict_close_write(&variant_iter, &dict_iter) ||
- !dbus_message_iter_close_container(&iter, &variant_iter)) {
- dbus_message_unref(reply);
- reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
- NULL);
+ !dbus_message_iter_close_container(iter, &variant_iter)) {
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
goto out;
}
+ success = TRUE;
+
out:
iterator = props;
while (*iterator) {
iterator++;
}
os_free(props);
- return reply;
+ return success;
}
/**
* wpas_dbus_setter_network_properties - Set options for a configured network
- * @message: Pointer to incoming dbus message
- * @net: wpa_supplicant structure for a network interface and
- * wpa_ssid structure for a configured network
- * Returns: NULL indicating success or DBus error on failure
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Setter for "Properties" property of a configured network.
*/
-DBusMessage * wpas_dbus_setter_network_properties(
- DBusMessage *message, struct network_handler_args *net)
+dbus_bool_t wpas_dbus_setter_network_properties(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
{
+ struct network_handler_args *net = user_data;
struct wpa_ssid *ssid = net->ssid;
+ DBusMessageIter variant_iter;
- DBusMessage *reply = NULL;
- DBusMessageIter iter, variant_iter;
-
- dbus_message_iter_init(message, &iter);
-
- dbus_message_iter_next(&iter);
- dbus_message_iter_next(&iter);
-
- dbus_message_iter_recurse(&iter, &variant_iter);
-
- reply = set_network_properties(message, net->wpa_s, ssid,
- &variant_iter);
- if (reply)
- wpa_printf(MSG_DEBUG, "dbus control interface couldn't set "
- "network properties");
-
- return reply;
+ dbus_message_iter_recurse(iter, &variant_iter);
+ return set_network_properties(net->wpa_s, ssid, &variant_iter, error);
}
unsigned int id;
};
-DBusMessage * wpas_dbus_simple_property_getter(DBusMessage *message,
- const int type,
- const void *val);
-
-DBusMessage * wpas_dbus_simple_property_setter(DBusMessage *message,
- const int type, void *val);
-
-DBusMessage * wpas_dbus_simple_array_property_getter(DBusMessage *message,
- const int type,
- const void *array,
- size_t array_len);
+dbus_bool_t wpas_dbus_simple_property_getter(DBusMessageIter *iter,
+ const int type,
+ const void *val,
+ DBusError *error);
+
+dbus_bool_t wpas_dbus_simple_property_setter(DBusMessageIter *iter,
+ DBusError *error,
+ const int type, void *val);
+
+dbus_bool_t wpas_dbus_simple_array_property_getter(DBusMessageIter *iter,
+ const int type,
+ const void *array,
+ size_t array_len,
+ DBusError *error);
+
+dbus_bool_t wpas_dbus_simple_array_array_property_getter(DBusMessageIter *iter,
+ const int type,
+ struct wpabuf **array,
+ size_t array_len,
+ DBusError *error);
DBusMessage * wpas_dbus_handler_create_interface(DBusMessage *message,
struct wpa_global *global);
DBusMessage * wpas_dbus_handler_get_interface(DBusMessage *message,
struct wpa_global *global);
-DBusMessage * wpas_dbus_getter_debug_level(DBusMessage *message,
- struct wpa_global *global);
+dbus_bool_t wpas_dbus_getter_debug_level(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_getter_debug_timestamp(DBusMessage *message,
- struct wpa_global *global);
+dbus_bool_t wpas_dbus_getter_debug_timestamp(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_getter_debug_show_keys(DBusMessage *message,
- struct wpa_global *global);
+dbus_bool_t wpas_dbus_getter_debug_show_keys(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_setter_debug_level(DBusMessage *message,
- struct wpa_global *global);
+dbus_bool_t wpas_dbus_setter_debug_level(DBusMessageIter *iter,
+ DBusError *error, void *user_data);
-DBusMessage * wpas_dbus_setter_debug_timestamp(DBusMessage *message,
- struct wpa_global *global);
+dbus_bool_t wpas_dbus_setter_debug_timestamp(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_setter_debug_show_keys(DBusMessage *message,
- struct wpa_global *global);
+dbus_bool_t wpas_dbus_setter_debug_show_keys(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_getter_interfaces(DBusMessage *message,
- struct wpa_global *global);
+dbus_bool_t wpas_dbus_getter_interfaces(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_getter_eap_methods(DBusMessage *message,
- void *nothing);
+dbus_bool_t wpas_dbus_getter_eap_methods(DBusMessageIter *iter,
+ DBusError *error, void *user_data);
DBusMessage * wpas_dbus_handler_scan(DBusMessage *message,
struct wpa_supplicant *wpa_s);
DBusMessage * wpas_dbus_handler_disconnect(DBusMessage *message,
struct wpa_supplicant *wpa_s);
+dbus_bool_t set_network_properties(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid,
+ DBusMessageIter *iter,
+ DBusError *error);
+
DBusMessage * wpas_dbus_handler_add_network(DBusMessage *message,
struct wpa_supplicant *wpa_s);
DBusMessage * wpas_dbus_handler_remove_network(DBusMessage *message,
struct wpa_supplicant *wpa_s);
+DBusMessage * wpas_dbus_handler_remove_all_networks(
+ DBusMessage *message, struct wpa_supplicant *wpa_s);
+
DBusMessage * wpas_dbus_handler_select_network(DBusMessage *message,
struct wpa_supplicant *wpa_s);
+DBusMessage * wpas_dbus_handler_network_reply(DBusMessage *message,
+ struct wpa_supplicant *wpa_s);
+
DBusMessage * wpas_dbus_handler_add_blob(DBusMessage *message,
struct wpa_supplicant *wpa_s);
DBusMessage * wpas_dbus_handler_remove_blob(DBusMessage *message,
struct wpa_supplicant *wpa_s);
-DBusMessage * wpas_dbus_getter_capabilities(DBusMessage *message,
- struct wpa_supplicant *wpa_s);
+DBusMessage * wpas_dbus_handler_flush_bss(DBusMessage *message,
+ struct wpa_supplicant *wpa_s);
-DBusMessage * wpas_dbus_getter_state(DBusMessage *message,
- struct wpa_supplicant *wpa_s);
+dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter,
+ DBusError *error, void *user_data);
-DBusMessage * wpas_dbus_getter_scanning(DBusMessage *message,
- struct wpa_supplicant *wpa_s);
+dbus_bool_t wpas_dbus_getter_state(DBusMessageIter *iter, DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_getter_ap_scan(DBusMessage *message,
- struct wpa_supplicant *wpa_s);
+dbus_bool_t wpas_dbus_getter_scanning(DBusMessageIter *iter, DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_setter_ap_scan(DBusMessage *message,
- struct wpa_supplicant *wpa_s);
+dbus_bool_t wpas_dbus_getter_ap_scan(DBusMessageIter *iter, DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_getter_ifname(DBusMessage *message,
- struct wpa_supplicant *wpa_s);
+dbus_bool_t wpas_dbus_setter_ap_scan(DBusMessageIter *iter, DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_getter_driver(DBusMessage *message,
- struct wpa_supplicant *wpa_s);
+dbus_bool_t wpas_dbus_getter_fast_reauth(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_getter_bridge_ifname(DBusMessage *message,
- struct wpa_supplicant *wpa_s);
+dbus_bool_t wpas_dbus_setter_fast_reauth(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_getter_current_bss(DBusMessage *message,
- struct wpa_supplicant *wpa_s);
+dbus_bool_t wpas_dbus_getter_bss_expire_age(DBusMessageIter *iter,
+ DBusError *error, void *user_data);
-DBusMessage * wpas_dbus_getter_current_network(DBusMessage *message,
- struct wpa_supplicant *wpa_s);
+dbus_bool_t wpas_dbus_setter_bss_expire_age(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_getter_bss_expire_count(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_setter_bss_expire_count(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_getter_bsss(DBusMessage *message,
- struct wpa_supplicant *wpa_s);
+dbus_bool_t wpas_dbus_getter_country(DBusMessageIter *iter, DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_getter_networks(DBusMessage *message,
- struct wpa_supplicant *wpa_s);
+dbus_bool_t wpas_dbus_setter_country(DBusMessageIter *iter, DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_getter_blobs(DBusMessage *message,
- struct wpa_supplicant *bss);
+dbus_bool_t wpas_dbus_getter_ifname(DBusMessageIter *iter, DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_getter_bss_bssid(DBusMessage *message,
- struct bss_handler_args *bss);
+dbus_bool_t wpas_dbus_getter_driver(DBusMessageIter *iter, DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_getter_bss_ssid(DBusMessage *message,
- struct bss_handler_args *bss);
+dbus_bool_t wpas_dbus_getter_bridge_ifname(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_getter_bss_privacy(DBusMessage *message,
- struct bss_handler_args *bss);
+dbus_bool_t wpas_dbus_getter_current_bss(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_getter_bss_mode(DBusMessage *message,
- struct bss_handler_args *bss);
+dbus_bool_t wpas_dbus_getter_current_network(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_getter_bss_signal(DBusMessage *message,
- struct bss_handler_args *bss);
+dbus_bool_t wpas_dbus_getter_current_auth_mode(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_getter_bss_frequency(DBusMessage *message,
- struct bss_handler_args *bss);
+dbus_bool_t wpas_dbus_getter_bsss(DBusMessageIter *iter, DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_getter_bss_rates(DBusMessage *message,
- struct bss_handler_args *bss);
+dbus_bool_t wpas_dbus_getter_networks(DBusMessageIter *iter, DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_getter_bss_wpa(DBusMessage *message,
- struct bss_handler_args *bss);
+dbus_bool_t wpas_dbus_getter_blobs(DBusMessageIter *iter, DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_getter_bss_rsn(DBusMessage *message,
- struct bss_handler_args *bss);
+dbus_bool_t wpas_dbus_getter_bss_bssid(DBusMessageIter *iter, DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_getter_bss_ies(DBusMessage *message,
- struct bss_handler_args *bss);
+dbus_bool_t wpas_dbus_getter_bss_ssid(DBusMessageIter *iter, DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_getter_enabled(DBusMessage *message,
- struct network_handler_args *net);
+dbus_bool_t wpas_dbus_getter_bss_privacy(DBusMessageIter *iter,
+ DBusError *error, void *user_data);
-DBusMessage * wpas_dbus_setter_enabled(DBusMessage *message,
- struct network_handler_args *net);
+dbus_bool_t wpas_dbus_getter_bss_mode(DBusMessageIter *iter, DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_getter_network_properties(
- DBusMessage *message, struct network_handler_args *net);
+dbus_bool_t wpas_dbus_getter_bss_signal(DBusMessageIter *iter,
+ DBusError *error, void *user_data);
-DBusMessage * wpas_dbus_setter_network_properties(
- DBusMessage *message, struct network_handler_args *net);
+dbus_bool_t wpas_dbus_getter_bss_frequency(DBusMessageIter *iter,
+ DBusError *error, void *user_data);
+
+dbus_bool_t wpas_dbus_getter_bss_rates(DBusMessageIter *iter,
+ DBusError *error, void *user_data);
+
+dbus_bool_t wpas_dbus_getter_bss_wpa(DBusMessageIter *iter, DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_getter_bss_rsn(DBusMessageIter *iter, DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_getter_bss_ies(DBusMessageIter *iter, DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_getter_enabled(DBusMessageIter *iter, DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_setter_enabled(DBusMessageIter *iter, DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_getter_network_properties(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_setter_network_properties(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
DBusMessage * wpas_dbus_handler_wps_start(DBusMessage *message,
struct wpa_supplicant *wpa_s);
-DBusMessage * wpas_dbus_getter_process_credentials(
- DBusMessage *message, struct wpa_supplicant *wpa_s);
-
-DBusMessage * wpas_dbus_setter_process_credentials(
- DBusMessage *message, struct wpa_supplicant *wpa_s);
+dbus_bool_t wpas_dbus_getter_process_credentials(DBusMessageIter *iter,
+ DBusError *error, void *user_data);
-DBusMessage * wpas_dbus_getter_credentials(DBusMessage *message,
- struct wpa_supplicant *wpa_s);
+dbus_bool_t wpas_dbus_setter_process_credentials(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
DBusMessage * wpas_dbus_error_invalid_args(DBusMessage *message,
const char *arg);
--- /dev/null
+/*
+ * WPA Supplicant / dbus-based control interface (P2P)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "utils/includes.h"
+#include "common.h"
+#include "../config.h"
+#include "../wpa_supplicant_i.h"
+#include "../wps_supplicant.h"
+#include "../notify.h"
+#include "dbus_new_helpers.h"
+#include "dbus_new.h"
+#include "dbus_new_handlers.h"
+#include "dbus_new_handlers_p2p.h"
+#include "dbus_dict_helpers.h"
+#include "p2p/p2p.h"
+#include "common/ieee802_11_defs.h"
+#include "ap/hostapd.h"
+#include "ap/ap_config.h"
+#include "ap/wps_hostapd.h"
+
+#include "../p2p_supplicant.h"
+
+/**
+ * Parses out the mac address from the peer object path.
+ * @peer_path - object path of the form
+ * /fi/w1/wpa_supplicant1/Interfaces/n/Peers/00112233445566 (no colons)
+ * @addr - out param must be of ETH_ALEN size
+ * Returns 0 if valid (including MAC), -1 otherwise
+ */
+static int parse_peer_object_path(char *peer_path, u8 addr[ETH_ALEN])
+{
+ char *p;
+
+ if (!peer_path)
+ return -1;
+ p = strrchr(peer_path, '/');
+ if (!p)
+ return -1;
+ p++;
+ return hwaddr_compact_aton(p, addr);
+}
+
+
+/**
+ * wpas_dbus_error_persistent_group_unknown - Return a new PersistentGroupUnknown
+ * error message
+ * @message: Pointer to incoming dbus message this error refers to
+ * Returns: a dbus error message
+ *
+ * Convenience function to create and return an invalid persistent group error.
+ */
+static DBusMessage * wpas_dbus_error_persistent_group_unknown(
+ DBusMessage *message)
+{
+ return dbus_message_new_error(message, WPAS_DBUS_ERROR_NETWORK_UNKNOWN,
+ "There is no such persistent group in "
+ "this P2P device.");
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_find(DBusMessage *message,
+ struct wpa_supplicant *wpa_s)
+{
+ struct wpa_dbus_dict_entry entry;
+ DBusMessage *reply = NULL;
+ DBusMessageIter iter;
+ DBusMessageIter iter_dict;
+ unsigned int timeout = 0;
+ enum p2p_discovery_type type = P2P_FIND_ONLY_SOCIAL;
+ int num_req_dev_types = 0;
+ unsigned int i;
+ u8 *req_dev_types = NULL;
+
+ dbus_message_iter_init(message, &iter);
+ entry.key = NULL;
+
+ if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
+ goto error;
+
+ while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+ if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+ goto error;
+
+ if (!os_strcmp(entry.key, "Timeout") &&
+ (entry.type == DBUS_TYPE_INT32)) {
+ timeout = entry.uint32_value;
+ } else if (os_strcmp(entry.key, "RequestedDeviceTypes") == 0) {
+ if ((entry.type != DBUS_TYPE_ARRAY) ||
+ (entry.array_type != WPAS_DBUS_TYPE_BINARRAY))
+ goto error_clear;
+
+ os_free(req_dev_types);
+ req_dev_types =
+ os_malloc(WPS_DEV_TYPE_LEN * entry.array_len);
+ if (!req_dev_types)
+ goto error_clear;
+
+ for (i = 0; i < entry.array_len; i++) {
+ if (wpabuf_len(entry.binarray_value[i]) !=
+ WPS_DEV_TYPE_LEN)
+ goto error_clear;
+ os_memcpy(req_dev_types + i * WPS_DEV_TYPE_LEN,
+ wpabuf_head(entry.binarray_value[i]),
+ WPS_DEV_TYPE_LEN);
+ }
+ num_req_dev_types = entry.array_len;
+ } else if (!os_strcmp(entry.key, "DiscoveryType") &&
+ (entry.type == DBUS_TYPE_STRING)) {
+ if (!os_strcmp(entry.str_value, "start_with_full"))
+ type = P2P_FIND_START_WITH_FULL;
+ else if (!os_strcmp(entry.str_value, "social"))
+ type = P2P_FIND_ONLY_SOCIAL;
+ else if (!os_strcmp(entry.str_value, "progressive"))
+ type = P2P_FIND_PROGRESSIVE;
+ else
+ goto error_clear;
+ } else
+ goto error_clear;
+ wpa_dbus_dict_entry_clear(&entry);
+ }
+
+ wpas_p2p_find(wpa_s, timeout, type, num_req_dev_types, req_dev_types,
+ NULL);
+ os_free(req_dev_types);
+ return reply;
+
+error_clear:
+ wpa_dbus_dict_entry_clear(&entry);
+error:
+ os_free(req_dev_types);
+ reply = wpas_dbus_error_invalid_args(message, entry.key);
+ return reply;
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_stop_find(DBusMessage *message,
+ struct wpa_supplicant *wpa_s)
+{
+ wpas_p2p_stop_find(wpa_s);
+ return NULL;
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_rejectpeer(DBusMessage *message,
+ struct wpa_supplicant *wpa_s)
+{
+ DBusMessageIter iter;
+ char *peer_object_path = NULL;
+ u8 peer_addr[ETH_ALEN];
+
+ dbus_message_iter_init(message, &iter);
+ dbus_message_iter_get_basic(&iter, &peer_object_path);
+
+ if (parse_peer_object_path(peer_object_path, peer_addr) < 0)
+ return wpas_dbus_error_invalid_args(message, NULL);
+
+ if (wpas_p2p_reject(wpa_s, peer_addr) < 0)
+ return wpas_dbus_error_unknown_error(message,
+ "Failed to call wpas_p2p_reject method.");
+
+ return NULL;
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_listen(DBusMessage *message,
+ struct wpa_supplicant *wpa_s)
+{
+ dbus_int32_t timeout = 0;
+
+ if (!dbus_message_get_args(message, NULL, DBUS_TYPE_INT32, &timeout,
+ DBUS_TYPE_INVALID))
+ return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
+ NULL);
+
+ if (wpas_p2p_listen(wpa_s, (unsigned int)timeout))
+ return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
+ NULL);
+
+ return NULL;
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_extendedlisten(
+ DBusMessage *message, struct wpa_supplicant *wpa_s)
+{
+ unsigned int period = 0, interval = 0;
+ struct wpa_dbus_dict_entry entry;
+ DBusMessageIter iter;
+ DBusMessageIter iter_dict;
+
+ dbus_message_iter_init(message, &iter);
+ entry.key = NULL;
+
+ if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
+ goto error;
+
+ while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+ if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+ goto error;
+
+ if (!os_strcmp(entry.key, "period") &&
+ (entry.type == DBUS_TYPE_INT32))
+ period = entry.uint32_value;
+ else if (!os_strcmp(entry.key, "interval") &&
+ (entry.type == DBUS_TYPE_INT32))
+ interval = entry.uint32_value;
+ else
+ goto error_clear;
+ wpa_dbus_dict_entry_clear(&entry);
+ }
+
+ if (wpas_p2p_ext_listen(wpa_s, period, interval))
+ return wpas_dbus_error_unknown_error(
+ message, "failed to initiate a p2p_ext_listen.");
+
+ return NULL;
+
+error_clear:
+ wpa_dbus_dict_entry_clear(&entry);
+error:
+ return wpas_dbus_error_invalid_args(message, entry.key);
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_presence_request(
+ DBusMessage *message, struct wpa_supplicant *wpa_s)
+{
+ unsigned int dur1 = 0, int1 = 0, dur2 = 0, int2 = 0;
+ struct wpa_dbus_dict_entry entry;
+ DBusMessageIter iter;
+ DBusMessageIter iter_dict;
+
+ dbus_message_iter_init(message, &iter);
+ entry.key = NULL;
+
+ if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
+ goto error;
+
+ while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+ if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+ goto error;
+
+ if (!os_strcmp(entry.key, "duration1") &&
+ (entry.type == DBUS_TYPE_INT32))
+ dur1 = entry.uint32_value;
+ else if (!os_strcmp(entry.key, "interval1") &&
+ entry.type == DBUS_TYPE_INT32)
+ int1 = entry.uint32_value;
+ else if (!os_strcmp(entry.key, "duration2") &&
+ entry.type == DBUS_TYPE_INT32)
+ dur2 = entry.uint32_value;
+ else if (!os_strcmp(entry.key, "interval2") &&
+ entry.type == DBUS_TYPE_INT32)
+ int2 = entry.uint32_value;
+ else
+ goto error_clear;
+
+ wpa_dbus_dict_entry_clear(&entry);
+ }
+ if (wpas_p2p_presence_req(wpa_s, dur1, int1, dur2, int2) < 0)
+ return wpas_dbus_error_unknown_error(message,
+ "Failed to invoke presence request.");
+
+ return NULL;
+
+error_clear:
+ wpa_dbus_dict_entry_clear(&entry);
+error:
+ return wpas_dbus_error_invalid_args(message, entry.key);
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_group_add(DBusMessage *message,
+ struct wpa_supplicant *wpa_s)
+{
+ DBusMessageIter iter_dict;
+ DBusMessage *reply = NULL;
+ DBusMessageIter iter;
+ struct wpa_dbus_dict_entry entry;
+ char *pg_object_path = NULL;
+ int persistent_group = 0;
+ int freq = 0;
+ char *iface = NULL;
+ char *net_id_str = NULL;
+ unsigned int group_id = 0;
+ struct wpa_ssid *ssid;
+
+ dbus_message_iter_init(message, &iter);
+
+ if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
+ goto inv_args;
+
+ while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+ if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+ goto inv_args;
+
+ if (!os_strcmp(entry.key, "persistent") &&
+ (entry.type == DBUS_TYPE_BOOLEAN)) {
+ persistent_group = (entry.bool_value == TRUE) ? 1 : 0;
+ } else if (!os_strcmp(entry.key, "frequency") &&
+ (entry.type == DBUS_TYPE_INT32)) {
+ freq = entry.int32_value;
+ if (freq <= 0)
+ goto inv_args_clear;
+ } else if (!os_strcmp(entry.key, "persistent_group_object") &&
+ entry.type == DBUS_TYPE_OBJECT_PATH)
+ pg_object_path = os_strdup(entry.str_value);
+ else
+ goto inv_args_clear;
+
+ wpa_dbus_dict_entry_clear(&entry);
+ }
+
+ if (pg_object_path != NULL) {
+ /*
+ * A persistent group Object Path is defined meaning we want
+ * to re-invoke a persistent group.
+ */
+
+ iface = wpas_dbus_new_decompose_object_path(pg_object_path, 1,
+ &net_id_str, NULL);
+ if (iface == NULL ||
+ os_strcmp(iface, wpa_s->dbus_new_path) != 0) {
+ reply =
+ wpas_dbus_error_invalid_args(message,
+ pg_object_path);
+ goto out;
+ }
+
+ group_id = strtoul(net_id_str, NULL, 10);
+ if (errno == EINVAL) {
+ reply = wpas_dbus_error_invalid_args(
+ message, pg_object_path);
+ goto out;
+ }
+
+ /* Get the SSID structure from the persistent group id */
+ ssid = wpa_config_get_network(wpa_s->conf, group_id);
+ if (ssid == NULL || ssid->disabled != 2)
+ goto inv_args;
+
+ if (wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq)) {
+ reply = wpas_dbus_error_unknown_error(
+ message,
+ "Failed to reinvoke a persistent group");
+ goto out;
+ }
+ } else if (wpas_p2p_group_add(wpa_s, persistent_group, freq))
+ goto inv_args;
+
+out:
+ os_free(pg_object_path);
+ os_free(net_id_str);
+ os_free(iface);
+ return reply;
+inv_args_clear:
+ wpa_dbus_dict_entry_clear(&entry);
+inv_args:
+ reply = wpas_dbus_error_invalid_args(message, NULL);
+ goto out;
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_disconnect(DBusMessage *message,
+ struct wpa_supplicant *wpa_s)
+{
+ if (wpas_p2p_disconnect(wpa_s))
+ return wpas_dbus_error_unknown_error(message,
+ "failed to disconnect");
+
+ return NULL;
+}
+
+
+static dbus_bool_t wpa_dbus_p2p_check_enabled(struct wpa_supplicant *wpa_s,
+ DBusMessage *message,
+ DBusMessage **out_reply,
+ DBusError *error)
+{
+ /* Return an error message or an error if P2P isn't available */
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) {
+ if (out_reply) {
+ *out_reply = dbus_message_new_error(
+ message, DBUS_ERROR_FAILED,
+ "P2P is not available for this interface");
+ }
+ dbus_set_error_const(error, DBUS_ERROR_FAILED,
+ "P2P is not available for this "
+ "interface");
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_flush(DBusMessage *message,
+ struct wpa_supplicant *wpa_s)
+{
+ DBusMessage *reply = NULL;
+
+ if (!wpa_dbus_p2p_check_enabled(wpa_s, message, &reply, NULL))
+ return reply;
+
+ os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN);
+ wpa_s->force_long_sd = 0;
+ p2p_flush(wpa_s->global->p2p);
+
+ return NULL;
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_connect(DBusMessage *message,
+ struct wpa_supplicant *wpa_s)
+{
+ DBusMessageIter iter_dict;
+ DBusMessage *reply = NULL;
+ DBusMessageIter iter;
+ struct wpa_dbus_dict_entry entry;
+ char *peer_object_path = NULL;
+ int persistent_group = 0;
+ int join = 0;
+ int authorize_only = 0;
+ int go_intent = -1;
+ int freq = 0;
+ u8 addr[ETH_ALEN];
+ char *pin = NULL;
+ enum p2p_wps_method wps_method = WPS_NOT_READY;
+ int new_pin;
+ char *err_msg = NULL;
+ char *iface = NULL;
+
+ if (!wpa_dbus_p2p_check_enabled(wpa_s, message, &reply, NULL))
+ return reply;
+
+ dbus_message_iter_init(message, &iter);
+
+ if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
+ goto inv_args;
+
+ while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+ if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+ goto inv_args;
+
+ if (!os_strcmp(entry.key, "peer") &&
+ (entry.type == DBUS_TYPE_OBJECT_PATH)) {
+ peer_object_path = os_strdup(entry.str_value);
+ } else if (!os_strcmp(entry.key, "persistent") &&
+ (entry.type == DBUS_TYPE_BOOLEAN)) {
+ persistent_group = (entry.bool_value == TRUE) ? 1 : 0;
+ } else if (!os_strcmp(entry.key, "join") &&
+ (entry.type == DBUS_TYPE_BOOLEAN)) {
+ join = (entry.bool_value == TRUE) ? 1 : 0;
+ } else if (!os_strcmp(entry.key, "authorize_only") &&
+ (entry.type == DBUS_TYPE_BOOLEAN)) {
+ authorize_only = (entry.bool_value == TRUE) ? 1 : 0;
+ } else if (!os_strcmp(entry.key, "frequency") &&
+ (entry.type == DBUS_TYPE_INT32)) {
+ freq = entry.int32_value;
+ if (freq <= 0)
+ goto inv_args_clear;
+ } else if (!os_strcmp(entry.key, "go_intent") &&
+ (entry.type == DBUS_TYPE_INT32)) {
+ go_intent = entry.int32_value;
+ if ((go_intent < 0) || (go_intent > 15))
+ goto inv_args_clear;
+ } else if (!os_strcmp(entry.key, "wps_method") &&
+ (entry.type == DBUS_TYPE_STRING)) {
+ if (!os_strcmp(entry.str_value, "pbc"))
+ wps_method = WPS_PBC;
+ else if (!os_strcmp(entry.str_value, "pin"))
+ wps_method = WPS_PIN_DISPLAY;
+ else if (!os_strcmp(entry.str_value, "display"))
+ wps_method = WPS_PIN_DISPLAY;
+ else if (!os_strcmp(entry.str_value, "keypad"))
+ wps_method = WPS_PIN_KEYPAD;
+ else
+ goto inv_args_clear;
+ } else if (!os_strcmp(entry.key, "pin") &&
+ (entry.type == DBUS_TYPE_STRING)) {
+ pin = os_strdup(entry.str_value);
+ } else
+ goto inv_args_clear;
+
+ wpa_dbus_dict_entry_clear(&entry);
+ }
+
+ if (!peer_object_path || (wps_method == WPS_NOT_READY) ||
+ (parse_peer_object_path(peer_object_path, addr) < 0) ||
+ !p2p_peer_known(wpa_s->global->p2p, addr))
+ goto inv_args;
+
+ /*
+ * Validate the wps_method specified and the pin value.
+ */
+ if ((!pin || !pin[0]) && (wps_method == WPS_PIN_KEYPAD))
+ goto inv_args;
+
+ new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method,
+ persistent_group, join, authorize_only,
+ go_intent, freq);
+
+ if (new_pin >= 0) {
+ char npin[9];
+ char *generated_pin;
+ os_snprintf(npin, sizeof(npin), "%08d", new_pin);
+ generated_pin = npin;
+ reply = dbus_message_new_method_return(message);
+ dbus_message_append_args(reply, DBUS_TYPE_STRING,
+ &generated_pin, DBUS_TYPE_INVALID);
+ } else {
+ switch (new_pin) {
+ case -2:
+ err_msg = "connect failed due to channel "
+ "unavailability.";
+ iface = WPAS_DBUS_ERROR_CONNECT_CHANNEL_UNAVAILABLE;
+ break;
+
+ case -3:
+ err_msg = "connect failed due to unsupported channel.";
+ iface = WPAS_DBUS_ERROR_CONNECT_CHANNEL_UNSUPPORTED;
+ break;
+
+ default:
+ err_msg = "connect failed due to unspecified error.";
+ iface = WPAS_DBUS_ERROR_CONNECT_UNSPECIFIED_ERROR;
+ break;
+ }
+
+ /*
+ * TODO:
+ * Do we need specialized errors corresponding to above
+ * error conditions as against just returning a different
+ * error message?
+ */
+ reply = dbus_message_new_error(message, iface, err_msg);
+ }
+
+out:
+ os_free(peer_object_path);
+ os_free(pin);
+ return reply;
+inv_args_clear:
+ wpa_dbus_dict_entry_clear(&entry);
+inv_args:
+ reply = wpas_dbus_error_invalid_args(message, NULL);
+ goto out;
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_invite(DBusMessage *message,
+ struct wpa_supplicant *wpa_s)
+{
+ DBusMessageIter iter_dict;
+ DBusMessage *reply = NULL;
+ DBusMessageIter iter;
+ struct wpa_dbus_dict_entry entry;
+ char *peer_object_path = NULL;
+ char *pg_object_path = NULL;
+ char *iface = NULL;
+ char *net_id_str = NULL;
+ u8 peer_addr[ETH_ALEN];
+ unsigned int group_id = 0;
+ int persistent = 0;
+ struct wpa_ssid *ssid;
+
+ if (!wpa_dbus_p2p_check_enabled(wpa_s, message, &reply, NULL))
+ return reply;
+
+ dbus_message_iter_init(message, &iter);
+
+ if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
+ goto err;
+
+ while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+ if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+ goto err;
+
+ if (!os_strcmp(entry.key, "peer") &&
+ (entry.type == DBUS_TYPE_OBJECT_PATH)) {
+ peer_object_path = os_strdup(entry.str_value);
+ wpa_dbus_dict_entry_clear(&entry);
+ } else if (!os_strcmp(entry.key, "persistent_group_object") &&
+ (entry.type == DBUS_TYPE_OBJECT_PATH)) {
+ pg_object_path = os_strdup(entry.str_value);
+ persistent = 1;
+ wpa_dbus_dict_entry_clear(&entry);
+ } else {
+ wpa_dbus_dict_entry_clear(&entry);
+ goto err;
+ }
+ }
+
+ if (!peer_object_path ||
+ (parse_peer_object_path(peer_object_path, peer_addr) < 0) ||
+ !p2p_peer_known(wpa_s->global->p2p, peer_addr)) {
+ goto err;
+ }
+
+ if (persistent) {
+ /*
+ * A group ID is defined meaning we want to re-invoke a
+ * persistent group
+ */
+
+ iface = wpas_dbus_new_decompose_object_path(pg_object_path, 1,
+ &net_id_str, NULL);
+ if (iface == NULL ||
+ os_strcmp(iface, wpa_s->dbus_new_path) != 0) {
+ reply = wpas_dbus_error_invalid_args(message,
+ pg_object_path);
+ goto out;
+ }
+
+ group_id = strtoul(net_id_str, NULL, 10);
+ if (errno == EINVAL) {
+ reply = wpas_dbus_error_invalid_args(
+ message, pg_object_path);
+ goto out;
+ }
+
+ /* Get the SSID structure from the persistent group id */
+ ssid = wpa_config_get_network(wpa_s->conf, group_id);
+ if (ssid == NULL || ssid->disabled != 2)
+ goto err;
+
+ if (wpas_p2p_invite(wpa_s, peer_addr, ssid, NULL) < 0) {
+ reply = wpas_dbus_error_unknown_error(
+ message,
+ "Failed to reinvoke a persistent group");
+ goto out;
+ }
+ } else {
+ /*
+ * No group ID means propose to a peer to join my active group
+ */
+ if (wpas_p2p_invite_group(wpa_s, wpa_s->ifname,
+ peer_addr, NULL)) {
+ reply = wpas_dbus_error_unknown_error(
+ message, "Failed to join to an active group");
+ goto out;
+ }
+ }
+
+out:
+ os_free(pg_object_path);
+ os_free(peer_object_path);
+ return reply;
+
+err:
+ reply = wpas_dbus_error_invalid_args(message, NULL);
+ goto out;
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_prov_disc_req(DBusMessage *message,
+ struct wpa_supplicant *wpa_s)
+{
+ DBusMessageIter iter;
+ char *peer_object_path = NULL;
+ char *config_method = NULL;
+ u8 peer_addr[ETH_ALEN];
+
+ dbus_message_iter_init(message, &iter);
+ dbus_message_iter_get_basic(&iter, &peer_object_path);
+
+ if (parse_peer_object_path(peer_object_path, peer_addr) < 0)
+ return wpas_dbus_error_invalid_args(message, NULL);
+
+ dbus_message_iter_next(&iter);
+ dbus_message_iter_get_basic(&iter, &config_method);
+
+ /*
+ * Validation checks on config_method are being duplicated here
+ * to be able to return invalid args reply since the error code
+ * from p2p module are not granular enough (yet).
+ */
+ if (os_strcmp(config_method, "display") &&
+ os_strcmp(config_method, "keypad") &&
+ os_strcmp(config_method, "pbc") &&
+ os_strcmp(config_method, "pushbutton"))
+ return wpas_dbus_error_invalid_args(message, NULL);
+
+ if (wpas_p2p_prov_disc(wpa_s, peer_addr, config_method, 0) < 0)
+ return wpas_dbus_error_unknown_error(message,
+ "Failed to send provision discovery request");
+
+ return NULL;
+}
+
+
+/*
+ * P2P Device property accessor methods.
+ */
+
+dbus_bool_t wpas_dbus_getter_p2p_device_config(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ DBusMessageIter variant_iter, dict_iter;
+ DBusMessageIter iter_secdev_dict_entry, iter_secdev_dict_val,
+ iter_secdev_dict_array;
+ const char *dev_name;
+ int num_vendor_extensions = 0;
+ int i;
+ const struct wpabuf *vendor_ext[P2P_MAX_WPS_VENDOR_EXT];
+
+ if (!wpa_dbus_p2p_check_enabled(wpa_s, NULL, NULL, error))
+ return FALSE;
+
+ if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+ "a{sv}", &variant_iter) ||
+ !wpa_dbus_dict_open_write(&variant_iter, &dict_iter))
+ goto err_no_mem;
+
+ /* DeviceName */
+ dev_name = wpa_s->conf->device_name;
+ if (dev_name &&
+ !wpa_dbus_dict_append_string(&dict_iter, "DeviceName", dev_name))
+ goto err_no_mem;
+
+ /* Primary device type */
+ if (!wpa_dbus_dict_append_byte_array(&dict_iter, "PrimaryDeviceType",
+ (char *)wpa_s->conf->device_type,
+ WPS_DEV_TYPE_LEN))
+ goto err_no_mem;
+
+ /* Secondary device types */
+ if (wpa_s->conf->num_sec_device_types) {
+ if (!wpa_dbus_dict_begin_array(&dict_iter,
+ "SecondaryDeviceTypes",
+ DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_TYPE_BYTE_AS_STRING,
+ &iter_secdev_dict_entry,
+ &iter_secdev_dict_val,
+ &iter_secdev_dict_array))
+ goto err_no_mem;
+
+ for (i = 0; i < wpa_s->conf->num_sec_device_types; i++)
+ wpa_dbus_dict_bin_array_add_element(
+ &iter_secdev_dict_array,
+ wpa_s->conf->sec_device_type[i],
+ WPS_DEV_TYPE_LEN);
+
+ if (!wpa_dbus_dict_end_array(&dict_iter,
+ &iter_secdev_dict_entry,
+ &iter_secdev_dict_val,
+ &iter_secdev_dict_array))
+ goto err_no_mem;
+ }
+
+ /* Vendor Extensions */
+ for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
+ if (wpa_s->conf->wps_vendor_ext[i] == NULL)
+ continue;
+ vendor_ext[num_vendor_extensions++] =
+ wpa_s->conf->wps_vendor_ext[i];
+ }
+
+ if (num_vendor_extensions &&
+ !wpa_dbus_dict_append_wpabuf_array(&dict_iter,
+ "VendorExtension",
+ vendor_ext,
+ num_vendor_extensions))
+ goto err_no_mem;
+
+ /* GO Intent */
+ if (!wpa_dbus_dict_append_uint32(&dict_iter, "GOIntent",
+ wpa_s->conf->p2p_go_intent))
+ goto err_no_mem;
+
+ /* Persistent Reconnect */
+ if (!wpa_dbus_dict_append_bool(&dict_iter, "PersistentReconnect",
+ wpa_s->conf->persistent_reconnect))
+ goto err_no_mem;
+
+ /* Listen Reg Class */
+ if (!wpa_dbus_dict_append_uint32(&dict_iter, "ListenRegClass",
+ wpa_s->conf->p2p_listen_reg_class))
+ goto err_no_mem;
+
+ /* Listen Channel */
+ if (!wpa_dbus_dict_append_uint32(&dict_iter, "ListenChannel",
+ wpa_s->conf->p2p_listen_channel))
+ goto err_no_mem;
+
+ /* Oper Reg Class */
+ if (!wpa_dbus_dict_append_uint32(&dict_iter, "OperRegClass",
+ wpa_s->conf->p2p_oper_reg_class))
+ goto err_no_mem;
+
+ /* Oper Channel */
+ if (!wpa_dbus_dict_append_uint32(&dict_iter, "OperChannel",
+ wpa_s->conf->p2p_oper_channel))
+ goto err_no_mem;
+
+ /* SSID Postfix */
+ if (wpa_s->conf->p2p_ssid_postfix &&
+ !wpa_dbus_dict_append_string(&dict_iter, "SsidPostfix",
+ wpa_s->conf->p2p_ssid_postfix))
+ goto err_no_mem;
+
+ /* Intra Bss */
+ if (!wpa_dbus_dict_append_bool(&dict_iter, "IntraBss",
+ wpa_s->conf->p2p_intra_bss))
+ goto err_no_mem;
+
+ /* Group Idle */
+ if (!wpa_dbus_dict_append_uint32(&dict_iter, "GroupIdle",
+ wpa_s->conf->p2p_group_idle))
+ goto err_no_mem;
+
+ /* Dissasociation low ack */
+ if (!wpa_dbus_dict_append_uint32(&dict_iter, "disassoc_low_ack",
+ wpa_s->conf->disassoc_low_ack))
+ goto err_no_mem;
+
+ if (!wpa_dbus_dict_close_write(&variant_iter, &dict_iter) ||
+ !dbus_message_iter_close_container(iter, &variant_iter))
+ goto err_no_mem;
+
+ return TRUE;
+
+err_no_mem:
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+ return FALSE;
+}
+
+
+dbus_bool_t wpas_dbus_setter_p2p_device_config(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ DBusMessageIter variant_iter, iter_dict;
+ struct wpa_dbus_dict_entry entry = {.type = DBUS_TYPE_STRING };
+ unsigned int i;
+
+ if (!wpa_dbus_p2p_check_enabled(wpa_s, NULL, NULL, error))
+ return FALSE;
+
+ dbus_message_iter_recurse(iter, &variant_iter);
+ if (!wpa_dbus_dict_open_read(&variant_iter, &iter_dict, error))
+ return FALSE;
+
+ while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+ if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) {
+ dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS,
+ "invalid message format");
+ return FALSE;
+ }
+
+ if (os_strcmp(entry.key, "DeviceName") == 0) {
+ char *devname;
+
+ if (entry.type != DBUS_TYPE_STRING)
+ goto error;
+
+ devname = os_strdup(entry.str_value);
+ if (devname == NULL)
+ goto err_no_mem_clear;
+
+ os_free(wpa_s->conf->device_name);
+ wpa_s->conf->device_name = devname;
+
+ wpa_s->conf->changed_parameters |=
+ CFG_CHANGED_DEVICE_NAME;
+ } else if (os_strcmp(entry.key, "PrimaryDeviceType") == 0) {
+ if (entry.type != DBUS_TYPE_ARRAY ||
+ entry.array_type != DBUS_TYPE_BYTE ||
+ entry.array_len != WPS_DEV_TYPE_LEN)
+ goto error;
+
+ os_memcpy(wpa_s->conf->device_type,
+ entry.bytearray_value,
+ WPS_DEV_TYPE_LEN);
+ wpa_s->conf->changed_parameters |=
+ CFG_CHANGED_DEVICE_TYPE;
+ } else if (os_strcmp(entry.key, "SecondaryDeviceTypes") == 0) {
+ if (entry.type != DBUS_TYPE_ARRAY ||
+ entry.array_type != WPAS_DBUS_TYPE_BINARRAY ||
+ entry.array_len > MAX_SEC_DEVICE_TYPES)
+ goto error;
+
+ for (i = 0; i < entry.array_len; i++)
+ if (wpabuf_len(entry.binarray_value[i]) !=
+ WPS_DEV_TYPE_LEN)
+ goto err_no_mem_clear;
+ for (i = 0; i < entry.array_len; i++)
+ os_memcpy(wpa_s->conf->sec_device_type[i],
+ wpabuf_head(entry.binarray_value[i]),
+ WPS_DEV_TYPE_LEN);
+ wpa_s->conf->num_sec_device_types = entry.array_len;
+ wpa_s->conf->changed_parameters |=
+ CFG_CHANGED_SEC_DEVICE_TYPE;
+ } else if (os_strcmp(entry.key, "VendorExtension") == 0) {
+ if ((entry.type != DBUS_TYPE_ARRAY) ||
+ (entry.array_type != WPAS_DBUS_TYPE_BINARRAY) ||
+ (entry.array_len > P2P_MAX_WPS_VENDOR_EXT))
+ goto error;
+
+ wpa_s->conf->changed_parameters |=
+ CFG_CHANGED_VENDOR_EXTENSION;
+
+ for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
+ wpabuf_free(wpa_s->conf->wps_vendor_ext[i]);
+ if (i < entry.array_len) {
+ wpa_s->conf->wps_vendor_ext[i] =
+ entry.binarray_value[i];
+ entry.binarray_value[i] = NULL;
+ } else
+ wpa_s->conf->wps_vendor_ext[i] = NULL;
+ }
+ } else if ((os_strcmp(entry.key, "GOIntent") == 0) &&
+ (entry.type == DBUS_TYPE_UINT32) &&
+ (entry.uint32_value <= 15))
+ wpa_s->conf->p2p_go_intent = entry.uint32_value;
+ else if ((os_strcmp(entry.key, "PersistentReconnect") == 0) &&
+ (entry.type == DBUS_TYPE_BOOLEAN))
+ wpa_s->conf->persistent_reconnect = entry.bool_value;
+ else if ((os_strcmp(entry.key, "ListenRegClass") == 0) &&
+ (entry.type == DBUS_TYPE_UINT32)) {
+ wpa_s->conf->p2p_listen_reg_class = entry.uint32_value;
+ wpa_s->conf->changed_parameters |=
+ CFG_CHANGED_P2P_LISTEN_CHANNEL;
+ } else if ((os_strcmp(entry.key, "ListenChannel") == 0) &&
+ (entry.type == DBUS_TYPE_UINT32)) {
+ wpa_s->conf->p2p_listen_channel = entry.uint32_value;
+ wpa_s->conf->changed_parameters |=
+ CFG_CHANGED_P2P_LISTEN_CHANNEL;
+ } else if ((os_strcmp(entry.key, "OperRegClass") == 0) &&
+ (entry.type == DBUS_TYPE_UINT32)) {
+ wpa_s->conf->p2p_oper_reg_class = entry.uint32_value;
+ wpa_s->conf->changed_parameters |=
+ CFG_CHANGED_P2P_OPER_CHANNEL;
+ } else if ((os_strcmp(entry.key, "OperChannel") == 0) &&
+ (entry.type == DBUS_TYPE_UINT32)) {
+ wpa_s->conf->p2p_oper_channel = entry.uint32_value;
+ wpa_s->conf->changed_parameters |=
+ CFG_CHANGED_P2P_OPER_CHANNEL;
+ } else if (os_strcmp(entry.key, "SsidPostfix") == 0) {
+ char *postfix;
+
+ if (entry.type != DBUS_TYPE_STRING)
+ goto error;
+
+ postfix = os_strdup(entry.str_value);
+ if (!postfix)
+ goto err_no_mem_clear;
+
+ os_free(wpa_s->conf->p2p_ssid_postfix);
+ wpa_s->conf->p2p_ssid_postfix = postfix;
+
+ wpa_s->conf->changed_parameters |=
+ CFG_CHANGED_P2P_SSID_POSTFIX;
+ } else if ((os_strcmp(entry.key, "IntraBss") == 0) &&
+ (entry.type == DBUS_TYPE_BOOLEAN)) {
+ wpa_s->conf->p2p_intra_bss = entry.bool_value;
+ wpa_s->conf->changed_parameters |=
+ CFG_CHANGED_P2P_INTRA_BSS;
+ } else if ((os_strcmp(entry.key, "GroupIdle") == 0) &&
+ (entry.type == DBUS_TYPE_UINT32))
+ wpa_s->conf->p2p_group_idle = entry.uint32_value;
+ else if (os_strcmp(entry.key, "disassoc_low_ack") == 0 &&
+ entry.type == DBUS_TYPE_UINT32)
+ wpa_s->conf->disassoc_low_ack = entry.uint32_value;
+ else
+ goto error;
+
+ wpa_dbus_dict_entry_clear(&entry);
+ }
+
+ if (wpa_s->conf->changed_parameters) {
+ /* Some changed parameters requires to update config*/
+ wpa_supplicant_update_config(wpa_s);
+ }
+
+ return TRUE;
+
+ error:
+ dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS,
+ "invalid message format");
+ wpa_dbus_dict_entry_clear(&entry);
+ return FALSE;
+
+ err_no_mem_clear:
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+ wpa_dbus_dict_entry_clear(&entry);
+ return FALSE;
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_peers(DBusMessageIter *iter, DBusError *error,
+ void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ struct p2p_data *p2p = wpa_s->global->p2p;
+ int next = 0, i = 0;
+ int num = 0, out_of_mem = 0;
+ const u8 *addr;
+ const struct p2p_peer_info *peer_info = NULL;
+ dbus_bool_t success = FALSE;
+
+ struct dl_list peer_objpath_list;
+ struct peer_objpath_node {
+ struct dl_list list;
+ char path[WPAS_DBUS_OBJECT_PATH_MAX];
+ } *node, *tmp;
+
+ char **peer_obj_paths = NULL;
+
+ if (!wpa_dbus_p2p_check_enabled(wpa_s, NULL, NULL, error))
+ return FALSE;
+
+ dl_list_init(&peer_objpath_list);
+
+ /* Get the first peer info */
+ peer_info = p2p_get_peer_found(p2p, NULL, next);
+
+ /* Get next and accumulate them */
+ next = 1;
+ while (peer_info != NULL) {
+ node = os_zalloc(sizeof(struct peer_objpath_node));
+ if (!node) {
+ out_of_mem = 1;
+ goto error;
+ }
+
+ addr = peer_info->p2p_device_addr;
+ os_snprintf(node->path, WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART
+ "/" COMPACT_MACSTR,
+ wpa_s->dbus_new_path, MAC2STR(addr));
+ dl_list_add_tail(&peer_objpath_list, &node->list);
+ num++;
+
+ peer_info = p2p_get_peer_found(p2p, addr, next);
+ }
+
+ /*
+ * Now construct the peer object paths in a form suitable for
+ * array_property_getter helper below.
+ */
+ peer_obj_paths = os_zalloc(num * sizeof(char *));
+
+ if (!peer_obj_paths) {
+ out_of_mem = 1;
+ goto error;
+ }
+
+ dl_list_for_each_safe(node, tmp, &peer_objpath_list,
+ struct peer_objpath_node, list)
+ peer_obj_paths[i++] = node->path;
+
+ success = wpas_dbus_simple_array_property_getter(iter,
+ DBUS_TYPE_OBJECT_PATH,
+ peer_obj_paths, num,
+ error);
+
+error:
+ if (peer_obj_paths)
+ os_free(peer_obj_paths);
+
+ dl_list_for_each_safe(node, tmp, &peer_objpath_list,
+ struct peer_objpath_node, list) {
+ dl_list_del(&node->list);
+ os_free(node);
+ }
+ if (out_of_mem)
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+
+ return success;
+}
+
+
+enum wpas_p2p_role {
+ WPAS_P2P_ROLE_DEVICE,
+ WPAS_P2P_ROLE_GO,
+ WPAS_P2P_ROLE_CLIENT,
+};
+
+static enum wpas_p2p_role wpas_get_p2p_role(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+ if (!ssid)
+ return WPAS_P2P_ROLE_DEVICE;
+ if (wpa_s->wpa_state != WPA_COMPLETED)
+ return WPAS_P2P_ROLE_DEVICE;
+
+ switch (ssid->mode) {
+ case WPAS_MODE_P2P_GO:
+ case WPAS_MODE_P2P_GROUP_FORMATION:
+ return WPAS_P2P_ROLE_GO;
+ case WPAS_MODE_INFRA:
+ if (ssid->p2p_group)
+ return WPAS_P2P_ROLE_CLIENT;
+ return WPAS_P2P_ROLE_DEVICE;
+ default:
+ return WPAS_P2P_ROLE_DEVICE;
+ }
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_role(DBusMessageIter *iter, DBusError *error,
+ void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ char *str;
+
+ switch (wpas_get_p2p_role(wpa_s)) {
+ case WPAS_P2P_ROLE_GO:
+ str = "GO";
+ break;
+ case WPAS_P2P_ROLE_CLIENT:
+ str = "client";
+ break;
+ default:
+ str = "device";
+ }
+
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, &str,
+ error);
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_group(DBusMessageIter *iter, DBusError *error,
+ void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ char path_buf[WPAS_DBUS_OBJECT_PATH_MAX];
+ char *dbus_groupobj_path = path_buf;
+
+ if (wpa_s->dbus_groupobj_path == NULL)
+ os_snprintf(dbus_groupobj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+ "/");
+ else
+ os_snprintf(dbus_groupobj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s", wpa_s->dbus_groupobj_path);
+
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_OBJECT_PATH,
+ &dbus_groupobj_path, error);
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_peergo(DBusMessageIter *iter,
+ DBusError *error, void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ char go_peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
+
+ if (wpas_get_p2p_role(wpa_s) != WPAS_P2P_ROLE_CLIENT)
+ os_snprintf(go_peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "/");
+ else
+ os_snprintf(go_peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/"
+ COMPACT_MACSTR,
+ wpa_s->dbus_new_path, MAC2STR(wpa_s->go_dev_addr));
+
+ path = go_peer_obj_path;
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_OBJECT_PATH,
+ &path, error);
+}
+
+
+/*
+ * Peer object properties accessor methods
+ */
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_device_name(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct peer_handler_args *peer_args = user_data;
+ const struct p2p_peer_info *info;
+ char *tmp;
+
+ if (!wpa_dbus_p2p_check_enabled(peer_args->wpa_s, NULL, NULL, error))
+ return FALSE;
+
+ /* get the peer info */
+ info = p2p_get_peer_found(peer_args->wpa_s->global->p2p,
+ peer_args->p2p_device_addr, 0);
+ if (info == NULL) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "failed to find peer");
+ return FALSE;
+ }
+
+ tmp = os_strdup(info->device_name);
+ if (!tmp) {
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+ return FALSE;
+ }
+
+ if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, &tmp,
+ error)) {
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+ os_free(tmp);
+ return FALSE;
+ }
+
+ os_free(tmp);
+ return TRUE;
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_primary_device_type(
+ DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+ struct peer_handler_args *peer_args = user_data;
+ const struct p2p_peer_info *info;
+
+ info = p2p_get_peer_found(peer_args->wpa_s->global->p2p,
+ peer_args->p2p_device_addr, 0);
+ if (info == NULL) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "failed to find peer");
+ return FALSE;
+ }
+
+ if (!wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE,
+ (char *)
+ info->pri_dev_type,
+ WPS_DEV_TYPE_LEN, error)) {
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_config_method(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct peer_handler_args *peer_args = user_data;
+ const struct p2p_peer_info *info;
+
+ info = p2p_get_peer_found(peer_args->wpa_s->global->p2p,
+ peer_args->p2p_device_addr, 0);
+ if (info == NULL) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "failed to find peer");
+ return FALSE;
+ }
+
+ if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT16,
+ &info->config_methods, error)) {
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_level(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct peer_handler_args *peer_args = user_data;
+ const struct p2p_peer_info *info;
+
+ info = p2p_get_peer_found(peer_args->wpa_s->global->p2p,
+ peer_args->p2p_device_addr, 0);
+ if (info == NULL) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "failed to find peer");
+ return FALSE;
+ }
+
+ if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_INT32,
+ &info->level, error)) {
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_device_capability(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct peer_handler_args *peer_args = user_data;
+ const struct p2p_peer_info *info;
+
+ info = p2p_get_peer_found(peer_args->wpa_s->global->p2p,
+ peer_args->p2p_device_addr, 0);
+ if (info == NULL) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "failed to find peer");
+ return FALSE;
+ }
+
+ if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BYTE,
+ &info->dev_capab, error)) {
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_group_capability(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct peer_handler_args *peer_args = user_data;
+ const struct p2p_peer_info *info;
+
+ info = p2p_get_peer_found(peer_args->wpa_s->global->p2p,
+ peer_args->p2p_device_addr, 0);
+ if (info == NULL) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "failed to find peer");
+ return FALSE;
+ }
+
+ if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BYTE,
+ &info->group_capab, error)) {
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_secondary_device_types(
+ DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+ struct peer_handler_args *peer_args = user_data;
+ const struct p2p_peer_info *info;
+ DBusMessageIter variant_iter, array_iter;
+
+ info = p2p_get_peer_found(peer_args->wpa_s->global->p2p,
+ peer_args->p2p_device_addr, 0);
+ if (info == NULL) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "failed to find peer");
+ return FALSE;
+ }
+
+ if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+ DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_TYPE_BYTE_AS_STRING,
+ &variant_iter)) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "%s: failed to construct message 1", __func__);
+ return FALSE;
+ }
+
+ if (!dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_TYPE_BYTE_AS_STRING,
+ &array_iter)) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "%s: failed to construct message 2", __func__);
+ return FALSE;
+ }
+
+ if (info->wps_sec_dev_type_list_len) {
+ const u8 *sec_dev_type_list = info->wps_sec_dev_type_list;
+ int num_sec_device_types =
+ info->wps_sec_dev_type_list_len / WPS_DEV_TYPE_LEN;
+ int i;
+ DBusMessageIter inner_array_iter;
+
+ for (i = 0; i < num_sec_device_types; i++) {
+ if (!dbus_message_iter_open_container(
+ &array_iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_BYTE_AS_STRING,
+ &inner_array_iter)) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "%s: failed to construct "
+ "message 3 (%d)",
+ __func__, i);
+ return FALSE;
+ }
+
+ if (!dbus_message_iter_append_fixed_array(
+ &inner_array_iter, DBUS_TYPE_BYTE,
+ &sec_dev_type_list, WPS_DEV_TYPE_LEN)) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "%s: failed to construct "
+ "message 4 (%d)",
+ __func__, i);
+ return FALSE;
+ }
+
+ if (!dbus_message_iter_close_container(
+ &array_iter, &inner_array_iter)) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "%s: failed to construct "
+ "message 5 (%d)",
+ __func__, i);
+ return FALSE;
+ }
+
+ sec_dev_type_list += WPS_DEV_TYPE_LEN;
+ }
+ }
+
+ if (!dbus_message_iter_close_container(&variant_iter, &array_iter)) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "%s: failed to construct message 6", __func__);
+ return FALSE;
+ }
+
+ if (!dbus_message_iter_close_container(iter, &variant_iter)) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "%s: failed to construct message 7", __func__);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_vendor_extension(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct wpabuf *vendor_extension[P2P_MAX_WPS_VENDOR_EXT];
+ int i, num;
+ struct peer_handler_args *peer_args = user_data;
+ const struct p2p_peer_info *info;
+
+ info = p2p_get_peer_found(peer_args->wpa_s->global->p2p,
+ peer_args->p2p_device_addr, 0);
+ if (info == NULL) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "failed to find peer");
+ return FALSE;
+ }
+
+ /* Add WPS vendor extensions attribute */
+ for (i = 0, num = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
+ if (info->wps_vendor_ext[i] == NULL)
+ continue;
+ vendor_extension[num] = info->wps_vendor_ext[i];
+ num++;
+ }
+
+ if (!wpas_dbus_simple_array_array_property_getter(iter, DBUS_TYPE_BYTE,
+ vendor_extension,
+ num, error))
+ return FALSE;
+
+ return TRUE;
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_ies(DBusMessageIter *iter,
+ DBusError *error, void *user_data)
+{
+ dbus_bool_t success;
+ /* struct peer_handler_args *peer_args = user_data; */
+
+ success = wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE,
+ NULL, 0, error);
+ return success;
+}
+
+
+/**
+ * wpas_dbus_getter_persistent_groups - Get array of persistent group objects
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "PersistentGroups" property.
+ */
+dbus_bool_t wpas_dbus_getter_persistent_groups(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ struct wpa_ssid *ssid;
+ char **paths;
+ unsigned int i = 0, num = 0;
+ dbus_bool_t success = FALSE;
+
+ if (wpa_s->conf == NULL) {
+ wpa_printf(MSG_ERROR, "dbus: %s: "
+ "An error occurred getting persistent groups list",
+ __func__);
+ dbus_set_error_const(error, DBUS_ERROR_FAILED, "an error "
+ "occurred getting persistent groups list");
+ return FALSE;
+ }
+
+ for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next)
+ if (network_is_persistent_group(ssid))
+ num++;
+
+ paths = os_zalloc(num * sizeof(char *));
+ if (!paths) {
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+ return FALSE;
+ }
+
+ /* Loop through configured networks and append object path of each */
+ for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+ if (!network_is_persistent_group(ssid))
+ continue;
+ paths[i] = os_zalloc(WPAS_DBUS_OBJECT_PATH_MAX);
+ if (paths[i] == NULL) {
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY,
+ "no memory");
+ goto out;
+ }
+ /* Construct the object path for this network. */
+ os_snprintf(paths[i++], WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s/" WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/%d",
+ wpa_s->dbus_new_path, ssid->id);
+ }
+
+ success = wpas_dbus_simple_array_property_getter(iter,
+ DBUS_TYPE_OBJECT_PATH,
+ paths, num, error);
+
+out:
+ while (i)
+ os_free(paths[--i]);
+ os_free(paths);
+ return success;
+}
+
+
+/**
+ * wpas_dbus_getter_persistent_group_properties - Get options for a persistent
+ * group
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "Properties" property of a persistent group.
+ */
+dbus_bool_t wpas_dbus_getter_persistent_group_properties(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct network_handler_args *net = user_data;
+
+ /* Leveraging the fact that persistent group object is still
+ * represented in same manner as network within.
+ */
+ return wpas_dbus_getter_network_properties(iter, error, net);
+}
+
+
+/**
+ * wpas_dbus_setter_persistent_group_properties - Get options for a persistent
+ * group
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Setter for "Properties" property of a persistent group.
+ */
+dbus_bool_t wpas_dbus_setter_persistent_group_properties(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct network_handler_args *net = user_data;
+ struct wpa_ssid *ssid = net->ssid;
+ DBusMessageIter variant_iter;
+
+ /*
+ * Leveraging the fact that persistent group object is still
+ * represented in same manner as network within.
+ */
+ dbus_message_iter_recurse(iter, &variant_iter);
+ return set_network_properties(net->wpa_s, ssid, &variant_iter, error);
+}
+
+
+/**
+ * wpas_dbus_new_iface_add_persistent_group - Add a new configured
+ * persistent_group
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: A dbus message containing the object path of the new
+ * persistent group
+ *
+ * Handler function for "AddPersistentGroup" method call of a P2P Device
+ * interface.
+ */
+DBusMessage * wpas_dbus_handler_add_persistent_group(
+ DBusMessage *message, struct wpa_supplicant *wpa_s)
+{
+ DBusMessage *reply = NULL;
+ DBusMessageIter iter;
+ struct wpa_ssid *ssid = NULL;
+ char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *path = path_buf;
+ DBusError error;
+
+ dbus_message_iter_init(message, &iter);
+
+ ssid = wpa_config_add_network(wpa_s->conf);
+ if (ssid == NULL) {
+ wpa_printf(MSG_ERROR, "dbus: %s: "
+ "Cannot add new persistent group", __func__);
+ reply = wpas_dbus_error_unknown_error(
+ message,
+ "wpa_supplicant could not add "
+ "a persistent group on this interface.");
+ goto err;
+ }
+
+ /* Mark the ssid as being a persistent group before the notification */
+ ssid->disabled = 2;
+ ssid->p2p_persistent_group = 1;
+ wpas_notify_persistent_group_added(wpa_s, ssid);
+
+ wpa_config_set_network_defaults(ssid);
+
+ dbus_error_init(&error);
+ if (!set_network_properties(wpa_s, ssid, &iter, &error)) {
+ wpa_printf(MSG_DEBUG, "dbus: %s: "
+ "Control interface could not set persistent group "
+ "properties", __func__);
+ reply = wpas_dbus_reply_new_from_error(message, &error,
+ DBUS_ERROR_INVALID_ARGS,
+ "Failed to set network "
+ "properties");
+ dbus_error_free(&error);
+ goto err;
+ }
+
+ /* Construct the object path for this network. */
+ os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s/" WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/%d",
+ wpa_s->dbus_new_path, ssid->id);
+
+ reply = dbus_message_new_method_return(message);
+ if (reply == NULL) {
+ reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
+ NULL);
+ goto err;
+ }
+ if (!dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID)) {
+ dbus_message_unref(reply);
+ reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
+ NULL);
+ goto err;
+ }
+
+ return reply;
+
+err:
+ if (ssid) {
+ wpas_notify_persistent_group_removed(wpa_s, ssid);
+ wpa_config_remove_network(wpa_s->conf, ssid->id);
+ }
+ return reply;
+}
+
+
+/**
+ * wpas_dbus_handler_remove_persistent_group - Remove a configured persistent
+ * group
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL on success or dbus error on failure
+ *
+ * Handler function for "RemovePersistentGroup" method call of a P2P Device
+ * interface.
+ */
+DBusMessage * wpas_dbus_handler_remove_persistent_group(
+ DBusMessage *message, struct wpa_supplicant *wpa_s)
+{
+ DBusMessage *reply = NULL;
+ const char *op;
+ char *iface = NULL, *persistent_group_id = NULL;
+ int id;
+ struct wpa_ssid *ssid;
+
+ dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &op,
+ DBUS_TYPE_INVALID);
+
+ /*
+ * Extract the network ID and ensure the network is actually a child of
+ * this interface.
+ */
+ iface = wpas_dbus_new_decompose_object_path(op, 1,
+ &persistent_group_id,
+ NULL);
+ if (iface == NULL || os_strcmp(iface, wpa_s->dbus_new_path) != 0) {
+ reply = wpas_dbus_error_invalid_args(message, op);
+ goto out;
+ }
+
+ id = strtoul(persistent_group_id, NULL, 10);
+ if (errno == EINVAL) {
+ reply = wpas_dbus_error_invalid_args(message, op);
+ goto out;
+ }
+
+ ssid = wpa_config_get_network(wpa_s->conf, id);
+ if (ssid == NULL) {
+ reply = wpas_dbus_error_persistent_group_unknown(message);
+ goto out;
+ }
+
+ wpas_notify_persistent_group_removed(wpa_s, ssid);
+
+ if (wpa_config_remove_network(wpa_s->conf, id) < 0) {
+ wpa_printf(MSG_ERROR, "dbus: %s: "
+ "error occurred when removing persistent group %d",
+ __func__, id);
+ reply = wpas_dbus_error_unknown_error(
+ message,
+ "error removing the specified persistent group on "
+ "this interface.");
+ goto out;
+ }
+
+out:
+ os_free(iface);
+ os_free(persistent_group_id);
+ return reply;
+}
+
+
+static void remove_persistent_group(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+ wpas_notify_persistent_group_removed(wpa_s, ssid);
+
+ if (wpa_config_remove_network(wpa_s->conf, ssid->id) < 0) {
+ wpa_printf(MSG_ERROR, "dbus: %s: "
+ "error occurred when removing persistent group %d",
+ __func__, ssid->id);
+ return;
+ }
+}
+
+
+/**
+ * wpas_dbus_handler_remove_all_persistent_groups - Remove all configured
+ * persistent groups
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL on success or dbus error on failure
+ *
+ * Handler function for "RemoveAllPersistentGroups" method call of a
+ * P2P Device interface.
+ */
+DBusMessage * wpas_dbus_handler_remove_all_persistent_groups(
+ DBusMessage *message, struct wpa_supplicant *wpa_s)
+{
+ struct wpa_ssid *ssid, *next;
+ struct wpa_config *config;
+
+ config = wpa_s->conf;
+ ssid = config->ssid;
+ while (ssid) {
+ next = ssid->next;
+ if (network_is_persistent_group(ssid))
+ remove_persistent_group(wpa_s, ssid);
+ ssid = next;
+ }
+ return NULL;
+}
+
+
+/*
+ * Group object properties accessor methods
+ */
+
+dbus_bool_t wpas_dbus_getter_p2p_group_members(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ struct wpa_ssid *ssid;
+ unsigned int num_members;
+ char **paths;
+ unsigned int i;
+ void *next = NULL;
+ const u8 *addr;
+ dbus_bool_t success = FALSE;
+
+ /* Verify correct role for this property */
+ if (wpas_get_p2p_role(wpa_s) != WPAS_P2P_ROLE_GO) {
+ return wpas_dbus_simple_array_property_getter(
+ iter, DBUS_TYPE_OBJECT_PATH, NULL, 0, error);
+ }
+
+ ssid = wpa_s->conf->ssid;
+ /* At present WPAS P2P_GO mode only applicable for p2p_go */
+ if (ssid->mode != WPAS_MODE_P2P_GO &&
+ ssid->mode != WPAS_MODE_AP &&
+ ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION)
+ return FALSE;
+
+ num_members = p2p_get_group_num_members(wpa_s->p2p_group);
+
+ paths = os_zalloc(num_members * sizeof(char *));
+ if (!paths)
+ goto out_of_memory;
+
+ i = 0;
+ while ((addr = p2p_iterate_group_members(wpa_s->p2p_group, &next))) {
+ paths[i] = os_zalloc(WPAS_DBUS_OBJECT_PATH_MAX);
+ if (!paths[i])
+ goto out_of_memory;
+ os_snprintf(paths[i], WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s/" WPAS_DBUS_NEW_P2P_GROUPMEMBERS_PART
+ "/" COMPACT_MACSTR,
+ wpa_s->dbus_groupobj_path, MAC2STR(addr));
+ i++;
+ }
+
+ success = wpas_dbus_simple_array_property_getter(iter,
+ DBUS_TYPE_OBJECT_PATH,
+ paths, num_members,
+ error);
+
+ for (i = 0; i < num_members; i++)
+ os_free(paths[i]);
+ os_free(paths);
+ return success;
+
+out_of_memory:
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+ if (paths) {
+ for (i = 0; i < num_members; i++)
+ os_free(paths[i]);
+ os_free(paths);
+ }
+ return FALSE;
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_group_ssid(DBusMessageIter *iter,
+ DBusError *error, void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ if (wpa_s->current_ssid == NULL)
+ return FALSE;
+ return wpas_dbus_simple_array_property_getter(
+ iter, DBUS_TYPE_BYTE, wpa_s->current_ssid->ssid,
+ wpa_s->current_ssid->ssid_len, error);
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_group_bssid(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ u8 role = wpas_get_p2p_role(wpa_s);
+ u8 *p_bssid;
+
+ if (role == WPAS_P2P_ROLE_CLIENT) {
+ if (wpa_s->current_ssid == NULL)
+ return FALSE;
+ p_bssid = wpa_s->current_ssid->bssid;
+ } else {
+ if (wpa_s->ap_iface == NULL)
+ return FALSE;
+ p_bssid = wpa_s->ap_iface->bss[0]->own_addr;
+ }
+
+ return wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE,
+ p_bssid, ETH_ALEN,
+ error);
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_group_frequency(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ u16 op_freq;
+ u8 role = wpas_get_p2p_role(wpa_s);
+
+ if (role == WPAS_P2P_ROLE_CLIENT) {
+ if (wpa_s->go_params == NULL)
+ return FALSE;
+ op_freq = wpa_s->go_params->freq;
+ } else {
+ if (wpa_s->ap_iface == NULL)
+ return FALSE;
+ op_freq = wpa_s->ap_iface->freq;
+ }
+
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT16,
+ &op_freq, error);
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_group_passphrase(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ u8 role = wpas_get_p2p_role(wpa_s);
+ char *p_pass = NULL;
+
+ /* Verify correct role for this property */
+ if (role == WPAS_P2P_ROLE_GO) {
+ if (wpa_s->current_ssid == NULL)
+ return FALSE;
+ p_pass = wpa_s->current_ssid->passphrase;
+ } else
+ p_pass = "";
+
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
+ &p_pass, error);
+
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_group_psk(DBusMessageIter *iter,
+ DBusError *error, void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ u8 role = wpas_get_p2p_role(wpa_s);
+ u8 *p_psk = NULL;
+ u8 psk_len = 0;
+
+ /* Verify correct role for this property */
+ if (role == WPAS_P2P_ROLE_CLIENT) {
+ if (wpa_s->current_ssid == NULL)
+ return FALSE;
+ p_psk = wpa_s->current_ssid->psk;
+ psk_len = 32;
+ }
+
+ return wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE,
+ &p_psk, psk_len, error);
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_group_vendor_ext(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ struct hostapd_data *hapd;
+ struct wpabuf *vendor_ext[MAX_WPS_VENDOR_EXTENSIONS];
+ int num_vendor_ext = 0;
+ int i;
+
+ /* Verify correct role for this property */
+ if (wpas_get_p2p_role(wpa_s) == WPAS_P2P_ROLE_GO) {
+ if (wpa_s->ap_iface == NULL)
+ return FALSE;
+ hapd = wpa_s->ap_iface->bss[0];
+
+ /* Parse WPS Vendor Extensions sent in Beacon/Probe Response */
+ for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) {
+ if (hapd->conf->wps_vendor_ext[i] == NULL)
+ vendor_ext[i] = NULL;
+ else {
+ vendor_ext[num_vendor_ext++] =
+ hapd->conf->wps_vendor_ext[i];
+ }
+ }
+ }
+
+ /* Return vendor extensions or no data */
+ return wpas_dbus_simple_array_array_property_getter(iter,
+ DBUS_TYPE_BYTE,
+ vendor_ext,
+ num_vendor_ext,
+ error);
+}
+
+
+dbus_bool_t wpas_dbus_setter_p2p_group_vendor_ext(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ DBusMessageIter variant_iter, iter_dict;
+ struct wpa_dbus_dict_entry entry = { .type = DBUS_TYPE_STRING };
+ unsigned int i;
+ struct hostapd_data *hapd = NULL;
+
+ if (wpas_get_p2p_role(wpa_s) == WPAS_P2P_ROLE_GO &&
+ wpa_s->ap_iface != NULL)
+ hapd = wpa_s->ap_iface->bss[0];
+ else
+ return FALSE;
+
+ dbus_message_iter_recurse(iter, &variant_iter);
+ if (!wpa_dbus_dict_open_read(&variant_iter, &iter_dict, error))
+ return FALSE;
+
+ while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+ if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) {
+ dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS,
+ "invalid message format");
+ return FALSE;
+ }
+
+ if (os_strcmp(entry.key, "WPSVendorExtensions") == 0) {
+ if (entry.type != DBUS_TYPE_ARRAY ||
+ entry.array_type != WPAS_DBUS_TYPE_BINARRAY ||
+ entry.array_len > MAX_WPS_VENDOR_EXTENSIONS)
+ goto error;
+
+ for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) {
+ if (i < entry.array_len) {
+ hapd->conf->wps_vendor_ext[i] =
+ entry.binarray_value[i];
+ entry.binarray_value[i] = NULL;
+ } else
+ hapd->conf->wps_vendor_ext[i] = NULL;
+ }
+
+ hostapd_update_wps(hapd);
+ } else
+ goto error;
+
+ wpa_dbus_dict_entry_clear(&entry);
+ }
+
+ return TRUE;
+
+error:
+ wpa_dbus_dict_entry_clear(&entry);
+ dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS,
+ "invalid message format");
+ return FALSE;
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_add_service(DBusMessage *message,
+ struct wpa_supplicant *wpa_s)
+{
+ DBusMessageIter iter_dict;
+ DBusMessage *reply = NULL;
+ DBusMessageIter iter;
+ struct wpa_dbus_dict_entry entry;
+ int upnp = 0;
+ int bonjour = 0;
+ char *service = NULL;
+ struct wpabuf *query = NULL;
+ struct wpabuf *resp = NULL;
+ u8 version = 0;
+
+ dbus_message_iter_init(message, &iter);
+
+ if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
+ goto error;
+
+ if (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+ if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+ goto error;
+
+ if (!os_strcmp(entry.key, "service_type") &&
+ (entry.type == DBUS_TYPE_STRING)) {
+ if (!os_strcmp(entry.str_value, "upnp"))
+ upnp = 1;
+ else if (!os_strcmp(entry.str_value, "bonjour"))
+ bonjour = 1;
+ else
+ goto error_clear;
+ wpa_dbus_dict_entry_clear(&entry);
+ }
+ }
+
+ if (upnp == 1) {
+ while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+ if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+ goto error;
+
+ if (!os_strcmp(entry.key, "version") &&
+ entry.type == DBUS_TYPE_INT32)
+ version = entry.uint32_value;
+ else if (!os_strcmp(entry.key, "service") &&
+ entry.type == DBUS_TYPE_STRING)
+ service = os_strdup(entry.str_value);
+ wpa_dbus_dict_entry_clear(&entry);
+ }
+ if (version <= 0 || service == NULL)
+ goto error;
+
+ if (wpas_p2p_service_add_upnp(wpa_s, version, service) != 0)
+ goto error;
+
+ os_free(service);
+ } else if (bonjour == 1) {
+ while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+ if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+ goto error;
+
+ if (!os_strcmp(entry.key, "query")) {
+ if ((entry.type != DBUS_TYPE_ARRAY) ||
+ (entry.array_type != DBUS_TYPE_BYTE))
+ goto error_clear;
+ query = wpabuf_alloc_copy(
+ entry.bytearray_value,
+ entry.array_len);
+ } else if (!os_strcmp(entry.key, "response")) {
+ if ((entry.type != DBUS_TYPE_ARRAY) ||
+ (entry.array_type != DBUS_TYPE_BYTE))
+ goto error_clear;
+ resp = wpabuf_alloc_copy(entry.bytearray_value,
+ entry.array_len);
+ }
+
+ wpa_dbus_dict_entry_clear(&entry);
+ }
+
+ if (query == NULL || resp == NULL)
+ goto error;
+
+ if (wpas_p2p_service_add_bonjour(wpa_s, query, resp) < 0) {
+ wpabuf_free(query);
+ wpabuf_free(resp);
+ goto error;
+ }
+ } else
+ goto error;
+
+ return reply;
+error_clear:
+ wpa_dbus_dict_entry_clear(&entry);
+error:
+ return wpas_dbus_error_invalid_args(message, NULL);
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_delete_service(
+ DBusMessage *message, struct wpa_supplicant *wpa_s)
+{
+ DBusMessageIter iter_dict;
+ DBusMessage *reply = NULL;
+ DBusMessageIter iter;
+ struct wpa_dbus_dict_entry entry;
+ int upnp = 0;
+ int bonjour = 0;
+ int ret = 0;
+ char *service = NULL;
+ struct wpabuf *query = NULL;
+ u8 version = 0;
+
+ dbus_message_iter_init(message, &iter);
+
+ if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
+ goto error;
+
+ if (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+ if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+ goto error;
+
+ if (!os_strcmp(entry.key, "service_type") &&
+ (entry.type == DBUS_TYPE_STRING)) {
+ if (!os_strcmp(entry.str_value, "upnp"))
+ upnp = 1;
+ else if (!os_strcmp(entry.str_value, "bonjour"))
+ bonjour = 1;
+ else
+ goto error_clear;
+ wpa_dbus_dict_entry_clear(&entry);
+ }
+ }
+ if (upnp == 1) {
+ while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+ if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+ goto error;
+ if (!os_strcmp(entry.key, "version") &&
+ entry.type == DBUS_TYPE_INT32)
+ version = entry.uint32_value;
+ else if (!os_strcmp(entry.key, "service") &&
+ entry.type == DBUS_TYPE_STRING)
+ service = os_strdup(entry.str_value);
+ else
+ goto error_clear;
+
+ wpa_dbus_dict_entry_clear(&entry);
+ }
+
+ if (version <= 0 || service == NULL)
+ goto error;
+
+ ret = wpas_p2p_service_del_upnp(wpa_s, version, service);
+ os_free(service);
+ if (ret != 0)
+ goto error;
+ } else if (bonjour == 1) {
+ while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+ if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+ goto error;
+
+ if (!os_strcmp(entry.key, "query")) {
+ if ((entry.type != DBUS_TYPE_ARRAY) ||
+ (entry.array_type != DBUS_TYPE_BYTE))
+ goto error_clear;
+ query = wpabuf_alloc_copy(
+ entry.bytearray_value,
+ entry.array_len);
+ } else
+ goto error_clear;
+
+ wpa_dbus_dict_entry_clear(&entry);
+ }
+
+ if (query == NULL)
+ goto error;
+
+ ret = wpas_p2p_service_del_bonjour(wpa_s, query);
+ if (ret != 0)
+ goto error;
+ wpabuf_free(query);
+ } else
+ goto error;
+
+ return reply;
+error_clear:
+ wpa_dbus_dict_entry_clear(&entry);
+error:
+ return wpas_dbus_error_invalid_args(message, NULL);
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_flush_service(DBusMessage *message,
+ struct wpa_supplicant *wpa_s)
+{
+ wpas_p2p_service_flush(wpa_s);
+ return NULL;
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_service_sd_req(
+ DBusMessage *message, struct wpa_supplicant *wpa_s)
+{
+ DBusMessageIter iter_dict;
+ DBusMessage *reply = NULL;
+ DBusMessageIter iter;
+ struct wpa_dbus_dict_entry entry;
+ int upnp = 0;
+ char *service = NULL;
+ char *peer_object_path = NULL;
+ struct wpabuf *tlv = NULL;
+ u8 version = 0;
+ u64 ref = 0;
+ u8 addr_buf[ETH_ALEN], *addr;
+
+ dbus_message_iter_init(message, &iter);
+
+ if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
+ goto error;
+
+ while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+ if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+ goto error;
+ if (!os_strcmp(entry.key, "peer_object") &&
+ entry.type == DBUS_TYPE_OBJECT_PATH) {
+ peer_object_path = os_strdup(entry.str_value);
+ } else if (!os_strcmp(entry.key, "service_type") &&
+ entry.type == DBUS_TYPE_STRING) {
+ if (!os_strcmp(entry.str_value, "upnp"))
+ upnp = 1;
+ else
+ goto error_clear;
+ } else if (!os_strcmp(entry.key, "version") &&
+ entry.type == DBUS_TYPE_INT32) {
+ version = entry.uint32_value;
+ } else if (!os_strcmp(entry.key, "service") &&
+ entry.type == DBUS_TYPE_STRING) {
+ service = os_strdup(entry.str_value);
+ } else if (!os_strcmp(entry.key, "tlv")) {
+ if (entry.type != DBUS_TYPE_ARRAY ||
+ entry.array_type != DBUS_TYPE_BYTE)
+ goto error_clear;
+ tlv = wpabuf_alloc_copy(entry.bytearray_value,
+ entry.array_len);
+ } else
+ goto error_clear;
+
+ wpa_dbus_dict_entry_clear(&entry);
+ }
+
+ if (!peer_object_path) {
+ addr = NULL;
+ } else {
+ if (parse_peer_object_path(peer_object_path, addr_buf) < 0 ||
+ !p2p_peer_known(wpa_s->global->p2p, addr_buf))
+ goto error;
+
+ addr = addr_buf;
+ }
+
+ if (upnp == 1) {
+ if (version <= 0 || service == NULL)
+ goto error;
+
+ ref = (unsigned long) wpas_p2p_sd_request_upnp(wpa_s, addr,
+ version,
+ service);
+ } else {
+ if (tlv == NULL)
+ goto error;
+ ref = (unsigned long)wpas_p2p_sd_request(wpa_s, addr, tlv);
+ wpabuf_free(tlv);
+ }
+
+ if (ref != 0) {
+ reply = dbus_message_new_method_return(message);
+ dbus_message_append_args(reply, DBUS_TYPE_UINT64,
+ &ref, DBUS_TYPE_INVALID);
+ } else {
+ reply = wpas_dbus_error_unknown_error(
+ message, "Unable to send SD request");
+ }
+out:
+ os_free(service);
+ os_free(peer_object_path);
+ return reply;
+error_clear:
+ wpa_dbus_dict_entry_clear(&entry);
+error:
+ if (tlv)
+ wpabuf_free(tlv);
+ reply = wpas_dbus_error_invalid_args(message, NULL);
+ goto out;
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_service_sd_res(
+ DBusMessage *message, struct wpa_supplicant *wpa_s)
+{
+ DBusMessageIter iter_dict;
+ DBusMessage *reply = NULL;
+ DBusMessageIter iter;
+ struct wpa_dbus_dict_entry entry;
+ char *peer_object_path = NULL;
+ struct wpabuf *tlv = NULL;
+ int freq = 0;
+ int dlg_tok = 0;
+ u8 addr[ETH_ALEN];
+
+ dbus_message_iter_init(message, &iter);
+
+ if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
+ goto error;
+
+ while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+ if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+ goto error;
+
+ if (!os_strcmp(entry.key, "peer_object") &&
+ entry.type == DBUS_TYPE_OBJECT_PATH) {
+ peer_object_path = os_strdup(entry.str_value);
+ } else if (!os_strcmp(entry.key, "frequency") &&
+ entry.type == DBUS_TYPE_INT32) {
+ freq = entry.uint32_value;
+ } else if (!os_strcmp(entry.key, "dialog_token") &&
+ entry.type == DBUS_TYPE_UINT32) {
+ dlg_tok = entry.uint32_value;
+ } else if (!os_strcmp(entry.key, "tlvs")) {
+ if (entry.type != DBUS_TYPE_ARRAY ||
+ entry.array_type != DBUS_TYPE_BYTE)
+ goto error_clear;
+ tlv = wpabuf_alloc_copy(entry.bytearray_value,
+ entry.array_len);
+ } else
+ goto error_clear;
+
+ wpa_dbus_dict_entry_clear(&entry);
+ }
+ if (!peer_object_path ||
+ (parse_peer_object_path(peer_object_path, addr) < 0) ||
+ !p2p_peer_known(wpa_s->global->p2p, addr))
+ goto error;
+
+ if (tlv == NULL)
+ goto error;
+
+ wpas_p2p_sd_response(wpa_s, freq, addr, (u8) dlg_tok, tlv);
+ wpabuf_free(tlv);
+out:
+ os_free(peer_object_path);
+ return reply;
+error_clear:
+ wpa_dbus_dict_entry_clear(&entry);
+error:
+ reply = wpas_dbus_error_invalid_args(message, NULL);
+ goto out;
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_service_sd_cancel_req(
+ DBusMessage *message, struct wpa_supplicant *wpa_s)
+{
+ DBusMessageIter iter;
+ u64 req = 0;
+
+ dbus_message_iter_init(message, &iter);
+ dbus_message_iter_get_basic(&iter, &req);
+
+ if (req == 0)
+ goto error;
+
+ if (!wpas_p2p_sd_cancel_request(wpa_s, (void *)(unsigned long) req))
+ goto error;
+
+ return NULL;
+error:
+ return wpas_dbus_error_invalid_args(message, NULL);
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_service_update(
+ DBusMessage *message, struct wpa_supplicant *wpa_s)
+{
+ wpas_p2p_sd_service_update(wpa_s);
+ return NULL;
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_serv_disc_external(
+ DBusMessage *message, struct wpa_supplicant *wpa_s)
+{
+ DBusMessageIter iter;
+ int ext = 0;
+
+ dbus_message_iter_init(message, &iter);
+ dbus_message_iter_get_basic(&iter, &ext);
+
+ wpa_s->p2p_sd_over_ctrl_iface = ext;
+
+ return NULL;
+
+}
--- /dev/null
+
+/*
+ * WPA Supplicant / dbus-based control interface for p2p
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef DBUS_NEW_HANDLERS_P2P_H
+#define DBUS_NEW_HANDLERS_P2P_H
+
+struct peer_handler_args {
+ struct wpa_supplicant *wpa_s;
+ u8 p2p_device_addr[ETH_ALEN];
+};
+
+struct groupmember_handler_args {
+ struct wpa_supplicant *wpa_s;
+ u8 member_addr[ETH_ALEN];
+};
+
+/*
+ * P2P Device methods
+ */
+
+DBusMessage *wpas_dbus_handler_p2p_find(
+ DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_stop_find(
+ DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_rejectpeer(
+ DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_listen(
+ DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_extendedlisten(
+ DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_presence_request(
+ DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_prov_disc_req(
+ DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_group_add(
+ DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_connect(
+ DBusMessage *message,
+ struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_invite(
+ DBusMessage *message,
+ struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_disconnect(
+ DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_flush(
+ DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_add_service(
+ DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_delete_service(
+ DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_flush_service(
+ DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_service_sd_req(
+ DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_service_sd_res(
+ DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_service_sd_cancel_req(
+ DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_service_update(
+ DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_serv_disc_external(
+ DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+/*
+ * P2P Device property accessor methods.
+ */
+dbus_bool_t wpas_dbus_setter_p2p_device_config(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_device_config(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_peers(DBusMessageIter *iter, DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_role(DBusMessageIter *iter, DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_group(DBusMessageIter *iter, DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_peergo(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
+
+/*
+ * P2P Peer properties.
+ */
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_device_name(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_primary_device_type(
+ DBusMessageIter *iter, DBusError *error, void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_config_method(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_level(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_device_capability(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_group_capability(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_secondary_device_types(
+ DBusMessageIter *iter, DBusError *error, void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_vendor_extension(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_ies(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
+
+/*
+ * P2P Group properties
+ */
+
+dbus_bool_t wpas_dbus_getter_p2p_group_members(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_group_ssid(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_group_bssid(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_group_frequency(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_group_passphrase(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_group_psk(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_group_vendor_ext(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_setter_p2p_group_vendor_ext(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
+
+/*
+ * P2P Persistent Groups and properties
+ */
+
+dbus_bool_t wpas_dbus_getter_persistent_groups(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_getter_persistent_group_properties(DBusMessageIter *iter,
+ DBusError *error, void *user_data);
+
+dbus_bool_t wpas_dbus_setter_persistent_group_properties(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
+
+DBusMessage * wpas_dbus_handler_add_persistent_group(
+ DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_handler_remove_persistent_group(
+ DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_handler_remove_all_persistent_groups(
+ DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+
+#endif /* DBUS_NEW_HANDLERS_P2P_H */
#include "../config.h"
#include "../wpa_supplicant_i.h"
#include "../wps_supplicant.h"
+#include "../driver_i.h"
+#include "../ap.h"
#include "dbus_new_helpers.h"
#include "dbus_new.h"
#include "dbus_new_handlers.h"
int type; /* 0 - not set, 1 - pin, 2 - pbc */
u8 *bssid;
char *pin;
+ u8 *p2p_dev_addr;
};
dbus_message_iter_recurse(entry_iter, &variant_iter);
if (dbus_message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_ARRAY ||
dbus_message_iter_get_element_type(&variant_iter) !=
- DBUS_TYPE_ARRAY) {
+ DBUS_TYPE_BYTE) {
wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Wrong Bssid type, "
"byte array required");
*reply = wpas_dbus_error_invalid_args(
}
+#ifdef CONFIG_P2P
+static int wpas_dbus_handler_wps_p2p_dev_addr(DBusMessage *message,
+ DBusMessageIter *entry_iter,
+ struct wps_start_params *params,
+ DBusMessage **reply)
+{
+ DBusMessageIter variant_iter, array_iter;
+ int len;
+
+ dbus_message_iter_recurse(entry_iter, &variant_iter);
+ if (dbus_message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_ARRAY ||
+ dbus_message_iter_get_element_type(&variant_iter) !=
+ DBUS_TYPE_BYTE) {
+ wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Wrong "
+ "P2PDeviceAddress type, byte array required");
+ *reply = wpas_dbus_error_invalid_args(
+ message, "P2PDeviceAddress must be a byte array");
+ return -1;
+ }
+ dbus_message_iter_recurse(&variant_iter, &array_iter);
+ dbus_message_iter_get_fixed_array(&array_iter, ¶ms->p2p_dev_addr,
+ &len);
+ if (len != ETH_ALEN) {
+ wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Wrong "
+ "P2PDeviceAddress length %d", len);
+ *reply = wpas_dbus_error_invalid_args(message,
+ "P2PDeviceAddress "
+ "has wrong length");
+ return -1;
+ }
+ return 0;
+}
+#endif /* CONFIG_P2P */
+
+
static int wpas_dbus_handler_wps_start_entry(DBusMessage *message, char *key,
DBusMessageIter *entry_iter,
struct wps_start_params *params,
else if (os_strcmp(key, "Pin") == 0)
return wpas_dbus_handler_wps_pin(message, entry_iter,
params, reply);
+#ifdef CONFIG_P2P
+ else if (os_strcmp(key, "P2PDeviceAddress") == 0)
+ return wpas_dbus_handler_wps_p2p_dev_addr(message, entry_iter,
+ params, reply);
+#endif /* CONFIG_P2P */
wpa_printf(MSG_DEBUG, "dbus: WPS.Start - unknown key %s", key);
*reply = wpas_dbus_error_invalid_args(message, key);
ret = wpas_wps_start_reg(wpa_s, params.bssid, params.pin,
NULL);
else if (params.type == 1) {
- ret = wpas_wps_start_pin(wpa_s, params.bssid, params.pin);
- if (ret > 0)
- os_snprintf(npin, sizeof(npin), "%08d", ret);
- } else
- ret = wpas_wps_start_pbc(wpa_s, params.bssid);
+#ifdef CONFIG_AP
+ if (wpa_s->ap_iface)
+ ret = wpa_supplicant_ap_wps_pin(wpa_s,
+ params.bssid,
+ params.pin,
+ npin, sizeof(npin));
+ else
+#endif /* CONFIG_AP */
+ {
+ ret = wpas_wps_start_pin(wpa_s, params.bssid,
+ params.pin, 0,
+ DEV_PW_DEFAULT);
+ if (ret > 0)
+ os_snprintf(npin, sizeof(npin), "%08d", ret);
+ }
+ } else {
+#ifdef CONFIG_AP
+ if (wpa_s->ap_iface)
+ ret = wpa_supplicant_ap_wps_pbc(wpa_s,
+ params.bssid,
+ params.p2p_dev_addr);
+ else
+#endif /* CONFIG_AP */
+ ret = wpas_wps_start_pbc(wpa_s, params.bssid, 0);
+ }
if (ret < 0) {
wpa_printf(MSG_DEBUG, "dbus: WPS.Start wpas_wps_failed in "
* wpas_dbus_getter_process_credentials - Check if credentials are processed
* @message: Pointer to incoming dbus message
* @wpa_s: %wpa_supplicant data structure
- * Returns: DBus message with a boolean on success or DBus error on failure
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "ProcessCredentials" property. Returns returned boolean will be
* true if wps_cred_processing configuration field is not equal to 1 or false
* if otherwise.
*/
-DBusMessage * wpas_dbus_getter_process_credentials(
- DBusMessage *message, struct wpa_supplicant *wpa_s)
+dbus_bool_t wpas_dbus_getter_process_credentials(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
{
+ struct wpa_supplicant *wpa_s = user_data;
dbus_bool_t process = (wpa_s->conf->wps_cred_processing != 1);
- return wpas_dbus_simple_property_getter(message, DBUS_TYPE_BOOLEAN,
- &process);
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN,
+ &process, error);
}
/**
* wpas_dbus_setter_process_credentials - Set credentials_processed conf param
- * @message: Pointer to incoming dbus message
- * @wpa_s: %wpa_supplicant data structure
- * Returns: NULL on success or DBus error on failure
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Setter for "ProcessCredentials" property. Sets credentials_processed on 2
* if boolean argument is true or on 1 if otherwise.
*/
-DBusMessage * wpas_dbus_setter_process_credentials(
- DBusMessage *message, struct wpa_supplicant *wpa_s)
+dbus_bool_t wpas_dbus_setter_process_credentials(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
{
- DBusMessage *reply = NULL;
+ struct wpa_supplicant *wpa_s = user_data;
dbus_bool_t process_credentials, old_pc;
- reply = wpas_dbus_simple_property_setter(message, DBUS_TYPE_BOOLEAN,
- &process_credentials);
- if (reply)
- return reply;
+ if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_BOOLEAN,
+ &process_credentials))
+ return FALSE;
old_pc = (wpa_s->conf->wps_cred_processing != 1);
wpa_s->conf->wps_cred_processing = (process_credentials ? 2 : 1);
WPAS_DBUS_NEW_IFACE_WPS,
"ProcessCredentials");
- return NULL;
+ return TRUE;
}
#include "dbus_common_i.h"
#include "dbus_new.h"
#include "dbus_new_helpers.h"
+#include "dbus_dict_helpers.h"
-/**
- * recursive_iter_copy - Reads arguments from one iterator and
- * writes to another recursively
- * @from: iterator to read from
- * @to: iterator to write to
- *
- * Copies one iterator's elements to another. If any element in
- * iterator is of container type, its content is copied recursively
- */
-static void recursive_iter_copy(DBusMessageIter *from, DBusMessageIter *to)
-{
-
- char *subtype = NULL;
- int type;
-
- /* iterate over iterator to copy */
- while ((type = dbus_message_iter_get_arg_type(from)) !=
- DBUS_TYPE_INVALID) {
-
- /* simply copy basic type entries */
- if (dbus_type_is_basic(type)) {
- if (dbus_type_is_fixed(type)) {
- /*
- * According to DBus documentation all
- * fixed-length types are guaranteed to fit
- * 8 bytes
- */
- dbus_uint64_t v;
- dbus_message_iter_get_basic(from, &v);
- dbus_message_iter_append_basic(to, type, &v);
- } else {
- char *v;
- dbus_message_iter_get_basic(from, &v);
- dbus_message_iter_append_basic(to, type, &v);
- }
- } else {
- /* recursively copy container type entries */
- DBusMessageIter write_subiter, read_subiter;
-
- dbus_message_iter_recurse(from, &read_subiter);
-
- if (type == DBUS_TYPE_VARIANT ||
- type == DBUS_TYPE_ARRAY) {
- subtype = dbus_message_iter_get_signature(
- &read_subiter);
- }
-
- dbus_message_iter_open_container(to, type, subtype,
- &write_subiter);
-
- recursive_iter_copy(&read_subiter, &write_subiter);
-
- dbus_message_iter_close_container(to, &write_subiter);
- if (subtype)
- dbus_free(subtype);
- }
-
- dbus_message_iter_next(from);
- }
-}
-
-
-static unsigned int fill_dict_with_properties(
- DBusMessageIter *dict_iter, const struct wpa_dbus_property_desc *props,
- const char *interface, const void *user_data)
+static dbus_bool_t fill_dict_with_properties(
+ DBusMessageIter *dict_iter,
+ const struct wpa_dbus_property_desc *props,
+ const char *interface, void *user_data, DBusError *error)
{
- DBusMessage *reply;
- DBusMessageIter entry_iter, ret_iter;
- unsigned int counter = 0;
+ DBusMessageIter entry_iter;
const struct wpa_dbus_property_desc *dsc;
for (dsc = props; dsc && dsc->dbus_property; dsc++) {
- if (!os_strncmp(dsc->dbus_interface, interface,
- WPAS_DBUS_INTERFACE_MAX) &&
- dsc->access != W && dsc->getter) {
- reply = dsc->getter(NULL, user_data);
- if (!reply)
- continue;
-
- if (dbus_message_get_type(reply) ==
- DBUS_MESSAGE_TYPE_ERROR) {
- dbus_message_unref(reply);
- continue;
- }
+ /* Only return properties for the requested D-Bus interface */
+ if (os_strncmp(dsc->dbus_interface, interface,
+ WPAS_DBUS_INTERFACE_MAX) != 0)
+ continue;
- dbus_message_iter_init(reply, &ret_iter);
+ /* Skip write-only properties */
+ if (dsc->getter == NULL)
+ continue;
- dbus_message_iter_open_container(dict_iter,
- DBUS_TYPE_DICT_ENTRY,
- NULL, &entry_iter);
- dbus_message_iter_append_basic(
- &entry_iter, DBUS_TYPE_STRING,
- &dsc->dbus_property);
+ if (!dbus_message_iter_open_container(dict_iter,
+ DBUS_TYPE_DICT_ENTRY,
+ NULL, &entry_iter)) {
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY,
+ "no memory");
+ return FALSE;
+ }
+ if (!dbus_message_iter_append_basic(&entry_iter,
+ DBUS_TYPE_STRING,
+ &dsc->dbus_property)) {
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY,
+ "no memory");
+ return FALSE;
+ }
- recursive_iter_copy(&ret_iter, &entry_iter);
+ /* An error getting a property fails the request entirely */
+ if (!dsc->getter(&entry_iter, error, user_data))
+ return FALSE;
- dbus_message_iter_close_container(dict_iter,
- &entry_iter);
- dbus_message_unref(reply);
- counter++;
- }
+ dbus_message_iter_close_container(dict_iter, &entry_iter);
}
- return counter;
+ return TRUE;
}
* specified as argument. Returned message contains one dict argument
* with properties names as keys and theirs values as values.
*/
-static DBusMessage * get_all_properties(
- DBusMessage *message, char *interface,
- struct wpa_dbus_object_desc *obj_dsc)
+static DBusMessage * get_all_properties(DBusMessage *message, char *interface,
+ struct wpa_dbus_object_desc *obj_dsc)
{
- /* Create and initialize the return message */
- DBusMessage *reply = dbus_message_new_method_return(message);
+ DBusMessage *reply;
DBusMessageIter iter, dict_iter;
- int props_num;
-
- dbus_message_iter_init_append(reply, &iter);
-
- dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
- DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
- DBUS_TYPE_STRING_AS_STRING
- DBUS_TYPE_VARIANT_AS_STRING
- DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
- &dict_iter);
+ DBusError error;
- props_num = fill_dict_with_properties(&dict_iter, obj_dsc->properties,
- interface, obj_dsc->user_data);
+ reply = dbus_message_new_method_return(message);
+ if (reply == NULL) {
+ wpa_printf(MSG_ERROR, "%s: out of memory creating dbus reply",
+ __func__);
+ return NULL;
+ }
- dbus_message_iter_close_container(&iter, &dict_iter);
+ dbus_message_iter_init_append(reply, &iter);
+ if (!wpa_dbus_dict_open_write(&iter, &dict_iter)) {
+ wpa_printf(MSG_ERROR, "%s: out of memory creating reply",
+ __func__);
+ dbus_message_unref(reply);
+ reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
+ "out of memory");
+ return reply;
+ }
- if (props_num == 0) {
+ dbus_error_init(&error);
+ if (!fill_dict_with_properties(&dict_iter, obj_dsc->properties,
+ interface, obj_dsc->user_data, &error))
+ {
dbus_message_unref(reply);
- reply = dbus_message_new_error(message,
- DBUS_ERROR_INVALID_ARGS,
- "No readable properties in "
- "this interface");
+ reply = wpas_dbus_reply_new_from_error(message, &error,
+ DBUS_ERROR_INVALID_ARGS,
+ "No readable properties"
+ " in this interface");
+ dbus_error_free(&error);
+ return reply;
}
+ wpa_dbus_dict_close_write(&iter, &dict_iter);
return reply;
}
const struct wpa_dbus_property_desc *dsc,
void *user_data)
{
- if (os_strcmp(dbus_message_get_signature(message), "ss"))
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusError error;
+
+ if (os_strcmp(dbus_message_get_signature(message), "ss")) {
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
NULL);
+ }
+
+ if (dsc->getter == NULL) {
+ return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
+ "Property is write-only");
+ }
- if (dsc->access != W && dsc->getter)
- return dsc->getter(message, user_data);
+ reply = dbus_message_new_method_return(message);
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_error_init(&error);
+ if (dsc->getter(&iter, &error, user_data) == FALSE) {
+ dbus_message_unref(reply);
+ reply = wpas_dbus_reply_new_from_error(
+ message, &error, DBUS_ERROR_FAILED,
+ "Failed to read property");
+ dbus_error_free(&error);
+ }
- return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
- "Property is write-only");
+ return reply;
}
const struct wpa_dbus_property_desc *dsc,
void *user_data)
{
- if (os_strcmp(dbus_message_get_signature(message), "ssv"))
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusError error;
+
+ if (os_strcmp(dbus_message_get_signature(message), "ssv")) {
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
NULL);
+ }
+
+ if (dsc->setter == NULL) {
+ return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
+ "Property is read-only");
+ }
+
+ dbus_message_iter_init(message, &iter);
+ /* Skip the interface name and the property name */
+ dbus_message_iter_next(&iter);
+ dbus_message_iter_next(&iter);
- if (dsc->access != R && dsc->setter)
- return dsc->setter(message, user_data);
+ /* Iter will now point to the property's new value */
+ dbus_error_init(&error);
+ if (dsc->setter(&iter, &error, user_data) == TRUE) {
+ /* Success */
+ reply = dbus_message_new_method_return(message);
+ } else {
+ reply = wpas_dbus_reply_new_from_error(
+ message, &error, DBUS_ERROR_FAILED,
+ "Failed to set property");
+ dbus_error_free(&error);
+ }
- return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
- "Property is read-only");
+ return reply;
}
struct wpa_dbus_object_desc *obj_desc)
{
DBusConnection *con;
+ DBusError error;
DBusObjectPathVTable vtable = {
&free_dbus_object_desc_cb, &message_handler,
obj_desc->connection = con;
obj_desc->path = os_strdup(path);
+ dbus_error_init(&error);
/* Register the message handler for the interface functions */
- if (!dbus_connection_register_object_path(con, path, &vtable,
- obj_desc)) {
- wpa_printf(MSG_ERROR, "dbus: Could not set up message "
- "handler for interface %s object %s", ifname, path);
+ if (!dbus_connection_try_register_object_path(con, path, &vtable,
+ obj_desc, &error)) {
+ if (!os_strcmp(error.name, DBUS_ERROR_OBJECT_PATH_IN_USE)) {
+ wpa_printf(MSG_DEBUG, "dbus: %s", error.message);
+ } else {
+ wpa_printf(MSG_ERROR, "dbus: Could not set up message "
+ "handler for interface %s object %s",
+ ifname, path);
+ wpa_printf(MSG_ERROR, "dbus error: %s", error.name);
+ wpa_printf(MSG_ERROR, "dbus: %s", error.message);
+ }
+ dbus_error_free(&error);
return -1;
}
+ dbus_error_free(&error);
return 0;
}
}
-static void put_changed_properties(const struct wpa_dbus_object_desc *obj_dsc,
- const char *interface,
- DBusMessageIter *dict_iter)
+static dbus_bool_t put_changed_properties(
+ const struct wpa_dbus_object_desc *obj_dsc, const char *interface,
+ DBusMessageIter *dict_iter, int clear_changed)
{
- DBusMessage *getter_reply;
- DBusMessageIter prop_iter, entry_iter;
+ DBusMessageIter entry_iter;
const struct wpa_dbus_property_desc *dsc;
int i;
+ DBusError error;
for (dsc = obj_dsc->properties, i = 0; dsc && dsc->dbus_property;
dsc++, i++) {
continue;
if (os_strcmp(dsc->dbus_interface, interface) != 0)
continue;
- obj_dsc->prop_changed_flags[i] = 0;
-
- getter_reply = dsc->getter(NULL, obj_dsc->user_data);
- if (!getter_reply ||
- dbus_message_get_type(getter_reply) ==
- DBUS_MESSAGE_TYPE_ERROR) {
- wpa_printf(MSG_ERROR, "dbus: %s: Cannot get new value "
- "of property %s", __func__,
- dsc->dbus_property);
- continue;
- }
+ if (clear_changed)
+ obj_dsc->prop_changed_flags[i] = 0;
- if (!dbus_message_iter_init(getter_reply, &prop_iter) ||
- !dbus_message_iter_open_container(dict_iter,
+ if (!dbus_message_iter_open_container(dict_iter,
DBUS_TYPE_DICT_ENTRY,
- NULL, &entry_iter) ||
- !dbus_message_iter_append_basic(&entry_iter,
+ NULL, &entry_iter))
+ return FALSE;
+
+ if (!dbus_message_iter_append_basic(&entry_iter,
DBUS_TYPE_STRING,
&dsc->dbus_property))
- goto err;
-
- recursive_iter_copy(&prop_iter, &entry_iter);
+ return FALSE;
+
+ dbus_error_init(&error);
+ if (!dsc->getter(&entry_iter, &error, obj_dsc->user_data)) {
+ if (dbus_error_is_set (&error)) {
+ wpa_printf(MSG_ERROR, "dbus: %s: Cannot get "
+ "new value of property %s: (%s) %s",
+ __func__, dsc->dbus_property,
+ error.name, error.message);
+ } else {
+ wpa_printf(MSG_ERROR, "dbus: %s: Cannot get "
+ "new value of property %s",
+ __func__, dsc->dbus_property);
+ }
+ dbus_error_free(&error);
+ return FALSE;
+ }
if (!dbus_message_iter_close_container(dict_iter, &entry_iter))
- goto err;
-
- dbus_message_unref(getter_reply);
+ return FALSE;
}
+ return TRUE;
+}
+
+
+static void do_send_prop_changed_signal(
+ DBusConnection *con, const char *path, const char *interface,
+ const struct wpa_dbus_object_desc *obj_dsc)
+{
+ DBusMessage *msg;
+ DBusMessageIter signal_iter, dict_iter;
+
+ msg = dbus_message_new_signal(path, DBUS_INTERFACE_PROPERTIES,
+ "PropertiesChanged");
+ if (msg == NULL)
+ return;
+
+ dbus_message_iter_init_append(msg, &signal_iter);
+
+ if (!dbus_message_iter_append_basic(&signal_iter, DBUS_TYPE_STRING,
+ &interface))
+ goto err;
+
+ /* Changed properties dict */
+ if (!dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY,
+ "{sv}", &dict_iter))
+ goto err;
+
+ if (!put_changed_properties(obj_dsc, interface, &dict_iter, 0))
+ goto err;
+
+ if (!dbus_message_iter_close_container(&signal_iter, &dict_iter))
+ goto err;
+
+ /* Invalidated properties array (empty) */
+ if (!dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY,
+ "s", &dict_iter))
+ goto err;
+
+ if (!dbus_message_iter_close_container(&signal_iter, &dict_iter))
+ goto err;
+
+ dbus_connection_send(con, msg, NULL);
+
+out:
+ dbus_message_unref(msg);
return;
err:
- wpa_printf(MSG_ERROR, "dbus: %s: Cannot construct signal", __func__);
+ wpa_printf(MSG_DEBUG, "dbus: %s: Failed to construct signal",
+ __func__);
+ goto out;
}
-static void send_prop_changed_signal(
+static void do_send_deprecated_prop_changed_signal(
DBusConnection *con, const char *path, const char *interface,
const struct wpa_dbus_object_desc *obj_dsc)
{
"{sv}", &dict_iter))
goto err;
- put_changed_properties(obj_dsc, interface, &dict_iter);
+ if (!put_changed_properties(obj_dsc, interface, &dict_iter, 1))
+ goto err;
if (!dbus_message_iter_close_container(&signal_iter, &dict_iter))
goto err;
}
+static void send_prop_changed_signal(
+ DBusConnection *con, const char *path, const char *interface,
+ const struct wpa_dbus_object_desc *obj_dsc)
+{
+ /*
+ * First, send property change notification on the standardized
+ * org.freedesktop.DBus.Properties interface. This call will not
+ * clear the property change bits, so that they are preserved for
+ * the call that follows.
+ */
+ do_send_prop_changed_signal(con, path, interface, obj_dsc);
+
+ /*
+ * Now send PropertiesChanged on our own interface for backwards
+ * compatibility. This is deprecated and will be removed in a future
+ * release.
+ */
+ do_send_deprecated_prop_changed_signal(con, path, interface, obj_dsc);
+
+ /* Property change bits have now been cleared. */
+}
+
+
static void flush_object_timeout_handler(void *eloop_ctx, void *timeout_ctx)
{
DBusConnection *con = eloop_ctx;
* @iface: dbus priv struct
* @path: path to DBus object which properties will be obtained
* @interface: interface name which properties will be obtained
- * @dict_iter: correct, open DBus dictionary iterator.
+ * @iter: DBus message iter at which to append property dictionary.
*
* Iterates over all properties registered with object and execute getters
* of those, which are readable and which interface matches interface
* specified as argument. Obtained properties values are stored in
* dict_iter dictionary.
*/
-void wpa_dbus_get_object_properties(struct wpas_dbus_priv *iface,
- const char *path, const char *interface,
- DBusMessageIter *dict_iter)
+dbus_bool_t wpa_dbus_get_object_properties(struct wpas_dbus_priv *iface,
+ const char *path,
+ const char *interface,
+ DBusMessageIter *iter)
{
struct wpa_dbus_object_desc *obj_desc = NULL;
+ DBusMessageIter dict_iter;
+ DBusError error;
dbus_connection_get_object_path_data(iface->con, path,
(void **) &obj_desc);
if (!obj_desc) {
- wpa_printf(MSG_ERROR, "dbus: wpa_dbus_get_object_properties: "
- "could not obtain object's private data: %s", path);
- return;
+ wpa_printf(MSG_ERROR, "dbus: %s: could not obtain object's "
+ "private data: %s", __func__, path);
+ return FALSE;
+ }
+
+ if (!wpa_dbus_dict_open_write(iter, &dict_iter)) {
+ wpa_printf(MSG_ERROR, "dbus: %s: failed to open message dict",
+ __func__);
+ return FALSE;
+ }
+
+ dbus_error_init(&error);
+ if (!fill_dict_with_properties(&dict_iter, obj_desc->properties,
+ interface, obj_desc->user_data,
+ &error)) {
+ wpa_printf(MSG_ERROR, "dbus: %s: failed to get object"
+ " properties: (%s) %s", __func__,
+ dbus_error_is_set(&error) ? error.name : "none",
+ dbus_error_is_set(&error) ? error.message : "none");
+ dbus_error_free(&error);
+ return FALSE;
}
- fill_dict_with_properties(dict_iter, obj_desc->properties,
- interface, obj_desc->user_data);
+ return wpa_dbus_dict_close_write(iter, &dict_iter);
+}
+
+/**
+ * wpas_dbus_new_decompose_object_path - Decompose an interface object path into parts
+ * @path: The dbus object path
+ * @p2p_persistent_group: indicates whether to parse the path as a P2P
+ * persistent group object
+ * @network: (out) the configured network this object path refers to, if any
+ * @bssid: (out) the scanned bssid this object path refers to, if any
+ * Returns: The object path of the network interface this path refers to
+ *
+ * For a given object path, decomposes the object path into object id, network,
+ * and BSSID parts, if those parts exist.
+ */
+char *wpas_dbus_new_decompose_object_path(const char *path,
+ int p2p_persistent_group,
+ char **network,
+ char **bssid)
+{
+ const unsigned int dev_path_prefix_len =
+ os_strlen(WPAS_DBUS_NEW_PATH_INTERFACES "/");
+ char *obj_path_only;
+ char *next_sep;
+
+ /* Be a bit paranoid about path */
+ if (!path || os_strncmp(path, WPAS_DBUS_NEW_PATH_INTERFACES "/",
+ dev_path_prefix_len))
+ return NULL;
+
+ /* Ensure there's something at the end of the path */
+ if ((path + dev_path_prefix_len)[0] == '\0')
+ return NULL;
+
+ obj_path_only = os_strdup(path);
+ if (obj_path_only == NULL)
+ return NULL;
+
+ next_sep = os_strchr(obj_path_only + dev_path_prefix_len, '/');
+ if (next_sep != NULL) {
+ const char *net_part = os_strstr(
+ next_sep, p2p_persistent_group ?
+ WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/" :
+ WPAS_DBUS_NEW_NETWORKS_PART "/");
+ const char *bssid_part = os_strstr(
+ next_sep, WPAS_DBUS_NEW_BSSIDS_PART "/");
+
+ if (network && net_part) {
+ /* Deal with a request for a configured network */
+ const char *net_name = net_part +
+ os_strlen(p2p_persistent_group ?
+ WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART
+ "/" :
+ WPAS_DBUS_NEW_NETWORKS_PART "/");
+ *network = NULL;
+ if (os_strlen(net_name))
+ *network = os_strdup(net_name);
+ } else if (bssid && bssid_part) {
+ /* Deal with a request for a scanned BSSID */
+ const char *bssid_name = bssid_part +
+ os_strlen(WPAS_DBUS_NEW_BSSIDS_PART "/");
+ if (os_strlen(bssid_name))
+ *bssid = os_strdup(bssid_name);
+ else
+ *bssid = NULL;
+ }
+
+ /* Cut off interface object path before "/" */
+ *next_sep = '\0';
+ }
+
+ return obj_path_only;
+}
+
+
+/**
+ * wpas_dbus_reply_new_from_error - Create a new D-Bus error message from a
+ * dbus error structure
+ * @message: The original request message for which the error is a reply
+ * @error: The error containing a name and a descriptive error cause
+ * @fallback_name: A generic error name if @error was not set
+ * @fallback_string: A generic error string if @error was not set
+ * Returns: A new D-Bus error message
+ *
+ * Given a DBusMessage structure, creates a new D-Bus error message using
+ * the error name and string contained in that structure.
+ */
+DBusMessage * wpas_dbus_reply_new_from_error(DBusMessage *message,
+ DBusError *error,
+ const char *fallback_name,
+ const char *fallback_string)
+{
+ if (error && error->name && error->message) {
+ return dbus_message_new_error(message, error->name,
+ error->message);
+ }
+ if (fallback_name && fallback_string) {
+ return dbus_message_new_error(message, fallback_name,
+ fallback_string);
+ }
+ return NULL;
}
void *user_data);
typedef void (* WPADBusArgumentFreeFunction)(void *handler_arg);
-typedef DBusMessage * (* WPADBusPropertyAccessor)(DBusMessage *message,
- const void *user_data);
+typedef dbus_bool_t (* WPADBusPropertyAccessor)(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
struct wpa_dbus_object_desc {
DBusConnection *connection;
WPADBusArgumentFreeFunction user_data_free_func;
};
-enum dbus_prop_access { R, W, RW };
-
enum dbus_arg_direction { ARG_IN, ARG_OUT };
struct wpa_dbus_argument {
/* method handling function */
WPADBusMethodHandler method_handler;
/* array of arguments */
- struct wpa_dbus_argument args[3];
+ struct wpa_dbus_argument args[4];
};
/**
/* signal interface */
const char *dbus_interface;
/* array of arguments */
- struct wpa_dbus_argument args[3];
+ struct wpa_dbus_argument args[4];
};
/**
WPADBusPropertyAccessor getter;
/* property setter function */
WPADBusPropertyAccessor setter;
- /* property access permissions */
- enum dbus_prop_access access;
};
#define WPAS_DBUS_OBJECT_PATH_MAX 150
#define WPAS_DBUS_INTERFACE_MAX 150
#define WPAS_DBUS_METHOD_SIGNAL_PROP_MAX 50
+#define WPAS_DBUS_AUTH_MODE_MAX 64
#define WPA_DBUS_INTROSPECTION_INTERFACE "org.freedesktop.DBus.Introspectable"
#define WPA_DBUS_INTROSPECTION_METHOD "Introspect"
struct wpas_dbus_priv *ctrl_iface,
const char *path);
-void wpa_dbus_get_object_properties(struct wpas_dbus_priv *iface,
- const char *path, const char *interface,
- DBusMessageIter *dict_iter);
+dbus_bool_t wpa_dbus_get_object_properties(struct wpas_dbus_priv *iface,
+ const char *path,
+ const char *interface,
+ DBusMessageIter *iter);
void wpa_dbus_flush_all_changed_properties(DBusConnection *con);
DBusMessage * wpa_dbus_introspect(DBusMessage *message,
struct wpa_dbus_object_desc *obj_dsc);
+char *wpas_dbus_new_decompose_object_path(const char *path,
+ int p2p_persistent_group,
+ char **network,
+ char **bssid);
+
+DBusMessage *wpas_dbus_reply_new_from_error(DBusMessage *message,
+ DBusError *error,
+ const char *fallback_name,
+ const char *fallback_string);
+
#endif /* WPA_DBUS_CTRL_H */
iface = os_zalloc(sizeof(struct interfaces));
if (!iface)
return NULL;
- iface->xml = wpabuf_alloc(3000);
+ iface->xml = wpabuf_alloc(6000);
if (iface->xml == NULL) {
os_free(iface);
return NULL;
static void add_property(struct wpabuf *xml,
const struct wpa_dbus_property_desc *dsc)
{
- wpabuf_printf(xml, "<property name=\"%s\" type=\"%s\" access=\"%s\"/>",
+ wpabuf_printf(xml, "<property name=\"%s\" type=\"%s\" "
+ "access=\"%s%s\"/>",
dsc->dbus_property, dsc->type,
- (dsc->access == R ? "read" :
- (dsc->access == W ? "write" : "readwrite")));
+ dsc->getter ? "read" : "",
+ dsc->setter ? "write" : "");
}
if (wpabuf_len(iface->xml) + 20 < wpabuf_tailroom(xml)) {
wpabuf_put_buf(xml, iface->xml);
wpabuf_put_str(xml, "</interface>");
+ } else {
+ wpa_printf(MSG_DEBUG, "dbus: Not enough room for "
+ "add_interfaces inspect data: tailroom %u, "
+ "add %u",
+ (unsigned int) wpabuf_tailroom(xml),
+ (unsigned int) wpabuf_len(iface->xml));
}
dl_list_del(&iface->list);
wpabuf_free(iface->xml);
DBusMessage *reply;
struct wpabuf *xml;
- xml = wpabuf_alloc(4000);
+ xml = wpabuf_alloc(10000);
if (xml == NULL)
return NULL;
else if (!os_strcmp(method, "wpsReg"))
reply = wpas_dbus_iface_wps_reg(message, wpa_s);
#endif /* CONFIG_WPS */
+ else if (!os_strcmp(method, "flush"))
+ reply = wpas_dbus_iface_flush(message, wpa_s);
}
/* If the message was handled, send back the reply */
}
#endif /* CONFIG_WPS */
+void wpa_supplicant_dbus_notify_certification(struct wpa_supplicant *wpa_s,
+ int depth, const char *subject,
+ const char *cert_hash,
+ const struct wpabuf *cert)
+{
+ struct wpas_dbus_priv *iface;
+ DBusMessage *_signal = NULL;
+ const char *hash;
+ const char *cert_hex;
+ int cert_hex_len;
+
+ /* Do nothing if the control interface is not turned on */
+ if (wpa_s->global == NULL)
+ return;
+ iface = wpa_s->global->dbus;
+ if (iface == NULL)
+ return;
+
+ _signal = dbus_message_new_signal(wpa_s->dbus_path,
+ WPAS_DBUS_IFACE_INTERFACE,
+ "Certification");
+ if (_signal == NULL) {
+ wpa_printf(MSG_ERROR,
+ "dbus: wpa_supplicant_dbus_notify_certification: "
+ "Could not create dbus signal; likely out of "
+ "memory");
+ return;
+ }
+
+ hash = cert_hash ? cert_hash : "";
+ cert_hex = cert ? wpabuf_head(cert) : "";
+ cert_hex_len = cert ? wpabuf_len(cert) : 0;
+
+ if (!dbus_message_append_args(_signal,
+ DBUS_TYPE_INT32,&depth,
+ DBUS_TYPE_STRING, &subject,
+ DBUS_TYPE_STRING, &hash,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
+ &cert_hex, cert_hex_len,
+ DBUS_TYPE_INVALID)) {
+ wpa_printf(MSG_ERROR,
+ "dbus: wpa_supplicant_dbus_notify_certification: "
+ "Not enough memory to construct signal");
+ goto out;
+ }
+
+ dbus_connection_send(iface->con, _signal, NULL);
+
+out:
+ dbus_message_unref(_signal);
+
+}
+
/**
* wpa_supplicant_dbus_ctrl_iface_init - Initialize dbus control interface
enum wpa_states old_state);
void wpa_supplicant_dbus_notify_wps_cred(struct wpa_supplicant *wpa_s,
const struct wps_credential *cred);
+void wpa_supplicant_dbus_notify_certification(struct wpa_supplicant *wpa_s,
+ int depth, const char *subject,
+ const char *cert_hash,
+ const struct wpabuf *cert);
char * wpas_dbus_decompose_object_path(const char *path, char **network,
char **bssid);
{
}
+static inline void
+wpa_supplicant_dbus_notify_certification(struct wpa_supplicant *wpa_s,
+ int depth, const char *subject,
+ const char *cert_hash,
+ const struct wpabuf *cert)
+{
+}
+
static inline int
wpas_dbus_register_iface(struct wpa_supplicant *wpa_s)
{
DBusMessageIter iter_dict;
struct wpa_dbus_dict_entry entry;
- if (!wpa_dbus_dict_open_read(&iter, &iter_dict))
+ if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
goto error;
while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
dbus_message_iter_init(message, &iter);
- if (!wpa_dbus_dict_open_read(&iter, &iter_dict)) {
+ if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) {
reply = wpas_dbus_new_invalid_opts_error(message, NULL);
goto out;
}
if (!dbus_message_iter_init(message, &iter))
goto error;
- if (!wpa_dbus_dict_open_read(&iter, &iter_dict))
+ if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
goto error;
while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
dbus_message_iter_init(message, &iter);
- if (!wpa_dbus_dict_open_read(&iter, &iter_dict))
+ if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
return wpas_dbus_new_invalid_opts_error(message, NULL);
while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
return wpas_dbus_new_success_reply(message);
}
+
+
+/**
+ * wpas_dbus_iface_flush - Clear BSS of old or all inactive entries
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: %wpa_supplicant data structure
+ * Returns: a dbus message containing a UINT32 indicating success (1) or
+ * failure (0), or returns a dbus error message with more information
+ *
+ * Handler function for "flush" method call. Handles requests for an
+ * interface with an optional "age" parameter that specifies the minimum
+ * age of a BSS to be flushed.
+ */
+DBusMessage * wpas_dbus_iface_flush(DBusMessage *message,
+ struct wpa_supplicant *wpa_s)
+{
+ int flush_age = 0;
+
+ if (os_strlen(dbus_message_get_signature(message)) != 0 &&
+ !dbus_message_get_args(message, NULL,
+ DBUS_TYPE_INT32, &flush_age,
+ DBUS_TYPE_INVALID)) {
+ return wpas_dbus_new_invalid_opts_error(message, NULL);
+ }
+
+ if (flush_age == 0)
+ wpa_bss_flush(wpa_s);
+ else
+ wpa_bss_flush_by_age(wpa_s, flush_age);
+
+ return wpas_dbus_new_success_reply(message);
+}
DBusMessage * wpas_dbus_iface_wps_reg(DBusMessage *message,
struct wpa_supplicant *wpa_s);
+DBusMessage * wpas_dbus_iface_flush(DBusMessage *message,
+ struct wpa_supplicant *wpa_s);
+
DBusMessage * wpas_dbus_new_success_reply(DBusMessage *message);
DBusMessage * wpas_dbus_new_invalid_opts_error(DBusMessage *message,
const char *arg);
return wpas_dbus_new_invalid_opts_error(message, NULL);
if (!os_strcmp(arg_bssid, "any"))
- ret = wpas_wps_start_pbc(wpa_s, NULL);
+ ret = wpas_wps_start_pbc(wpa_s, NULL, 0);
else if (!hwaddr_aton(arg_bssid, bssid))
- ret = wpas_wps_start_pbc(wpa_s, bssid);
+ ret = wpas_wps_start_pbc(wpa_s, bssid, 0);
else {
return wpas_dbus_new_invalid_opts_error(message,
"Invalid BSSID");
}
if (os_strlen(pin) > 0)
- ret = wpas_wps_start_pin(wpa_s, _bssid, pin);
+ ret = wpas_wps_start_pin(wpa_s, _bssid, pin, 0,
+ DEV_PW_DEFAULT);
else
- ret = wpas_wps_start_pin(wpa_s, _bssid, NULL);
+ ret = wpas_wps_start_pin(wpa_s, _bssid, NULL, 0,
+ DEV_PW_DEFAULT);
if (ret < 0) {
return dbus_message_new_error(message,
+++ /dev/null
-[D-BUS Service]
-Name=fi.epitest.hostap.WPASupplicant
-Exec=/sbin/wpa_supplicant -u
-User=root
--- /dev/null
+[D-BUS Service]
+Name=fi.epitest.hostap.WPASupplicant
+Exec=@BINDIR@/wpa_supplicant -u
+User=root
+SystemdService=wpa_supplicant.service
+++ /dev/null
-[D-BUS Service]
-Name=fi.w1.wpa_supplicant1
-Exec=/sbin/wpa_supplicant -u
-User=root
--- /dev/null
+[D-BUS Service]
+Name=fi.w1.wpa_supplicant1
+Exec=@BINDIR@/wpa_supplicant -u
+User=root
+SystemdService=wpa_supplicant.service
#CONFIG_DRIVER_RALINK=y
# Driver interface for generic Linux wireless extensions
+# Note: WEXT is deprecated in the current Linux kernel version and no new
+# functionality is added to it. nl80211-based interface is the new
+# replacement for WEXT and its use allows wpa_supplicant to properly control
+# the driver to improve existing functionality like roaming and to support new
+# functionality.
CONFIG_DRIVER_WEXT=y
# Driver interface for Linux drivers using the nl80211 kernel interface
-#CONFIG_DRIVER_NL80211=y
+CONFIG_DRIVER_NL80211=y
# Driver interface for FreeBSD net80211 layer (e.g., Atheros driver)
#CONFIG_DRIVER_BSD=y
# Driver interface for development testing
#CONFIG_DRIVER_TEST=y
-# Include client MLME (management frame processing) for test driver
-# This can be used to test MLME operations in hostapd with the test interface.
-# space.
-#CONFIG_CLIENT_MLME=y
-
# Driver interface for wired Ethernet drivers
CONFIG_DRIVER_WIRED=y
# Driver interface for no driver (e.g., WPS ER only)
#CONFIG_DRIVER_NONE=y
+# Solaris libraries
+#LIBS += -lsocket -ldlpi -lnsl
+#LIBS_c += -lsocket
+
# Enable IEEE 802.1X Supplicant (automatically included if any EAP method is
# included)
CONFIG_IEEE8021X_EAPOL=y
# EAP-PSK (experimental; this is _not_ needed for WPA-PSK)
#CONFIG_EAP_PSK=y
+# EAP-pwd (secure authentication using only a password)
+#CONFIG_EAP_PWD=y
+
# EAP-PAX
#CONFIG_EAP_PAX=y
# Wi-Fi Protected Setup (WPS)
#CONFIG_WPS=y
+# Enable WSC 2.0 support
+#CONFIG_WPS2=y
+# Enable WPS external registrar functionality
+#CONFIG_WPS_ER=y
+# Disable credentials for an open network by default when acting as a WPS
+# registrar.
+#CONFIG_WPS_REG_DISABLE_OPEN=y
# EAP-IKEv2
#CONFIG_EAP_IKEV2=y
# the resulting binary.
#CONFIG_READLINE=y
+# Include internal line edit mode in wpa_cli. This can be used as a replacement
+# for GNU Readline to provide limited command line editing and history support.
+#CONFIG_WPA_CLI_EDIT=y
+
# Remove debugging code that is printing out debug message to stdout.
# This can be used to reduce the size of the wpa_supplicant considerably
# if debugging code is not needed. The size reduction can be around 35%
# Select TLS implementation
# openssl = OpenSSL (default)
-# gnutls = GnuTLS (needed for TLS/IA, see also CONFIG_GNUTLS_EXTRA)
+# gnutls = GnuTLS
# internal = Internal TLSv1 implementation (experimental)
# none = Empty template
#CONFIG_TLS=openssl
-# Whether to enable TLS/IA support, which is required for EAP-TTLSv1.
-# You need CONFIG_TLS=gnutls for this to have any effect. Please note that
-# even though the core GnuTLS library is released under LGPL, this extra
-# library uses GPL and as such, the terms of GPL apply to the combination
-# of wpa_supplicant and GnuTLS if this option is enabled. BSD license may not
-# apply for distribution of the resulting binary.
-#CONFIG_GNUTLS_EXTRA=y
+# TLS-based EAP methods require at least TLS v1.0. Newer version of TLS (v1.1)
+# can be enabled to get a stronger construction of messages when block ciphers
+# are used. It should be noted that some existing TLS v1.0 -based
+# implementation may not be compatible with TLS v1.1 message (ClientHello is
+# sent prior to negotiating which version will be used)
+#CONFIG_TLSV11=y
# If CONFIG_TLS=internal is used, additional library and include paths are
# needed for LibTomMath. Alternatively, an integrated, minimal version of
# Add support for writing debug log to a file (/tmp/wpa_supplicant-log-#.txt)
#CONFIG_DEBUG_FILE=y
+# Send debug messages to syslog instead of stdout
+#CONFIG_DEBUG_SYSLOG=y
+# Set syslog facility for debug messages
+#CONFIG_DEBUG_SYSLOG_FACILITY=LOG_DAEMON
+
# Enable privilege separation (see README 'Privilege separation' for details)
#CONFIG_PRIVSEP=y
# This tracks use of memory allocations and other registrations and reports
# incorrect use with a backtrace of call (or allocation) location.
#CONFIG_WPA_TRACE=y
-# For BSD, comment out these.
+# For BSD, uncomment these.
#LIBS += -lexecinfo
#LIBS_p += -lexecinfo
#LIBS_c += -lexecinfo
# This enables use of libbfd to get more detailed symbols for the backtraces
# generated by CONFIG_WPA_TRACE=y.
#CONFIG_WPA_TRACE_BFD=y
-# For BSD, comment out these.
+# For BSD, uncomment these.
#LIBS += -lbfd -liberty -lz
#LIBS_p += -lbfd -liberty -lz
#LIBS_c += -lbfd -liberty -lz
+
+# wpa_supplicant depends on strong random number generation being available
+# from the operating system. os_get_random() function is used to fetch random
+# data when needed, e.g., for key generation. On Linux and BSD systems, this
+# works by reading /dev/urandom. It should be noted that the OS entropy pool
+# needs to be properly initialized before wpa_supplicant is started. This is
+# important especially on embedded devices that do not have a hardware random
+# number generator and may by default start up with minimal entropy available
+# for random number generation.
+#
+# As a safety net, wpa_supplicant is by default trying to internally collect
+# additional entropy for generating random data to mix in with the data fetched
+# from the OS. This by itself is not considered to be very strong, but it may
+# help in cases where the system pool is not initialized properly. However, it
+# is very strongly recommended that the system pool is initialized with enough
+# entropy either by using hardware assisted random number generator or by
+# storing state over device reboots.
+#
+# wpa_supplicant can be configured to maintain its own entropy store over
+# restarts to enhance random number generation. This is not perfect, but it is
+# much more secure than using the same sequence of random numbers after every
+# reboot. This can be enabled with -e<entropy file> command line option. The
+# specified file needs to be readable and writable by wpa_supplicant.
+#
+# If the os_get_random() is known to provide strong random data (e.g., on
+# Linux/BSD, the board in question is known to have reliable source of random
+# data from /dev/urandom), the internal wpa_supplicant random pool can be
+# disabled. This will save some in binary size and CPU use. However, this
+# should only be considered for builds that are known to be used on devices
+# that meet the requirements described above.
+#CONFIG_NO_RANDOM_POOL=y
+
+# IEEE 802.11n (High Throughput) support (mainly for AP mode)
+#CONFIG_IEEE80211N=y
+
+# Interworking (IEEE 802.11u)
+# This can be used to enable functionality to improve interworking with
+# external networks (GAS/ANQP to learn more about the networks and network
+# selection based on available credentials).
+#CONFIG_INTERWORKING=y
</varlistentry>
<varlistentry>
- <term>atmel</term>
- <listitem>
- <para>ATMEL AT76C5XXx (USB, PCMCIA).</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
<term>wext</term>
<listitem>
<para>Linux wireless extensions (generic).</para>
</varlistentry>
<varlistentry>
- <term>ndiswrapper</term>
- <listitem>
- <para>Linux ndiswrapper.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
<term>broadcom</term>
<listitem>
<para>Broadcom wl.o driver.</para>
</varlistentry>
<varlistentry>
- <term>ipw</term>
- <listitem>
- <para>Intel ipw2100/2200 driver.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
<term>wired</term>
<listitem>
<para>wpa_supplicant wired Ethernet driver</para>
</varlistentry>
<varlistentry>
- <term>ATMEL AT76C5XXx driver for USB and PCMCIA cards</term>
- <listitem>
- <para> (http://atmelwlandriver.sourceforge.net/).</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
<term>Linux ndiswrapper</term>
<listitem>
<para> (http://ndiswrapper.sourceforge.net/) with Windows
return -1;
}
+static inline int wpa_drv_sched_scan(struct wpa_supplicant *wpa_s,
+ struct wpa_driver_scan_params *params,
+ u32 interval)
+{
+ if (wpa_s->driver->sched_scan)
+ return wpa_s->driver->sched_scan(wpa_s->drv_priv,
+ params, interval);
+ return -1;
+}
+
+static inline int wpa_drv_stop_sched_scan(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->driver->stop_sched_scan)
+ return wpa_s->driver->stop_sched_scan(wpa_s->drv_priv);
+ return -1;
+}
+
static inline struct wpa_scan_results * wpa_drv_get_scan_results2(
struct wpa_supplicant *wpa_s)
{
return NULL;
}
-static inline int wpa_drv_set_channel(struct wpa_supplicant *wpa_s,
- enum hostapd_hw_mode phymode, int chan,
- int freq)
-{
- if (wpa_s->driver->set_channel)
- return wpa_s->driver->set_channel(wpa_s->drv_priv, phymode,
- chan, freq);
- return -1;
-}
-
-static inline int wpa_drv_set_ssid(struct wpa_supplicant *wpa_s,
- const u8 *ssid, size_t ssid_len)
-{
- if (wpa_s->driver->set_ssid) {
- return wpa_s->driver->set_ssid(wpa_s->drv_priv, ssid,
- ssid_len);
- }
- return -1;
-}
-
-static inline int wpa_drv_set_bssid(struct wpa_supplicant *wpa_s,
- const u8 *bssid)
-{
- if (wpa_s->driver->set_bssid) {
- return wpa_s->driver->set_bssid(wpa_s->drv_priv, bssid);
- }
- return -1;
-}
-
static inline int wpa_drv_set_country(struct wpa_supplicant *wpa_s,
const char *alpha2)
{
return -1;
}
-static inline int wpa_drv_mlme_add_sta(struct wpa_supplicant *wpa_s,
- const u8 *addr, const u8 *supp_rates,
- size_t supp_rates_len)
-{
- if (wpa_s->driver->mlme_add_sta)
- return wpa_s->driver->mlme_add_sta(wpa_s->drv_priv, addr,
- supp_rates, supp_rates_len);
- return -1;
-}
-
-static inline int wpa_drv_mlme_remove_sta(struct wpa_supplicant *wpa_s,
- const u8 *addr)
-{
- if (wpa_s->driver->mlme_remove_sta)
- return wpa_s->driver->mlme_remove_sta(wpa_s->drv_priv, addr);
- return -1;
-}
-
static inline int wpa_drv_update_ft_ies(struct wpa_supplicant *wpa_s,
const u8 *md,
const u8 *ies, size_t ies_len)
return -1;
}
-static inline int wpa_drv_set_beacon(struct wpa_supplicant *wpa_s,
- const u8 *head, size_t head_len,
- const u8 *tail, size_t tail_len,
- int dtim_period, int beacon_int)
+static inline int wpa_drv_set_ap(struct wpa_supplicant *wpa_s,
+ struct wpa_driver_ap_params *params)
{
- if (wpa_s->driver->set_beacon)
- return wpa_s->driver->set_beacon(wpa_s->drv_priv, head,
- head_len, tail, tail_len,
- dtim_period, beacon_int);
+ if (wpa_s->driver->set_ap)
+ return wpa_s->driver->set_ap(wpa_s->drv_priv, params);
return -1;
}
static inline int wpa_drv_hapd_send_eapol(struct wpa_supplicant *wpa_s,
const u8 *addr, const u8 *data,
size_t data_len, int encrypt,
- const u8 *own_addr)
+ const u8 *own_addr, u32 flags)
{
if (wpa_s->driver->hapd_send_eapol)
return wpa_s->driver->hapd_send_eapol(wpa_s->drv_priv, addr,
data, data_len, encrypt,
- own_addr);
+ own_addr, flags);
return -1;
}
static inline int wpa_drv_send_action(struct wpa_supplicant *wpa_s,
unsigned int freq,
+ unsigned int wait,
const u8 *dst, const u8 *src,
const u8 *bssid,
- const u8 *data, size_t data_len)
+ const u8 *data, size_t data_len,
+ int no_cck)
{
if (wpa_s->driver->send_action)
return wpa_s->driver->send_action(wpa_s->drv_priv, freq,
- dst, src, bssid, data,
- data_len);
+ wait, dst, src, bssid,
+ data, data_len, no_cck);
+ return -1;
+}
+
+static inline void wpa_drv_send_action_cancel_wait(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->driver->send_action_cancel_wait)
+ wpa_s->driver->send_action_cancel_wait(wpa_s->drv_priv);
+}
+
+static inline int wpa_drv_set_freq(struct wpa_supplicant *wpa_s,
+ struct hostapd_freq_params *freq)
+{
+ if (wpa_s->driver->set_freq)
+ return wpa_s->driver->set_freq(wpa_s->drv_priv, freq);
return -1;
}
enum wpa_driver_if_type type,
const char *ifname, const u8 *addr,
void *bss_ctx, char *force_ifname,
- u8 *if_addr)
+ u8 *if_addr, const char *bridge)
{
if (wpa_s->driver->if_add)
return wpa_s->driver->if_add(wpa_s->drv_priv, type, ifname,
addr, bss_ctx, NULL, force_ifname,
- if_addr);
+ if_addr, bridge);
return -1;
}
return -1;
}
-static inline int wpa_drv_disable_11b_rates(struct wpa_supplicant *wpa_s,
- int disabled)
-{
- if (wpa_s->driver->disable_11b_rates)
- return wpa_s->driver->disable_11b_rates(wpa_s->drv_priv,
- disabled);
- return -1;
-}
-
static inline int wpa_drv_deinit_ap(struct wpa_supplicant *wpa_s)
{
if (wpa_s->driver->deinit_ap)
return 0;
}
+static inline int wpa_drv_deinit_p2p_cli(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->driver->deinit_p2p_cli)
+ return wpa_s->driver->deinit_p2p_cli(wpa_s->drv_priv);
+ return 0;
+}
+
static inline void wpa_drv_suspend(struct wpa_supplicant *wpa_s)
{
if (wpa_s->driver->suspend)
return -1;
}
+static inline int wpa_drv_signal_poll(struct wpa_supplicant *wpa_s,
+ struct wpa_signal_info *si)
+{
+ if (wpa_s->driver->signal_poll)
+ return wpa_s->driver->signal_poll(wpa_s->drv_priv, si);
+ return -1;
+}
+
static inline int wpa_drv_set_ap_wps_ie(struct wpa_supplicant *wpa_s,
const struct wpabuf *beacon,
- const struct wpabuf *proberesp)
+ const struct wpabuf *proberesp,
+ const struct wpabuf *assocresp)
{
if (!wpa_s->driver->set_ap_wps_ie)
return -1;
return wpa_s->driver->set_ap_wps_ie(wpa_s->drv_priv, beacon,
- proberesp);
+ proberesp, assocresp);
+}
+
+static inline int wpa_drv_shared_freq(struct wpa_supplicant *wpa_s)
+{
+ if (!wpa_s->driver->shared_freq)
+ return -1;
+ return wpa_s->driver->shared_freq(wpa_s->drv_priv);
+}
+
+static inline int wpa_drv_get_noa(struct wpa_supplicant *wpa_s,
+ u8 *buf, size_t buf_len)
+{
+ if (!wpa_s->driver->get_noa)
+ return -1;
+ return wpa_s->driver->get_noa(wpa_s->drv_priv, buf, buf_len);
+}
+
+static inline int wpa_drv_set_p2p_powersave(struct wpa_supplicant *wpa_s,
+ int legacy_ps, int opp_ps,
+ int ctwindow)
+{
+ if (!wpa_s->driver->set_p2p_powersave)
+ return -1;
+ return wpa_s->driver->set_p2p_powersave(wpa_s->drv_priv, legacy_ps,
+ opp_ps, ctwindow);
+}
+
+static inline int wpa_drv_ampdu(struct wpa_supplicant *wpa_s, int ampdu)
+{
+ if (!wpa_s->driver->ampdu)
+ return -1;
+ return wpa_s->driver->ampdu(wpa_s->drv_priv, ampdu);
+}
+
+static inline int wpa_drv_p2p_find(struct wpa_supplicant *wpa_s,
+ unsigned int timeout, int type)
+{
+ if (!wpa_s->driver->p2p_find)
+ return -1;
+ return wpa_s->driver->p2p_find(wpa_s->drv_priv, timeout, type);
+}
+
+static inline int wpa_drv_p2p_stop_find(struct wpa_supplicant *wpa_s)
+{
+ if (!wpa_s->driver->p2p_stop_find)
+ return -1;
+ return wpa_s->driver->p2p_stop_find(wpa_s->drv_priv);
+}
+
+static inline int wpa_drv_p2p_listen(struct wpa_supplicant *wpa_s,
+ unsigned int timeout)
+{
+ if (!wpa_s->driver->p2p_listen)
+ return -1;
+ return wpa_s->driver->p2p_listen(wpa_s->drv_priv, timeout);
+}
+
+static inline int wpa_drv_p2p_connect(struct wpa_supplicant *wpa_s,
+ const u8 *peer_addr, int wps_method,
+ int go_intent,
+ const u8 *own_interface_addr,
+ unsigned int force_freq,
+ int persistent_group)
+{
+ if (!wpa_s->driver->p2p_connect)
+ return -1;
+ return wpa_s->driver->p2p_connect(wpa_s->drv_priv, peer_addr,
+ wps_method, go_intent,
+ own_interface_addr, force_freq,
+ persistent_group);
+}
+
+static inline int wpa_drv_wps_success_cb(struct wpa_supplicant *wpa_s,
+ const u8 *peer_addr)
+{
+ if (!wpa_s->driver->wps_success_cb)
+ return -1;
+ return wpa_s->driver->wps_success_cb(wpa_s->drv_priv, peer_addr);
+}
+
+static inline int
+wpa_drv_p2p_group_formation_failed(struct wpa_supplicant *wpa_s)
+{
+ if (!wpa_s->driver->p2p_group_formation_failed)
+ return -1;
+ return wpa_s->driver->p2p_group_formation_failed(wpa_s->drv_priv);
+}
+
+static inline int wpa_drv_p2p_set_params(struct wpa_supplicant *wpa_s,
+ const struct p2p_params *params)
+{
+ if (!wpa_s->driver->p2p_set_params)
+ return -1;
+ return wpa_s->driver->p2p_set_params(wpa_s->drv_priv, params);
+}
+
+static inline int wpa_drv_p2p_prov_disc_req(struct wpa_supplicant *wpa_s,
+ const u8 *peer_addr,
+ u16 config_methods, int join)
+{
+ if (!wpa_s->driver->p2p_prov_disc_req)
+ return -1;
+ return wpa_s->driver->p2p_prov_disc_req(wpa_s->drv_priv, peer_addr,
+ config_methods, join);
+}
+
+static inline u64 wpa_drv_p2p_sd_request(struct wpa_supplicant *wpa_s,
+ const u8 *dst,
+ const struct wpabuf *tlvs)
+{
+ if (!wpa_s->driver->p2p_sd_request)
+ return 0;
+ return wpa_s->driver->p2p_sd_request(wpa_s->drv_priv, dst, tlvs);
+}
+
+static inline int wpa_drv_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s,
+ u64 req)
+{
+ if (!wpa_s->driver->p2p_sd_cancel_request)
+ return -1;
+ return wpa_s->driver->p2p_sd_cancel_request(wpa_s->drv_priv, req);
+}
+
+static inline int wpa_drv_p2p_sd_response(struct wpa_supplicant *wpa_s,
+ int freq, const u8 *dst,
+ u8 dialog_token,
+ const struct wpabuf *resp_tlvs)
+{
+ if (!wpa_s->driver->p2p_sd_response)
+ return -1;
+ return wpa_s->driver->p2p_sd_response(wpa_s->drv_priv, freq, dst,
+ dialog_token, resp_tlvs);
+}
+
+static inline int wpa_drv_p2p_service_update(struct wpa_supplicant *wpa_s)
+{
+ if (!wpa_s->driver->p2p_service_update)
+ return -1;
+ return wpa_s->driver->p2p_service_update(wpa_s->drv_priv);
+}
+
+static inline int wpa_drv_p2p_reject(struct wpa_supplicant *wpa_s,
+ const u8 *addr)
+{
+ if (!wpa_s->driver->p2p_reject)
+ return -1;
+ return wpa_s->driver->p2p_reject(wpa_s->drv_priv, addr);
+}
+
+static inline int wpa_drv_p2p_invite(struct wpa_supplicant *wpa_s,
+ const u8 *peer, int role, const u8 *bssid,
+ const u8 *ssid, size_t ssid_len,
+ const u8 *go_dev_addr,
+ int persistent_group)
+{
+ if (!wpa_s->driver->p2p_invite)
+ return -1;
+ return wpa_s->driver->p2p_invite(wpa_s->drv_priv, peer, role, bssid,
+ ssid, ssid_len, go_dev_addr,
+ persistent_group);
+}
+
+static inline int wpa_drv_send_tdls_mgmt(struct wpa_supplicant *wpa_s,
+ const u8 *dst, u8 action_code,
+ u8 dialog_token, u16 status_code,
+ const u8 *buf, size_t len)
+{
+ if (wpa_s->driver->send_tdls_mgmt) {
+ return wpa_s->driver->send_tdls_mgmt(wpa_s->drv_priv, dst,
+ action_code, dialog_token,
+ status_code, buf, len);
+ }
+ return -1;
+}
+
+static inline int wpa_drv_tdls_oper(struct wpa_supplicant *wpa_s,
+ enum tdls_oper oper, const u8 *peer)
+{
+ if (!wpa_s->driver->tdls_oper)
+ return -1;
+ return wpa_s->driver->tdls_oper(wpa_s->drv_priv, oper, peer);
+}
+
+static inline void wpa_drv_set_rekey_info(struct wpa_supplicant *wpa_s,
+ const u8 *kek, const u8 *kck,
+ const u8 *replay_ctr)
+{
+ if (!wpa_s->driver->set_rekey_info)
+ return;
+ wpa_s->driver->set_rekey_info(wpa_s->drv_priv, kek, kck, replay_ctr);
}
#endif /* DRIVER_I_H */
ret = eap_peer_tnc_register();
#endif /* EAP_TNC */
+#ifdef EAP_PWD
+ if (ret == 0)
+ ret = eap_peer_pwd_register();
+#endif /* EAP_PWD */
#ifdef EAP_SERVER_IDENTITY
if (ret == 0)
ret = eap_server_tnc_register();
#endif /* EAP_SERVER_TNC */
+#ifdef EAP_SERVER_PWD
+ if (ret == 0)
+ ret = eap_server_pwd_register();
+#endif /* EAP_SERVER_PWD */
+
return ret;
}
/*
* WPA Supplicant - test code
- * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2011, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
#include "config.h"
#include "eapol_supp/eapol_supp_sm.h"
#include "eap_peer/eap.h"
+#include "eap_server/eap_methods.h"
#include "eloop.h"
+#include "utils/base64.h"
#include "rsn_supp/wpa.h"
#include "eap_peer/eap_i.h"
#include "wpa_supplicant_i.h"
#include "radius/radius.h"
#include "radius/radius_client.h"
+#include "common/wpa_ctrl.h"
#include "ctrl_iface.h"
#include "pcsc_funcs.h"
char *connect_info;
u8 own_addr[ETH_ALEN];
struct extra_radius_attr *extra_attrs;
+
+ FILE *server_cert_file;
};
static struct eapol_test_data eapol_test;
}
}
- radius_client_send(e->radius, msg, RADIUS_AUTH, e->wpa_s->own_addr);
+ if (radius_client_send(e->radius, msg, RADIUS_AUTH, e->wpa_s->own_addr)
+ < 0)
+ goto fail;
return;
fail:
static int eapol_test_eapol_send(void *ctx, int type, const u8 *buf,
size_t len)
{
- /* struct wpa_supplicant *wpa_s = ctx; */
printf("WPA: eapol_test_eapol_send(type=%d len=%lu)\n",
type, (unsigned long) len);
if (type == IEEE802_1X_TYPE_EAP_PACKET) {
static void eapol_test_set_config_blob(void *ctx,
struct wpa_config_blob *blob)
{
- struct wpa_supplicant *wpa_s = ctx;
- wpa_config_set_blob(wpa_s->conf, blob);
+ struct eapol_test_data *e = ctx;
+ wpa_config_set_blob(e->wpa_s->conf, blob);
}
static const struct wpa_config_blob *
eapol_test_get_config_blob(void *ctx, const char *name)
{
- struct wpa_supplicant *wpa_s = ctx;
- return wpa_config_get_blob(wpa_s->conf, name);
+ struct eapol_test_data *e = ctx;
+ return wpa_config_get_blob(e->wpa_s->conf, name);
}
}
+static void eapol_test_write_cert(FILE *f, const char *subject,
+ const struct wpabuf *cert)
+{
+ unsigned char *encoded;
+
+ encoded = base64_encode(wpabuf_head(cert), wpabuf_len(cert), NULL);
+ if (encoded == NULL)
+ return;
+ fprintf(f, "%s\n-----BEGIN CERTIFICATE-----\n%s"
+ "-----END CERTIFICATE-----\n\n", subject, encoded);
+ os_free(encoded);
+}
+
+
+static void eapol_test_cert_cb(void *ctx, int depth, const char *subject,
+ const char *cert_hash,
+ const struct wpabuf *cert)
+{
+ struct eapol_test_data *e = ctx;
+
+ wpa_msg(e->wpa_s, MSG_INFO, WPA_EVENT_EAP_PEER_CERT
+ "depth=%d subject='%s'%s%s",
+ depth, subject,
+ cert_hash ? " hash=" : "",
+ cert_hash ? cert_hash : "");
+
+ if (cert) {
+ char *cert_hex;
+ size_t len = wpabuf_len(cert) * 2 + 1;
+ cert_hex = os_malloc(len);
+ if (cert_hex) {
+ wpa_snprintf_hex(cert_hex, len, wpabuf_head(cert),
+ wpabuf_len(cert));
+ wpa_msg_ctrl(e->wpa_s, MSG_INFO,
+ WPA_EVENT_EAP_PEER_CERT
+ "depth=%d subject='%s' cert=%s",
+ depth, subject, cert_hex);
+ os_free(cert_hex);
+ }
+
+ if (e->server_cert_file)
+ eapol_test_write_cert(e->server_cert_file,
+ subject, cert);
+ }
+}
+
+
static int test_eapol(struct eapol_test_data *e, struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
printf("Failed to allocate EAPOL context.\n");
return -1;
}
- ctx->ctx = wpa_s;
+ ctx->ctx = e;
ctx->msg_ctx = wpa_s;
ctx->scard_ctx = wpa_s->scard;
ctx->cb = eapol_sm_cb;
ctx->opensc_engine_path = wpa_s->conf->opensc_engine_path;
ctx->pkcs11_engine_path = wpa_s->conf->pkcs11_engine_path;
ctx->pkcs11_module_path = wpa_s->conf->pkcs11_module_path;
+ ctx->cert_cb = eapol_test_cert_cb;
+ ctx->cert_in_cb = 1;
wpa_s->eapol = eapol_sm_init(ctx);
if (wpa_s->eapol == NULL) {
"eapol_test [-nWS] -c<conf> [-a<AS IP>] [-p<AS port>] "
"[-s<AS secret>]\\\n"
" [-r<count>] [-t<timeout>] [-C<Connect-Info>] \\\n"
- " [-M<client MAC address>] \\\n"
+ " [-M<client MAC address>] [-o<server cert file] \\\n"
" [-N<attr spec>] \\\n"
" [-A<client IP>]\n"
"eapol_test scard\n"
" -M<client MAC address> = Set own MAC address "
"(Calling-Station-Id,\n"
" default: 02:00:00:00:00:01)\n"
+ " -o<server cert file> = Write received server certificate\n"
+ " chain to the specified file\n"
" -N<attr spec> = send arbitrary attribute specified by:\n"
" attr_id:syntax:value or attr_id\n"
" attr_id - number id of the attribute\n"
wpa_debug_show_keys = 1;
for (;;) {
- c = getopt(argc, argv, "a:A:c:C:M:nN:p:r:s:St:W");
+ c = getopt(argc, argv, "a:A:c:C:M:nN:o:p:r:s:St:W");
if (c < 0)
break;
switch (c) {
case 'n':
eapol_test.no_mppe_keys++;
break;
+ case 'o':
+ if (eapol_test.server_cert_file)
+ fclose(eapol_test.server_cert_file);
+ eapol_test.server_cert_file = fopen(optarg, "w");
+ if (eapol_test.server_cert_file == NULL) {
+ printf("Could not open '%s' for writing\n",
+ optarg);
+ return -1;
+ }
+ break;
case 'p':
as_port = atoi(optarg);
break;
test_eapol_clean(&eapol_test, &wpa_s);
eap_peer_unregister_methods();
+#ifdef CONFIG_AP
+ eap_server_unregister_methods();
+#endif /* CONFIG_AP */
eloop_destroy();
+ if (eapol_test.server_cert_file)
+ fclose(eapol_test.server_cert_file);
+
printf("MPPE keys OK: %d mismatch: %d\n",
eapol_test.num_mppe_ok, eapol_test.num_mppe_mismatch);
if (eapol_test.num_mppe_mismatch)
/*
* WPA Supplicant - Driver event processing
- * Copyright (c) 2003-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2011, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
#include "common/wpa_ctrl.h"
#include "eap_peer/eap.h"
#include "ap/hostapd.h"
+#include "p2p/p2p.h"
#include "notify.h"
#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "crypto/random.h"
#include "blacklist.h"
#include "wpas_glue.h"
#include "wps_supplicant.h"
#include "ibss_rsn.h"
#include "sme.h"
+#include "gas_query.h"
+#include "p2p_supplicant.h"
#include "bgscan.h"
#include "ap.h"
#include "bss.h"
-#include "mlme.h"
#include "scan.h"
+#include "offchannel.h"
static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s)
if (wpa_s->conf->ap_scan == 1 && wpa_s->current_ssid)
return 0;
- wpa_printf(MSG_DEBUG, "Select network based on association "
- "information");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Select network based on association "
+ "information");
ssid = wpa_supplicant_get_ssid(wpa_s);
if (ssid == NULL) {
- wpa_printf(MSG_INFO, "No network configuration found for the "
- "current AP");
+ wpa_msg(wpa_s, MSG_INFO,
+ "No network configuration found for the current AP");
return -1;
}
if (ssid->disabled) {
- wpa_printf(MSG_DEBUG, "Selected network is disabled");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Selected network is disabled");
return -1;
}
- wpa_printf(MSG_DEBUG, "Network configuration found for the current "
- "AP");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Network configuration found for the "
+ "current AP");
if (ssid->key_mgmt & (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_IEEE8021X |
WPA_KEY_MGMT_WPA_NONE |
WPA_KEY_MGMT_FT_PSK | WPA_KEY_MGMT_FT_IEEE8021X |
}
-static void wpa_supplicant_stop_countermeasures(void *eloop_ctx,
- void *sock_ctx)
+void wpa_supplicant_stop_countermeasures(void *eloop_ctx, void *sock_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
{
int bssid_changed;
+#ifdef CONFIG_IBSS_RSN
+ ibss_rsn_deinit(wpa_s->ibss_rsn);
+ wpa_s->ibss_rsn = NULL;
+#endif /* CONFIG_IBSS_RSN */
+
+#ifdef CONFIG_AP
+ wpa_supplicant_ap_deinit(wpa_s);
+#endif /* CONFIG_AP */
+
+ if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
+ return;
+
wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
bssid_changed = !is_zero_ether_addr(wpa_s->bssid);
os_memset(wpa_s->bssid, 0, ETH_ALEN);
os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
+#ifdef CONFIG_P2P
+ os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN);
+#endif /* CONFIG_P2P */
wpa_s->current_bss = NULL;
+ wpa_s->assoc_freq = 0;
+#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_SME
+ if (wpa_s->sme.ft_ies)
+ sme_update_ft_ies(wpa_s, NULL, NULL, 0);
+#endif /* CONFIG_SME */
+#endif /* CONFIG_IEEE80211R */
+
if (bssid_changed)
wpas_notify_bssid_changed(wpa_s);
}
}
- wpa_printf(MSG_DEBUG, "RSN: PMKID from assoc IE %sfound from PMKSA "
- "cache", pmksa_set == 0 ? "" : "not ");
+ wpa_dbg(wpa_s, MSG_DEBUG, "RSN: PMKID from assoc IE %sfound from "
+ "PMKSA cache", pmksa_set == 0 ? "" : "not ");
}
union wpa_event_data *data)
{
if (data == NULL) {
- wpa_printf(MSG_DEBUG, "RSN: No data in PMKID candidate event");
+ wpa_dbg(wpa_s, MSG_DEBUG, "RSN: No data in PMKID candidate "
+ "event");
return;
}
- wpa_printf(MSG_DEBUG, "RSN: PMKID candidate event - bssid=" MACSTR
- " index=%d preauth=%d",
- MAC2STR(data->pmkid_candidate.bssid),
- data->pmkid_candidate.index,
- data->pmkid_candidate.preauth);
+ wpa_dbg(wpa_s, MSG_DEBUG, "RSN: PMKID candidate event - bssid=" MACSTR
+ " index=%d preauth=%d",
+ MAC2STR(data->pmkid_candidate.bssid),
+ data->pmkid_candidate.index,
+ data->pmkid_candidate.preauth);
pmksa_candidate_add(wpa_s->wpa, data->pmkid_candidate.bssid,
data->pmkid_candidate.index,
struct wpa_ssid *ssid)
{
#ifdef IEEE8021X_EAPOL
+#ifdef PCSC_FUNCS
int aka = 0, sim = 0, type;
if (ssid->eap.pcsc == NULL || wpa_s->scard != NULL)
aka = 0;
if (!sim && !aka) {
- wpa_printf(MSG_DEBUG, "Selected network is configured to use "
- "SIM, but neither EAP-SIM nor EAP-AKA are enabled");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Selected network is configured to "
+ "use SIM, but neither EAP-SIM nor EAP-AKA are "
+ "enabled");
return 0;
}
- wpa_printf(MSG_DEBUG, "Selected network is configured to use SIM "
- "(sim=%d aka=%d) - initialize PCSC", sim, aka);
+ wpa_dbg(wpa_s, MSG_DEBUG, "Selected network is configured to use SIM "
+ "(sim=%d aka=%d) - initialize PCSC", sim, aka);
if (sim && aka)
type = SCARD_TRY_BOTH;
else if (aka)
wpa_s->scard = scard_init(type);
if (wpa_s->scard == NULL) {
- wpa_printf(MSG_WARNING, "Failed to initialize SIM "
- "(pcsc-lite)");
+ wpa_msg(wpa_s, MSG_WARNING, "Failed to initialize SIM "
+ "(pcsc-lite)");
return -1;
}
wpa_sm_set_scard_ctx(wpa_s->wpa, wpa_s->scard);
eapol_sm_register_scard_ctx(wpa_s->eapol, wpa_s->scard);
+#endif /* PCSC_FUNCS */
#endif /* IEEE8021X_EAPOL */
return 0;
privacy = 1;
#endif /* IEEE8021X_EAPOL */
+ if (wpa_key_mgmt_wpa(ssid->key_mgmt))
+ privacy = 1;
+
if (bss->caps & IEEE80211_CAP_PRIVACY)
return privacy;
return !privacy;
int proto_match = 0;
const u8 *rsn_ie, *wpa_ie;
int ret;
+ int wep_ok;
ret = wpas_wps_ssid_bss_match(wpa_s, ssid, bss);
if (ret >= 0)
return ret;
+ /* Allow TSN if local configuration accepts WEP use without WPA/WPA2 */
+ wep_ok = !wpa_key_mgmt_wpa(ssid->key_mgmt) &&
+ (((ssid->key_mgmt & WPA_KEY_MGMT_NONE) &&
+ ssid->wep_key_len[ssid->wep_tx_keyidx] > 0) ||
+ (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA));
+
rsn_ie = wpa_scan_get_ie(bss, WLAN_EID_RSN);
while ((ssid->proto & WPA_PROTO_RSN) && rsn_ie) {
proto_match++;
if (wpa_parse_wpa_ie(rsn_ie, 2 + rsn_ie[1], &ie)) {
- wpa_printf(MSG_DEBUG, " skip RSN IE - parse failed");
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - parse "
+ "failed");
break;
}
+
+ if (wep_ok &&
+ (ie.group_cipher & (WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)))
+ {
+ wpa_dbg(wpa_s, MSG_DEBUG, " selected based on TSN "
+ "in RSN IE");
+ return 1;
+ }
+
if (!(ie.proto & ssid->proto)) {
- wpa_printf(MSG_DEBUG, " skip RSN IE - proto "
- "mismatch");
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - proto "
+ "mismatch");
break;
}
if (!(ie.pairwise_cipher & ssid->pairwise_cipher)) {
- wpa_printf(MSG_DEBUG, " skip RSN IE - PTK cipher "
- "mismatch");
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - PTK "
+ "cipher mismatch");
break;
}
if (!(ie.group_cipher & ssid->group_cipher)) {
- wpa_printf(MSG_DEBUG, " skip RSN IE - GTK cipher "
- "mismatch");
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - GTK "
+ "cipher mismatch");
break;
}
if (!(ie.key_mgmt & ssid->key_mgmt)) {
- wpa_printf(MSG_DEBUG, " skip RSN IE - key mgmt "
- "mismatch");
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - key mgmt "
+ "mismatch");
break;
}
#ifdef CONFIG_IEEE80211W
if (!(ie.capabilities & WPA_CAPABILITY_MFPC) &&
ssid->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED) {
- wpa_printf(MSG_DEBUG, " skip RSN IE - no mgmt frame "
- "protection");
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - no mgmt "
+ "frame protection");
break;
}
#endif /* CONFIG_IEEE80211W */
- wpa_printf(MSG_DEBUG, " selected based on RSN IE");
+ wpa_dbg(wpa_s, MSG_DEBUG, " selected based on RSN IE");
return 1;
}
proto_match++;
if (wpa_parse_wpa_ie(wpa_ie, 2 + wpa_ie[1], &ie)) {
- wpa_printf(MSG_DEBUG, " skip WPA IE - parse failed");
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip WPA IE - parse "
+ "failed");
break;
}
+
+ if (wep_ok &&
+ (ie.group_cipher & (WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)))
+ {
+ wpa_dbg(wpa_s, MSG_DEBUG, " selected based on TSN "
+ "in WPA IE");
+ return 1;
+ }
+
if (!(ie.proto & ssid->proto)) {
- wpa_printf(MSG_DEBUG, " skip WPA IE - proto "
- "mismatch");
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip WPA IE - proto "
+ "mismatch");
break;
}
if (!(ie.pairwise_cipher & ssid->pairwise_cipher)) {
- wpa_printf(MSG_DEBUG, " skip WPA IE - PTK cipher "
- "mismatch");
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip WPA IE - PTK "
+ "cipher mismatch");
break;
}
if (!(ie.group_cipher & ssid->group_cipher)) {
- wpa_printf(MSG_DEBUG, " skip WPA IE - GTK cipher "
- "mismatch");
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip WPA IE - GTK "
+ "cipher mismatch");
break;
}
if (!(ie.key_mgmt & ssid->key_mgmt)) {
- wpa_printf(MSG_DEBUG, " skip WPA IE - key mgmt "
- "mismatch");
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip WPA IE - key mgmt "
+ "mismatch");
break;
}
- wpa_printf(MSG_DEBUG, " selected based on WPA IE");
+ wpa_dbg(wpa_s, MSG_DEBUG, " selected based on WPA IE");
return 1;
}
- if (proto_match == 0)
- wpa_printf(MSG_DEBUG, " skip - no WPA/RSN proto match");
+ if ((ssid->proto & (WPA_PROTO_WPA | WPA_PROTO_RSN)) &&
+ wpa_key_mgmt_wpa(ssid->key_mgmt) && proto_match == 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - no WPA/RSN proto match");
+ return 0;
+ }
+
+ if (!wpa_key_mgmt_wpa(ssid->key_mgmt)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " allow in non-WPA/WPA2");
+ return 1;
+ }
+
+ wpa_dbg(wpa_s, MSG_DEBUG, " reject due to mismatch with "
+ "WPA/WPA2");
return 0;
}
}
-static struct wpa_bss *
-wpa_supplicant_select_bss_wpa(struct wpa_supplicant *wpa_s,
- struct wpa_scan_results *scan_res,
- struct wpa_ssid *group,
- struct wpa_ssid **selected_ssid)
+static int ht_supported(const struct hostapd_hw_modes *mode)
{
- struct wpa_ssid *ssid;
- struct wpa_scan_res *bss;
- size_t i;
- struct wpa_blacklist *e;
- const u8 *ie;
+ if (!(mode->flags & HOSTAPD_MODE_FLAG_HT_INFO_KNOWN)) {
+ /*
+ * The driver did not indicate whether it supports HT. Assume
+ * it does to avoid connection issues.
+ */
+ return 1;
+ }
- wpa_printf(MSG_DEBUG, "Try to find WPA-enabled AP");
- for (i = 0; i < scan_res->num; i++) {
- const u8 *ssid_;
- u8 wpa_ie_len, rsn_ie_len, ssid_len;
- bss = scan_res->res[i];
+ /*
+ * IEEE Std 802.11n-2009 20.1.1:
+ * An HT non-AP STA shall support all EQM rates for one spatial stream.
+ */
+ return mode->mcs_set[0] == 0xff;
+}
- ie = wpa_scan_get_ie(bss, WLAN_EID_SSID);
- ssid_ = ie ? ie + 2 : (u8 *) "";
- ssid_len = ie ? ie[1] : 0;
- ie = wpa_scan_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
- wpa_ie_len = ie ? ie[1] : 0;
+static int rate_match(struct wpa_supplicant *wpa_s, struct wpa_scan_res *bss)
+{
+ const struct hostapd_hw_modes *mode = NULL, *modes;
+ const u8 scan_ie[2] = { WLAN_EID_SUPP_RATES, WLAN_EID_EXT_SUPP_RATES };
+ const u8 *rate_ie;
+ int i, j, k;
- ie = wpa_scan_get_ie(bss, WLAN_EID_RSN);
- rsn_ie_len = ie ? ie[1] : 0;
+ if (bss->freq == 0)
+ return 1; /* Cannot do matching without knowing band */
- wpa_printf(MSG_DEBUG, "%d: " MACSTR " ssid='%s' "
- "wpa_ie_len=%u rsn_ie_len=%u caps=0x%x",
- (int) i, MAC2STR(bss->bssid),
- wpa_ssid_txt(ssid_, ssid_len),
- wpa_ie_len, rsn_ie_len, bss->caps);
+ modes = wpa_s->hw.modes;
+ if (modes == NULL) {
+ /*
+ * The driver does not provide any additional information
+ * about the utilized hardware, so allow the connection attempt
+ * to continue.
+ */
+ return 1;
+ }
- e = wpa_blacklist_get(wpa_s, bss->bssid);
- if (e && e->count > 1) {
- wpa_printf(MSG_DEBUG, " skip - blacklisted");
- continue;
+ for (i = 0; i < wpa_s->hw.num_modes; i++) {
+ for (j = 0; j < modes[i].num_channels; j++) {
+ int freq = modes[i].channels[j].freq;
+ if (freq == bss->freq) {
+ if (mode &&
+ mode->mode == HOSTAPD_MODE_IEEE80211G)
+ break; /* do not allow 802.11b replace
+ * 802.11g */
+ mode = &modes[i];
+ break;
+ }
}
+ }
- if (ssid_len == 0) {
- wpa_printf(MSG_DEBUG, " skip - SSID not known");
- continue;
- }
+ if (mode == NULL)
+ return 0;
- if (wpa_ie_len == 0 && rsn_ie_len == 0) {
- wpa_printf(MSG_DEBUG, " skip - no WPA/RSN IE");
+ for (i = 0; i < (int) sizeof(scan_ie); i++) {
+ rate_ie = wpa_scan_get_ie(bss, scan_ie[i]);
+ if (rate_ie == NULL)
continue;
- }
- for (ssid = group; ssid; ssid = ssid->pnext) {
- int check_ssid = 1;
+ for (j = 2; j < rate_ie[1] + 2; j++) {
+ int flagged = !!(rate_ie[j] & 0x80);
+ int r = (rate_ie[j] & 0x7f) * 5;
- if (ssid->disabled) {
- wpa_printf(MSG_DEBUG, " skip - disabled");
+ /*
+ * IEEE Std 802.11n-2009 7.3.2.2:
+ * The new BSS Membership selector value is encoded
+ * like a legacy basic rate, but it is not a rate and
+ * only indicates if the BSS members are required to
+ * support the mandatory features of Clause 20 [HT PHY]
+ * in order to join the BSS.
+ */
+ if (flagged && ((rate_ie[j] & 0x7f) ==
+ BSS_MEMBERSHIP_SELECTOR_HT_PHY)) {
+ if (!ht_supported(mode)) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " hardware does not support "
+ "HT PHY");
+ return 0;
+ }
continue;
}
-#ifdef CONFIG_WPS
- if (ssid->ssid_len == 0 &&
- wpas_wps_ssid_wildcard_ok(wpa_s, ssid, bss))
- check_ssid = 0;
-#endif /* CONFIG_WPS */
-
- if (check_ssid &&
- (ssid_len != ssid->ssid_len ||
- os_memcmp(ssid_, ssid->ssid, ssid_len) != 0)) {
- wpa_printf(MSG_DEBUG, " skip - "
- "SSID mismatch");
+ if (!flagged)
continue;
- }
- if (ssid->bssid_set &&
- os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) != 0)
- {
- wpa_printf(MSG_DEBUG, " skip - "
- "BSSID mismatch");
- continue;
+ /* check for legacy basic rates */
+ for (k = 0; k < mode->num_rates; k++) {
+ if (mode->rates[k] == r)
+ break;
}
-
- if (!wpa_supplicant_ssid_bss_match(wpa_s, ssid, bss))
- continue;
-
- if (!freq_allowed(ssid->freq_list, bss->freq)) {
- wpa_printf(MSG_DEBUG, " skip - "
- "frequency not allowed");
- continue;
+ if (k == mode->num_rates) {
+ /*
+ * IEEE Std 802.11-2007 7.3.2.2 demands that in
+ * order to join a BSS all required rates
+ * have to be supported by the hardware.
+ */
+ wpa_dbg(wpa_s, MSG_DEBUG, " hardware does "
+ "not support required rate %d.%d Mbps",
+ r / 10, r % 10);
+ return 0;
}
-
- wpa_printf(MSG_DEBUG, " selected WPA AP "
- MACSTR " ssid='%s'",
- MAC2STR(bss->bssid),
- wpa_ssid_txt(ssid_, ssid_len));
- *selected_ssid = ssid;
- return wpa_bss_get(wpa_s, bss->bssid, ssid_, ssid_len);
}
}
- return NULL;
+ return 1;
}
-static struct wpa_bss *
-wpa_supplicant_select_bss_non_wpa(struct wpa_supplicant *wpa_s,
- struct wpa_scan_results *scan_res,
- struct wpa_ssid *group,
- struct wpa_ssid **selected_ssid)
+static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
+ int i, struct wpa_scan_res *bss,
+ struct wpa_ssid *group)
{
- struct wpa_ssid *ssid;
- struct wpa_scan_res *bss;
- size_t i;
+ const u8 *ssid_;
+ u8 wpa_ie_len, rsn_ie_len, ssid_len;
+ int wpa;
struct wpa_blacklist *e;
const u8 *ie;
+ struct wpa_ssid *ssid;
- wpa_printf(MSG_DEBUG, "Try to find non-WPA AP");
- for (i = 0; i < scan_res->num; i++) {
- const u8 *ssid_;
- u8 wpa_ie_len, rsn_ie_len, ssid_len;
- bss = scan_res->res[i];
+ ie = wpa_scan_get_ie(bss, WLAN_EID_SSID);
+ ssid_ = ie ? ie + 2 : (u8 *) "";
+ ssid_len = ie ? ie[1] : 0;
- ie = wpa_scan_get_ie(bss, WLAN_EID_SSID);
- ssid_ = ie ? ie + 2 : (u8 *) "";
- ssid_len = ie ? ie[1] : 0;
+ ie = wpa_scan_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
+ wpa_ie_len = ie ? ie[1] : 0;
- ie = wpa_scan_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
- wpa_ie_len = ie ? ie[1] : 0;
+ ie = wpa_scan_get_ie(bss, WLAN_EID_RSN);
+ rsn_ie_len = ie ? ie[1] : 0;
- ie = wpa_scan_get_ie(bss, WLAN_EID_RSN);
- rsn_ie_len = ie ? ie[1] : 0;
+ wpa_dbg(wpa_s, MSG_DEBUG, "%d: " MACSTR " ssid='%s' "
+ "wpa_ie_len=%u rsn_ie_len=%u caps=0x%x level=%d%s",
+ i, MAC2STR(bss->bssid), wpa_ssid_txt(ssid_, ssid_len),
+ wpa_ie_len, rsn_ie_len, bss->caps, bss->level,
+ wpa_scan_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE) ? " wps" : "");
- wpa_printf(MSG_DEBUG, "%d: " MACSTR " ssid='%s' "
- "wpa_ie_len=%u rsn_ie_len=%u caps=0x%x",
- (int) i, MAC2STR(bss->bssid),
- wpa_ssid_txt(ssid_, ssid_len),
- wpa_ie_len, rsn_ie_len, bss->caps);
+ e = wpa_blacklist_get(wpa_s, bss->bssid);
+ if (e) {
+ int limit = 1;
+ if (wpa_supplicant_enabled_networks(wpa_s->conf) == 1) {
+ /*
+ * When only a single network is enabled, we can
+ * trigger blacklisting on the first failure. This
+ * should not be done with multiple enabled networks to
+ * avoid getting forced to move into a worse ESS on
+ * single error if there are no other BSSes of the
+ * current ESS.
+ */
+ limit = 0;
+ }
+ if (e->count > limit) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - blacklisted "
+ "(count=%d limit=%d)", e->count, limit);
+ return NULL;
+ }
+ }
+
+ if (ssid_len == 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - SSID not known");
+ return NULL;
+ }
- e = wpa_blacklist_get(wpa_s, bss->bssid);
- if (e && e->count > 1) {
- wpa_printf(MSG_DEBUG, " skip - blacklisted");
+ wpa = wpa_ie_len > 0 || rsn_ie_len > 0;
+
+ for (ssid = group; ssid; ssid = ssid->pnext) {
+ int check_ssid = wpa ? 1 : (ssid->ssid_len != 0);
+
+ if (ssid->disabled) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - disabled");
continue;
}
- if (ssid_len == 0) {
- wpa_printf(MSG_DEBUG, " skip - SSID not known");
+#ifdef CONFIG_WPS
+ if ((ssid->key_mgmt & WPA_KEY_MGMT_WPS) && e && e->count > 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - blacklisted "
+ "(WPS)");
continue;
}
- for (ssid = group; ssid; ssid = ssid->pnext) {
- int check_ssid = ssid->ssid_len != 0;
+ if (wpa && ssid->ssid_len == 0 &&
+ wpas_wps_ssid_wildcard_ok(wpa_s, ssid, bss))
+ check_ssid = 0;
- if (ssid->disabled) {
- wpa_printf(MSG_DEBUG, " skip - disabled");
- continue;
- }
-
-#ifdef CONFIG_WPS
- if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) {
- /* Only allow wildcard SSID match if an AP
- * advertises active WPS operation that matches
- * with our mode. */
- check_ssid = 1;
- if (ssid->ssid_len == 0 &&
- wpas_wps_ssid_wildcard_ok(wpa_s, ssid,
- bss))
- check_ssid = 0;
- }
+ if (!wpa && (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) {
+ /* Only allow wildcard SSID match if an AP
+ * advertises active WPS operation that matches
+ * with our mode. */
+ check_ssid = 1;
+ if (ssid->ssid_len == 0 &&
+ wpas_wps_ssid_wildcard_ok(wpa_s, ssid, bss))
+ check_ssid = 0;
+ }
#endif /* CONFIG_WPS */
- if (check_ssid &&
- (ssid_len != ssid->ssid_len ||
- os_memcmp(ssid_, ssid->ssid, ssid_len) != 0)) {
- wpa_printf(MSG_DEBUG, " skip - "
- "SSID mismatch");
- continue;
- }
+ if (ssid->bssid_set && ssid->ssid_len == 0 &&
+ os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) == 0)
+ check_ssid = 0;
- if (ssid->bssid_set &&
- os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) != 0)
- {
- wpa_printf(MSG_DEBUG, " skip - "
- "BSSID mismatch");
- continue;
- }
+ if (check_ssid &&
+ (ssid_len != ssid->ssid_len ||
+ os_memcmp(ssid_, ssid->ssid, ssid_len) != 0)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - SSID mismatch");
+ continue;
+ }
- if (!(ssid->key_mgmt & WPA_KEY_MGMT_NONE) &&
- !(ssid->key_mgmt & WPA_KEY_MGMT_WPS) &&
- !(ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA))
- {
- wpa_printf(MSG_DEBUG, " skip - "
- "non-WPA network not allowed");
- continue;
- }
+ if (ssid->bssid_set &&
+ os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) != 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - BSSID mismatch");
+ continue;
+ }
- if ((ssid->key_mgmt &
- (WPA_KEY_MGMT_IEEE8021X | WPA_KEY_MGMT_PSK |
- WPA_KEY_MGMT_FT_IEEE8021X | WPA_KEY_MGMT_FT_PSK |
- WPA_KEY_MGMT_IEEE8021X_SHA256 |
- WPA_KEY_MGMT_PSK_SHA256)) &&
- (wpa_ie_len != 0 || rsn_ie_len != 0)) {
- wpa_printf(MSG_DEBUG, " skip - "
- "WPA network");
- continue;
- }
+ if (!wpa_supplicant_ssid_bss_match(wpa_s, ssid, bss))
+ continue;
- if (!wpa_supplicant_match_privacy(bss, ssid)) {
- wpa_printf(MSG_DEBUG, " skip - "
- "privacy mismatch");
- continue;
- }
+ if (!wpa &&
+ !(ssid->key_mgmt & WPA_KEY_MGMT_NONE) &&
+ !(ssid->key_mgmt & WPA_KEY_MGMT_WPS) &&
+ !(ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - non-WPA network "
+ "not allowed");
+ continue;
+ }
- if (bss->caps & IEEE80211_CAP_IBSS) {
- wpa_printf(MSG_DEBUG, " skip - "
- "IBSS (adhoc) network");
- continue;
- }
+ if (!wpa_supplicant_match_privacy(bss, ssid)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - privacy "
+ "mismatch");
+ continue;
+ }
- if (!freq_allowed(ssid->freq_list, bss->freq)) {
- wpa_printf(MSG_DEBUG, " skip - "
- "frequency not allowed");
- continue;
- }
+ if (bss->caps & IEEE80211_CAP_IBSS) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - IBSS (adhoc) "
+ "network");
+ continue;
+ }
- wpa_printf(MSG_DEBUG, " selected non-WPA AP "
- MACSTR " ssid='%s'",
- MAC2STR(bss->bssid),
- wpa_ssid_txt(ssid_, ssid_len));
- *selected_ssid = ssid;
- return wpa_bss_get(wpa_s, bss->bssid, ssid_, ssid_len);
+ if (!freq_allowed(ssid->freq_list, bss->freq)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - frequency not "
+ "allowed");
+ continue;
}
+
+ if (!rate_match(wpa_s, bss)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - rate sets do "
+ "not match");
+ continue;
+ }
+
+#ifdef CONFIG_P2P
+ /*
+ * TODO: skip the AP if its P2P IE has Group Formation
+ * bit set in the P2P Group Capability Bitmap and we
+ * are not in Group Formation with that device.
+ */
+#endif /* CONFIG_P2P */
+
+ /* Matching configuration found */
+ return ssid;
}
+ /* No matching configuration found */
return NULL;
}
struct wpa_ssid *group,
struct wpa_ssid **selected_ssid)
{
- struct wpa_bss *selected;
+ size_t i;
- wpa_printf(MSG_DEBUG, "Selecting BSS from priority group %d",
- group->priority);
+ wpa_dbg(wpa_s, MSG_DEBUG, "Selecting BSS from priority group %d",
+ group->priority);
- /* First, try to find WPA-enabled AP */
- selected = wpa_supplicant_select_bss_wpa(wpa_s, scan_res, group,
- selected_ssid);
- if (selected)
- return selected;
+ for (i = 0; i < scan_res->num; i++) {
+ struct wpa_scan_res *bss = scan_res->res[i];
+ const u8 *ie, *ssid;
+ u8 ssid_len;
- /* If no WPA-enabled AP found, try to find non-WPA AP, if configuration
- * allows this. */
- return wpa_supplicant_select_bss_non_wpa(wpa_s, scan_res, group,
- selected_ssid);
+ *selected_ssid = wpa_scan_res_match(wpa_s, i, bss, group);
+ if (!*selected_ssid)
+ continue;
+
+ ie = wpa_scan_get_ie(bss, WLAN_EID_SSID);
+ ssid = ie ? ie + 2 : (u8 *) "";
+ ssid_len = ie ? ie[1] : 0;
+
+ wpa_dbg(wpa_s, MSG_DEBUG, " selected BSS " MACSTR
+ " ssid='%s'",
+ MAC2STR(bss->bssid), wpa_ssid_txt(ssid, ssid_len));
+ return wpa_bss_get(wpa_s, bss->bssid, ssid, ssid_len);
+ }
+
+ return NULL;
}
}
if (selected == NULL && wpa_s->blacklist) {
- wpa_printf(MSG_DEBUG, "No APs found - clear blacklist "
- "and try again");
+ wpa_dbg(wpa_s, MSG_DEBUG, "No APs found - clear "
+ "blacklist and try again");
wpa_blacklist_clear(wpa_s);
wpa_s->blacklist_cleared++;
} else if (selected == NULL)
}
-void wpa_supplicant_connect(struct wpa_supplicant *wpa_s,
- struct wpa_bss *selected,
- struct wpa_ssid *ssid)
+int wpa_supplicant_connect(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *selected,
+ struct wpa_ssid *ssid)
{
if (wpas_wps_scan_pbc_overlap(wpa_s, selected, ssid)) {
wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_OVERLAP
"PBC session overlap");
- wpa_supplicant_req_new_scan(wpa_s, 10, 0);
- return;
+#ifdef CONFIG_P2P
+ if (wpas_p2p_notif_pbc_overlap(wpa_s) == 1)
+ return -1;
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_WPS
+ wpas_wps_cancel(wpa_s);
+#endif /* CONFIG_WPS */
+ return -1;
}
/*
*/
if (wpa_s->reassociate ||
(os_memcmp(selected->bssid, wpa_s->bssid, ETH_ALEN) != 0 &&
- (wpa_s->wpa_state != WPA_ASSOCIATING ||
+ ((wpa_s->wpa_state != WPA_ASSOCIATING &&
+ wpa_s->wpa_state != WPA_AUTHENTICATING) ||
os_memcmp(selected->bssid, wpa_s->pending_bssid, ETH_ALEN) !=
0))) {
if (wpa_supplicant_scard_init(wpa_s, ssid)) {
wpa_supplicant_req_new_scan(wpa_s, 10, 0);
- return;
+ return 0;
}
+ wpa_msg(wpa_s, MSG_DEBUG, "Request association: "
+ "reassociate: %d selected: "MACSTR " bssid: " MACSTR
+ " pending: " MACSTR " wpa_state: %s",
+ wpa_s->reassociate, MAC2STR(selected->bssid),
+ MAC2STR(wpa_s->bssid), MAC2STR(wpa_s->pending_bssid),
+ wpa_supplicant_state_txt(wpa_s->wpa_state));
wpa_supplicant_associate(wpa_s, selected, ssid);
} else {
- wpa_printf(MSG_DEBUG, "Already associated with the selected "
- "AP");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Already associated with the "
+ "selected AP");
}
+
+ return 0;
}
/* TODO: move the rsn_preauth_scan_result*() to be called from notify.c based
* on BSS added and BSS changed events */
static void wpa_supplicant_rsn_preauth_scan_results(
- struct wpa_supplicant *wpa_s, struct wpa_scan_results *scan_res)
+ struct wpa_supplicant *wpa_s)
{
- int i;
+ struct wpa_bss *bss;
if (rsn_preauth_scan_results(wpa_s->wpa) < 0)
return;
- for (i = scan_res->num - 1; i >= 0; i--) {
+ dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
const u8 *ssid, *rsn;
- struct wpa_scan_res *r;
-
- r = scan_res->res[i];
- ssid = wpa_scan_get_ie(r, WLAN_EID_SSID);
+ ssid = wpa_bss_get_ie(bss, WLAN_EID_SSID);
if (ssid == NULL)
continue;
- rsn = wpa_scan_get_ie(r, WLAN_EID_RSN);
+ rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN);
if (rsn == NULL)
continue;
- rsn_preauth_scan_result(wpa_s->wpa, r->bssid, ssid, rsn);
+ rsn_preauth_scan_result(wpa_s->wpa, bss->bssid, ssid, rsn);
}
}
if (wpa_s->current_ssid != ssid)
return 1; /* different network block */
+ if (wpas_driver_bss_selection(wpa_s))
+ return 0; /* Driver-based roaming */
+
for (i = 0; i < scan_res->num; i++) {
struct wpa_scan_res *res = scan_res->res[i];
const u8 *ie;
if (!current_bss)
return 1; /* current BSS not seen in scan results */
- wpa_printf(MSG_DEBUG, "Considering within-ESS reassociation");
- wpa_printf(MSG_DEBUG, "Current BSS: " MACSTR " level=%d",
- MAC2STR(current_bss->bssid), current_bss->level);
- wpa_printf(MSG_DEBUG, "Selected BSS: " MACSTR " level=%d",
- MAC2STR(selected->bssid), selected->level);
+ wpa_dbg(wpa_s, MSG_DEBUG, "Considering within-ESS reassociation");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Current BSS: " MACSTR " level=%d",
+ MAC2STR(current_bss->bssid), current_bss->level);
+ wpa_dbg(wpa_s, MSG_DEBUG, "Selected BSS: " MACSTR " level=%d",
+ MAC2STR(selected->bssid), selected->level);
if (wpa_s->current_ssid->bssid_set &&
os_memcmp(selected->bssid, wpa_s->current_ssid->bssid, ETH_ALEN) ==
0) {
- wpa_printf(MSG_DEBUG, "Allow reassociation - selected BSS has "
- "preferred BSSID");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Allow reassociation - selected BSS "
+ "has preferred BSSID");
return 1;
}
min_diff = 5;
}
if (abs(current_bss->level - selected->level) < min_diff) {
- wpa_printf(MSG_DEBUG, "Skip roam - too small difference in "
- "signal level");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Skip roam - too small difference "
+ "in signal level");
return 0;
}
return 1;
}
-
-static void wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
+/* Return < 0 if no scan results could be fetched. */
+static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
union wpa_event_data *data)
{
struct wpa_bss *selected;
wpa_supplicant_notify_scanning(wpa_s, 0);
+#ifdef CONFIG_P2P
+ if (wpa_s->p2p_cb_on_scan_complete && !wpa_s->global->p2p_disabled &&
+ wpa_s->global->p2p != NULL) {
+ wpa_s->p2p_cb_on_scan_complete = 0;
+ if (p2p_other_scan_completed(wpa_s->global->p2p) == 1) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Pending P2P operation "
+ "stopped scan processing");
+ return -1;
+ }
+ }
+#endif /* CONFIG_P2P */
+
scan_res = wpa_supplicant_get_scan_results(wpa_s,
data ? &data->scan_info :
NULL, 1);
if (scan_res == NULL) {
if (wpa_s->conf->ap_scan == 2 || ap)
- return;
- wpa_printf(MSG_DEBUG, "Failed to get scan results - try "
- "scanning again");
+ return -1;
+ wpa_dbg(wpa_s, MSG_DEBUG, "Failed to get scan results - try "
+ "scanning again");
wpa_supplicant_req_new_scan(wpa_s, 1, 0);
- return;
+ return -1;
}
+#ifndef CONFIG_NO_RANDOM_POOL
+ size_t i, num;
+ num = scan_res->num;
+ if (num > 10)
+ num = 10;
+ for (i = 0; i < num; i++) {
+ u8 buf[5];
+ struct wpa_scan_res *res = scan_res->res[i];
+ buf[0] = res->bssid[5];
+ buf[1] = res->qual & 0xff;
+ buf[2] = res->noise & 0xff;
+ buf[3] = res->level & 0xff;
+ buf[4] = res->tsf & 0xff;
+ random_add_randomness(buf, sizeof(buf));
+ }
+#endif /* CONFIG_NO_RANDOM_POOL */
+
if (wpa_s->scan_res_handler) {
- wpa_s->scan_res_handler(wpa_s, scan_res);
+ void (*scan_res_handler)(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_results *scan_res);
+
+ scan_res_handler = wpa_s->scan_res_handler;
wpa_s->scan_res_handler = NULL;
+ scan_res_handler(wpa_s, scan_res);
+
wpa_scan_results_free(scan_res);
- return;
+ return 0;
}
if (ap) {
- wpa_printf(MSG_DEBUG, "Ignore scan results in AP mode");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Ignore scan results in AP mode");
+#ifdef CONFIG_AP
+ if (wpa_s->ap_iface->scan_cb)
+ wpa_s->ap_iface->scan_cb(wpa_s->ap_iface);
+#endif /* CONFIG_AP */
wpa_scan_results_free(scan_res);
- return;
+ return 0;
}
- wpa_printf(MSG_DEBUG, "New scan results available");
+ wpa_dbg(wpa_s, MSG_DEBUG, "New scan results available");
wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS);
wpas_notify_scan_results(wpa_s);
if ((wpa_s->conf->ap_scan == 2 && !wpas_wps_searching(wpa_s))) {
wpa_scan_results_free(scan_res);
- return;
+ return 0;
}
if (wpa_s->disconnected) {
wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
wpa_scan_results_free(scan_res);
- return;
+ return 0;
}
- if (bgscan_notify_scan(wpa_s) == 1) {
+ if (!wpas_driver_bss_selection(wpa_s) &&
+ bgscan_notify_scan(wpa_s, scan_res) == 1) {
wpa_scan_results_free(scan_res);
- return;
+ return 0;
}
- wpa_supplicant_rsn_preauth_scan_results(wpa_s, scan_res);
-
selected = wpa_supplicant_pick_network(wpa_s, scan_res, &ssid);
if (selected) {
skip = !wpa_supplicant_need_to_roam(wpa_s, selected, ssid,
scan_res);
wpa_scan_results_free(scan_res);
- if (skip)
- return;
- wpa_supplicant_connect(wpa_s, selected, ssid);
+ if (skip) {
+ wpa_supplicant_rsn_preauth_scan_results(wpa_s);
+ return 0;
+ }
+
+ if (wpa_supplicant_connect(wpa_s, selected, ssid) < 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Connect failed");
+ return -1;
+ }
+ wpa_supplicant_rsn_preauth_scan_results(wpa_s);
} else {
wpa_scan_results_free(scan_res);
- wpa_printf(MSG_DEBUG, "No suitable network found");
+ wpa_dbg(wpa_s, MSG_DEBUG, "No suitable network found");
ssid = wpa_supplicant_pick_new_network(wpa_s);
if (ssid) {
- wpa_printf(MSG_DEBUG, "Setup a new network");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Setup a new network");
wpa_supplicant_associate(wpa_s, NULL, ssid);
+ wpa_supplicant_rsn_preauth_scan_results(wpa_s);
} else {
- int timeout_sec = 5;
+ int timeout_sec = wpa_s->scan_interval;
int timeout_usec = 0;
- wpa_supplicant_req_new_scan(wpa_s, timeout_sec,
- timeout_usec);
+#ifdef CONFIG_P2P
+ if (wpa_s->p2p_in_provisioning) {
+ /*
+ * Use shorter wait during P2P Provisioning
+ * state to speed up group formation.
+ */
+ timeout_sec = 0;
+ timeout_usec = 250000;
+ wpa_supplicant_req_new_scan(wpa_s, timeout_sec,
+ timeout_usec);
+ return 0;
+ }
+#endif /* CONFIG_P2P */
+ if (wpa_supplicant_req_sched_scan(wpa_s))
+ wpa_supplicant_req_new_scan(wpa_s, timeout_sec,
+ timeout_usec);
}
}
+ return 0;
}
+
+
+static void wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
+ union wpa_event_data *data)
+{
+ const char *rn, *rn2;
+ struct wpa_supplicant *ifs;
+
+ if (_wpa_supplicant_event_scan_results(wpa_s, data) < 0) {
+ /*
+ * If no scan results could be fetched, then no need to
+ * notify those interfaces that did not actually request
+ * this scan.
+ */
+ return;
+ }
+
+ /*
+ * Check other interfaces to see if they have the same radio-name. If
+ * so, they get updated with this same scan info.
+ */
+ if (!wpa_s->driver->get_radio_name)
+ return;
+
+ rn = wpa_s->driver->get_radio_name(wpa_s->drv_priv);
+ if (rn == NULL || rn[0] == '\0')
+ return;
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "Checking for other virtual interfaces "
+ "sharing same radio (%s) in event_scan_results", rn);
+
+ for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) {
+ if (ifs == wpa_s || !ifs->driver->get_radio_name)
+ continue;
+
+ rn2 = ifs->driver->get_radio_name(ifs->drv_priv);
+ if (rn2 && os_strcmp(rn, rn2) == 0) {
+ wpa_printf(MSG_DEBUG, "%s: Updating scan results from "
+ "sibling", ifs->ifname);
+ _wpa_supplicant_event_scan_results(ifs, data);
+ }
+ }
+}
+
#endif /* CONFIG_NO_SCAN_PROCESSING */
int l, len, found = 0, wpa_found, rsn_found;
const u8 *p;
- wpa_printf(MSG_DEBUG, "Association info event");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Association info event");
if (data->assoc_info.req_ies)
wpa_hexdump(MSG_DEBUG, "req_ies", data->assoc_info.req_ies,
data->assoc_info.req_ies_len);
- if (data->assoc_info.resp_ies)
+ if (data->assoc_info.resp_ies) {
wpa_hexdump(MSG_DEBUG, "resp_ies", data->assoc_info.resp_ies,
data->assoc_info.resp_ies_len);
+#ifdef CONFIG_TDLS
+ wpa_tdls_assoc_resp_ies(wpa_s->wpa, data->assoc_info.resp_ies,
+ data->assoc_info.resp_ies_len);
+#endif /* CONFIG_TDLS */
+ }
if (data->assoc_info.beacon_ies)
wpa_hexdump(MSG_DEBUG, "beacon_ies",
data->assoc_info.beacon_ies,
data->assoc_info.beacon_ies_len);
if (data->assoc_info.freq)
- wpa_printf(MSG_DEBUG, "freq=%u MHz", data->assoc_info.freq);
+ wpa_dbg(wpa_s, MSG_DEBUG, "freq=%u MHz",
+ data->assoc_info.freq);
p = data->assoc_info.req_ies;
l = data->assoc_info.req_ies_len;
data->assoc_info.resp_ies,
data->assoc_info.resp_ies_len,
bssid) < 0) {
- wpa_printf(MSG_DEBUG, "FT: Validation of "
- "Reassociation Response failed");
+ wpa_dbg(wpa_s, MSG_DEBUG, "FT: Validation of "
+ "Reassociation Response failed");
wpa_supplicant_deauthenticate(
wpa_s, WLAN_REASON_INVALID_IE);
return -1;
p = data->assoc_info.resp_ies;
l = data->assoc_info.resp_ies_len;
+#ifdef CONFIG_WPS_STRICT
+ if (p && wpa_s->current_ssid &&
+ wpa_s->current_ssid->key_mgmt == WPA_KEY_MGMT_WPS) {
+ struct wpabuf *wps;
+ wps = ieee802_11_vendor_ie_concat(p, l, WPS_IE_VENDOR_TYPE);
+ if (wps == NULL) {
+ wpa_msg(wpa_s, MSG_INFO, "WPS-STRICT: AP did not "
+ "include WPS IE in (Re)Association Response");
+ return -1;
+ }
+
+ if (wps_validate_assoc_resp(wps) < 0) {
+ wpabuf_free(wps);
+ wpa_supplicant_deauthenticate(
+ wpa_s, WLAN_REASON_INVALID_IE);
+ return -1;
+ }
+ wpabuf_free(wps);
+ }
+#endif /* CONFIG_WPS_STRICT */
+
/* Go through the IEs and make a copy of the MDIE, if present. */
while (p && l >= 2) {
len = p[1] + 2;
if (wpa_found || rsn_found)
wpa_s->ap_ies_from_associnfo = 1;
+ if (wpa_s->assoc_freq && data->assoc_info.freq &&
+ wpa_s->assoc_freq != data->assoc_info.freq) {
+ wpa_printf(MSG_DEBUG, "Operating frequency changed from "
+ "%u to %u MHz",
+ wpa_s->assoc_freq, data->assoc_info.freq);
+ wpa_supplicant_update_scan_results(wpa_s);
+ }
+
wpa_s->assoc_freq = data->assoc_info.freq;
return 0;
hostapd_notif_assoc(wpa_s->ap_iface->bss[0],
data->assoc_info.addr,
data->assoc_info.req_ies,
- data->assoc_info.req_ies_len);
+ data->assoc_info.req_ies_len,
+ data->assoc_info.reassoc);
return;
}
#endif /* CONFIG_AP */
return;
wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATED);
- if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)
- os_memcpy(bssid, wpa_s->bssid, ETH_ALEN);
- if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) ||
- (wpa_drv_get_bssid(wpa_s, bssid) >= 0 &&
- os_memcmp(bssid, wpa_s->bssid, ETH_ALEN) != 0)) {
- wpa_msg(wpa_s, MSG_DEBUG, "Associated to a new BSS: BSSID="
+ if (wpa_drv_get_bssid(wpa_s, bssid) >= 0 &&
+ os_memcmp(bssid, wpa_s->bssid, ETH_ALEN) != 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Associated to a new BSS: BSSID="
MACSTR, MAC2STR(bssid));
+ random_add_randomness(bssid, ETH_ALEN);
bssid_changed = os_memcmp(wpa_s->bssid, bssid, ETH_ALEN);
os_memcpy(wpa_s->bssid, bssid, ETH_ALEN);
os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
eapol_sm_notify_portValid(wpa_s->eapol, TRUE);
eapol_sm_notify_eap_success(wpa_s->eapol, TRUE);
+ } else if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE) &&
+ wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) {
+ /*
+ * The driver will take care of RSN 4-way handshake, so we need
+ * to allow EAPOL supplicant to complete its work without
+ * waiting for WPA supplicant.
+ */
+ eapol_sm_notify_portValid(wpa_s->eapol, TRUE);
+ } else if (ft_completed) {
+ /*
+ * FT protocol completed - make sure EAPOL state machine ends
+ * up in authenticated.
+ */
+ wpa_supplicant_cancel_auth_timeout(wpa_s);
+ wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
+ eapol_sm_notify_portValid(wpa_s->eapol, TRUE);
+ eapol_sm_notify_eap_success(wpa_s->eapol, TRUE);
}
if (wpa_s->pending_eapol_rx) {
if (age.sec == 0 && age.usec < 100000 &&
os_memcmp(wpa_s->pending_eapol_rx_src, bssid, ETH_ALEN) ==
0) {
- wpa_printf(MSG_DEBUG, "Process pending EAPOL frame "
- "that was received just before association "
- "notification");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Process pending EAPOL "
+ "frame that was received just before "
+ "association notification");
wpa_supplicant_rx_eapol(
wpa_s, wpa_s->pending_eapol_rx_src,
wpabuf_head(wpa_s->pending_eapol_rx),
wpa_s->pending_eapol_rx = NULL;
}
-#ifdef CONFIG_BGSCAN
- if (wpa_s->current_ssid != wpa_s->bgscan_ssid) {
- bgscan_deinit(wpa_s);
- if (wpa_s->current_ssid && wpa_s->current_ssid->bgscan) {
- if (bgscan_init(wpa_s, wpa_s->current_ssid)) {
- wpa_printf(MSG_DEBUG, "Failed to initialize "
- "bgscan");
- /*
- * Live without bgscan; it is only used as a
- * roaming optimization, so the initial
- * connection is not affected.
- */
- } else
- wpa_s->bgscan_ssid = wpa_s->current_ssid;
- } else
- wpa_s->bgscan_ssid = NULL;
- }
-#endif /* CONFIG_BGSCAN */
-
if ((wpa_s->key_mgmt == WPA_KEY_MGMT_NONE ||
wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) &&
wpa_s->current_ssid && wpa_drv_get_capa(wpa_s, &capa) == 0 &&
/* Set static WEP keys again */
wpa_set_wep_keys(wpa_s, wpa_s->current_ssid);
}
+
+#ifdef CONFIG_IBSS_RSN
+ if (wpa_s->current_ssid &&
+ wpa_s->current_ssid->mode == WPAS_MODE_IBSS &&
+ wpa_s->key_mgmt != WPA_KEY_MGMT_NONE &&
+ wpa_s->key_mgmt != WPA_KEY_MGMT_WPA_NONE &&
+ wpa_s->ibss_rsn == NULL) {
+ wpa_s->ibss_rsn = ibss_rsn_init(wpa_s);
+ if (!wpa_s->ibss_rsn) {
+ wpa_msg(wpa_s, MSG_INFO, "Failed to init IBSS RSN");
+ wpa_supplicant_deauthenticate(
+ wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+ return;
+ }
+
+ ibss_rsn_set_psk(wpa_s->ibss_rsn, wpa_s->current_ssid->psk);
+ }
+#endif /* CONFIG_IBSS_RSN */
}
u16 reason_code)
{
const u8 *bssid;
-#ifdef CONFIG_SME
int authenticating;
u8 prev_pending_bssid[ETH_ALEN];
authenticating = wpa_s->wpa_state == WPA_AUTHENTICATING;
os_memcpy(prev_pending_bssid, wpa_s->pending_bssid, ETH_ALEN);
-#endif /* CONFIG_SME */
if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) {
/*
* generating streams of disconnected events when configuring
* IBSS for WPA-None. Ignore them for now.
*/
- wpa_printf(MSG_DEBUG, "Disconnect event - ignore in "
- "IBSS/WPA-None mode");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Disconnect event - ignore in "
+ "IBSS/WPA-None mode");
return;
}
wpa_msg(wpa_s, MSG_INFO, "WPA: 4-Way Handshake failed - "
"pre-shared key may be incorrect");
}
- if (wpa_s->wpa_state >= WPA_ASSOCIATED)
- wpa_supplicant_req_scan(wpa_s, 0, 100000);
+ if (!wpa_s->auto_reconnect_disabled ||
+ wpa_s->key_mgmt == WPA_KEY_MGMT_WPS) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Auto connect enabled: try to "
+ "reconnect (wps=%d)",
+ wpa_s->key_mgmt == WPA_KEY_MGMT_WPS);
+ if (wpa_s->wpa_state >= WPA_ASSOCIATING)
+ wpa_supplicant_req_scan(wpa_s, 0, 100000);
+ } else {
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Auto connect disabled: do not "
+ "try to re-connect");
+ wpa_s->reassociate = 0;
+ wpa_s->disconnected = 1;
+ wpa_supplicant_cancel_sched_scan(wpa_s);
+ }
bssid = wpa_s->bssid;
if (is_zero_ether_addr(bssid))
bssid = wpa_s->pending_bssid;
- wpa_blacklist_add(wpa_s, bssid);
+ wpas_connection_failed(wpa_s, bssid);
wpa_sm_notify_disassoc(wpa_s->wpa);
wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid=" MACSTR
" reason=%d",
MAC2STR(bssid), reason_code);
if (wpa_supplicant_dynamic_keys(wpa_s)) {
- wpa_printf(MSG_DEBUG, "Disconnect event - remove keys");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Disconnect event - remove keys");
wpa_s->keys_cleared = 0;
wpa_clear_keys(wpa_s, wpa_s->bssid);
}
wpa_supplicant_mark_disassoc(wpa_s);
- bgscan_deinit(wpa_s);
- wpa_s->bgscan_ssid = NULL;
-#ifdef CONFIG_SME
- if (authenticating &&
- (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)) {
- /*
- * mac80211-workaround to force deauth on failed auth cmd,
- * requires us to remain in authenticating state to allow the
- * second authentication attempt to be continued properly.
- */
- wpa_printf(MSG_DEBUG, "SME: Allow pending authentication to "
- "proceed after disconnection event");
- wpa_supplicant_set_state(wpa_s, WPA_AUTHENTICATING);
- os_memcpy(wpa_s->pending_bssid, prev_pending_bssid, ETH_ALEN);
- }
-#endif /* CONFIG_SME */
+
+ if (authenticating && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME))
+ sme_disassoc_while_authenticating(wpa_s, prev_pending_bssid);
}
#ifdef CONFIG_DELAYED_MIC_ERROR_REPORT
-static void wpa_supplicant_delayed_mic_error_report(void *eloop_ctx,
- void *sock_ctx)
+void wpa_supplicant_delayed_mic_error_report(void *eloop_ctx, void *sock_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
if (!wpa_s->pending_mic_error_report)
return;
- wpa_printf(MSG_DEBUG, "WPA: Sending pending MIC error report");
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Sending pending MIC error report");
wpa_sm_key_request(wpa_s->wpa, 1, wpa_s->pending_mic_error_pairwise);
wpa_s->pending_mic_error_report = 0;
}
sec = os_random() % 60;
else
sec = WPA_GET_BE32(rval) % 60;
- wpa_printf(MSG_DEBUG, "WPA: Delay MIC error report %d "
- "seconds", sec);
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Delay MIC error "
+ "report %d seconds", sec);
wpa_s->pending_mic_error_report = 1;
wpa_s->pending_mic_error_pairwise = pairwise;
eloop_cancel_timeout(
if (!wpa_s->interface_removed)
break;
wpa_s->interface_removed = 0;
- wpa_printf(MSG_DEBUG, "Configured interface was added.");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Configured interface was added");
if (wpa_supplicant_driver_init(wpa_s) < 0) {
- wpa_printf(MSG_INFO, "Failed to initialize the driver "
- "after interface was added.");
+ wpa_msg(wpa_s, MSG_INFO, "Failed to initialize the "
+ "driver after interface was added");
}
+ wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
break;
case EVENT_INTERFACE_REMOVED:
- wpa_printf(MSG_DEBUG, "Configured interface was removed.");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Configured interface was removed");
wpa_s->interface_removed = 1;
wpa_supplicant_mark_disassoc(wpa_s);
+ wpa_supplicant_set_state(wpa_s, WPA_INTERFACE_DISABLED);
l2_packet_deinit(wpa_s->l2);
wpa_s->l2 = NULL;
+#ifdef CONFIG_IBSS_RSN
+ ibss_rsn_deinit(wpa_s->ibss_rsn);
+ wpa_s->ibss_rsn = NULL;
+#endif /* CONFIG_IBSS_RSN */
#ifdef CONFIG_TERMINATE_ONLASTIF
/* check if last interface */
if (!any_interfaces(wpa_s->global->ifaces))
#endif /* CONFIG_PEERKEY */
+#ifdef CONFIG_TDLS
+static void wpa_supplicant_event_tdls(struct wpa_supplicant *wpa_s,
+ union wpa_event_data *data)
+{
+ if (data == NULL)
+ return;
+ switch (data->tdls.oper) {
+ case TDLS_REQUEST_SETUP:
+ wpa_tdls_start(wpa_s->wpa, data->tdls.peer);
+ break;
+ case TDLS_REQUEST_TEARDOWN:
+ wpa_tdls_send_teardown(wpa_s->wpa, data->tdls.peer,
+ data->tdls.reason_code);
+ break;
+ }
+}
+#endif /* CONFIG_TDLS */
+
+
#ifdef CONFIG_IEEE80211R
static void
wpa_supplicant_event_ft_response(struct wpa_supplicant *wpa_s,
static void wpa_supplicant_event_ibss_rsn_start(struct wpa_supplicant *wpa_s,
union wpa_event_data *data)
{
+ struct wpa_ssid *ssid;
+ if (wpa_s->wpa_state < WPA_ASSOCIATED)
+ return;
if (data == NULL)
return;
+ ssid = wpa_s->current_ssid;
+ if (ssid == NULL)
+ return;
+ if (ssid->mode != WPAS_MODE_IBSS || !wpa_key_mgmt_wpa(ssid->key_mgmt))
+ return;
+
ibss_rsn_start(wpa_s->ibss_rsn, data->ibss_rsn_start.peer);
}
#endif /* CONFIG_IBSS_RSN */
sta_addr = data + 1;
target_ap_addr = data + 1 + ETH_ALEN;
status = WPA_GET_LE16(data + 1 + 2 * ETH_ALEN);
- wpa_printf(MSG_DEBUG, "FT: Received FT Action Response: STA " MACSTR
- " TargetAP " MACSTR " status %u",
- MAC2STR(sta_addr), MAC2STR(target_ap_addr), status);
+ wpa_dbg(wpa_s, MSG_DEBUG, "FT: Received FT Action Response: STA "
+ MACSTR " TargetAP " MACSTR " status %u",
+ MAC2STR(sta_addr), MAC2STR(target_ap_addr), status);
if (os_memcmp(sta_addr, wpa_s->own_addr, ETH_ALEN) != 0) {
- wpa_printf(MSG_DEBUG, "FT: Foreign STA Address " MACSTR
- " in FT Action Response", MAC2STR(sta_addr));
+ wpa_dbg(wpa_s, MSG_DEBUG, "FT: Foreign STA Address " MACSTR
+ " in FT Action Response", MAC2STR(sta_addr));
return;
}
if (status) {
- wpa_printf(MSG_DEBUG, "FT: FT Action Response indicates "
- "failure (status code %d)", status);
+ wpa_dbg(wpa_s, MSG_DEBUG, "FT: FT Action Response indicates "
+ "failure (status code %d)", status);
/* TODO: report error to FT code(?) */
return;
}
#endif /* CONFIG_IEEE80211R */
+static void wpa_supplicant_event_unprot_deauth(struct wpa_supplicant *wpa_s,
+ struct unprot_deauth *e)
+{
+#ifdef CONFIG_IEEE80211W
+ wpa_printf(MSG_DEBUG, "Unprotected Deauthentication frame "
+ "dropped: " MACSTR " -> " MACSTR
+ " (reason code %u)",
+ MAC2STR(e->sa), MAC2STR(e->da), e->reason_code);
+ sme_event_unprot_disconnect(wpa_s, e->sa, e->da, e->reason_code);
+#endif /* CONFIG_IEEE80211W */
+}
+
+
+static void wpa_supplicant_event_unprot_disassoc(struct wpa_supplicant *wpa_s,
+ struct unprot_disassoc *e)
+{
+#ifdef CONFIG_IEEE80211W
+ wpa_printf(MSG_DEBUG, "Unprotected Disassociation frame "
+ "dropped: " MACSTR " -> " MACSTR
+ " (reason code %u)",
+ MAC2STR(e->sa), MAC2STR(e->da), e->reason_code);
+ sme_event_unprot_disconnect(wpa_s, e->sa, e->da, e->reason_code);
+#endif /* CONFIG_IEEE80211W */
+}
+
+
+static void wnm_action_rx(struct wpa_supplicant *wpa_s, struct rx_action *rx)
+{
+ u8 action, mode;
+ const u8 *pos, *end;
+
+ if (rx->data == NULL || rx->len == 0)
+ return;
+
+ pos = rx->data;
+ end = pos + rx->len;
+ action = *pos++;
+
+ wpa_printf(MSG_DEBUG, "WNM: RX action %u from " MACSTR,
+ action, MAC2STR(rx->sa));
+ switch (action) {
+ case WNM_BSS_TRANS_MGMT_REQ:
+ if (pos + 5 > end)
+ break;
+ wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management "
+ "Request: dialog_token=%u request_mode=0x%x "
+ "disassoc_timer=%u validity_interval=%u",
+ pos[0], pos[1], WPA_GET_LE16(pos + 2), pos[4]);
+ mode = pos[1];
+ pos += 5;
+ if (mode & 0x08)
+ pos += 12; /* BSS Termination Duration */
+ if (mode & 0x10) {
+ char url[256];
+ if (pos + 1 > end || pos + 1 + pos[0] > end) {
+ wpa_printf(MSG_DEBUG, "WNM: Invalid BSS "
+ "Transition Management Request "
+ "(URL)");
+ break;
+ }
+ os_memcpy(url, pos + 1, pos[0]);
+ url[pos[0]] = '\0';
+ wpa_msg(wpa_s, MSG_INFO, "WNM: ESS Disassociation "
+ "Imminent - session_info_url=%s", url);
+ }
+ break;
+ }
+}
+
+
void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
union wpa_event_data *data)
{
struct wpa_supplicant *wpa_s = ctx;
u16 reason_code = 0;
+ if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED &&
+ event != EVENT_INTERFACE_ENABLED &&
+ event != EVENT_INTERFACE_STATUS) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Ignore event %s (%d) while interface is disabled",
+ event_to_string(event), event);
+ return;
+ }
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "Event %s (%d) received",
+ event_to_string(event), event);
+
switch (event) {
case EVENT_AUTH:
sme_event_auth(wpa_s, data);
wpa_supplicant_event_assoc(wpa_s, data);
break;
case EVENT_DISASSOC:
- wpa_printf(MSG_DEBUG, "Disassociation notification");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Disassociation notification");
+ if (data) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " * reason %u",
+ data->disassoc_info.reason_code);
+ if (data->disassoc_info.addr)
+ wpa_dbg(wpa_s, MSG_DEBUG, " * address " MACSTR,
+ MAC2STR(data->disassoc_info.addr));
+ }
#ifdef CONFIG_AP
if (wpa_s->ap_iface && data && data->disassoc_info.addr) {
hostapd_notif_disassoc(wpa_s->ap_iface->bss[0],
data->disassoc_info.addr);
break;
}
+ if (wpa_s->ap_iface) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Ignore disassoc event in "
+ "AP mode");
+ break;
+ }
#endif /* CONFIG_AP */
- if (data)
- reason_code = data->deauth_info.reason_code;
+ if (data) {
+ reason_code = data->disassoc_info.reason_code;
+ wpa_hexdump(MSG_DEBUG, "Disassociation frame IE(s)",
+ data->disassoc_info.ie,
+ data->disassoc_info.ie_len);
+#ifdef CONFIG_P2P
+ wpas_p2p_disassoc_notif(
+ wpa_s, data->disassoc_info.addr, reason_code,
+ data->disassoc_info.ie,
+ data->disassoc_info.ie_len);
+#endif /* CONFIG_P2P */
+ }
if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)
sme_event_disassoc(wpa_s, data);
/* fall through */
case EVENT_DEAUTH:
if (event == EVENT_DEAUTH) {
- wpa_printf(MSG_DEBUG, "Deauthentication notification");
- if (data)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Deauthentication notification");
+ if (data) {
reason_code = data->deauth_info.reason_code;
+ wpa_dbg(wpa_s, MSG_DEBUG, " * reason %u",
+ data->deauth_info.reason_code);
+ if (data->deauth_info.addr) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " * address "
+ MACSTR,
+ MAC2STR(data->deauth_info.
+ addr));
+ }
+ wpa_hexdump(MSG_DEBUG,
+ "Deauthentication frame IE(s)",
+ data->deauth_info.ie,
+ data->deauth_info.ie_len);
+#ifdef CONFIG_P2P
+ wpas_p2p_deauth_notif(
+ wpa_s, data->deauth_info.addr,
+ reason_code,
+ data->deauth_info.ie,
+ data->deauth_info.ie_len);
+#endif /* CONFIG_P2P */
+ }
}
#ifdef CONFIG_AP
if (wpa_s->ap_iface && data && data->deauth_info.addr) {
data->deauth_info.addr);
break;
}
+ if (wpa_s->ap_iface) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Ignore deauth event in "
+ "AP mode");
+ break;
+ }
#endif /* CONFIG_AP */
wpa_supplicant_event_disassoc(wpa_s, reason_code);
break;
wpa_supplicant_event_stkstart(wpa_s, data);
break;
#endif /* CONFIG_PEERKEY */
+#ifdef CONFIG_TDLS
+ case EVENT_TDLS:
+ wpa_supplicant_event_tdls(wpa_s, data);
+ break;
+#endif /* CONFIG_TDLS */
#ifdef CONFIG_IEEE80211R
case EVENT_FT_RESPONSE:
wpa_supplicant_event_ft_response(wpa_s, data);
break;
#endif /* CONFIG_IBSS_RSN */
case EVENT_ASSOC_REJECT:
- sme_event_assoc_reject(wpa_s, data);
+ if (data->assoc_reject.bssid)
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_ASSOC_REJECT
+ "bssid=" MACSTR " status_code=%u",
+ MAC2STR(data->assoc_reject.bssid),
+ data->assoc_reject.status_code);
+ else
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_ASSOC_REJECT
+ "status_code=%u",
+ data->assoc_reject.status_code);
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)
+ sme_event_assoc_reject(wpa_s, data);
break;
case EVENT_AUTH_TIMED_OUT:
- sme_event_auth_timed_out(wpa_s, data);
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)
+ sme_event_auth_timed_out(wpa_s, data);
break;
case EVENT_ASSOC_TIMED_OUT:
- sme_event_assoc_timed_out(wpa_s, data);
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)
+ sme_event_assoc_timed_out(wpa_s, data);
break;
-#ifdef CONFIG_AP
case EVENT_TX_STATUS:
- if (wpa_s->ap_iface == NULL)
+ wpa_dbg(wpa_s, MSG_DEBUG, "EVENT_TX_STATUS dst=" MACSTR
+ " type=%d stype=%d",
+ MAC2STR(data->tx_status.dst),
+ data->tx_status.type, data->tx_status.stype);
+#ifdef CONFIG_AP
+ if (wpa_s->ap_iface == NULL) {
+#ifdef CONFIG_OFFCHANNEL
+ if (data->tx_status.type == WLAN_FC_TYPE_MGMT &&
+ data->tx_status.stype == WLAN_FC_STYPE_ACTION)
+ offchannel_send_action_tx_status(
+ wpa_s, data->tx_status.dst,
+ data->tx_status.data,
+ data->tx_status.data_len,
+ data->tx_status.ack ?
+ OFFCHANNEL_SEND_ACTION_SUCCESS :
+ OFFCHANNEL_SEND_ACTION_NO_ACK);
+#endif /* CONFIG_OFFCHANNEL */
break;
+ }
+#endif /* CONFIG_AP */
+#ifdef CONFIG_OFFCHANNEL
+ wpa_dbg(wpa_s, MSG_DEBUG, "EVENT_TX_STATUS pending_dst="
+ MACSTR, MAC2STR(wpa_s->parent->pending_action_dst));
+ /*
+ * Catch TX status events for Action frames we sent via group
+ * interface in GO mode.
+ */
+ if (data->tx_status.type == WLAN_FC_TYPE_MGMT &&
+ data->tx_status.stype == WLAN_FC_STYPE_ACTION &&
+ os_memcmp(wpa_s->parent->pending_action_dst,
+ data->tx_status.dst, ETH_ALEN) == 0) {
+ offchannel_send_action_tx_status(
+ wpa_s->parent, data->tx_status.dst,
+ data->tx_status.data,
+ data->tx_status.data_len,
+ data->tx_status.ack ?
+ OFFCHANNEL_SEND_ACTION_SUCCESS :
+ OFFCHANNEL_SEND_ACTION_NO_ACK);
+ break;
+ }
+#endif /* CONFIG_OFFCHANNEL */
+#ifdef CONFIG_AP
switch (data->tx_status.type) {
case WLAN_FC_TYPE_MGMT:
ap_mgmt_tx_cb(wpa_s, data->tx_status.data,
data->tx_status.ack);
break;
}
+#endif /* CONFIG_AP */
+ break;
+#ifdef CONFIG_AP
+ case EVENT_DRIVER_CLIENT_POLL_OK:
+ ap_client_poll_ok(wpa_s, data->client_poll.addr);
break;
case EVENT_RX_FROM_UNKNOWN:
if (wpa_s->ap_iface == NULL)
break;
- ap_rx_from_unknown_sta(wpa_s, data->rx_from_unknown.frame,
- data->rx_from_unknown.len);
+ ap_rx_from_unknown_sta(wpa_s, data->rx_from_unknown.addr,
+ data->rx_from_unknown.wds);
break;
case EVENT_RX_MGMT:
- if (wpa_s->ap_iface == NULL)
+ if (wpa_s->ap_iface == NULL) {
+#ifdef CONFIG_P2P
+ u16 fc, stype;
+ const struct ieee80211_mgmt *mgmt;
+ mgmt = (const struct ieee80211_mgmt *)
+ data->rx_mgmt.frame;
+ fc = le_to_host16(mgmt->frame_control);
+ stype = WLAN_FC_GET_STYPE(fc);
+ if (stype == WLAN_FC_STYPE_PROBE_REQ &&
+ data->rx_mgmt.frame_len > 24) {
+ const u8 *src = mgmt->sa;
+ const u8 *ie = mgmt->u.probe_req.variable;
+ size_t ie_len = data->rx_mgmt.frame_len -
+ (mgmt->u.probe_req.variable -
+ data->rx_mgmt.frame);
+ wpas_p2p_probe_req_rx(wpa_s, src, mgmt->da,
+ mgmt->bssid, ie, ie_len);
+ break;
+ }
+#endif /* CONFIG_P2P */
+ wpa_dbg(wpa_s, MSG_DEBUG, "AP: ignore received "
+ "management frame in non-AP mode");
break;
+ }
ap_mgmt_rx(wpa_s, &data->rx_mgmt);
break;
#endif /* CONFIG_AP */
case EVENT_RX_ACTION:
- wpa_printf(MSG_DEBUG, "Received Action frame: SA=" MACSTR
- " Category=%u DataLen=%d freq=%d MHz",
- MAC2STR(data->rx_action.sa),
- data->rx_action.category, (int) data->rx_action.len,
- data->rx_action.freq);
+ wpa_dbg(wpa_s, MSG_DEBUG, "Received Action frame: SA=" MACSTR
+ " Category=%u DataLen=%d freq=%d MHz",
+ MAC2STR(data->rx_action.sa),
+ data->rx_action.category, (int) data->rx_action.len,
+ data->rx_action.freq);
#ifdef CONFIG_IEEE80211R
if (data->rx_action.category == WLAN_ACTION_FT) {
ft_rx_action(wpa_s, data->rx_action.data,
break;
}
#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IEEE80211W
+#ifdef CONFIG_SME
+ if (data->rx_action.category == WLAN_ACTION_SA_QUERY) {
+ sme_sa_query_rx(wpa_s, data->rx_action.sa,
+ data->rx_action.data,
+ data->rx_action.len);
+ break;
+ }
+#endif /* CONFIG_SME */
+#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_GAS
+ if (data->rx_action.category == WLAN_ACTION_PUBLIC &&
+ gas_query_rx(wpa_s->gas, data->rx_action.da,
+ data->rx_action.sa, data->rx_action.bssid,
+ data->rx_action.data, data->rx_action.len,
+ data->rx_action.freq) == 0)
+ break;
+#endif /* CONFIG_GAS */
+ if (data->rx_action.category == WLAN_ACTION_WNM) {
+ wnm_action_rx(wpa_s, &data->rx_action);
+ break;
+ }
+#ifdef CONFIG_TDLS
+ if (data->rx_action.category == WLAN_ACTION_PUBLIC &&
+ data->rx_action.len >= 4 &&
+ data->rx_action.data[0] == WLAN_TDLS_DISCOVERY_RESPONSE) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "TDLS: Received Discovery "
+ "Response from " MACSTR,
+ MAC2STR(data->rx_action.sa));
+ break;
+ }
+#endif /* CONFIG_TDLS */
+#ifdef CONFIG_P2P
+ wpas_p2p_rx_action(wpa_s, data->rx_action.da,
+ data->rx_action.sa,
+ data->rx_action.bssid,
+ data->rx_action.category,
+ data->rx_action.data,
+ data->rx_action.len, data->rx_action.freq);
+#endif /* CONFIG_P2P */
break;
-#ifdef CONFIG_CLIENT_MLME
- case EVENT_MLME_RX: {
- struct ieee80211_rx_status rx_status;
- os_memset(&rx_status, 0, sizeof(rx_status));
- rx_status.freq = data->mlme_rx.freq;
- rx_status.channel = data->mlme_rx.channel;
- rx_status.ssi = data->mlme_rx.ssi;
- ieee80211_sta_rx(wpa_s, data->mlme_rx.buf, data->mlme_rx.len,
- &rx_status);
+ case EVENT_RX_PROBE_REQ:
+ if (data->rx_probe_req.sa == NULL ||
+ data->rx_probe_req.ie == NULL)
+ break;
+#ifdef CONFIG_AP
+ if (wpa_s->ap_iface) {
+ hostapd_probe_req_rx(wpa_s->ap_iface->bss[0],
+ data->rx_probe_req.sa,
+ data->rx_probe_req.da,
+ data->rx_probe_req.bssid,
+ data->rx_probe_req.ie,
+ data->rx_probe_req.ie_len);
+ break;
+ }
+#endif /* CONFIG_AP */
+#ifdef CONFIG_P2P
+ wpas_p2p_probe_req_rx(wpa_s, data->rx_probe_req.sa,
+ data->rx_probe_req.da,
+ data->rx_probe_req.bssid,
+ data->rx_probe_req.ie,
+ data->rx_probe_req.ie_len);
+#endif /* CONFIG_P2P */
break;
- }
-#endif /* CONFIG_CLIENT_MLME */
+ case EVENT_REMAIN_ON_CHANNEL:
+#ifdef CONFIG_OFFCHANNEL
+ offchannel_remain_on_channel_cb(
+ wpa_s, data->remain_on_channel.freq,
+ data->remain_on_channel.duration);
+#endif /* CONFIG_OFFCHANNEL */
+#ifdef CONFIG_P2P
+ wpas_p2p_remain_on_channel_cb(
+ wpa_s, data->remain_on_channel.freq,
+ data->remain_on_channel.duration);
+#endif /* CONFIG_P2P */
+ break;
+ case EVENT_CANCEL_REMAIN_ON_CHANNEL:
+#ifdef CONFIG_OFFCHANNEL
+ offchannel_cancel_remain_on_channel_cb(
+ wpa_s, data->remain_on_channel.freq);
+#endif /* CONFIG_OFFCHANNEL */
+#ifdef CONFIG_P2P
+ wpas_p2p_cancel_remain_on_channel_cb(
+ wpa_s, data->remain_on_channel.freq);
+#endif /* CONFIG_P2P */
+ break;
+#ifdef CONFIG_P2P
+ case EVENT_P2P_DEV_FOUND: {
+ struct p2p_peer_info peer_info;
+
+ os_memset(&peer_info, 0, sizeof(peer_info));
+ if (data->p2p_dev_found.dev_addr)
+ os_memcpy(peer_info.p2p_device_addr,
+ data->p2p_dev_found.dev_addr, ETH_ALEN);
+ if (data->p2p_dev_found.pri_dev_type)
+ os_memcpy(peer_info.pri_dev_type,
+ data->p2p_dev_found.pri_dev_type,
+ sizeof(peer_info.pri_dev_type));
+ if (data->p2p_dev_found.dev_name)
+ os_strlcpy(peer_info.device_name,
+ data->p2p_dev_found.dev_name,
+ sizeof(peer_info.device_name));
+ peer_info.config_methods = data->p2p_dev_found.config_methods;
+ peer_info.dev_capab = data->p2p_dev_found.dev_capab;
+ peer_info.group_capab = data->p2p_dev_found.group_capab;
+
+ /*
+ * FIX: new_device=1 is not necessarily correct. We should
+ * maintain a P2P peer database in wpa_supplicant and update
+ * this information based on whether the peer is truly new.
+ */
+ wpas_dev_found(wpa_s, data->p2p_dev_found.addr, &peer_info, 1);
+ break;
+ }
+ case EVENT_P2P_GO_NEG_REQ_RX:
+ wpas_go_neg_req_rx(wpa_s, data->p2p_go_neg_req_rx.src,
+ data->p2p_go_neg_req_rx.dev_passwd_id);
+ break;
+ case EVENT_P2P_GO_NEG_COMPLETED:
+ wpas_go_neg_completed(wpa_s, data->p2p_go_neg_completed.res);
+ break;
+ case EVENT_P2P_PROV_DISC_REQUEST:
+ wpas_prov_disc_req(wpa_s, data->p2p_prov_disc_req.peer,
+ data->p2p_prov_disc_req.config_methods,
+ data->p2p_prov_disc_req.dev_addr,
+ data->p2p_prov_disc_req.pri_dev_type,
+ data->p2p_prov_disc_req.dev_name,
+ data->p2p_prov_disc_req.supp_config_methods,
+ data->p2p_prov_disc_req.dev_capab,
+ data->p2p_prov_disc_req.group_capab,
+ NULL, 0);
+ break;
+ case EVENT_P2P_PROV_DISC_RESPONSE:
+ wpas_prov_disc_resp(wpa_s, data->p2p_prov_disc_resp.peer,
+ data->p2p_prov_disc_resp.config_methods);
+ break;
+ case EVENT_P2P_SD_REQUEST:
+ wpas_sd_request(wpa_s, data->p2p_sd_req.freq,
+ data->p2p_sd_req.sa,
+ data->p2p_sd_req.dialog_token,
+ data->p2p_sd_req.update_indic,
+ data->p2p_sd_req.tlvs,
+ data->p2p_sd_req.tlvs_len);
+ break;
+ case EVENT_P2P_SD_RESPONSE:
+ wpas_sd_response(wpa_s, data->p2p_sd_resp.sa,
+ data->p2p_sd_resp.update_indic,
+ data->p2p_sd_resp.tlvs,
+ data->p2p_sd_resp.tlvs_len);
+ break;
+#endif /* CONFIG_P2P */
case EVENT_EAPOL_RX:
wpa_supplicant_rx_eapol(wpa_s, data->eapol_rx.src,
data->eapol_rx.data,
break;
case EVENT_SIGNAL_CHANGE:
bgscan_notify_signal_change(
- wpa_s, data->signal_change.above_threshold);
+ wpa_s, data->signal_change.above_threshold,
+ data->signal_change.current_signal,
+ data->signal_change.current_noise,
+ data->signal_change.current_txrate);
+ break;
+ case EVENT_INTERFACE_ENABLED:
+ wpa_dbg(wpa_s, MSG_DEBUG, "Interface was enabled");
+ if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
+ wpa_supplicant_update_mac_addr(wpa_s);
+#ifdef CONFIG_AP
+ if (!wpa_s->ap_iface) {
+ wpa_supplicant_set_state(wpa_s,
+ WPA_DISCONNECTED);
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+ } else
+ wpa_supplicant_set_state(wpa_s,
+ WPA_COMPLETED);
+#else /* CONFIG_AP */
+ wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+#endif /* CONFIG_AP */
+ }
+ break;
+ case EVENT_INTERFACE_DISABLED:
+ wpa_dbg(wpa_s, MSG_DEBUG, "Interface was disabled");
+ wpa_supplicant_mark_disassoc(wpa_s);
+ wpa_supplicant_set_state(wpa_s, WPA_INTERFACE_DISABLED);
+ break;
+ case EVENT_CHANNEL_LIST_CHANGED:
+ if (wpa_s->drv_priv == NULL)
+ break; /* Ignore event during drv initialization */
+
+ free_hw_features(wpa_s);
+ wpa_s->hw.modes = wpa_drv_get_hw_feature_data(
+ wpa_s, &wpa_s->hw.num_modes, &wpa_s->hw.flags);
+
+#ifdef CONFIG_P2P
+ wpas_p2p_update_channel_list(wpa_s);
+#endif /* CONFIG_P2P */
+ break;
+ case EVENT_INTERFACE_UNAVAILABLE:
+#ifdef CONFIG_P2P
+ wpas_p2p_interface_unavailable(wpa_s);
+#endif /* CONFIG_P2P */
+ break;
+ case EVENT_BEST_CHANNEL:
+ wpa_dbg(wpa_s, MSG_DEBUG, "Best channel event received "
+ "(%d %d %d)",
+ data->best_chan.freq_24, data->best_chan.freq_5,
+ data->best_chan.freq_overall);
+ wpa_s->best_24_freq = data->best_chan.freq_24;
+ wpa_s->best_5_freq = data->best_chan.freq_5;
+ wpa_s->best_overall_freq = data->best_chan.freq_overall;
+#ifdef CONFIG_P2P
+ wpas_p2p_update_best_channels(wpa_s, data->best_chan.freq_24,
+ data->best_chan.freq_5,
+ data->best_chan.freq_overall);
+#endif /* CONFIG_P2P */
+ break;
+ case EVENT_UNPROT_DEAUTH:
+ wpa_supplicant_event_unprot_deauth(wpa_s,
+ &data->unprot_deauth);
+ break;
+ case EVENT_UNPROT_DISASSOC:
+ wpa_supplicant_event_unprot_disassoc(wpa_s,
+ &data->unprot_disassoc);
+ break;
+ case EVENT_STATION_LOW_ACK:
+#ifdef CONFIG_AP
+ if (wpa_s->ap_iface && data)
+ hostapd_event_sta_low_ack(wpa_s->ap_iface->bss[0],
+ data->low_ack.addr);
+#endif /* CONFIG_AP */
+#ifdef CONFIG_TDLS
+ if (data)
+ wpa_tdls_disable_link(wpa_s->wpa, data->low_ack.addr);
+#endif /* CONFIG_TDLS */
+ break;
+ case EVENT_IBSS_PEER_LOST:
+#ifdef CONFIG_IBSS_RSN
+ ibss_rsn_stop(wpa_s->ibss_rsn, data->ibss_peer_lost.peer);
+#endif /* CONFIG_IBSS_RSN */
+ break;
+ case EVENT_DRIVER_GTK_REKEY:
+ if (os_memcmp(data->driver_gtk_rekey.bssid,
+ wpa_s->bssid, ETH_ALEN))
+ break;
+ if (!wpa_s->wpa)
+ break;
+ wpa_sm_update_replay_ctr(wpa_s->wpa,
+ data->driver_gtk_rekey.replay_ctr);
+ break;
+ case EVENT_SCHED_SCAN_STOPPED:
+ wpa_s->sched_scanning = 0;
+ wpa_supplicant_notify_scanning(wpa_s, 0);
+
+ /*
+ * If we timed out, start a new sched scan to continue
+ * searching for more SSIDs.
+ */
+ if (wpa_s->sched_scan_timed_out)
+ wpa_supplicant_req_sched_scan(wpa_s);
+ break;
+ case EVENT_WPS_BUTTON_PUSHED:
+#ifdef CONFIG_WPS
+ wpas_wps_start_pbc(wpa_s, NULL, 0);
+#endif /* CONFIG_WPS */
break;
default:
- wpa_printf(MSG_INFO, "Unknown event %d", event);
+ wpa_msg(wpa_s, MSG_INFO, "Unknown event %d", event);
break;
}
}
--- /dev/null
+#!/bin/sh
+
+IFNAME=$1
+CMD=$2
+
+kill_daemon() {
+ NAME=$1
+ PF=$2
+
+ if [ ! -r $PF ]; then
+ return
+ fi
+
+ PID=`cat $PF`
+ if [ $PID -gt 0 ]; then
+ if ps $PID | grep -q $NAME; then
+ kill $PID
+ fi
+ fi
+ rm $PF
+}
+
+if [ "$CMD" = "P2P-GROUP-STARTED" ]; then
+ GIFNAME=$3
+ if [ "$4" = "GO" ]; then
+ kill_daemon udhcpc /var/run/udhcpc-$GIFNAME.pid
+ ifconfig $GIFNAME 192.168.42.1 up
+ udhcpd /etc/udhcpd-p2p.conf
+ fi
+ if [ "$4" = "client" ]; then
+ kill_daemon udhcpc /var/run/udhcpc-$GIFNAME.pid
+ kill_daemon udhcpd /var/run/udhcpd-$GIFNAME.pid
+ udhcpc -i $GIFNAME -p /var/run/udhcpc-$GIFNAME.pid \
+ -s /etc/udhcpc.script
+ fi
+fi
+
+if [ "$CMD" = "P2P-GROUP-REMOVED" ]; then
+ GIFNAME=$3
+ if [ "$4" = "GO" ]; then
+ kill_daemon udhcpd /var/run/udhcpd-$GIFNAME.pid
+ ifconfig $GIFNAME 0.0.0.0
+ fi
+ if [ "$4" = "client" ]; then
+ kill_daemon udhcpc /var/run/udhcpc-$GIFNAME.pid
+ ifconfig $GIFNAME 0.0.0.0
+ fi
+fi
+
+if [ "$CMD" = "P2P-CROSS-CONNECT-ENABLE" ]; then
+ GIFNAME=$3
+ UPLINK=$4
+ # enable NAT/masquarade $GIFNAME -> $UPLINK
+ iptables -P FORWARD DROP
+ iptables -t nat -A POSTROUTING -o $UPLINK -j MASQUERADE
+ iptables -A FORWARD -i $UPLINK -o $GIFNAME -m state --state RELATED,ESTABLISHED -j ACCEPT
+ iptables -A FORWARD -i $GIFNAME -o $UPLINK -j ACCEPT
+ sysctl net.ipv4.ip_forward=1
+fi
+
+if [ "$CMD" = "P2P-CROSS-CONNECT-DISABLE" ]; then
+ GIFNAME=$3
+ UPLINK=$4
+ # disable NAT/masquarade $GIFNAME -> $UPLINK
+ sysctl net.ipv4.ip_forward=0
+ iptables -t nat -D POSTROUTING -o $UPLINK -j MASQUERADE
+ iptables -D FORWARD -i $UPLINK -o $GIFNAME -m state --state RELATED,ESTABLISHED -j ACCEPT
+ iptables -D FORWARD -i $GIFNAME -o $UPLINK -j ACCEPT
+fi
--- /dev/null
+#!/bin/sh
+
+IFNAME=$1
+CMD=$2
+
+kill_daemon() {
+ NAME=$1
+ PF=$2
+
+ if [ ! -r $PF ]; then
+ return
+ fi
+
+ PID=`cat $PF`
+ if [ $PID -gt 0 ]; then
+ if ps $PID | grep -q $NAME; then
+ kill $PID
+ fi
+ fi
+ rm $PF
+}
+
+if [ "$CMD" = "P2P-GROUP-STARTED" ]; then
+ GIFNAME=$3
+ if [ "$4" = "GO" ]; then
+ kill_daemon dhclient /var/run/dhclient-$GIFNAME.pid
+ rm /var/run/dhclient.leases-$GIFNAME
+ kill_daemon dnsmasq /var/run/dnsmasq.pid-$GIFNAME
+ ifconfig $GIFNAME 192.168.42.1 up
+ if ! dnsmasq -x /var/run/dnsmasq.pid-$GIFNAME \
+ -i $GIFNAME \
+ -F192.168.42.11,192.168.42.99; then
+ # another dnsmasq instance may be running and blocking us; try to
+ # start with -z to avoid that
+ dnsmasq -x /var/run/dnsmasq.pid-$GIFNAME \
+ -i $GIFNAME \
+ -F192.168.42.11,192.168.42.99 --listen-address 192.168.42.1 -z
+ fi
+ fi
+ if [ "$4" = "client" ]; then
+ kill_daemon dhclient /var/run/dhclient-$GIFNAME.pid
+ rm /var/run/dhclient.leases-$GIFNAME
+ kill_daemon dnsmasq /var/run/dnsmasq.pid-$GIFNAME
+ dhclient -pf /var/run/dhclient-$GIFNAME.pid \
+ -lf /var/run/dhclient.leases-$GIFNAME \
+ -nw \
+ $GIFNAME
+ fi
+fi
+
+if [ "$CMD" = "P2P-GROUP-REMOVED" ]; then
+ GIFNAME=$3
+ if [ "$4" = "GO" ]; then
+ kill_daemon dnsmasq /var/run/dnsmasq.pid-$GIFNAME
+ ifconfig $GIFNAME 0.0.0.0
+ fi
+ if [ "$4" = "client" ]; then
+ kill_daemon dhclient /var/run/dhclient-$GIFNAME.pid
+ rm /var/run/dhclient.leases-$GIFNAME
+ ifconfig $GIFNAME 0.0.0.0
+ fi
+fi
+
+if [ "$CMD" = "P2P-CROSS-CONNECT-ENABLE" ]; then
+ GIFNAME=$3
+ UPLINK=$4
+ # enable NAT/masquarade $GIFNAME -> $UPLINK
+ iptables -P FORWARD DROP
+ iptables -t nat -A POSTROUTING -o $UPLINK -j MASQUERADE
+ iptables -A FORWARD -i $UPLINK -o $GIFNAME -m state --state RELATED,ESTABLISHED -j ACCEPT
+ iptables -A FORWARD -i $GIFNAME -o $UPLINK -j ACCEPT
+ sysctl net.ipv4.ip_forward=1
+fi
+
+if [ "$CMD" = "P2P-CROSS-CONNECT-DISABLE" ]; then
+ GIFNAME=$3
+ UPLINK=$4
+ # disable NAT/masquarade $GIFNAME -> $UPLINK
+ sysctl net.ipv4.ip_forward=0
+ iptables -t nat -D POSTROUTING -o $UPLINK -j MASQUERADE
+ iptables -D FORWARD -i $UPLINK -o $GIFNAME -m state --state RELATED,ESTABLISHED -j ACCEPT
+ iptables -D FORWARD -i $GIFNAME -o $UPLINK -j ACCEPT
+fi
--- /dev/null
+# Sample udhcpd configuration file (/etc/udhcpd.conf)
+
+# The start and end of the IP lease block
+
+start 192.168.42.20 #default: 192.168.0.20
+end 192.168.42.254 #default: 192.168.0.254
+
+
+# The interface that udhcpd will use
+
+interface wlan2 #default: eth0
+
+
+# The maximim number of leases (includes addressesd reserved
+# by OFFER's, DECLINE's, and ARP conficts
+
+#max_leases 254 #default: 254
+
+
+# If remaining is true (default), udhcpd will store the time
+# remaining for each lease in the udhcpd leases file. This is
+# for embedded systems that cannot keep time between reboots.
+# If you set remaining to no, the absolute time that the lease
+# expires at will be stored in the dhcpd.leases file.
+
+#remaining yes #default: yes
+
+
+# The time period at which udhcpd will write out a dhcpd.leases
+# file. If this is 0, udhcpd will never automatically write a
+# lease file. (specified in seconds)
+
+#auto_time 7200 #default: 7200 (2 hours)
+
+
+# The amount of time that an IP will be reserved (leased) for if a
+# DHCP decline message is received (seconds).
+
+#decline_time 3600 #default: 3600 (1 hour)
+
+
+# The amount of time that an IP will be reserved (leased) for if an
+# ARP conflct occurs. (seconds
+
+#conflict_time 3600 #default: 3600 (1 hour)
+
+
+# How long an offered address is reserved (leased) in seconds
+
+#offer_time 60 #default: 60 (1 minute)
+
+# If a lease to be given is below this value, the full lease time is
+# instead used (seconds).
+
+#min_lease 60 #defult: 60
+
+
+# The location of the leases file
+
+#lease_file /var/lib/misc/udhcpd.leases #defualt: /var/lib/misc/udhcpd.leases
+
+# The location of the pid file
+pidfile /var/run/udhcpd-wlan2.pid #default: /var/run/udhcpd.pid
+
+# Every time udhcpd writes a leases file, the below script will be called.
+# Useful for writing the lease file to flash every few hours.
+
+#notify_file #default: (no script)
+
+#notify_file dumpleases # <--- useful for debugging
+
+# The following are bootp specific options, setable by udhcpd.
+
+#siaddr 192.168.0.22 #default: 0.0.0.0
+
+#sname zorak #default: (none)
+
+#boot_file /var/nfs_root #default: (none)
+
+# The remainer of options are DHCP options and can be specifed with the
+# keyword 'opt' or 'option'. If an option can take multiple items, such
+# as the dns option, they can be listed on the same line, or multiple
+# lines. The only option with a default is 'lease'.
+
+#Examles
+opt dns 192.168.2.1
+option subnet 255.255.255.0
+option domain atherosowl.com
+option lease 864000 # 10 days of seconds
+
+
+# Currently supported options, for more info, see options.c
+#opt subnet
+#opt timezone
+#opt router
+#opt timesvr
+#opt namesvr
+#opt dns
+#opt logsvr
+#opt cookiesvr
+#opt lprsvr
+#opt bootsize
+#opt domain
+#opt swapsvr
+#opt rootpath
+#opt ipttl
+#opt mtu
+#opt broadcast
+#opt wins
+#opt lease
+#opt ntpsrv
+#opt tftp
+#opt bootfile
+
+
+# Static leases map
+#static_lease 00:60:08:11:CE:4E 192.168.0.54
+#static_lease 00:60:08:11:CE:3E 192.168.0.44
+
+
dbus_interface=dbus.PROPERTIES_IFACE)
ssid = byte_array_to_string(val)
- val = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'WPAIE',
+ val = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'WPA',
dbus_interface=dbus.PROPERTIES_IFACE)
wpa = "no"
if val != None:
wpa = "yes"
- val = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'RSNIE',
+ val = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'RSN',
dbus_interface=dbus.PROPERTIES_IFACE)
wpa2 = "no"
if val != None:
--- /dev/null
+/*
+ * Generic advertisement service (GAS) query
+ * Copyright (c) 2009, Atheros Communications
+ * Copyright (c) 2011, Qualcomm Atheros
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "common/gas.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "offchannel.h"
+#include "gas_query.h"
+
+
+#define GAS_QUERY_TIMEOUT 5
+
+
+struct gas_query_pending {
+ struct dl_list list;
+ u8 addr[ETH_ALEN];
+ u8 dialog_token;
+ u8 next_frag_id;
+ unsigned int wait_comeback:1;
+ unsigned int offchannel_tx_started:1;
+ int freq;
+ u16 status_code;
+ struct wpabuf *adv_proto;
+ struct wpabuf *resp;
+ void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
+ enum gas_query_result result,
+ const struct wpabuf *adv_proto,
+ const struct wpabuf *resp, u16 status_code);
+ void *ctx;
+};
+
+struct gas_query {
+ struct wpa_supplicant *wpa_s;
+ struct dl_list pending; /* struct gas_query_pending */
+};
+
+
+static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx);
+static void gas_query_timeout(void *eloop_data, void *user_ctx);
+
+
+struct gas_query * gas_query_init(struct wpa_supplicant *wpa_s)
+{
+ struct gas_query *gas;
+
+ gas = os_zalloc(sizeof(*gas));
+ if (gas == NULL)
+ return NULL;
+
+ gas->wpa_s = wpa_s;
+ dl_list_init(&gas->pending);
+
+ return gas;
+}
+
+
+static void gas_query_done(struct gas_query *gas,
+ struct gas_query_pending *query,
+ enum gas_query_result result)
+{
+ if (query->offchannel_tx_started)
+ offchannel_send_action_done(gas->wpa_s);
+ eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
+ eloop_cancel_timeout(gas_query_timeout, gas, query);
+ dl_list_del(&query->list);
+ query->cb(query->ctx, query->addr, query->dialog_token, result,
+ query->adv_proto, query->resp, query->status_code);
+ wpabuf_free(query->adv_proto);
+ wpabuf_free(query->resp);
+ os_free(query);
+}
+
+
+void gas_query_deinit(struct gas_query *gas)
+{
+ struct gas_query_pending *query, *next;
+
+ if (gas == NULL)
+ return;
+
+ dl_list_for_each_safe(query, next, &gas->pending,
+ struct gas_query_pending, list)
+ gas_query_done(gas, query, GAS_QUERY_DELETED_AT_DEINIT);
+
+ os_free(gas);
+}
+
+
+static struct gas_query_pending *
+gas_query_get_pending(struct gas_query *gas, const u8 *addr, u8 dialog_token)
+{
+ struct gas_query_pending *q;
+ dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) {
+ if (os_memcmp(q->addr, addr, ETH_ALEN) == 0 &&
+ q->dialog_token == dialog_token)
+ return q;
+ }
+ return NULL;
+}
+
+
+static int gas_query_append(struct gas_query_pending *query, const u8 *data,
+ size_t len)
+{
+ if (wpabuf_resize(&query->resp, len) < 0) {
+ wpa_printf(MSG_DEBUG, "GAS: No memory to store the response");
+ return -1;
+ }
+ wpabuf_put_data(query->resp, data, len);
+ return 0;
+}
+
+
+static int gas_query_tx(struct gas_query *gas, struct gas_query_pending *query,
+ struct wpabuf *req)
+{
+ int res;
+ wpa_printf(MSG_DEBUG, "GAS: Send action frame to " MACSTR " len=%u "
+ "freq=%d", MAC2STR(query->addr),
+ (unsigned int) wpabuf_len(req), query->freq);
+ res = offchannel_send_action(gas->wpa_s, query->freq, query->addr,
+ gas->wpa_s->own_addr, query->addr,
+ wpabuf_head(req), wpabuf_len(req), 1000,
+ NULL, 0);
+ if (res == 0)
+ query->offchannel_tx_started = 1;
+ return res;
+}
+
+
+static void gas_query_tx_comeback_req(struct gas_query *gas,
+ struct gas_query_pending *query)
+{
+ struct wpabuf *req;
+
+ req = gas_build_comeback_req(query->dialog_token);
+ if (req == NULL) {
+ gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
+ return;
+ }
+
+ if (gas_query_tx(gas, query, req) < 0) {
+ wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
+ MACSTR, MAC2STR(query->addr));
+ gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
+ }
+
+ wpabuf_free(req);
+}
+
+
+static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx)
+{
+ struct gas_query *gas = eloop_data;
+ struct gas_query_pending *query = user_ctx;
+
+ wpa_printf(MSG_DEBUG, "GAS: Comeback timeout for request to " MACSTR,
+ MAC2STR(query->addr));
+ gas_query_tx_comeback_req(gas, query);
+}
+
+
+static void gas_query_tx_comeback_req_delay(struct gas_query *gas,
+ struct gas_query_pending *query,
+ u16 comeback_delay)
+{
+ unsigned int secs, usecs;
+
+ secs = (comeback_delay * 1024) / 1000000;
+ usecs = comeback_delay * 1024 - secs * 1000000;
+ wpa_printf(MSG_DEBUG, "GAS: Send comeback request to " MACSTR
+ " in %u secs %u usecs", MAC2STR(query->addr), secs, usecs);
+ eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
+ eloop_register_timeout(secs, usecs, gas_query_tx_comeback_timeout,
+ gas, query);
+}
+
+
+static void gas_query_rx_initial(struct gas_query *gas,
+ struct gas_query_pending *query,
+ const u8 *adv_proto, const u8 *resp,
+ size_t len, u16 comeback_delay)
+{
+ wpa_printf(MSG_DEBUG, "GAS: Received initial response from "
+ MACSTR " (dialog_token=%u comeback_delay=%u)",
+ MAC2STR(query->addr), query->dialog_token, comeback_delay);
+
+ query->adv_proto = wpabuf_alloc_copy(adv_proto, 2 + adv_proto[1]);
+ if (query->adv_proto == NULL) {
+ gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
+ return;
+ }
+
+ if (comeback_delay) {
+ query->wait_comeback = 1;
+ gas_query_tx_comeback_req_delay(gas, query, comeback_delay);
+ return;
+ }
+
+ /* Query was completed without comeback mechanism */
+ if (gas_query_append(query, resp, len) < 0) {
+ gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
+ return;
+ }
+
+ gas_query_done(gas, query, GAS_QUERY_SUCCESS);
+}
+
+
+static void gas_query_rx_comeback(struct gas_query *gas,
+ struct gas_query_pending *query,
+ const u8 *adv_proto, const u8 *resp,
+ size_t len, u8 frag_id, u8 more_frags,
+ u16 comeback_delay)
+{
+ wpa_printf(MSG_DEBUG, "GAS: Received comeback response from "
+ MACSTR " (dialog_token=%u frag_id=%u more_frags=%u "
+ "comeback_delay=%u)",
+ MAC2STR(query->addr), query->dialog_token, frag_id,
+ more_frags, comeback_delay);
+
+ if ((size_t) 2 + adv_proto[1] != wpabuf_len(query->adv_proto) ||
+ os_memcmp(adv_proto, wpabuf_head(query->adv_proto),
+ wpabuf_len(query->adv_proto)) != 0) {
+ wpa_printf(MSG_DEBUG, "GAS: Advertisement Protocol changed "
+ "between initial and comeback response from "
+ MACSTR, MAC2STR(query->addr));
+ gas_query_done(gas, query, GAS_QUERY_PEER_ERROR);
+ return;
+ }
+
+ if (comeback_delay) {
+ if (frag_id) {
+ wpa_printf(MSG_DEBUG, "GAS: Invalid comeback response "
+ "with non-zero frag_id and comeback_delay "
+ "from " MACSTR, MAC2STR(query->addr));
+ gas_query_done(gas, query, GAS_QUERY_PEER_ERROR);
+ return;
+ }
+ gas_query_tx_comeback_req_delay(gas, query, comeback_delay);
+ return;
+ }
+
+ if (frag_id != query->next_frag_id) {
+ wpa_printf(MSG_DEBUG, "GAS: Unexpected frag_id in response "
+ "from " MACSTR, MAC2STR(query->addr));
+ gas_query_done(gas, query, GAS_QUERY_PEER_ERROR);
+ return;
+ }
+ query->next_frag_id++;
+
+ if (gas_query_append(query, resp, len) < 0) {
+ gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
+ return;
+ }
+
+ if (more_frags) {
+ gas_query_tx_comeback_req(gas, query);
+ return;
+ }
+
+ gas_query_done(gas, query, GAS_QUERY_SUCCESS);
+}
+
+
+int gas_query_rx(struct gas_query *gas, const u8 *da, const u8 *sa,
+ const u8 *bssid, const u8 *data, size_t len, int freq)
+{
+ struct gas_query_pending *query;
+ u8 action, dialog_token, frag_id = 0, more_frags = 0;
+ u16 comeback_delay, resp_len;
+ const u8 *pos, *adv_proto;
+
+ if (gas == NULL || len < 4)
+ return -1;
+
+ pos = data;
+ action = *pos++;
+ dialog_token = *pos++;
+
+ if (action != WLAN_PA_GAS_INITIAL_RESP &&
+ action != WLAN_PA_GAS_COMEBACK_RESP)
+ return -1; /* Not a GAS response */
+
+ query = gas_query_get_pending(gas, sa, dialog_token);
+ if (query == NULL) {
+ wpa_printf(MSG_DEBUG, "GAS: No pending query found for " MACSTR
+ " dialog token %u", MAC2STR(sa), dialog_token);
+ return -1;
+ }
+
+ if (query->wait_comeback && action == WLAN_PA_GAS_INITIAL_RESP) {
+ wpa_printf(MSG_DEBUG, "GAS: Unexpected initial response from "
+ MACSTR " dialog token %u when waiting for comeback "
+ "response", MAC2STR(sa), dialog_token);
+ return 0;
+ }
+
+ if (!query->wait_comeback && action == WLAN_PA_GAS_COMEBACK_RESP) {
+ wpa_printf(MSG_DEBUG, "GAS: Unexpected comeback response from "
+ MACSTR " dialog token %u when waiting for initial "
+ "response", MAC2STR(sa), dialog_token);
+ return 0;
+ }
+
+ query->status_code = WPA_GET_LE16(pos);
+ pos += 2;
+
+ if (query->status_code != WLAN_STATUS_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "GAS: Query to " MACSTR " dialog token "
+ "%u failed - status code %u",
+ MAC2STR(sa), dialog_token, query->status_code);
+ gas_query_done(gas, query, GAS_QUERY_FAILURE);
+ return 0;
+ }
+
+ if (action == WLAN_PA_GAS_COMEBACK_RESP) {
+ if (pos + 1 > data + len)
+ return 0;
+ frag_id = *pos & 0x7f;
+ more_frags = (*pos & 0x80) >> 7;
+ pos++;
+ }
+
+ /* Comeback Delay */
+ if (pos + 2 > data + len)
+ return 0;
+ comeback_delay = WPA_GET_LE16(pos);
+ pos += 2;
+
+ /* Advertisement Protocol element */
+ if (pos + 2 > data + len || pos + 2 + pos[1] > data + len) {
+ wpa_printf(MSG_DEBUG, "GAS: No room for Advertisement "
+ "Protocol element in the response from " MACSTR,
+ MAC2STR(sa));
+ return 0;
+ }
+
+ if (*pos != WLAN_EID_ADV_PROTO) {
+ wpa_printf(MSG_DEBUG, "GAS: Unexpected Advertisement "
+ "Protocol element ID %u in response from " MACSTR,
+ *pos, MAC2STR(sa));
+ return 0;
+ }
+
+ adv_proto = pos;
+ pos += 2 + pos[1];
+
+ /* Query Response Length */
+ if (pos + 2 > data + len) {
+ wpa_printf(MSG_DEBUG, "GAS: No room for GAS Response Length");
+ return 0;
+ }
+ resp_len = WPA_GET_LE16(pos);
+ pos += 2;
+
+ if (pos + resp_len > data + len) {
+ wpa_printf(MSG_DEBUG, "GAS: Truncated Query Response in "
+ "response from " MACSTR, MAC2STR(sa));
+ return 0;
+ }
+
+ if (pos + resp_len < data + len) {
+ wpa_printf(MSG_DEBUG, "GAS: Ignore %u octets of extra data "
+ "after Query Response from " MACSTR,
+ (unsigned int) (data + len - pos - resp_len),
+ MAC2STR(sa));
+ }
+
+ if (action == WLAN_PA_GAS_COMEBACK_RESP)
+ gas_query_rx_comeback(gas, query, adv_proto, pos, resp_len,
+ frag_id, more_frags, comeback_delay);
+ else
+ gas_query_rx_initial(gas, query, adv_proto, pos, resp_len,
+ comeback_delay);
+
+ return 0;
+}
+
+
+static void gas_query_timeout(void *eloop_data, void *user_ctx)
+{
+ struct gas_query *gas = eloop_data;
+ struct gas_query_pending *query = user_ctx;
+
+ wpa_printf(MSG_DEBUG, "GAS: No response received for query to " MACSTR,
+ MAC2STR(query->addr));
+ gas_query_done(gas, query, GAS_QUERY_TIMEOUT);
+}
+
+
+static int gas_query_dialog_token_available(struct gas_query *gas,
+ const u8 *dst, u8 dialog_token)
+{
+ struct gas_query_pending *q;
+ dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) {
+ if (os_memcmp(dst, q->addr, ETH_ALEN) == 0 &&
+ dialog_token == q->dialog_token)
+ return 0;
+ }
+
+ return 1;
+}
+
+
+int gas_query_req(struct gas_query *gas, const u8 *dst, int freq,
+ struct wpabuf *req,
+ void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
+ enum gas_query_result result,
+ const struct wpabuf *adv_proto,
+ const struct wpabuf *resp, u16 status_code),
+ void *ctx)
+{
+ struct gas_query_pending *query;
+ int dialog_token;
+
+ if (wpabuf_len(req) < 3)
+ return -1;
+
+ for (dialog_token = 0; dialog_token < 256; dialog_token++) {
+ if (gas_query_dialog_token_available(gas, dst, dialog_token))
+ break;
+ }
+ if (dialog_token == 256)
+ return -1; /* Too many pending queries */
+
+ query = os_zalloc(sizeof(*query));
+ if (query == NULL)
+ return -1;
+
+ os_memcpy(query->addr, dst, ETH_ALEN);
+ query->dialog_token = dialog_token;
+ query->freq = freq;
+ query->cb = cb;
+ query->ctx = ctx;
+ dl_list_add(&gas->pending, &query->list);
+
+ *(wpabuf_mhead_u8(req) + 2) = dialog_token;
+
+ wpa_printf(MSG_DEBUG, "GAS: Starting request for " MACSTR
+ " dialog_token %u", MAC2STR(dst), dialog_token);
+ if (gas_query_tx(gas, query, req) < 0) {
+ wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
+ MACSTR, MAC2STR(query->addr));
+ os_free(query);
+ return -1;
+ }
+
+ eloop_register_timeout(GAS_QUERY_TIMEOUT, 0, gas_query_timeout,
+ gas, query);
+
+ return dialog_token;
+}
+
+
+void gas_query_cancel(struct gas_query *gas, const u8 *dst, u8 dialog_token)
+{
+ struct gas_query_pending *query;
+
+ query = gas_query_get_pending(gas, dst, dialog_token);
+ if (query)
+ gas_query_done(gas, query, GAS_QUERY_CANCELLED);
+
+}
--- /dev/null
+/*
+ * Generic advertisement service (GAS) query
+ * Copyright (c) 2009, Atheros Communications
+ * Copyright (c) 2011, Qualcomm Atheros
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef GAS_QUERY_H
+#define GAS_QUERY_H
+
+struct gas_query;
+
+#ifdef CONFIG_GAS
+
+struct gas_query * gas_query_init(struct wpa_supplicant *wpa_s);
+void gas_query_deinit(struct gas_query *gas);
+int gas_query_rx(struct gas_query *gas, const u8 *da, const u8 *sa,
+ const u8 *bssid, const u8 *data, size_t len, int freq);
+
+enum gas_query_result {
+ GAS_QUERY_SUCCESS,
+ GAS_QUERY_FAILURE,
+ GAS_QUERY_TIMEOUT,
+ GAS_QUERY_PEER_ERROR,
+ GAS_QUERY_INTERNAL_ERROR,
+ GAS_QUERY_CANCELLED,
+ GAS_QUERY_DELETED_AT_DEINIT
+};
+
+int gas_query_req(struct gas_query *gas, const u8 *dst, int freq,
+ struct wpabuf *req,
+ void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
+ enum gas_query_result result,
+ const struct wpabuf *adv_proto,
+ const struct wpabuf *resp, u16 status_code),
+ void *ctx);
+void gas_query_cancel(struct gas_query *gas, const u8 *dst, u8 dialog_token);
+
+#else /* CONFIG_GAS */
+
+static inline struct gas_query * gas_query_init(struct wpa_supplicant *wpa_s)
+{
+ return (void *) 1;
+}
+
+static inline void gas_query_deinit(struct gas_query *gas)
+{
+}
+
+#endif /* CONFIG_GAS */
+
+
+#endif /* GAS_QUERY_H */
}
+static enum wpa_states supp_get_state(void *ctx)
+{
+ struct ibss_rsn_peer *peer = ctx;
+ return peer->supp_state;
+}
+
+
static int supp_ether_send(void *ctx, const u8 *dest, u16 proto, const u8 *buf,
size_t len)
{
}
}
+ if (is_broadcast_ether_addr(addr))
+ addr = peer->addr;
return wpa_drv_set_key(peer->ibss_rsn->wpa_s, alg, addr, key_idx,
set_tx, seq, seq_len, key, key_len);
}
}
+static void supp_deauthenticate(void * ctx, int reason_code)
+{
+ wpa_printf(MSG_DEBUG, "SUPP: %s (TODO)", __func__);
+}
+
+
int ibss_rsn_supp_init(struct ibss_rsn_peer *peer, const u8 *own_addr,
const u8 *psk)
{
ctx->ctx = peer;
ctx->msg_ctx = peer->ibss_rsn->wpa_s;
ctx->set_state = supp_set_state;
+ ctx->get_state = supp_get_state;
ctx->ether_send = supp_ether_send;
ctx->get_beacon_ie = supp_get_beacon_ie;
ctx->alloc_eapol = supp_alloc_eapol;
ctx->get_network_ctx = supp_get_network_ctx;
ctx->mlme_setprotection = supp_mlme_setprotection;
ctx->cancel_auth_timeout = supp_cancel_auth_timeout;
+ ctx->deauthenticate = supp_deauthenticate;
peer->supp = wpa_sm_init(ctx);
if (peer->supp == NULL) {
wpa_printf(MSG_DEBUG, "SUPP: wpa_sm_init() failed");
}
+static int auth_for_each_sta(void *ctx, int (*cb)(struct wpa_state_machine *sm,
+ void *ctx),
+ void *cb_ctx)
+{
+ struct ibss_rsn *ibss_rsn = ctx;
+ struct ibss_rsn_peer *peer;
+
+ wpa_printf(MSG_DEBUG, "AUTH: for_each_sta");
+
+ for (peer = ibss_rsn->peers; peer; peer = peer->next) {
+ if (peer->auth && cb(peer->auth, cb_ctx))
+ return 1;
+ }
+
+ return 0;
+}
+
+
static int ibss_rsn_auth_init_group(struct ibss_rsn *ibss_rsn,
const u8 *own_addr)
{
conf.rsn_pairwise = WPA_CIPHER_CCMP;
conf.wpa_group = WPA_CIPHER_CCMP;
conf.eapol_version = 2;
+ conf.wpa_group_rekey = 600;
os_memset(&cb, 0, sizeof(cb));
cb.ctx = ibss_rsn;
cb.send_eapol = auth_send_eapol;
cb.get_psk = auth_get_psk;
cb.set_key = auth_set_key;
+ cb.for_each_sta = auth_for_each_sta;
ibss_rsn->auth_group = wpa_init(own_addr, &conf, &cb);
if (ibss_rsn->auth_group == NULL) {
return -1;
}
+ wpa_init_keys(ibss_rsn->auth_group);
+
return 0;
}
{
struct ibss_rsn_peer *peer;
+ if (ibss_rsn == NULL)
+ return -1;
+
+ for (peer = ibss_rsn->peers; peer; peer = peer->next) {
+ if (os_memcmp(addr, peer->addr, ETH_ALEN) == 0) {
+ wpa_printf(MSG_DEBUG, "RSN: IBSS Authenticator and "
+ "Supplicant for peer " MACSTR " already "
+ "running", MAC2STR(addr));
+ return 0;
+ }
+ }
+
wpa_printf(MSG_DEBUG, "RSN: Starting IBSS Authenticator and "
"Supplicant for peer " MACSTR, MAC2STR(addr));
}
+void ibss_rsn_stop(struct ibss_rsn *ibss_rsn, const u8 *peermac)
+{
+ struct ibss_rsn_peer *peer, *prev;
+
+ if (ibss_rsn == NULL)
+ return;
+
+ if (peermac == NULL) {
+ /* remove all peers */
+ wpa_printf(MSG_DEBUG, "%s: Remove all peers", __func__);
+ peer = ibss_rsn->peers;
+ while (peer) {
+ prev = peer;
+ peer = peer->next;
+ ibss_rsn_free(prev);
+ ibss_rsn->peers = peer;
+ }
+ } else {
+ /* remove specific peer */
+ wpa_printf(MSG_DEBUG, "%s: Remove specific peer " MACSTR,
+ __func__, MAC2STR(peermac));
+
+ for (prev = NULL, peer = ibss_rsn->peers; peer != NULL;
+ prev = peer, peer = peer->next) {
+ if (os_memcmp(peermac, peer->addr, ETH_ALEN) == 0) {
+ if (prev == NULL)
+ ibss_rsn->peers = peer->next;
+ else
+ prev->next = peer->next;
+ ibss_rsn_free(peer);
+ wpa_printf(MSG_DEBUG, "%s: Successfully "
+ "removed a specific peer",
+ __func__);
+ break;
+ }
+ }
+ }
+}
+
+
struct ibss_rsn * ibss_rsn_init(struct wpa_supplicant *wpa_s)
{
struct ibss_rsn *ibss_rsn;
{
struct ibss_rsn_peer *peer;
+ if (ibss_rsn == NULL)
+ return -1;
+
for (peer = ibss_rsn->peers; peer; peer = peer->next) {
if (os_memcmp(src_addr, peer->addr, ETH_ALEN) == 0)
return ibss_rsn_process_rx_eapol(ibss_rsn, peer,
void ibss_rsn_set_psk(struct ibss_rsn *ibss_rsn, const u8 *psk)
{
+ if (ibss_rsn == NULL)
+ return;
os_memcpy(ibss_rsn->psk, psk, PMK_LEN);
}
struct ibss_rsn * ibss_rsn_init(struct wpa_supplicant *wpa_s);
void ibss_rsn_deinit(struct ibss_rsn *ibss_rsn);
int ibss_rsn_start(struct ibss_rsn *ibss_rsn, const u8 *addr);
+void ibss_rsn_stop(struct ibss_rsn *ibss_rsn, const u8 *peermac);
int ibss_rsn_rx_eapol(struct ibss_rsn *ibss_rsn, const u8 *src_addr,
const u8 *buf, size_t len);
void ibss_rsn_set_psk(struct ibss_rsn *ibss_rsn, const u8 *psk);
--- /dev/null
+/*
+ * Interworking (IEEE 802.11u)
+ * Copyright (c) 2011, Qualcomm Atheros
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/gas.h"
+#include "common/wpa_ctrl.h"
+#include "drivers/driver.h"
+#include "eap_common/eap_defs.h"
+#include "eap_peer/eap_methods.h"
+#include "wpa_supplicant_i.h"
+#include "config.h"
+#include "bss.h"
+#include "scan.h"
+#include "notify.h"
+#include "gas_query.h"
+#include "interworking.h"
+
+
+#if defined(EAP_SIM) | defined(EAP_SIM_DYNAMIC)
+#define INTERWORKING_3GPP
+#else
+#if defined(EAP_AKA) | defined(EAP_AKA_DYNAMIC)
+#define INTERWORKING_3GPP
+#else
+#if defined(EAP_AKA_PRIME) | defined(EAP_AKA_PRIME_DYNAMIC)
+#define INTERWORKING_3GPP
+#endif
+#endif
+#endif
+
+static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s);
+
+
+static struct wpabuf * anqp_build_req(u16 info_ids[], size_t num_ids,
+ struct wpabuf *extra)
+{
+ struct wpabuf *buf;
+ size_t i;
+ u8 *len_pos;
+
+ buf = gas_anqp_build_initial_req(0, 4 + num_ids * 2 +
+ (extra ? wpabuf_len(extra) : 0));
+ if (buf == NULL)
+ return NULL;
+
+ len_pos = gas_anqp_add_element(buf, ANQP_QUERY_LIST);
+ for (i = 0; i < num_ids; i++)
+ wpabuf_put_le16(buf, info_ids[i]);
+ gas_anqp_set_element_len(buf, len_pos);
+ if (extra)
+ wpabuf_put_buf(buf, extra);
+
+ gas_anqp_set_len(buf);
+
+ return buf;
+}
+
+
+static void interworking_anqp_resp_cb(void *ctx, const u8 *dst,
+ u8 dialog_token,
+ enum gas_query_result result,
+ const struct wpabuf *adv_proto,
+ const struct wpabuf *resp,
+ u16 status_code)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ anqp_resp_cb(wpa_s, dst, dialog_token, result, adv_proto, resp,
+ status_code);
+ interworking_next_anqp_fetch(wpa_s);
+}
+
+
+static int interworking_anqp_send_req(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *bss)
+{
+ struct wpabuf *buf;
+ int ret = 0;
+ int res;
+ u16 info_ids[] = {
+ ANQP_CAPABILITY_LIST,
+ ANQP_VENUE_NAME,
+ ANQP_NETWORK_AUTH_TYPE,
+ ANQP_ROAMING_CONSORTIUM,
+ ANQP_IP_ADDR_TYPE_AVAILABILITY,
+ ANQP_NAI_REALM,
+ ANQP_3GPP_CELLULAR_NETWORK,
+ ANQP_DOMAIN_NAME
+ };
+ struct wpabuf *extra = NULL;
+
+ wpa_printf(MSG_DEBUG, "Interworking: ANQP Query Request to " MACSTR,
+ MAC2STR(bss->bssid));
+
+ buf = anqp_build_req(info_ids, sizeof(info_ids) / sizeof(info_ids[0]),
+ extra);
+ wpabuf_free(extra);
+ if (buf == NULL)
+ return -1;
+
+ res = gas_query_req(wpa_s->gas, bss->bssid, bss->freq, buf,
+ interworking_anqp_resp_cb, wpa_s);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request");
+ ret = -1;
+ } else
+ wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token "
+ "%u", res);
+
+ wpabuf_free(buf);
+ return ret;
+}
+
+
+struct nai_realm_eap {
+ u8 method;
+ u8 inner_method;
+ enum nai_realm_eap_auth_inner_non_eap inner_non_eap;
+ u8 cred_type;
+ u8 tunneled_cred_type;
+};
+
+struct nai_realm {
+ u8 encoding;
+ char *realm;
+ u8 eap_count;
+ struct nai_realm_eap *eap;
+};
+
+
+static void nai_realm_free(struct nai_realm *realms, u16 count)
+{
+ u16 i;
+
+ if (realms == NULL)
+ return;
+ for (i = 0; i < count; i++) {
+ os_free(realms[i].eap);
+ os_free(realms[i].realm);
+ }
+ os_free(realms);
+}
+
+
+static const u8 * nai_realm_parse_eap(struct nai_realm_eap *e, const u8 *pos,
+ const u8 *end)
+{
+ u8 elen, auth_count, a;
+ const u8 *e_end;
+
+ if (pos + 3 > end) {
+ wpa_printf(MSG_DEBUG, "No room for EAP Method fixed fields");
+ return NULL;
+ }
+
+ elen = *pos++;
+ if (pos + elen > end || elen < 2) {
+ wpa_printf(MSG_DEBUG, "No room for EAP Method subfield");
+ return NULL;
+ }
+ e_end = pos + elen;
+ e->method = *pos++;
+ auth_count = *pos++;
+ wpa_printf(MSG_DEBUG, "EAP Method: len=%u method=%u auth_count=%u",
+ elen, e->method, auth_count);
+
+ for (a = 0; a < auth_count; a++) {
+ u8 id, len;
+
+ if (pos + 2 > end || pos + 2 + pos[1] > end) {
+ wpa_printf(MSG_DEBUG, "No room for Authentication "
+ "Parameter subfield");
+ return NULL;
+ }
+
+ id = *pos++;
+ len = *pos++;
+
+ switch (id) {
+ case NAI_REALM_EAP_AUTH_NON_EAP_INNER_AUTH:
+ if (len < 1)
+ break;
+ e->inner_non_eap = *pos;
+ if (e->method != EAP_TYPE_TTLS)
+ break;
+ switch (*pos) {
+ case NAI_REALM_INNER_NON_EAP_PAP:
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP");
+ break;
+ case NAI_REALM_INNER_NON_EAP_CHAP:
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP");
+ break;
+ case NAI_REALM_INNER_NON_EAP_MSCHAP:
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP");
+ break;
+ case NAI_REALM_INNER_NON_EAP_MSCHAPV2:
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2");
+ break;
+ }
+ break;
+ case NAI_REALM_EAP_AUTH_INNER_AUTH_EAP_METHOD:
+ if (len < 1)
+ break;
+ e->inner_method = *pos;
+ wpa_printf(MSG_DEBUG, "Inner EAP method: %u",
+ e->inner_method);
+ break;
+ case NAI_REALM_EAP_AUTH_CRED_TYPE:
+ if (len < 1)
+ break;
+ e->cred_type = *pos;
+ wpa_printf(MSG_DEBUG, "Credential Type: %u",
+ e->cred_type);
+ break;
+ case NAI_REALM_EAP_AUTH_TUNNELED_CRED_TYPE:
+ if (len < 1)
+ break;
+ e->tunneled_cred_type = *pos;
+ wpa_printf(MSG_DEBUG, "Tunneled EAP Method Credential "
+ "Type: %u", e->tunneled_cred_type);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "Unsupported Authentication "
+ "Parameter: id=%u len=%u", id, len);
+ wpa_hexdump(MSG_DEBUG, "Authentication Parameter "
+ "Value", pos, len);
+ break;
+ }
+
+ pos += len;
+ }
+
+ return e_end;
+}
+
+
+static const u8 * nai_realm_parse_realm(struct nai_realm *r, const u8 *pos,
+ const u8 *end)
+{
+ u16 len;
+ const u8 *f_end;
+ u8 realm_len, e;
+
+ if (end - pos < 4) {
+ wpa_printf(MSG_DEBUG, "No room for NAI Realm Data "
+ "fixed fields");
+ return NULL;
+ }
+
+ len = WPA_GET_LE16(pos); /* NAI Realm Data field Length */
+ pos += 2;
+ if (pos + len > end || len < 3) {
+ wpa_printf(MSG_DEBUG, "No room for NAI Realm Data "
+ "(len=%u; left=%u)",
+ len, (unsigned int) (end - pos));
+ return NULL;
+ }
+ f_end = pos + len;
+
+ r->encoding = *pos++;
+ realm_len = *pos++;
+ if (pos + realm_len > f_end) {
+ wpa_printf(MSG_DEBUG, "No room for NAI Realm "
+ "(len=%u; left=%u)",
+ realm_len, (unsigned int) (f_end - pos));
+ return NULL;
+ }
+ wpa_hexdump_ascii(MSG_DEBUG, "NAI Realm", pos, realm_len);
+ r->realm = os_malloc(realm_len + 1);
+ if (r->realm == NULL)
+ return NULL;
+ os_memcpy(r->realm, pos, realm_len);
+ r->realm[realm_len] = '\0';
+ pos += realm_len;
+
+ if (pos + 1 > f_end) {
+ wpa_printf(MSG_DEBUG, "No room for EAP Method Count");
+ return NULL;
+ }
+ r->eap_count = *pos++;
+ wpa_printf(MSG_DEBUG, "EAP Count: %u", r->eap_count);
+ if (pos + r->eap_count * 3 > f_end) {
+ wpa_printf(MSG_DEBUG, "No room for EAP Methods");
+ return NULL;
+ }
+ r->eap = os_zalloc(r->eap_count * sizeof(struct nai_realm_eap));
+ if (r->eap == NULL)
+ return NULL;
+
+ for (e = 0; e < r->eap_count; e++) {
+ pos = nai_realm_parse_eap(&r->eap[e], pos, f_end);
+ if (pos == NULL)
+ return NULL;
+ }
+
+ return f_end;
+}
+
+
+static struct nai_realm * nai_realm_parse(struct wpabuf *anqp, u16 *count)
+{
+ struct nai_realm *realm;
+ const u8 *pos, *end;
+ u16 i, num;
+
+ if (anqp == NULL || wpabuf_len(anqp) < 2)
+ return NULL;
+
+ pos = wpabuf_head_u8(anqp);
+ end = pos + wpabuf_len(anqp);
+ num = WPA_GET_LE16(pos);
+ wpa_printf(MSG_DEBUG, "NAI Realm Count: %u", num);
+ pos += 2;
+
+ if (num * 5 > end - pos) {
+ wpa_printf(MSG_DEBUG, "Invalid NAI Realm Count %u - not "
+ "enough data (%u octets) for that many realms",
+ num, (unsigned int) (end - pos));
+ return NULL;
+ }
+
+ realm = os_zalloc(num * sizeof(struct nai_realm));
+ if (realm == NULL)
+ return NULL;
+
+ for (i = 0; i < num; i++) {
+ pos = nai_realm_parse_realm(&realm[i], pos, end);
+ if (pos == NULL) {
+ nai_realm_free(realm, num);
+ return NULL;
+ }
+ }
+
+ *count = num;
+ return realm;
+}
+
+
+static int nai_realm_match(struct nai_realm *realm, const char *home_realm)
+{
+ char *tmp, *pos, *end;
+ int match = 0;
+
+ if (realm->realm == NULL || home_realm == NULL)
+ return 0;
+
+ if (os_strchr(realm->realm, ';') == NULL)
+ return os_strcasecmp(realm->realm, home_realm) == 0;
+
+ tmp = os_strdup(realm->realm);
+ if (tmp == NULL)
+ return 0;
+
+ pos = tmp;
+ while (*pos) {
+ end = os_strchr(pos, ';');
+ if (end)
+ *end = '\0';
+ if (os_strcasecmp(pos, home_realm) == 0) {
+ match = 1;
+ break;
+ }
+ if (end == NULL)
+ break;
+ pos = end + 1;
+ }
+
+ os_free(tmp);
+
+ return match;
+}
+
+
+static int nai_realm_cred_username(struct nai_realm_eap *eap)
+{
+ if (eap_get_name(EAP_VENDOR_IETF, eap->method) == NULL)
+ return 0; /* method not supported */
+
+ if (eap->method != EAP_TYPE_TTLS && eap->method != EAP_TYPE_PEAP) {
+ /* Only tunneled methods with username/password supported */
+ return 0;
+ }
+
+ if (eap->method == EAP_TYPE_PEAP &&
+ eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL)
+ return 0;
+
+ if (eap->method == EAP_TYPE_TTLS) {
+ if (eap->inner_method == 0 && eap->inner_non_eap == 0)
+ return 0;
+ if (eap->inner_method &&
+ eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL)
+ return 0;
+ if (eap->inner_non_eap &&
+ eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_PAP &&
+ eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_CHAP &&
+ eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_MSCHAP &&
+ eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_MSCHAPV2)
+ return 0;
+ }
+
+ if (eap->inner_method &&
+ eap->inner_method != EAP_TYPE_GTC &&
+ eap->inner_method != EAP_TYPE_MSCHAPV2)
+ return 0;
+
+ return 1;
+}
+
+
+struct nai_realm_eap * nai_realm_find_eap(struct wpa_supplicant *wpa_s,
+ struct nai_realm *realm)
+{
+ u8 e;
+
+ if (wpa_s->conf->home_username == NULL ||
+ wpa_s->conf->home_username[0] == '\0' ||
+ wpa_s->conf->home_password == NULL ||
+ wpa_s->conf->home_password[0] == '\0')
+ return NULL;
+
+ for (e = 0; e < realm->eap_count; e++) {
+ struct nai_realm_eap *eap = &realm->eap[e];
+ if (nai_realm_cred_username(eap))
+ return eap;
+ }
+
+ return NULL;
+}
+
+
+#ifdef INTERWORKING_3GPP
+
+static int plmn_id_match(struct wpabuf *anqp, const char *imsi)
+{
+ const char *sep;
+ u8 plmn[3];
+ const u8 *pos, *end;
+ u8 udhl;
+
+ sep = os_strchr(imsi, '-');
+ if (sep == NULL || (sep - imsi != 5 && sep - imsi != 6))
+ return 0;
+
+ /* See Annex A of 3GPP TS 24.234 v8.1.0 for description */
+ plmn[0] = (imsi[0] - '0') | ((imsi[1] - '0') << 4);
+ plmn[1] = imsi[2] - '0';
+ if (sep - imsi == 6)
+ plmn[1] |= (imsi[5] - '0') << 4;
+ else
+ plmn[1] |= 0xf0;
+ plmn[2] = (imsi[3] - '0') | ((imsi[4] - '0') << 4);
+
+ if (anqp == NULL)
+ return 0;
+ pos = wpabuf_head_u8(anqp);
+ end = pos + wpabuf_len(anqp);
+ if (pos + 2 > end)
+ return 0;
+ if (*pos != 0) {
+ wpa_printf(MSG_DEBUG, "Unsupported GUD version 0x%x", *pos);
+ return 0;
+ }
+ pos++;
+ udhl = *pos++;
+ if (pos + udhl > end) {
+ wpa_printf(MSG_DEBUG, "Invalid UDHL");
+ return 0;
+ }
+ end = pos + udhl;
+
+ while (pos + 2 <= end) {
+ u8 iei, len;
+ const u8 *l_end;
+ iei = *pos++;
+ len = *pos++ & 0x7f;
+ if (pos + len > end)
+ break;
+ l_end = pos + len;
+
+ if (iei == 0 && len > 0) {
+ /* PLMN List */
+ u8 num, i;
+ num = *pos++;
+ for (i = 0; i < num; i++) {
+ if (pos + 3 > end)
+ break;
+ if (os_memcmp(pos, plmn, 3) == 0)
+ return 1; /* Found matching PLMN */
+ }
+ }
+
+ pos = l_end;
+ }
+
+ return 0;
+}
+
+
+static int set_root_nai(struct wpa_ssid *ssid, const char *imsi, char prefix)
+{
+ const char *sep, *msin;
+ char nai[100], *end, *pos;
+ size_t msin_len, plmn_len;
+
+ /*
+ * TS 23.003, Clause 14 (3GPP to WLAN Interworking)
+ * Root NAI:
+ * <aka:0|sim:1><IMSI>@wlan.mnc<MNC>.mcc<MCC>.3gppnetwork.org
+ * <MNC> is zero-padded to three digits in case two-digit MNC is used
+ */
+
+ if (imsi == NULL || os_strlen(imsi) > 16) {
+ wpa_printf(MSG_DEBUG, "No valid IMSI available");
+ return -1;
+ }
+ sep = os_strchr(imsi, '-');
+ if (sep == NULL)
+ return -1;
+ plmn_len = sep - imsi;
+ if (plmn_len != 5 && plmn_len != 6)
+ return -1;
+ msin = sep + 1;
+ msin_len = os_strlen(msin);
+
+ pos = nai;
+ end = pos + sizeof(nai);
+ *pos++ = prefix;
+ os_memcpy(pos, imsi, plmn_len);
+ pos += plmn_len;
+ os_memcpy(pos, msin, msin_len);
+ pos += msin_len;
+ pos += os_snprintf(pos, end - pos, "@wlan.mnc");
+ if (plmn_len == 5) {
+ *pos++ = '0';
+ *pos++ = imsi[3];
+ *pos++ = imsi[4];
+ } else {
+ *pos++ = imsi[3];
+ *pos++ = imsi[4];
+ *pos++ = imsi[5];
+ }
+ pos += os_snprintf(pos, end - pos, ".mcc%c%c%c.3gppnetwork.org",
+ imsi[0], imsi[1], imsi[2]);
+
+ return wpa_config_set_quoted(ssid, "identity", nai);
+}
+
+#endif /* INTERWORKING_3GPP */
+
+
+static int interworking_connect_3gpp(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *bss)
+{
+#ifdef INTERWORKING_3GPP
+ struct wpa_ssid *ssid;
+ const u8 *ie;
+
+ ie = wpa_bss_get_ie(bss, WLAN_EID_SSID);
+ if (ie == NULL)
+ return -1;
+ wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR " (3GPP)",
+ MAC2STR(bss->bssid));
+
+ ssid = wpa_config_add_network(wpa_s->conf);
+ if (ssid == NULL)
+ return -1;
+
+ wpas_notify_network_added(wpa_s, ssid);
+ wpa_config_set_network_defaults(ssid);
+ ssid->temporary = 1;
+ ssid->ssid = os_zalloc(ie[1] + 1);
+ if (ssid->ssid == NULL)
+ goto fail;
+ os_memcpy(ssid->ssid, ie + 2, ie[1]);
+ ssid->ssid_len = ie[1];
+
+ /* TODO: figure out whether to use EAP-SIM, EAP-AKA, or EAP-AKA' */
+ if (wpa_config_set(ssid, "eap", "SIM", 0) < 0) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM not supported");
+ goto fail;
+ }
+ if (set_root_nai(ssid, wpa_s->conf->home_imsi, '1') < 0) {
+ wpa_printf(MSG_DEBUG, "Failed to set Root NAI");
+ goto fail;
+ }
+
+ if (wpa_s->conf->home_milenage && wpa_s->conf->home_milenage[0]) {
+ if (wpa_config_set_quoted(ssid, "password",
+ wpa_s->conf->home_milenage) < 0)
+ goto fail;
+ } else {
+ /* TODO: PIN */
+ if (wpa_config_set_quoted(ssid, "pcsc", "") < 0)
+ goto fail;
+ }
+
+ if (wpa_s->conf->home_password && wpa_s->conf->home_password[0] &&
+ wpa_config_set_quoted(ssid, "password", wpa_s->conf->home_password)
+ < 0)
+ goto fail;
+
+ wpa_supplicant_select_network(wpa_s, ssid);
+
+ return 0;
+
+fail:
+ wpas_notify_network_removed(wpa_s, ssid);
+ wpa_config_remove_network(wpa_s->conf, ssid->id);
+#endif /* INTERWORKING_3GPP */
+ return -1;
+}
+
+
+int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+{
+ struct wpa_ssid *ssid;
+ struct nai_realm *realm;
+ struct nai_realm_eap *eap = NULL;
+ u16 count, i;
+ char buf[100];
+ const u8 *ie;
+
+ if (bss == NULL)
+ return -1;
+ ie = wpa_bss_get_ie(bss, WLAN_EID_SSID);
+ if (ie == NULL || ie[1] == 0) {
+ wpa_printf(MSG_DEBUG, "Interworking: No SSID known for "
+ MACSTR, MAC2STR(bss->bssid));
+ return -1;
+ }
+
+ realm = nai_realm_parse(bss->anqp_nai_realm, &count);
+ if (realm == NULL) {
+ wpa_printf(MSG_DEBUG, "Interworking: Could not parse NAI "
+ "Realm list from " MACSTR, MAC2STR(bss->bssid));
+ count = 0;
+ }
+
+ for (i = 0; i < count; i++) {
+ if (!nai_realm_match(&realm[i], wpa_s->conf->home_realm))
+ continue;
+ eap = nai_realm_find_eap(wpa_s, &realm[i]);
+ if (eap)
+ break;
+ }
+
+ if (!eap) {
+ if (interworking_connect_3gpp(wpa_s, bss) == 0) {
+ if (realm)
+ nai_realm_free(realm, count);
+ return 0;
+ }
+
+ wpa_printf(MSG_DEBUG, "Interworking: No matching credentials "
+ "and EAP method found for " MACSTR,
+ MAC2STR(bss->bssid));
+ nai_realm_free(realm, count);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR,
+ MAC2STR(bss->bssid));
+
+ ssid = wpa_config_add_network(wpa_s->conf);
+ if (ssid == NULL) {
+ nai_realm_free(realm, count);
+ return -1;
+ }
+ wpas_notify_network_added(wpa_s, ssid);
+ wpa_config_set_network_defaults(ssid);
+ ssid->temporary = 1;
+ ssid->ssid = os_zalloc(ie[1] + 1);
+ if (ssid->ssid == NULL)
+ goto fail;
+ os_memcpy(ssid->ssid, ie + 2, ie[1]);
+ ssid->ssid_len = ie[1];
+
+ if (wpa_config_set(ssid, "eap", eap_get_name(EAP_VENDOR_IETF,
+ eap->method), 0) < 0)
+ goto fail;
+
+ if (wpa_s->conf->home_username && wpa_s->conf->home_username[0] &&
+ wpa_config_set_quoted(ssid, "identity",
+ wpa_s->conf->home_username) < 0)
+ goto fail;
+
+ if (wpa_s->conf->home_password && wpa_s->conf->home_password[0] &&
+ wpa_config_set_quoted(ssid, "password", wpa_s->conf->home_password)
+ < 0)
+ goto fail;
+
+ switch (eap->method) {
+ case EAP_TYPE_TTLS:
+ if (eap->inner_method) {
+ os_snprintf(buf, sizeof(buf), "\"autheap=%s\"",
+ eap_get_name(EAP_VENDOR_IETF,
+ eap->inner_method));
+ if (wpa_config_set(ssid, "phase2", buf, 0) < 0)
+ goto fail;
+ break;
+ }
+ switch (eap->inner_non_eap) {
+ case NAI_REALM_INNER_NON_EAP_PAP:
+ if (wpa_config_set(ssid, "phase2", "\"auth=PAP\"", 0) <
+ 0)
+ goto fail;
+ break;
+ case NAI_REALM_INNER_NON_EAP_CHAP:
+ if (wpa_config_set(ssid, "phase2", "\"auth=CHAP\"", 0)
+ < 0)
+ goto fail;
+ break;
+ case NAI_REALM_INNER_NON_EAP_MSCHAP:
+ if (wpa_config_set(ssid, "phase2", "\"auth=MSCHAP\"",
+ 0) < 0)
+ goto fail;
+ break;
+ case NAI_REALM_INNER_NON_EAP_MSCHAPV2:
+ if (wpa_config_set(ssid, "phase2", "\"auth=MSCHAPV2\"",
+ 0) < 0)
+ goto fail;
+ break;
+ }
+ break;
+ case EAP_TYPE_PEAP:
+ os_snprintf(buf, sizeof(buf), "\"auth=%s\"",
+ eap_get_name(EAP_VENDOR_IETF, eap->inner_method));
+ if (wpa_config_set(ssid, "phase2", buf, 0) < 0)
+ goto fail;
+ break;
+ }
+
+ if (wpa_s->conf->home_ca_cert && wpa_s->conf->home_ca_cert[0] &&
+ wpa_config_set_quoted(ssid, "ca_cert", wpa_s->conf->home_ca_cert) <
+ 0)
+ goto fail;
+
+ nai_realm_free(realm, count);
+
+ wpa_supplicant_select_network(wpa_s, ssid);
+
+ return 0;
+
+fail:
+ wpas_notify_network_removed(wpa_s, ssid);
+ wpa_config_remove_network(wpa_s->conf, ssid->id);
+ nai_realm_free(realm, count);
+ return -1;
+}
+
+
+static int interworking_credentials_available_3gpp(
+ struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+{
+ int ret = 0;
+
+#ifdef INTERWORKING_3GPP
+ if (bss->anqp_3gpp == NULL)
+ return ret;
+
+ if (wpa_s->conf->home_imsi == NULL || !wpa_s->conf->home_imsi[0] ||
+ wpa_s->conf->home_milenage == NULL ||
+ !wpa_s->conf->home_milenage[0])
+ return ret;
+
+ wpa_printf(MSG_DEBUG, "Interworking: Parsing 3GPP info from " MACSTR,
+ MAC2STR(bss->bssid));
+ ret = plmn_id_match(bss->anqp_3gpp, wpa_s->conf->home_imsi);
+ wpa_printf(MSG_DEBUG, "PLMN match %sfound", ret ? "" : "not ");
+#endif /* INTERWORKING_3GPP */
+ return ret;
+}
+
+
+static int interworking_credentials_available_realm(
+ struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+{
+ struct nai_realm *realm;
+ u16 count, i;
+ int found = 0;
+
+ if (bss->anqp_nai_realm == NULL)
+ return 0;
+
+ if (wpa_s->conf->home_realm == NULL)
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "Interworking: Parsing NAI Realm list from "
+ MACSTR, MAC2STR(bss->bssid));
+ realm = nai_realm_parse(bss->anqp_nai_realm, &count);
+ if (realm == NULL) {
+ wpa_printf(MSG_DEBUG, "Interworking: Could not parse NAI "
+ "Realm list from " MACSTR, MAC2STR(bss->bssid));
+ return 0;
+ }
+
+ for (i = 0; i < count; i++) {
+ if (!nai_realm_match(&realm[i], wpa_s->conf->home_realm))
+ continue;
+ if (nai_realm_find_eap(wpa_s, &realm[i])) {
+ found++;
+ break;
+ }
+ }
+
+ nai_realm_free(realm, count);
+
+ return found;
+}
+
+
+static int interworking_credentials_available(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *bss)
+{
+ return interworking_credentials_available_realm(wpa_s, bss) ||
+ interworking_credentials_available_3gpp(wpa_s, bss);
+}
+
+
+static void interworking_select_network(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_bss *bss, *selected = NULL;
+ unsigned int count = 0;
+
+ wpa_s->network_select = 0;
+
+ dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+ if (!interworking_credentials_available(wpa_s, bss))
+ continue;
+ count++;
+ wpa_msg(wpa_s, MSG_INFO, INTERWORKING_AP MACSTR,
+ MAC2STR(bss->bssid));
+ if (selected == NULL && wpa_s->auto_select)
+ selected = bss;
+ }
+
+ if (count == 0) {
+ wpa_msg(wpa_s, MSG_INFO, INTERWORKING_NO_MATCH "No network "
+ "with matching credentials found");
+ }
+
+ if (selected)
+ interworking_connect(wpa_s, selected);
+}
+
+
+static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_bss *bss;
+ int found = 0;
+ const u8 *ie;
+
+ if (!wpa_s->fetch_anqp_in_progress)
+ return;
+
+ dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+ if (!(bss->caps & IEEE80211_CAP_ESS))
+ continue;
+ ie = wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB);
+ if (ie == NULL || ie[1] < 4 || !(ie[5] & 0x80))
+ continue; /* AP does not support Interworking */
+
+ if (!(bss->flags & WPA_BSS_ANQP_FETCH_TRIED)) {
+ found++;
+ bss->flags |= WPA_BSS_ANQP_FETCH_TRIED;
+ wpa_msg(wpa_s, MSG_INFO, "Starting ANQP fetch for "
+ MACSTR, MAC2STR(bss->bssid));
+ interworking_anqp_send_req(wpa_s, bss);
+ break;
+ }
+ }
+
+ if (found == 0) {
+ wpa_msg(wpa_s, MSG_INFO, "ANQP fetch completed");
+ wpa_s->fetch_anqp_in_progress = 0;
+ if (wpa_s->network_select)
+ interworking_select_network(wpa_s);
+ }
+}
+
+
+static void interworking_start_fetch_anqp(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_bss *bss;
+
+ dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list)
+ bss->flags &= ~WPA_BSS_ANQP_FETCH_TRIED;
+
+ wpa_s->fetch_anqp_in_progress = 1;
+ interworking_next_anqp_fetch(wpa_s);
+}
+
+
+int interworking_fetch_anqp(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->fetch_anqp_in_progress || wpa_s->network_select)
+ return 0;
+
+ wpa_s->network_select = 0;
+
+ interworking_start_fetch_anqp(wpa_s);
+
+ return 0;
+}
+
+
+void interworking_stop_fetch_anqp(struct wpa_supplicant *wpa_s)
+{
+ if (!wpa_s->fetch_anqp_in_progress)
+ return;
+
+ wpa_s->fetch_anqp_in_progress = 0;
+}
+
+
+int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst,
+ u16 info_ids[], size_t num_ids)
+{
+ struct wpabuf *buf;
+ int ret = 0;
+ int freq;
+ struct wpa_bss *bss;
+ int res;
+
+ freq = wpa_s->assoc_freq;
+ bss = wpa_bss_get_bssid(wpa_s, dst);
+ if (bss)
+ freq = bss->freq;
+ if (freq <= 0)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "ANQP: Query Request to " MACSTR " for %u id(s)",
+ MAC2STR(dst), (unsigned int) num_ids);
+
+ buf = anqp_build_req(info_ids, num_ids, NULL);
+ if (buf == NULL)
+ return -1;
+
+ res = gas_query_req(wpa_s->gas, dst, freq, buf, anqp_resp_cb, wpa_s);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request");
+ ret = -1;
+ } else
+ wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token "
+ "%u", res);
+
+ wpabuf_free(buf);
+ return ret;
+}
+
+
+static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s,
+ const u8 *sa, u16 info_id,
+ const u8 *data, size_t slen)
+{
+ const u8 *pos = data;
+ struct wpa_bss *bss = wpa_bss_get_bssid(wpa_s, sa);
+
+ switch (info_id) {
+ case ANQP_CAPABILITY_LIST:
+ wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
+ " ANQP Capability list", MAC2STR(sa));
+ break;
+ case ANQP_VENUE_NAME:
+ wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
+ " Venue Name", MAC2STR(sa));
+ wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Venue Name", pos, slen);
+ if (bss) {
+ wpabuf_free(bss->anqp_venue_name);
+ bss->anqp_venue_name = wpabuf_alloc_copy(pos, slen);
+ }
+ break;
+ case ANQP_NETWORK_AUTH_TYPE:
+ wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
+ " Network Authentication Type information",
+ MAC2STR(sa));
+ wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Network Authentication "
+ "Type", pos, slen);
+ if (bss) {
+ wpabuf_free(bss->anqp_network_auth_type);
+ bss->anqp_network_auth_type =
+ wpabuf_alloc_copy(pos, slen);
+ }
+ break;
+ case ANQP_ROAMING_CONSORTIUM:
+ wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
+ " Roaming Consortium list", MAC2STR(sa));
+ wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Roaming Consortium",
+ pos, slen);
+ if (bss) {
+ wpabuf_free(bss->anqp_roaming_consortium);
+ bss->anqp_roaming_consortium =
+ wpabuf_alloc_copy(pos, slen);
+ }
+ break;
+ case ANQP_IP_ADDR_TYPE_AVAILABILITY:
+ wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
+ " IP Address Type Availability information",
+ MAC2STR(sa));
+ wpa_hexdump(MSG_MSGDUMP, "ANQP: IP Address Availability",
+ pos, slen);
+ if (bss) {
+ wpabuf_free(bss->anqp_ip_addr_type_availability);
+ bss->anqp_ip_addr_type_availability =
+ wpabuf_alloc_copy(pos, slen);
+ }
+ break;
+ case ANQP_NAI_REALM:
+ wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
+ " NAI Realm list", MAC2STR(sa));
+ wpa_hexdump_ascii(MSG_DEBUG, "ANQP: NAI Realm", pos, slen);
+ if (bss) {
+ wpabuf_free(bss->anqp_nai_realm);
+ bss->anqp_nai_realm = wpabuf_alloc_copy(pos, slen);
+ }
+ break;
+ case ANQP_3GPP_CELLULAR_NETWORK:
+ wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
+ " 3GPP Cellular Network information", MAC2STR(sa));
+ wpa_hexdump_ascii(MSG_DEBUG, "ANQP: 3GPP Cellular Network",
+ pos, slen);
+ if (bss) {
+ wpabuf_free(bss->anqp_3gpp);
+ bss->anqp_3gpp = wpabuf_alloc_copy(pos, slen);
+ }
+ break;
+ case ANQP_DOMAIN_NAME:
+ wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
+ " Domain Name list", MAC2STR(sa));
+ wpa_hexdump_ascii(MSG_MSGDUMP, "ANQP: Domain Name", pos, slen);
+ if (bss) {
+ wpabuf_free(bss->anqp_domain_name);
+ bss->anqp_domain_name = wpabuf_alloc_copy(pos, slen);
+ }
+ break;
+ case ANQP_VENDOR_SPECIFIC:
+ if (slen < 3)
+ return;
+
+ switch (WPA_GET_BE24(pos)) {
+ default:
+ wpa_printf(MSG_DEBUG, "Interworking: Unsupported "
+ "vendor-specific ANQP OUI %06x",
+ WPA_GET_BE24(pos));
+ return;
+ }
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "Interworking: Unsupported ANQP Info ID "
+ "%u", info_id);
+ break;
+ }
+}
+
+
+void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token,
+ enum gas_query_result result,
+ const struct wpabuf *adv_proto,
+ const struct wpabuf *resp, u16 status_code)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ const u8 *pos;
+ const u8 *end;
+ u16 info_id;
+ u16 slen;
+
+ if (result != GAS_QUERY_SUCCESS)
+ return;
+
+ pos = wpabuf_head(adv_proto);
+ if (wpabuf_len(adv_proto) < 4 || pos[0] != WLAN_EID_ADV_PROTO ||
+ pos[1] < 2 || pos[3] != ACCESS_NETWORK_QUERY_PROTOCOL) {
+ wpa_printf(MSG_DEBUG, "ANQP: Unexpected Advertisement "
+ "Protocol in response");
+ return;
+ }
+
+ pos = wpabuf_head(resp);
+ end = pos + wpabuf_len(resp);
+
+ while (pos < end) {
+ if (pos + 4 > end) {
+ wpa_printf(MSG_DEBUG, "ANQP: Invalid element");
+ break;
+ }
+ info_id = WPA_GET_LE16(pos);
+ pos += 2;
+ slen = WPA_GET_LE16(pos);
+ pos += 2;
+ if (pos + slen > end) {
+ wpa_printf(MSG_DEBUG, "ANQP: Invalid element length "
+ "for Info ID %u", info_id);
+ break;
+ }
+ interworking_parse_rx_anqp_resp(wpa_s, dst, info_id, pos,
+ slen);
+ pos += slen;
+ }
+}
+
+
+static void interworking_scan_res_handler(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_results *scan_res)
+{
+ wpa_printf(MSG_DEBUG, "Interworking: Scan results available - start "
+ "ANQP fetch");
+ interworking_start_fetch_anqp(wpa_s);
+}
+
+
+int interworking_select(struct wpa_supplicant *wpa_s, int auto_select)
+{
+ interworking_stop_fetch_anqp(wpa_s);
+ wpa_s->network_select = 1;
+ wpa_s->auto_select = !!auto_select;
+ wpa_printf(MSG_DEBUG, "Interworking: Start scan for network "
+ "selection");
+ wpa_s->scan_res_handler = interworking_scan_res_handler;
+ wpa_s->scan_req = 2;
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+
+ return 0;
+}
--- /dev/null
+/*
+ * Interworking (IEEE 802.11u)
+ * Copyright (c) 2011, Qualcomm Atheros
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef INTERWORKING_H
+#define INTERWORKING_H
+
+enum gas_query_result;
+
+int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst,
+ u16 info_ids[], size_t num_ids);
+void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token,
+ enum gas_query_result result,
+ const struct wpabuf *adv_proto,
+ const struct wpabuf *resp, u16 status_code);
+int interworking_fetch_anqp(struct wpa_supplicant *wpa_s);
+void interworking_stop_fetch_anqp(struct wpa_supplicant *wpa_s);
+int interworking_select(struct wpa_supplicant *wpa_s, int auto_select);
+int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss);
+
+#endif /* INTERWORKING_H */
"[-g<global ctrl>] \\\n"
" -i<ifname> -c<config file> [-C<ctrl>] [-D<driver>] "
"[-p<driver_param>] \\\n"
- " [-b<br_ifname>] [-f<debug file>] \\\n"
+ " [-b<br_ifname>] [-f<debug file>] [-e<entropy file>] "
+ "\\\n"
" [-o<override driver>] [-O<override ctrl>] \\\n"
" [-N -i<ifname> -c<conf> [-C<ctrl>] "
"[-D<driver>] \\\n"
" -C = ctrl_interface parameter (only used if -c is not)\n"
" -i = interface name\n"
" -d = increase debugging verbosity (-dd even more)\n"
- " -D = driver name (can be multiple drivers: nl80211,wext)\n");
+ " -D = driver name (can be multiple drivers: nl80211,wext)\n"
+ " -e = entropy file\n");
#ifdef CONFIG_DEBUG_FILE
printf(" -f = log output to debug file instead of stdout\n");
#endif /* CONFIG_DEBUG_FILE */
wpa_supplicant_fd_workaround();
for (;;) {
- c = getopt(argc, argv, "b:Bc:C:D:df:g:hi:KLNo:O:p:P:qstuvW");
+ c = getopt(argc, argv, "b:Bc:C:D:de:f:g:hi:KLNo:O:p:P:qstuvW");
if (c < 0)
break;
switch (c) {
params.wpa_debug_level--;
break;
#endif /* CONFIG_NO_STDOUT_DEBUG */
+ case 'e':
+ params.entropy_file = optarg;
+ break;
#ifdef CONFIG_DEBUG_FILE
case 'f':
params.wpa_debug_file_path = optarg;
+++ /dev/null
-/*
- * WPA Supplicant - Client mode MLME
- * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi>
- * Copyright (c) 2004, Instant802 Networks, Inc.
- * Copyright (c) 2005-2006, Devicescape Software, Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
- */
-
-#include "includes.h"
-
-#include "common.h"
-#include "eloop.h"
-#include "config_ssid.h"
-#include "wpa_supplicant_i.h"
-#include "notify.h"
-#include "driver_i.h"
-#include "rsn_supp/wpa.h"
-#include "common/ieee802_11_defs.h"
-#include "common/ieee802_11_common.h"
-#include "mlme.h"
-
-
-/* Timeouts and intervals in milliseconds */
-#define IEEE80211_AUTH_TIMEOUT (200)
-#define IEEE80211_AUTH_MAX_TRIES 3
-#define IEEE80211_ASSOC_TIMEOUT (200)
-#define IEEE80211_ASSOC_MAX_TRIES 3
-#define IEEE80211_MONITORING_INTERVAL (2000)
-#define IEEE80211_PROBE_INTERVAL (60000)
-#define IEEE80211_RETRY_AUTH_INTERVAL (1000)
-#define IEEE80211_SCAN_INTERVAL (2000)
-#define IEEE80211_SCAN_INTERVAL_SLOW (15000)
-#define IEEE80211_IBSS_JOIN_TIMEOUT (20000)
-
-#define IEEE80211_PROBE_DELAY (33)
-#define IEEE80211_CHANNEL_TIME (33)
-#define IEEE80211_PASSIVE_CHANNEL_TIME (200)
-#define IEEE80211_SCAN_RESULT_EXPIRE (10000)
-#define IEEE80211_IBSS_MERGE_INTERVAL (30000)
-#define IEEE80211_IBSS_INACTIVITY_LIMIT (60000)
-
-#define IEEE80211_IBSS_MAX_STA_ENTRIES 128
-
-
-#define IEEE80211_FC(type, stype) host_to_le16((type << 2) | (stype << 4))
-
-
-struct ieee80211_sta_bss {
- struct ieee80211_sta_bss *next;
- struct ieee80211_sta_bss *hnext;
-
- u8 bssid[ETH_ALEN];
- u8 ssid[MAX_SSID_LEN];
- size_t ssid_len;
- u16 capability; /* host byte order */
- int hw_mode;
- int channel;
- int freq;
- int rssi;
- u8 *ie;
- size_t ie_len;
- u8 *wpa_ie;
- size_t wpa_ie_len;
- u8 *rsn_ie;
- size_t rsn_ie_len;
- u8 *wmm_ie;
- size_t wmm_ie_len;
- u8 *mdie;
- size_t mdie_len;
-#define IEEE80211_MAX_SUPP_RATES 32
- u8 supp_rates[IEEE80211_MAX_SUPP_RATES];
- size_t supp_rates_len;
- int beacon_int;
- u64 timestamp;
-
- int probe_resp;
- struct os_time last_update;
-};
-
-
-static void ieee80211_send_probe_req(struct wpa_supplicant *wpa_s,
- const u8 *dst,
- const u8 *ssid, size_t ssid_len);
-static struct ieee80211_sta_bss *
-ieee80211_bss_get(struct wpa_supplicant *wpa_s, const u8 *bssid);
-static int ieee80211_sta_find_ibss(struct wpa_supplicant *wpa_s);
-static int ieee80211_sta_wep_configured(struct wpa_supplicant *wpa_s);
-static void ieee80211_sta_timer(void *eloop_ctx, void *timeout_ctx);
-static void ieee80211_sta_scan_timer(void *eloop_ctx, void *timeout_ctx);
-static void ieee80211_build_tspec(struct wpabuf *buf);
-static int ieee80211_sta_set_probe_req_ie(struct wpa_supplicant *wpa_s,
- const u8 *ies, size_t ies_len);
-
-
-static int ieee80211_sta_set_channel(struct wpa_supplicant *wpa_s,
- enum hostapd_hw_mode phymode, int chan,
- int freq)
-{
- size_t i;
- struct hostapd_hw_modes *mode;
-
- for (i = 0; i < wpa_s->mlme.num_modes; i++) {
- mode = &wpa_s->mlme.modes[i];
- if (mode->mode == phymode) {
- wpa_s->mlme.curr_rates = mode->rates;
- wpa_s->mlme.num_curr_rates = mode->num_rates;
- break;
- }
- }
-
- return wpa_drv_set_channel(wpa_s, phymode, chan, freq);
-}
-
-
-static int ecw2cw(int ecw)
-{
- int cw = 1;
- while (ecw > 0) {
- cw <<= 1;
- ecw--;
- }
- return cw - 1;
-}
-
-
-static void ieee80211_sta_wmm_params(struct wpa_supplicant *wpa_s,
- const u8 *wmm_param, size_t wmm_param_len)
-{
- size_t left;
- int count;
- const u8 *pos;
- u8 wmm_acm;
-
- if (wmm_param_len < 8 || wmm_param[5] /* version */ != 1)
- return;
- count = wmm_param[6] & 0x0f;
- if (count == wpa_s->mlme.wmm_last_param_set)
- return;
- wpa_s->mlme.wmm_last_param_set = count;
-
- pos = wmm_param + 8;
- left = wmm_param_len - 8;
-
- wmm_acm = 0;
- for (; left >= 4; left -= 4, pos += 4) {
- int aci = (pos[0] >> 5) & 0x03;
- int acm = (pos[0] >> 4) & 0x01;
- int aifs, cw_max, cw_min, burst_time;
-
- switch (aci) {
- case 1: /* AC_BK */
- if (acm)
- wmm_acm |= BIT(1) | BIT(2); /* BK/- */
- break;
- case 2: /* AC_VI */
- if (acm)
- wmm_acm |= BIT(4) | BIT(5); /* CL/VI */
- break;
- case 3: /* AC_VO */
- if (acm)
- wmm_acm |= BIT(6) | BIT(7); /* VO/NC */
- break;
- case 0: /* AC_BE */
- default:
- if (acm)
- wmm_acm |= BIT(0) | BIT(3); /* BE/EE */
- break;
- }
-
- aifs = pos[0] & 0x0f;
- cw_max = ecw2cw((pos[1] & 0xf0) >> 4);
- cw_min = ecw2cw(pos[1] & 0x0f);
- /* TXOP is in units of 32 usec; burst_time in 0.1 ms */
- burst_time = (pos[2] | (pos[3] << 8)) * 32 / 100;
- wpa_printf(MSG_DEBUG, "MLME: WMM aci=%d acm=%d aifs=%d "
- "cWmin=%d cWmax=%d burst=%d",
- aci, acm, aifs, cw_min, cw_max, burst_time);
- /* TODO: driver configuration */
- }
-}
-
-
-static void ieee80211_set_associated(struct wpa_supplicant *wpa_s, int assoc)
-{
- if (wpa_s->mlme.associated == assoc && !assoc)
- return;
-
- wpa_s->mlme.associated = assoc;
-
- if (assoc) {
- union wpa_event_data data;
- os_memset(&data, 0, sizeof(data));
- wpa_s->mlme.prev_bssid_set = 1;
- os_memcpy(wpa_s->mlme.prev_bssid, wpa_s->bssid, ETH_ALEN);
- data.assoc_info.req_ies = wpa_s->mlme.assocreq_ies;
- data.assoc_info.req_ies_len = wpa_s->mlme.assocreq_ies_len;
- data.assoc_info.resp_ies = wpa_s->mlme.assocresp_ies;
- data.assoc_info.resp_ies_len = wpa_s->mlme.assocresp_ies_len;
- data.assoc_info.freq = wpa_s->mlme.freq;
- wpa_supplicant_event(wpa_s, EVENT_ASSOC, &data);
- } else {
- wpa_supplicant_event(wpa_s, EVENT_DISASSOC, NULL);
- }
- os_get_time(&wpa_s->mlme.last_probe);
-}
-
-
-static int ieee80211_sta_tx(struct wpa_supplicant *wpa_s, const u8 *buf,
- size_t len)
-{
- return wpa_drv_send_mlme(wpa_s, buf, len);
-}
-
-
-static void ieee80211_send_auth(struct wpa_supplicant *wpa_s,
- int transaction, const u8 *extra,
- size_t extra_len, int encrypt)
-{
- u8 *buf;
- size_t len;
- struct ieee80211_mgmt *mgmt;
-
- buf = os_malloc(sizeof(*mgmt) + 6 + extra_len);
- if (buf == NULL) {
- wpa_printf(MSG_DEBUG, "MLME: failed to allocate buffer for "
- "auth frame");
- return;
- }
-
- mgmt = (struct ieee80211_mgmt *) buf;
- len = 24 + 6;
- os_memset(mgmt, 0, 24 + 6);
- mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
- WLAN_FC_STYPE_AUTH);
- if (encrypt)
- mgmt->frame_control |= host_to_le16(WLAN_FC_ISWEP);
- os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
- os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
- os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
- mgmt->u.auth.auth_alg = host_to_le16(wpa_s->mlme.auth_alg);
- mgmt->u.auth.auth_transaction = host_to_le16(transaction);
- wpa_s->mlme.auth_transaction = transaction + 1;
- mgmt->u.auth.status_code = host_to_le16(0);
- if (extra) {
- os_memcpy(buf + len, extra, extra_len);
- len += extra_len;
- }
-
- ieee80211_sta_tx(wpa_s, buf, len);
- os_free(buf);
-}
-
-
-static void ieee80211_reschedule_timer(struct wpa_supplicant *wpa_s, int ms)
-{
- eloop_cancel_timeout(ieee80211_sta_timer, wpa_s, NULL);
- eloop_register_timeout(ms / 1000, 1000 * (ms % 1000),
- ieee80211_sta_timer, wpa_s, NULL);
-}
-
-
-static void ieee80211_authenticate(struct wpa_supplicant *wpa_s)
-{
- u8 *extra;
- size_t extra_len;
-
- wpa_s->mlme.auth_tries++;
- if (wpa_s->mlme.auth_tries > IEEE80211_AUTH_MAX_TRIES) {
- wpa_printf(MSG_DEBUG, "MLME: authentication with AP " MACSTR
- " timed out", MAC2STR(wpa_s->bssid));
- return;
- }
-
- wpa_s->mlme.state = IEEE80211_AUTHENTICATE;
- wpa_printf(MSG_DEBUG, "MLME: authenticate with AP " MACSTR,
- MAC2STR(wpa_s->bssid));
-
- extra = NULL;
- extra_len = 0;
-
-#ifdef CONFIG_IEEE80211R
- if ((wpa_s->mlme.key_mgmt == KEY_MGMT_FT_802_1X ||
- wpa_s->mlme.key_mgmt == KEY_MGMT_FT_PSK) &&
- wpa_s->mlme.ft_ies) {
- struct ieee80211_sta_bss *bss;
- struct rsn_mdie *mdie = NULL;
- bss = ieee80211_bss_get(wpa_s, wpa_s->bssid);
- if (bss && bss->mdie_len >= 2 + sizeof(*mdie))
- mdie = (struct rsn_mdie *) (bss->mdie + 2);
- if (mdie &&
- os_memcmp(mdie->mobility_domain, wpa_s->mlme.current_md,
- MOBILITY_DOMAIN_ID_LEN) == 0) {
- wpa_printf(MSG_DEBUG, "MLME: Trying to use FT "
- "over-the-air");
- wpa_s->mlme.auth_alg = WLAN_AUTH_FT;
- extra = wpa_s->mlme.ft_ies;
- extra_len = wpa_s->mlme.ft_ies_len;
- }
- }
-#endif /* CONFIG_IEEE80211R */
-
- ieee80211_send_auth(wpa_s, 1, extra, extra_len, 0);
-
- ieee80211_reschedule_timer(wpa_s, IEEE80211_AUTH_TIMEOUT);
-}
-
-
-static void ieee80211_send_assoc(struct wpa_supplicant *wpa_s)
-{
- struct ieee80211_mgmt *mgmt;
- u8 *pos, *ies, *buf;
- int i, len;
- u16 capab;
- struct ieee80211_sta_bss *bss;
- int wmm = 0;
- size_t blen, buflen;
-
- if (wpa_s->mlme.curr_rates == NULL) {
- wpa_printf(MSG_DEBUG, "MLME: curr_rates not set for assoc");
- return;
- }
-
- buflen = sizeof(*mgmt) + 200 + wpa_s->mlme.extra_ie_len +
- wpa_s->mlme.ssid_len;
-#ifdef CONFIG_IEEE80211R
- if (wpa_s->mlme.ft_ies)
- buflen += wpa_s->mlme.ft_ies_len;
-#endif /* CONFIG_IEEE80211R */
- buf = os_malloc(buflen);
- if (buf == NULL) {
- wpa_printf(MSG_DEBUG, "MLME: failed to allocate buffer for "
- "assoc frame");
- return;
- }
- blen = 0;
-
- capab = wpa_s->mlme.capab;
- if (wpa_s->mlme.phymode == HOSTAPD_MODE_IEEE80211G) {
- capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME |
- WLAN_CAPABILITY_SHORT_PREAMBLE;
- }
- bss = ieee80211_bss_get(wpa_s, wpa_s->bssid);
- if (bss) {
- if (bss->capability & WLAN_CAPABILITY_PRIVACY)
- capab |= WLAN_CAPABILITY_PRIVACY;
- if (bss->wmm_ie) {
- wmm = 1;
- }
- }
-
- mgmt = (struct ieee80211_mgmt *) buf;
- blen += 24;
- os_memset(mgmt, 0, 24);
- os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
- os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
- os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
-
- if (wpa_s->mlme.prev_bssid_set) {
- blen += 10;
- mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
- WLAN_FC_STYPE_REASSOC_REQ);
- mgmt->u.reassoc_req.capab_info = host_to_le16(capab);
- mgmt->u.reassoc_req.listen_interval = host_to_le16(1);
- os_memcpy(mgmt->u.reassoc_req.current_ap,
- wpa_s->mlme.prev_bssid,
- ETH_ALEN);
- } else {
- blen += 4;
- mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
- WLAN_FC_STYPE_ASSOC_REQ);
- mgmt->u.assoc_req.capab_info = host_to_le16(capab);
- mgmt->u.assoc_req.listen_interval = host_to_le16(1);
- }
-
- /* SSID */
- ies = pos = buf + blen;
- blen += 2 + wpa_s->mlme.ssid_len;
- *pos++ = WLAN_EID_SSID;
- *pos++ = wpa_s->mlme.ssid_len;
- os_memcpy(pos, wpa_s->mlme.ssid, wpa_s->mlme.ssid_len);
-
- len = wpa_s->mlme.num_curr_rates;
- if (len > 8)
- len = 8;
- pos = buf + blen;
- blen += len + 2;
- *pos++ = WLAN_EID_SUPP_RATES;
- *pos++ = len;
- for (i = 0; i < len; i++)
- *pos++ = (u8) (wpa_s->mlme.curr_rates[i] / 5);
-
- if (wpa_s->mlme.num_curr_rates > len) {
- pos = buf + blen;
- blen += wpa_s->mlme.num_curr_rates - len + 2;
- *pos++ = WLAN_EID_EXT_SUPP_RATES;
- *pos++ = wpa_s->mlme.num_curr_rates - len;
- for (i = len; i < wpa_s->mlme.num_curr_rates; i++)
- *pos++ = (u8) (wpa_s->mlme.curr_rates[i] / 5);
- }
-
- if (wpa_s->mlme.extra_ie && wpa_s->mlme.auth_alg != WLAN_AUTH_FT) {
- pos = buf + blen;
- blen += wpa_s->mlme.extra_ie_len;
- os_memcpy(pos, wpa_s->mlme.extra_ie, wpa_s->mlme.extra_ie_len);
- }
-
-#ifdef CONFIG_IEEE80211R
- if ((wpa_s->mlme.key_mgmt == KEY_MGMT_FT_802_1X ||
- wpa_s->mlme.key_mgmt == KEY_MGMT_FT_PSK) &&
- wpa_s->mlme.auth_alg != WLAN_AUTH_FT &&
- bss && bss->mdie &&
- bss->mdie_len >= 2 + sizeof(struct rsn_mdie) &&
- bss->mdie[1] >= sizeof(struct rsn_mdie)) {
- pos = buf + blen;
- blen += 2 + sizeof(struct rsn_mdie);
- *pos++ = WLAN_EID_MOBILITY_DOMAIN;
- *pos++ = sizeof(struct rsn_mdie);
- os_memcpy(pos, bss->mdie + 2, MOBILITY_DOMAIN_ID_LEN);
- pos += MOBILITY_DOMAIN_ID_LEN;
- *pos++ = 0; /* FIX: copy from the target AP's MDIE */
- }
-
- if ((wpa_s->mlme.key_mgmt == KEY_MGMT_FT_802_1X ||
- wpa_s->mlme.key_mgmt == KEY_MGMT_FT_PSK) &&
- wpa_s->mlme.auth_alg == WLAN_AUTH_FT && wpa_s->mlme.ft_ies) {
- pos = buf + blen;
- os_memcpy(pos, wpa_s->mlme.ft_ies, wpa_s->mlme.ft_ies_len);
- pos += wpa_s->mlme.ft_ies_len;
- blen += wpa_s->mlme.ft_ies_len;
- }
-#endif /* CONFIG_IEEE80211R */
-
- if (wmm && wpa_s->mlme.wmm_enabled) {
- pos = buf + blen;
- blen += 9;
- *pos++ = WLAN_EID_VENDOR_SPECIFIC;
- *pos++ = 7; /* len */
- *pos++ = 0x00; /* Microsoft OUI 00:50:F2 */
- *pos++ = 0x50;
- *pos++ = 0xf2;
- *pos++ = 2; /* WMM */
- *pos++ = 0; /* WMM info */
- *pos++ = 1; /* WMM ver */
- *pos++ = 0;
- }
-
- os_free(wpa_s->mlme.assocreq_ies);
- wpa_s->mlme.assocreq_ies_len = (buf + blen) - ies;
- wpa_s->mlme.assocreq_ies = os_malloc(wpa_s->mlme.assocreq_ies_len);
- if (wpa_s->mlme.assocreq_ies) {
- os_memcpy(wpa_s->mlme.assocreq_ies, ies,
- wpa_s->mlme.assocreq_ies_len);
- }
-
- ieee80211_sta_tx(wpa_s, buf, blen);
- os_free(buf);
-}
-
-
-static void ieee80211_send_deauth(struct wpa_supplicant *wpa_s, u16 reason)
-{
- u8 *buf;
- size_t len;
- struct ieee80211_mgmt *mgmt;
-
- buf = os_zalloc(sizeof(*mgmt));
- if (buf == NULL) {
- wpa_printf(MSG_DEBUG, "MLME: failed to allocate buffer for "
- "deauth frame");
- return;
- }
-
- mgmt = (struct ieee80211_mgmt *) buf;
- len = 24;
- os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
- os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
- os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
- mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
- WLAN_FC_STYPE_DEAUTH);
- len += 2;
- mgmt->u.deauth.reason_code = host_to_le16(reason);
-
- ieee80211_sta_tx(wpa_s, buf, len);
- os_free(buf);
-}
-
-
-static void ieee80211_send_disassoc(struct wpa_supplicant *wpa_s, u16 reason)
-{
- u8 *buf;
- size_t len;
- struct ieee80211_mgmt *mgmt;
-
- buf = os_zalloc(sizeof(*mgmt));
- if (buf == NULL) {
- wpa_printf(MSG_DEBUG, "MLME: failed to allocate buffer for "
- "disassoc frame");
- return;
- }
-
- mgmt = (struct ieee80211_mgmt *) buf;
- len = 24;
- os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
- os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
- os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
- mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
- WLAN_FC_STYPE_DISASSOC);
- len += 2;
- mgmt->u.disassoc.reason_code = host_to_le16(reason);
-
- ieee80211_sta_tx(wpa_s, buf, len);
- os_free(buf);
-}
-
-
-static int ieee80211_privacy_mismatch(struct wpa_supplicant *wpa_s)
-{
- struct ieee80211_sta_bss *bss;
- int res = 0;
-
- if (wpa_s->mlme.mixed_cell ||
- wpa_s->mlme.key_mgmt != KEY_MGMT_NONE)
- return 0;
-
- bss = ieee80211_bss_get(wpa_s, wpa_s->bssid);
- if (bss == NULL)
- return 0;
-
- if (ieee80211_sta_wep_configured(wpa_s) !=
- !!(bss->capability & WLAN_CAPABILITY_PRIVACY))
- res = 1;
-
- return res;
-}
-
-
-static void ieee80211_associate(struct wpa_supplicant *wpa_s)
-{
- wpa_s->mlme.assoc_tries++;
- if (wpa_s->mlme.assoc_tries > IEEE80211_ASSOC_MAX_TRIES) {
- wpa_printf(MSG_DEBUG, "MLME: association with AP " MACSTR
- " timed out", MAC2STR(wpa_s->bssid));
- return;
- }
-
- wpa_s->mlme.state = IEEE80211_ASSOCIATE;
- wpa_printf(MSG_DEBUG, "MLME: associate with AP " MACSTR,
- MAC2STR(wpa_s->bssid));
- if (ieee80211_privacy_mismatch(wpa_s)) {
- wpa_printf(MSG_DEBUG, "MLME: mismatch in privacy "
- "configuration and mixed-cell disabled - abort "
- "association");
- return;
- }
-
- ieee80211_send_assoc(wpa_s);
-
- ieee80211_reschedule_timer(wpa_s, IEEE80211_ASSOC_TIMEOUT);
-}
-
-
-static void ieee80211_associated(struct wpa_supplicant *wpa_s)
-{
- int disassoc;
-
- /* TODO: start monitoring current AP signal quality and number of
- * missed beacons. Scan other channels every now and then and search
- * for better APs. */
- /* TODO: remove expired BSSes */
-
- wpa_s->mlme.state = IEEE80211_ASSOCIATED;
-
-#if 0 /* FIX */
- sta = sta_info_get(local, wpa_s->bssid);
- if (sta == NULL) {
- wpa_printf(MSG_DEBUG "MLME: No STA entry for own AP " MACSTR,
- MAC2STR(wpa_s->bssid));
- disassoc = 1;
- } else {
- disassoc = 0;
- if (time_after(jiffies,
- sta->last_rx + IEEE80211_MONITORING_INTERVAL)) {
- if (wpa_s->mlme.probereq_poll) {
- wpa_printf(MSG_DEBUG "MLME: No ProbeResp from "
- "current AP " MACSTR " - assume "
- "out of range",
- MAC2STR(wpa_s->bssid));
- disassoc = 1;
- } else {
- ieee80211_send_probe_req(
- wpa_s->bssid,
- wpa_s->mlme.scan_ssid,
- wpa_s->mlme.scan_ssid_len);
- wpa_s->mlme.probereq_poll = 1;
- }
- } else {
- wpa_s->mlme.probereq_poll = 0;
- if (time_after(jiffies, wpa_s->mlme.last_probe +
- IEEE80211_PROBE_INTERVAL)) {
- wpa_s->mlme.last_probe = jiffies;
- ieee80211_send_probe_req(wpa_s->bssid,
- wpa_s->mlme.ssid,
- wpa_s->mlme.ssid_len);
- }
- }
- sta_info_release(local, sta);
- }
-#else
- disassoc = 0;
-#endif
- if (disassoc) {
- wpa_supplicant_event(wpa_s, EVENT_DISASSOC, NULL);
- ieee80211_reschedule_timer(wpa_s,
- IEEE80211_MONITORING_INTERVAL +
- 30000);
- } else {
- ieee80211_reschedule_timer(wpa_s,
- IEEE80211_MONITORING_INTERVAL);
- }
-}
-
-
-static void ieee80211_send_probe_req(struct wpa_supplicant *wpa_s,
- const u8 *dst,
- const u8 *ssid, size_t ssid_len)
-{
- u8 *buf;
- size_t len;
- struct ieee80211_mgmt *mgmt;
- u8 *pos, *supp_rates;
- u8 *esupp_rates = NULL;
- int i;
-
- buf = os_malloc(sizeof(*mgmt) + 200 + wpa_s->mlme.extra_probe_ie_len);
- if (buf == NULL) {
- wpa_printf(MSG_DEBUG, "MLME: failed to allocate buffer for "
- "probe request");
- return;
- }
-
- mgmt = (struct ieee80211_mgmt *) buf;
- len = 24;
- os_memset(mgmt, 0, 24);
- mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
- WLAN_FC_STYPE_PROBE_REQ);
- os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
- if (dst) {
- os_memcpy(mgmt->da, dst, ETH_ALEN);
- os_memcpy(mgmt->bssid, dst, ETH_ALEN);
- } else {
- os_memset(mgmt->da, 0xff, ETH_ALEN);
- os_memset(mgmt->bssid, 0xff, ETH_ALEN);
- }
- pos = buf + len;
- len += 2 + ssid_len;
- *pos++ = WLAN_EID_SSID;
- *pos++ = ssid_len;
- os_memcpy(pos, ssid, ssid_len);
-
- supp_rates = buf + len;
- len += 2;
- supp_rates[0] = WLAN_EID_SUPP_RATES;
- supp_rates[1] = 0;
- for (i = 0; i < wpa_s->mlme.num_curr_rates; i++) {
- if (esupp_rates) {
- pos = buf + len;
- len++;
- esupp_rates[1]++;
- } else if (supp_rates[1] == 8) {
- esupp_rates = pos;
- esupp_rates[0] = WLAN_EID_EXT_SUPP_RATES;
- esupp_rates[1] = 1;
- pos = &esupp_rates[2];
- len += 3;
- } else {
- pos = buf + len;
- len++;
- supp_rates[1]++;
- }
- *pos++ = wpa_s->mlme.curr_rates[i] / 5;
- }
-
- if (wpa_s->mlme.extra_probe_ie) {
- os_memcpy(pos, wpa_s->mlme.extra_probe_ie,
- wpa_s->mlme.extra_probe_ie_len);
- len += wpa_s->mlme.extra_probe_ie_len;
- }
-
- ieee80211_sta_tx(wpa_s, buf, len);
- os_free(buf);
-}
-
-
-static int ieee80211_sta_wep_configured(struct wpa_supplicant *wpa_s)
-{
-#if 0 /* FIX */
- if (sdata == NULL || sdata->default_key == NULL ||
- sdata->default_key->alg != ALG_WEP)
- return 0;
- return 1;
-#else
- return 0;
-#endif
-}
-
-
-static void ieee80211_auth_completed(struct wpa_supplicant *wpa_s)
-{
- wpa_printf(MSG_DEBUG, "MLME: authenticated");
- wpa_s->mlme.authenticated = 1;
- ieee80211_associate(wpa_s);
-}
-
-
-static void ieee80211_auth_challenge(struct wpa_supplicant *wpa_s,
- struct ieee80211_mgmt *mgmt,
- size_t len,
- struct ieee80211_rx_status *rx_status)
-{
- u8 *pos;
- struct ieee802_11_elems elems;
-
- wpa_printf(MSG_DEBUG, "MLME: replying to auth challenge");
- pos = mgmt->u.auth.variable;
- if (ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems, 0)
- == ParseFailed) {
- wpa_printf(MSG_DEBUG, "MLME: failed to parse Auth(challenge)");
- return;
- }
- if (elems.challenge == NULL) {
- wpa_printf(MSG_DEBUG, "MLME: no challenge IE in shared key "
- "auth frame");
- return;
- }
- ieee80211_send_auth(wpa_s, 3, elems.challenge - 2,
- elems.challenge_len + 2, 1);
-}
-
-
-static void ieee80211_rx_mgmt_auth(struct wpa_supplicant *wpa_s,
- struct ieee80211_mgmt *mgmt,
- size_t len,
- struct ieee80211_rx_status *rx_status)
-{
- struct wpa_ssid *ssid = wpa_s->current_ssid;
- u16 auth_alg, auth_transaction, status_code;
- int adhoc;
-
- adhoc = ssid && ssid->mode == WPAS_MODE_IBSS;
-
- if (wpa_s->mlme.state != IEEE80211_AUTHENTICATE && !adhoc) {
- wpa_printf(MSG_DEBUG, "MLME: authentication frame received "
- "from " MACSTR ", but not in authenticate state - "
- "ignored", MAC2STR(mgmt->sa));
- return;
- }
-
- if (len < 24 + 6) {
- wpa_printf(MSG_DEBUG, "MLME: too short (%lu) authentication "
- "frame received from " MACSTR " - ignored",
- (unsigned long) len, MAC2STR(mgmt->sa));
- return;
- }
-
- if (!adhoc && os_memcmp(wpa_s->bssid, mgmt->sa, ETH_ALEN) != 0) {
- wpa_printf(MSG_DEBUG, "MLME: authentication frame received "
- "from unknown AP (SA=" MACSTR " BSSID=" MACSTR
- ") - ignored",
- MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid));
- return;
- }
-
- if (adhoc && os_memcmp(wpa_s->bssid, mgmt->bssid, ETH_ALEN) != 0) {
- wpa_printf(MSG_DEBUG, "MLME: authentication frame received "
- "from unknown BSSID (SA=" MACSTR " BSSID=" MACSTR
- ") - ignored",
- MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid));
- return;
- }
-
- auth_alg = le_to_host16(mgmt->u.auth.auth_alg);
- auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction);
- status_code = le_to_host16(mgmt->u.auth.status_code);
-
- wpa_printf(MSG_DEBUG, "MLME: RX authentication from " MACSTR
- " (alg=%d transaction=%d status=%d)",
- MAC2STR(mgmt->sa), auth_alg, auth_transaction, status_code);
-
- if (adhoc) {
- /* IEEE 802.11 standard does not require authentication in IBSS
- * networks and most implementations do not seem to use it.
- * However, try to reply to authentication attempts if someone
- * has actually implemented this.
- * TODO: Could implement shared key authentication. */
- if (auth_alg != WLAN_AUTH_OPEN || auth_transaction != 1) {
- wpa_printf(MSG_DEBUG, "MLME: unexpected IBSS "
- "authentication frame (alg=%d "
- "transaction=%d)",
- auth_alg, auth_transaction);
- return;
- }
- ieee80211_send_auth(wpa_s, 2, NULL, 0, 0);
- }
-
- if (auth_alg != wpa_s->mlme.auth_alg ||
- auth_transaction != wpa_s->mlme.auth_transaction) {
- wpa_printf(MSG_DEBUG, "MLME: unexpected authentication frame "
- "(alg=%d transaction=%d)",
- auth_alg, auth_transaction);
- return;
- }
-
- if (status_code != WLAN_STATUS_SUCCESS) {
- wpa_printf(MSG_DEBUG, "MLME: AP denied authentication "
- "(auth_alg=%d code=%d)", wpa_s->mlme.auth_alg,
- status_code);
- if (status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG) {
- const int num_algs = 3;
- u8 algs[num_algs];
- int i, pos;
- algs[0] = algs[1] = algs[2] = 0xff;
- if (wpa_s->mlme.auth_algs & WPA_AUTH_ALG_OPEN)
- algs[0] = WLAN_AUTH_OPEN;
- if (wpa_s->mlme.auth_algs & WPA_AUTH_ALG_SHARED)
- algs[1] = WLAN_AUTH_SHARED_KEY;
- if (wpa_s->mlme.auth_algs & WPA_AUTH_ALG_LEAP)
- algs[2] = WLAN_AUTH_LEAP;
- if (wpa_s->mlme.auth_alg == WLAN_AUTH_OPEN)
- pos = 0;
- else if (wpa_s->mlme.auth_alg == WLAN_AUTH_SHARED_KEY)
- pos = 1;
- else
- pos = 2;
- for (i = 0; i < num_algs; i++) {
- pos++;
- if (pos >= num_algs)
- pos = 0;
- if (algs[pos] == wpa_s->mlme.auth_alg ||
- algs[pos] == 0xff)
- continue;
- if (algs[pos] == WLAN_AUTH_SHARED_KEY &&
- !ieee80211_sta_wep_configured(wpa_s))
- continue;
- wpa_s->mlme.auth_alg = algs[pos];
- wpa_printf(MSG_DEBUG, "MLME: set auth_alg=%d "
- "for next try",
- wpa_s->mlme.auth_alg);
- break;
- }
- }
- return;
- }
-
- switch (wpa_s->mlme.auth_alg) {
- case WLAN_AUTH_OPEN:
- case WLAN_AUTH_LEAP:
- ieee80211_auth_completed(wpa_s);
- break;
- case WLAN_AUTH_SHARED_KEY:
- if (wpa_s->mlme.auth_transaction == 4)
- ieee80211_auth_completed(wpa_s);
- else
- ieee80211_auth_challenge(wpa_s, mgmt, len,
- rx_status);
- break;
-#ifdef CONFIG_IEEE80211R
- case WLAN_AUTH_FT:
- {
- union wpa_event_data data;
- struct wpabuf *ric = NULL;
- os_memset(&data, 0, sizeof(data));
- data.ft_ies.ies = mgmt->u.auth.variable;
- data.ft_ies.ies_len = len -
- (mgmt->u.auth.variable - (u8 *) mgmt);
- os_memcpy(data.ft_ies.target_ap, wpa_s->bssid, ETH_ALEN);
- if (os_strcmp(wpa_s->driver->name, "test") == 0 &&
- wpa_s->mlme.wmm_enabled) {
- ric = wpabuf_alloc(200);
- if (ric) {
- /* Build simple RIC-Request: RDIE | TSPEC */
-
- /* RIC Data (RDIE) */
- wpabuf_put_u8(ric, WLAN_EID_RIC_DATA);
- wpabuf_put_u8(ric, 4);
- wpabuf_put_u8(ric, 0); /* RDIE Identifier */
- wpabuf_put_u8(ric, 1); /* Resource Descriptor
- * Count */
- wpabuf_put_le16(ric, 0); /* Status Code */
-
- /* WMM TSPEC */
- ieee80211_build_tspec(ric);
-
- data.ft_ies.ric_ies = wpabuf_head(ric);
- data.ft_ies.ric_ies_len = wpabuf_len(ric);
- }
- }
-
- wpa_supplicant_event(wpa_s, EVENT_FT_RESPONSE, &data);
- wpabuf_free(ric);
- ieee80211_auth_completed(wpa_s);
- break;
- }
-#endif /* CONFIG_IEEE80211R */
- }
-}
-
-
-static void ieee80211_rx_mgmt_deauth(struct wpa_supplicant *wpa_s,
- struct ieee80211_mgmt *mgmt,
- size_t len,
- struct ieee80211_rx_status *rx_status)
-{
- u16 reason_code;
-
- if (len < 24 + 2) {
- wpa_printf(MSG_DEBUG, "MLME: too short (%lu) deauthentication "
- "frame received from " MACSTR " - ignored",
- (unsigned long) len, MAC2STR(mgmt->sa));
- return;
- }
-
- if (os_memcmp(wpa_s->bssid, mgmt->sa, ETH_ALEN) != 0) {
- wpa_printf(MSG_DEBUG, "MLME: deauthentication frame received "
- "from unknown AP (SA=" MACSTR " BSSID=" MACSTR
- ") - ignored",
- MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid));
- return;
- }
-
- reason_code = le_to_host16(mgmt->u.deauth.reason_code);
-
- wpa_printf(MSG_DEBUG, "MLME: RX deauthentication from " MACSTR
- " (reason=%d)", MAC2STR(mgmt->sa), reason_code);
-
- if (wpa_s->mlme.authenticated)
- wpa_printf(MSG_DEBUG, "MLME: deauthenticated");
-
- if (wpa_s->mlme.state == IEEE80211_AUTHENTICATE ||
- wpa_s->mlme.state == IEEE80211_ASSOCIATE ||
- wpa_s->mlme.state == IEEE80211_ASSOCIATED) {
- wpa_s->mlme.state = IEEE80211_AUTHENTICATE;
- ieee80211_reschedule_timer(wpa_s,
- IEEE80211_RETRY_AUTH_INTERVAL);
- }
-
- ieee80211_set_associated(wpa_s, 0);
- wpa_s->mlme.authenticated = 0;
-}
-
-
-static void ieee80211_rx_mgmt_disassoc(struct wpa_supplicant *wpa_s,
- struct ieee80211_mgmt *mgmt,
- size_t len,
- struct ieee80211_rx_status *rx_status)
-{
- u16 reason_code;
-
- if (len < 24 + 2) {
- wpa_printf(MSG_DEBUG, "MLME: too short (%lu) disassociation "
- "frame received from " MACSTR " - ignored",
- (unsigned long) len, MAC2STR(mgmt->sa));
- return;
- }
-
- if (os_memcmp(wpa_s->bssid, mgmt->sa, ETH_ALEN) != 0) {
- wpa_printf(MSG_DEBUG, "MLME: disassociation frame received "
- "from unknown AP (SA=" MACSTR " BSSID=" MACSTR
- ") - ignored",
- MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid));
- return;
- }
-
- reason_code = le_to_host16(mgmt->u.disassoc.reason_code);
-
- wpa_printf(MSG_DEBUG, "MLME: RX disassociation from " MACSTR
- " (reason=%d)", MAC2STR(mgmt->sa), reason_code);
-
- if (wpa_s->mlme.associated)
- wpa_printf(MSG_DEBUG, "MLME: disassociated");
-
- if (wpa_s->mlme.state == IEEE80211_ASSOCIATED) {
- wpa_s->mlme.state = IEEE80211_ASSOCIATE;
- ieee80211_reschedule_timer(wpa_s,
- IEEE80211_RETRY_AUTH_INTERVAL);
- }
-
- ieee80211_set_associated(wpa_s, 0);
-}
-
-
-static void ieee80211_build_tspec(struct wpabuf *buf)
-{
- struct wmm_tspec_element *tspec;
- int tid, up;
-
- tspec = wpabuf_put(buf, sizeof(*tspec));
- tspec->eid = WLAN_EID_VENDOR_SPECIFIC;
- tspec->length = sizeof(*tspec) - 2;
- tspec->oui[0] = 0x00;
- tspec->oui[1] = 0x50;
- tspec->oui[2] = 0xf2;
- tspec->oui_type = 2;
- tspec->oui_subtype = 2;
- tspec->version = 1;
-
- tid = 1;
- up = 6; /* Voice */
- tspec->ts_info[0] = (tid << 1) |
- (WMM_TSPEC_DIRECTION_BI_DIRECTIONAL << 5) |
- BIT(7);
- tspec->ts_info[1] = up << 3;
- tspec->nominal_msdu_size = host_to_le16(1530);
- tspec->mean_data_rate = host_to_le32(128000); /* bits per second */
- tspec->minimum_phy_rate = host_to_le32(6000000);
- tspec->surplus_bandwidth_allowance = host_to_le16(0x3000); /* 150% */
-}
-
-
-static void ieee80211_tx_addts(struct wpa_supplicant *wpa_s)
-{
- struct wpabuf *buf;
- struct ieee80211_mgmt *mgmt;
- size_t alen;
-
- wpa_printf(MSG_DEBUG, "MLME: Send ADDTS Request for Voice TSPEC");
- mgmt = NULL;
- alen = mgmt->u.action.u.wmm_action.variable - (u8 *) mgmt;
-
- buf = wpabuf_alloc(alen + sizeof(struct wmm_tspec_element));
- if (buf == NULL)
- return;
-
- mgmt = wpabuf_put(buf, alen);
- os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
- os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
- os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
- mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
- WLAN_FC_STYPE_ACTION);
- mgmt->u.action.category = WLAN_ACTION_WMM;
- mgmt->u.action.u.wmm_action.action_code = WMM_ACTION_CODE_ADDTS_REQ;
- mgmt->u.action.u.wmm_action.dialog_token = 1;
- mgmt->u.action.u.wmm_action.status_code = 0;
-
- ieee80211_build_tspec(buf);
-
- ieee80211_sta_tx(wpa_s, wpabuf_head(buf), wpabuf_len(buf));
- wpabuf_free(buf);
-}
-
-
-static void ieee80211_rx_mgmt_assoc_resp(struct wpa_supplicant *wpa_s,
- struct ieee80211_mgmt *mgmt,
- size_t len,
- struct ieee80211_rx_status *rx_status,
- int reassoc)
-{
- u8 rates[32];
- size_t rates_len;
- u16 capab_info, status_code, aid;
- struct ieee802_11_elems elems;
- u8 *pos;
-
- /* AssocResp and ReassocResp have identical structure, so process both
- * of them in this function. */
-
- if (wpa_s->mlme.state != IEEE80211_ASSOCIATE) {
- wpa_printf(MSG_DEBUG, "MLME: association frame received from "
- MACSTR ", but not in associate state - ignored",
- MAC2STR(mgmt->sa));
- return;
- }
-
- if (len < 24 + 6) {
- wpa_printf(MSG_DEBUG, "MLME: too short (%lu) association "
- "frame received from " MACSTR " - ignored",
- (unsigned long) len, MAC2STR(mgmt->sa));
- return;
- }
-
- if (os_memcmp(wpa_s->bssid, mgmt->sa, ETH_ALEN) != 0) {
- wpa_printf(MSG_DEBUG, "MLME: association frame received from "
- "unknown AP (SA=" MACSTR " BSSID=" MACSTR ") - "
- "ignored", MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid));
- return;
- }
-
- capab_info = le_to_host16(mgmt->u.assoc_resp.capab_info);
- status_code = le_to_host16(mgmt->u.assoc_resp.status_code);
- aid = le_to_host16(mgmt->u.assoc_resp.aid);
- if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14)))
- wpa_printf(MSG_DEBUG, "MLME: invalid aid value %d; bits 15:14 "
- "not set", aid);
- aid &= ~(BIT(15) | BIT(14));
-
- wpa_printf(MSG_DEBUG, "MLME: RX %sssocResp from " MACSTR
- " (capab=0x%x status=%d aid=%d)",
- reassoc ? "Rea" : "A", MAC2STR(mgmt->sa),
- capab_info, status_code, aid);
-
- pos = mgmt->u.assoc_resp.variable;
- if (ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems, 0)
- == ParseFailed) {
- wpa_printf(MSG_DEBUG, "MLME: failed to parse AssocResp");
- return;
- }
-
- if (status_code != WLAN_STATUS_SUCCESS) {
- wpa_printf(MSG_DEBUG, "MLME: AP denied association (code=%d)",
- status_code);
-#ifdef CONFIG_IEEE80211W
- if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY &&
- elems.timeout_int && elems.timeout_int_len == 5 &&
- elems.timeout_int[0] == WLAN_TIMEOUT_ASSOC_COMEBACK) {
- u32 tu, ms;
- tu = WPA_GET_LE32(elems.timeout_int + 1);
- ms = tu * 1024 / 1000;
- wpa_printf(MSG_DEBUG, "MLME: AP rejected association "
- "temporarily; comeback duration %u TU "
- "(%u ms)", tu, ms);
- if (ms > IEEE80211_ASSOC_TIMEOUT) {
- wpa_printf(MSG_DEBUG, "MLME: Update timer "
- "based on comeback duration");
- ieee80211_reschedule_timer(wpa_s, ms);
- }
- }
-#endif /* CONFIG_IEEE80211W */
- return;
- }
-
- if (elems.supp_rates == NULL) {
- wpa_printf(MSG_DEBUG, "MLME: no SuppRates element in "
- "AssocResp");
- return;
- }
-
- if (wpa_s->mlme.auth_alg == WLAN_AUTH_FT) {
- if (!reassoc) {
- wpa_printf(MSG_DEBUG, "MLME: AP tried to use "
- "association, not reassociation, response "
- "with FT");
- return;
- }
- if (wpa_ft_validate_reassoc_resp(
- wpa_s->wpa, pos, len - (pos - (u8 *) mgmt),
- mgmt->sa) < 0) {
- wpa_printf(MSG_DEBUG, "MLME: FT validation of Reassoc"
- "Resp failed");
- return;
- }
- } else if (wpa_sm_set_ft_params(wpa_s->wpa, pos,
- len - (pos - (u8 *) mgmt)) < 0)
- return;
-
- wpa_printf(MSG_DEBUG, "MLME: associated");
- wpa_s->mlme.aid = aid;
- wpa_s->mlme.ap_capab = capab_info;
-
- os_free(wpa_s->mlme.assocresp_ies);
- wpa_s->mlme.assocresp_ies_len = len - (pos - (u8 *) mgmt);
- wpa_s->mlme.assocresp_ies = os_malloc(wpa_s->mlme.assocresp_ies_len);
- if (wpa_s->mlme.assocresp_ies) {
- os_memcpy(wpa_s->mlme.assocresp_ies, pos,
- wpa_s->mlme.assocresp_ies_len);
- }
-
- ieee80211_set_associated(wpa_s, 1);
-
- rates_len = elems.supp_rates_len;
- if (rates_len > sizeof(rates))
- rates_len = sizeof(rates);
- os_memcpy(rates, elems.supp_rates, rates_len);
- if (elems.ext_supp_rates) {
- size_t _len = elems.ext_supp_rates_len;
- if (_len > sizeof(rates) - rates_len)
- _len = sizeof(rates) - rates_len;
- os_memcpy(rates + rates_len, elems.ext_supp_rates, _len);
- rates_len += _len;
- }
-
- if (wpa_drv_set_bssid(wpa_s, wpa_s->bssid) < 0) {
- wpa_printf(MSG_DEBUG, "MLME: failed to set BSSID for the "
- "netstack");
- }
- if (wpa_drv_set_ssid(wpa_s, wpa_s->mlme.ssid, wpa_s->mlme.ssid_len) <
- 0) {
- wpa_printf(MSG_DEBUG, "MLME: failed to set SSID for the "
- "netstack");
- }
-
- /* Remove STA entry before adding a new one just in case to avoid
- * problems with existing configuration (e.g., keys). */
- wpa_drv_mlme_remove_sta(wpa_s, wpa_s->bssid);
- if (wpa_drv_mlme_add_sta(wpa_s, wpa_s->bssid, rates, rates_len) < 0) {
- wpa_printf(MSG_DEBUG, "MLME: failed to add STA entry to the "
- "netstack");
- }
-
- if (elems.wmm && wpa_s->mlme.wmm_enabled)
- ieee80211_sta_wmm_params(wpa_s, elems.wmm, elems.wmm_len);
-
- ieee80211_associated(wpa_s);
-
- if (wpa_s->mlme.auth_alg != WLAN_AUTH_FT &&
- os_strcmp(wpa_s->driver->name, "test") == 0 &&
- elems.wmm && wpa_s->mlme.wmm_enabled) {
- /* Test WMM-AC - send ADDTS for WMM TSPEC */
- ieee80211_tx_addts(wpa_s);
- }
-}
-
-
-/* Caller must hold local->sta_bss_lock */
-static void __ieee80211_bss_hash_add(struct wpa_supplicant *wpa_s,
- struct ieee80211_sta_bss *bss)
-{
- bss->hnext = wpa_s->mlme.sta_bss_hash[STA_HASH(bss->bssid)];
- wpa_s->mlme.sta_bss_hash[STA_HASH(bss->bssid)] = bss;
-}
-
-
-/* Caller must hold local->sta_bss_lock */
-static void __ieee80211_bss_hash_del(struct wpa_supplicant *wpa_s,
- struct ieee80211_sta_bss *bss)
-{
- struct ieee80211_sta_bss *b, *prev = NULL;
- b = wpa_s->mlme.sta_bss_hash[STA_HASH(bss->bssid)];
- while (b) {
- if (b == bss) {
- if (prev == NULL) {
- wpa_s->mlme.sta_bss_hash[STA_HASH(bss->bssid)]
- = bss->hnext;
- } else {
- prev->hnext = bss->hnext;
- }
- break;
- }
- prev = b;
- b = b->hnext;
- }
-}
-
-
-static struct ieee80211_sta_bss *
-ieee80211_bss_add(struct wpa_supplicant *wpa_s, const u8 *bssid)
-{
- struct ieee80211_sta_bss *bss;
-
- bss = os_zalloc(sizeof(*bss));
- if (bss == NULL)
- return NULL;
- os_memcpy(bss->bssid, bssid, ETH_ALEN);
-
- /* TODO: order by RSSI? */
- bss->next = wpa_s->mlme.sta_bss_list;
- wpa_s->mlme.sta_bss_list = bss;
- __ieee80211_bss_hash_add(wpa_s, bss);
- return bss;
-}
-
-
-static struct ieee80211_sta_bss *
-ieee80211_bss_get(struct wpa_supplicant *wpa_s, const u8 *bssid)
-{
- struct ieee80211_sta_bss *bss;
-
- bss = wpa_s->mlme.sta_bss_hash[STA_HASH(bssid)];
- while (bss) {
- if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0)
- break;
- bss = bss->hnext;
- }
- return bss;
-}
-
-
-static void ieee80211_bss_free(struct wpa_supplicant *wpa_s,
- struct ieee80211_sta_bss *bss)
-{
- __ieee80211_bss_hash_del(wpa_s, bss);
- os_free(bss->ie);
- os_free(bss->wpa_ie);
- os_free(bss->rsn_ie);
- os_free(bss->wmm_ie);
- os_free(bss->mdie);
- os_free(bss);
-}
-
-
-static void ieee80211_bss_list_deinit(struct wpa_supplicant *wpa_s)
-{
- struct ieee80211_sta_bss *bss, *prev;
-
- bss = wpa_s->mlme.sta_bss_list;
- wpa_s->mlme.sta_bss_list = NULL;
- while (bss) {
- prev = bss;
- bss = bss->next;
- ieee80211_bss_free(wpa_s, prev);
- }
-}
-
-
-static void ieee80211_bss_info(struct wpa_supplicant *wpa_s,
- struct ieee80211_mgmt *mgmt,
- size_t len,
- struct ieee80211_rx_status *rx_status,
- int beacon)
-{
- struct ieee802_11_elems elems;
- size_t baselen;
- int channel, invalid = 0, clen;
- struct ieee80211_sta_bss *bss;
- u64 timestamp;
- u8 *pos, *ie_pos;
- size_t ie_len;
-
- if (!beacon && os_memcmp(mgmt->da, wpa_s->own_addr, ETH_ALEN))
- return; /* ignore ProbeResp to foreign address */
-
-#if 0
- wpa_printf(MSG_MSGDUMP, "MLME: RX %s from " MACSTR " to " MACSTR,
- beacon ? "Beacon" : "Probe Response",
- MAC2STR(mgmt->sa), MAC2STR(mgmt->da));
-#endif
-
- baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt;
- if (baselen > len)
- return;
-
- pos = mgmt->u.beacon.timestamp;
- timestamp = WPA_GET_LE64(pos);
-
-#if 0 /* FIX */
- if (local->conf.mode == IW_MODE_ADHOC && beacon &&
- os_memcmp(mgmt->bssid, local->bssid, ETH_ALEN) == 0) {
-#ifdef IEEE80211_IBSS_DEBUG
- static unsigned long last_tsf_debug = 0;
- u64 tsf;
- if (local->hw->get_tsf)
- tsf = local->hw->get_tsf(local->mdev);
- else
- tsf = -1LLU;
- if (time_after(jiffies, last_tsf_debug + 5 * HZ)) {
- wpa_printf(MSG_DEBUG, "RX beacon SA=" MACSTR " BSSID="
- MACSTR " TSF=0x%llx BCN=0x%llx diff=%lld "
- "@%ld",
- MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid),
- tsf, timestamp, tsf - timestamp, jiffies);
- last_tsf_debug = jiffies;
- }
-#endif /* IEEE80211_IBSS_DEBUG */
- }
-#endif
-
- ie_pos = mgmt->u.beacon.variable;
- ie_len = len - baselen;
- if (ieee802_11_parse_elems(ie_pos, ie_len, &elems, 0) == ParseFailed)
- invalid = 1;
-
-#if 0 /* FIX */
- if (local->conf.mode == IW_MODE_ADHOC && elems.supp_rates &&
- os_memcmp(mgmt->bssid, local->bssid, ETH_ALEN) == 0 &&
- (sta = sta_info_get(local, mgmt->sa))) {
- struct ieee80211_rate *rates;
- size_t num_rates;
- u32 supp_rates, prev_rates;
- int i, j, oper_mode;
-
- rates = local->curr_rates;
- num_rates = local->num_curr_rates;
- oper_mode = wpa_s->mlme.sta_scanning ?
- local->scan_oper_phymode : local->conf.phymode;
- for (i = 0; i < local->hw->num_modes; i++) {
- struct ieee80211_hw_modes *mode = &local->hw->modes[i];
- if (oper_mode == mode->mode) {
- rates = mode->rates;
- num_rates = mode->num_rates;
- break;
- }
- }
-
- supp_rates = 0;
- for (i = 0; i < elems.supp_rates_len +
- elems.ext_supp_rates_len; i++) {
- u8 rate = 0;
- int own_rate;
- if (i < elems.supp_rates_len)
- rate = elems.supp_rates[i];
- else if (elems.ext_supp_rates)
- rate = elems.ext_supp_rates
- [i - elems.supp_rates_len];
- own_rate = 5 * (rate & 0x7f);
- if (oper_mode == MODE_ATHEROS_TURBO)
- own_rate *= 2;
- for (j = 0; j < num_rates; j++)
- if (rates[j].rate == own_rate)
- supp_rates |= BIT(j);
- }
-
- prev_rates = sta->supp_rates;
- sta->supp_rates &= supp_rates;
- if (sta->supp_rates == 0) {
- /* No matching rates - this should not really happen.
- * Make sure that at least one rate is marked
- * supported to avoid issues with TX rate ctrl. */
- sta->supp_rates = wpa_s->mlme.supp_rates_bits;
- }
- if (sta->supp_rates != prev_rates) {
- wpa_printf(MSG_DEBUG, "MLME: updated supp_rates set "
- "for " MACSTR " based on beacon info "
- "(0x%x & 0x%x -> 0x%x)",
- MAC2STR(sta->addr), prev_rates,
- supp_rates, sta->supp_rates);
- }
- sta_info_release(local, sta);
- }
-#endif
-
- if (elems.ssid == NULL)
- return;
-
- if (elems.ds_params && elems.ds_params_len == 1)
- channel = elems.ds_params[0];
- else
- channel = rx_status->channel;
-
- bss = ieee80211_bss_get(wpa_s, mgmt->bssid);
- if (bss == NULL) {
- bss = ieee80211_bss_add(wpa_s, mgmt->bssid);
- if (bss == NULL)
- return;
- } else {
-#if 0
- /* TODO: order by RSSI? */
- spin_lock_bh(&local->sta_bss_lock);
- list_move_tail(&bss->list, &local->sta_bss_list);
- spin_unlock_bh(&local->sta_bss_lock);
-#endif
- }
-
- if (bss->probe_resp && beacon) {
- /* Do not allow beacon to override data from Probe Response. */
- return;
- }
-
- bss->beacon_int = le_to_host16(mgmt->u.beacon.beacon_int);
- bss->capability = le_to_host16(mgmt->u.beacon.capab_info);
-
- if (bss->ie == NULL || bss->ie_len < ie_len) {
- os_free(bss->ie);
- bss->ie = os_malloc(ie_len);
- }
- if (bss->ie) {
- os_memcpy(bss->ie, ie_pos, ie_len);
- bss->ie_len = ie_len;
- }
-
- if (elems.ssid && elems.ssid_len <= MAX_SSID_LEN) {
- os_memcpy(bss->ssid, elems.ssid, elems.ssid_len);
- bss->ssid_len = elems.ssid_len;
- }
-
- bss->supp_rates_len = 0;
- if (elems.supp_rates) {
- clen = IEEE80211_MAX_SUPP_RATES - bss->supp_rates_len;
- if (clen > elems.supp_rates_len)
- clen = elems.supp_rates_len;
- os_memcpy(&bss->supp_rates[bss->supp_rates_len],
- elems.supp_rates, clen);
- bss->supp_rates_len += clen;
- }
- if (elems.ext_supp_rates) {
- clen = IEEE80211_MAX_SUPP_RATES - bss->supp_rates_len;
- if (clen > elems.ext_supp_rates_len)
- clen = elems.ext_supp_rates_len;
- os_memcpy(&bss->supp_rates[bss->supp_rates_len],
- elems.ext_supp_rates, clen);
- bss->supp_rates_len += clen;
- }
-
- if (elems.wpa_ie &&
- (bss->wpa_ie == NULL || bss->wpa_ie_len != elems.wpa_ie_len ||
- os_memcmp(bss->wpa_ie, elems.wpa_ie, elems.wpa_ie_len))) {
- os_free(bss->wpa_ie);
- bss->wpa_ie = os_malloc(elems.wpa_ie_len + 2);
- if (bss->wpa_ie) {
- os_memcpy(bss->wpa_ie, elems.wpa_ie - 2,
- elems.wpa_ie_len + 2);
- bss->wpa_ie_len = elems.wpa_ie_len + 2;
- } else
- bss->wpa_ie_len = 0;
- } else if (!elems.wpa_ie && bss->wpa_ie) {
- os_free(bss->wpa_ie);
- bss->wpa_ie = NULL;
- bss->wpa_ie_len = 0;
- }
-
- if (elems.rsn_ie &&
- (bss->rsn_ie == NULL || bss->rsn_ie_len != elems.rsn_ie_len ||
- os_memcmp(bss->rsn_ie, elems.rsn_ie, elems.rsn_ie_len))) {
- os_free(bss->rsn_ie);
- bss->rsn_ie = os_malloc(elems.rsn_ie_len + 2);
- if (bss->rsn_ie) {
- os_memcpy(bss->rsn_ie, elems.rsn_ie - 2,
- elems.rsn_ie_len + 2);
- bss->rsn_ie_len = elems.rsn_ie_len + 2;
- } else
- bss->rsn_ie_len = 0;
- } else if (!elems.rsn_ie && bss->rsn_ie) {
- os_free(bss->rsn_ie);
- bss->rsn_ie = NULL;
- bss->rsn_ie_len = 0;
- }
-
- if (elems.wmm &&
- (bss->wmm_ie == NULL || bss->wmm_ie_len != elems.wmm_len ||
- os_memcmp(bss->wmm_ie, elems.wmm, elems.wmm_len))) {
- os_free(bss->wmm_ie);
- bss->wmm_ie = os_malloc(elems.wmm_len + 2);
- if (bss->wmm_ie) {
- os_memcpy(bss->wmm_ie, elems.wmm - 2,
- elems.wmm_len + 2);
- bss->wmm_ie_len = elems.wmm_len + 2;
- } else
- bss->wmm_ie_len = 0;
- } else if (!elems.wmm && bss->wmm_ie) {
- os_free(bss->wmm_ie);
- bss->wmm_ie = NULL;
- bss->wmm_ie_len = 0;
- }
-
-#ifdef CONFIG_IEEE80211R
- if (elems.mdie &&
- (bss->mdie == NULL || bss->mdie_len != elems.mdie_len ||
- os_memcmp(bss->mdie, elems.mdie, elems.mdie_len))) {
- os_free(bss->mdie);
- bss->mdie = os_malloc(elems.mdie_len + 2);
- if (bss->mdie) {
- os_memcpy(bss->mdie, elems.mdie - 2,
- elems.mdie_len + 2);
- bss->mdie_len = elems.mdie_len + 2;
- } else
- bss->mdie_len = 0;
- } else if (!elems.mdie && bss->mdie) {
- os_free(bss->mdie);
- bss->mdie = NULL;
- bss->mdie_len = 0;
- }
-#endif /* CONFIG_IEEE80211R */
-
- bss->hw_mode = wpa_s->mlme.phymode;
- bss->channel = channel;
- bss->freq = wpa_s->mlme.freq;
- if (channel != wpa_s->mlme.channel &&
- (wpa_s->mlme.phymode == HOSTAPD_MODE_IEEE80211G ||
- wpa_s->mlme.phymode == HOSTAPD_MODE_IEEE80211B) &&
- channel >= 1 && channel <= 14) {
- static const int freq_list[] = {
- 2412, 2417, 2422, 2427, 2432, 2437, 2442,
- 2447, 2452, 2457, 2462, 2467, 2472, 2484
- };
- /* IEEE 802.11g/b mode can receive packets from neighboring
- * channels, so map the channel into frequency. */
- bss->freq = freq_list[channel - 1];
- }
- bss->timestamp = timestamp;
- os_get_time(&bss->last_update);
- bss->rssi = rx_status->ssi;
- if (!beacon)
- bss->probe_resp++;
-}
-
-
-static void ieee80211_rx_mgmt_probe_resp(struct wpa_supplicant *wpa_s,
- struct ieee80211_mgmt *mgmt,
- size_t len,
- struct ieee80211_rx_status *rx_status)
-{
- ieee80211_bss_info(wpa_s, mgmt, len, rx_status, 0);
-}
-
-
-static void ieee80211_rx_mgmt_beacon(struct wpa_supplicant *wpa_s,
- struct ieee80211_mgmt *mgmt,
- size_t len,
- struct ieee80211_rx_status *rx_status)
-{
- int use_protection;
- size_t baselen;
- struct ieee802_11_elems elems;
-
- ieee80211_bss_info(wpa_s, mgmt, len, rx_status, 1);
-
- if (!wpa_s->mlme.associated ||
- os_memcmp(wpa_s->bssid, mgmt->bssid, ETH_ALEN) != 0)
- return;
-
- /* Process beacon from the current BSS */
- baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt;
- if (baselen > len)
- return;
-
- if (ieee802_11_parse_elems(mgmt->u.beacon.variable, len - baselen,
- &elems, 0) == ParseFailed)
- return;
-
- use_protection = 0;
- if (elems.erp_info && elems.erp_info_len >= 1) {
- use_protection =
- (elems.erp_info[0] & ERP_INFO_USE_PROTECTION) != 0;
- }
-
- if (use_protection != !!wpa_s->mlme.use_protection) {
- wpa_printf(MSG_DEBUG, "MLME: CTS protection %s (BSSID=" MACSTR
- ")",
- use_protection ? "enabled" : "disabled",
- MAC2STR(wpa_s->bssid));
- wpa_s->mlme.use_protection = use_protection ? 1 : 0;
- wpa_s->mlme.cts_protect_erp_frames = use_protection;
- }
-
- if (elems.wmm && wpa_s->mlme.wmm_enabled) {
- ieee80211_sta_wmm_params(wpa_s, elems.wmm,
- elems.wmm_len);
- }
-}
-
-
-static void ieee80211_rx_mgmt_probe_req(struct wpa_supplicant *wpa_s,
- struct ieee80211_mgmt *mgmt,
- size_t len,
- struct ieee80211_rx_status *rx_status)
-{
- int tx_last_beacon, adhoc;
-#if 0 /* FIX */
- struct ieee80211_mgmt *resp;
-#endif
- u8 *pos, *end;
- struct wpa_ssid *ssid = wpa_s->current_ssid;
-
- adhoc = ssid && ssid->mode == WPAS_MODE_IBSS;
-
- if (!adhoc || wpa_s->mlme.state != IEEE80211_IBSS_JOINED ||
- len < 24 + 2 || wpa_s->mlme.probe_resp == NULL)
- return;
-
-#if 0 /* FIX */
- if (local->hw->tx_last_beacon)
- tx_last_beacon = local->hw->tx_last_beacon(local->mdev);
- else
-#endif
- tx_last_beacon = 1;
-
-#ifdef IEEE80211_IBSS_DEBUG
- wpa_printf(MSG_DEBUG, "MLME: RX ProbeReq SA=" MACSTR " DA=" MACSTR
- " BSSID=" MACSTR " (tx_last_beacon=%d)",
- MAC2STR(mgmt->sa), MAC2STR(mgmt->da),
- MAC2STR(mgmt->bssid), tx_last_beacon);
-#endif /* IEEE80211_IBSS_DEBUG */
-
- if (!tx_last_beacon)
- return;
-
- if (os_memcmp(mgmt->bssid, wpa_s->bssid, ETH_ALEN) != 0 &&
- os_memcmp(mgmt->bssid, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) != 0)
- return;
-
- end = ((u8 *) mgmt) + len;
- pos = mgmt->u.probe_req.variable;
- if (pos[0] != WLAN_EID_SSID ||
- pos + 2 + pos[1] > end) {
- wpa_printf(MSG_DEBUG, "MLME: Invalid SSID IE in ProbeReq from "
- MACSTR, MAC2STR(mgmt->sa));
- return;
- }
- if (pos[1] != 0 &&
- (pos[1] != wpa_s->mlme.ssid_len ||
- os_memcmp(pos + 2, wpa_s->mlme.ssid, wpa_s->mlme.ssid_len) != 0))
- {
- /* Ignore ProbeReq for foreign SSID */
- return;
- }
-
-#if 0 /* FIX */
- /* Reply with ProbeResp */
- skb = skb_copy(wpa_s->mlme.probe_resp, GFP_ATOMIC);
- if (skb == NULL)
- return;
-
- resp = (struct ieee80211_mgmt *) skb->data;
- os_memcpy(resp->da, mgmt->sa, ETH_ALEN);
-#ifdef IEEE80211_IBSS_DEBUG
- wpa_printf(MSG_DEBUG, "MLME: Sending ProbeResp to " MACSTR,
- MAC2STR(resp->da));
-#endif /* IEEE80211_IBSS_DEBUG */
- ieee80211_sta_tx(wpa_s, skb, 0, 1);
-#endif
-}
-
-
-#ifdef CONFIG_IEEE80211R
-static void ieee80211_rx_mgmt_ft_action(struct wpa_supplicant *wpa_s,
- struct ieee80211_mgmt *mgmt,
- size_t len,
- struct ieee80211_rx_status *rx_status)
-{
- union wpa_event_data data;
- u16 status;
- u8 *sta_addr, *target_ap_addr;
-
- if (len < 24 + 1 + sizeof(mgmt->u.action.u.ft_action_resp)) {
- wpa_printf(MSG_DEBUG, "MLME: Too short FT Action frame");
- return;
- }
-
- /*
- * Only FT Action Response is needed for now since reservation
- * protocol is not supported.
- */
- if (mgmt->u.action.u.ft_action_resp.action != 2) {
- wpa_printf(MSG_DEBUG, "MLME: Unexpected FT Action %d",
- mgmt->u.action.u.ft_action_resp.action);
- return;
- }
-
- status = le_to_host16(mgmt->u.action.u.ft_action_resp.status_code);
- sta_addr = mgmt->u.action.u.ft_action_resp.sta_addr;
- target_ap_addr = mgmt->u.action.u.ft_action_resp.target_ap_addr;
- wpa_printf(MSG_DEBUG, "MLME: Received FT Action Response: STA " MACSTR
- " TargetAP " MACSTR " Status Code %d",
- MAC2STR(sta_addr), MAC2STR(target_ap_addr), status);
- if (os_memcmp(sta_addr, wpa_s->own_addr, ETH_ALEN) != 0) {
- wpa_printf(MSG_DEBUG, "MLME: Foreign STA Address " MACSTR
- " in FT Action Response", MAC2STR(sta_addr));
- return;
- }
-
- if (status) {
- wpa_printf(MSG_DEBUG, "MLME: FT Action Response indicates "
- "failure (status code %d)", status);
- /* TODO: report error to FT code(?) */
- return;
- }
-
- os_memset(&data, 0, sizeof(data));
- data.ft_ies.ies = mgmt->u.action.u.ft_action_resp.variable;
- data.ft_ies.ies_len = len - (mgmt->u.action.u.ft_action_resp.variable -
- (u8 *) mgmt);
- data.ft_ies.ft_action = 1;
- os_memcpy(data.ft_ies.target_ap, target_ap_addr, ETH_ALEN);
- wpa_supplicant_event(wpa_s, EVENT_FT_RESPONSE, &data);
- /* TODO: should only re-associate, if EVENT_FT_RESPONSE was processed
- * successfully */
- wpa_s->mlme.prev_bssid_set = 1;
- wpa_s->mlme.auth_alg = WLAN_AUTH_FT;
- os_memcpy(wpa_s->mlme.prev_bssid, wpa_s->bssid, ETH_ALEN);
- os_memcpy(wpa_s->bssid, target_ap_addr, ETH_ALEN);
- ieee80211_associate(wpa_s);
-}
-#endif /* CONFIG_IEEE80211R */
-
-
-#ifdef CONFIG_IEEE80211W
-
-/* MLME-SAQuery.response */
-static int ieee80211_sta_send_sa_query_resp(struct wpa_supplicant *wpa_s,
- const u8 *addr, const u8 *trans_id)
-{
- struct ieee80211_mgmt *mgmt;
- int res;
- size_t len;
-
- mgmt = os_zalloc(sizeof(*mgmt));
- if (mgmt == NULL) {
- wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for "
- "SA Query action frame");
- return -1;
- }
-
- len = 24;
- os_memcpy(mgmt->da, addr, ETH_ALEN);
- os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
- os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
- mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
- WLAN_FC_STYPE_ACTION);
- mgmt->u.action.category = WLAN_ACTION_SA_QUERY;
- mgmt->u.action.u.sa_query_resp.action = WLAN_SA_QUERY_RESPONSE;
- os_memcpy(mgmt->u.action.u.sa_query_resp.trans_id, trans_id,
- WLAN_SA_QUERY_TR_ID_LEN);
- len += 1 + sizeof(mgmt->u.action.u.sa_query_resp);
-
- res = ieee80211_sta_tx(wpa_s, (u8 *) mgmt, len);
- os_free(mgmt);
-
- return res;
-}
-
-
-static void ieee80211_rx_mgmt_sa_query_action(
- struct wpa_supplicant *wpa_s, struct ieee80211_mgmt *mgmt, size_t len,
- struct ieee80211_rx_status *rx_status)
-{
- if (len < 24 + 1 + sizeof(mgmt->u.action.u.sa_query_req)) {
- wpa_printf(MSG_DEBUG, "MLME: Too short SA Query Action frame");
- return;
- }
-
- if (mgmt->u.action.u.sa_query_req.action != WLAN_SA_QUERY_REQUEST) {
- wpa_printf(MSG_DEBUG, "MLME: Unexpected SA Query Action %d",
- mgmt->u.action.u.sa_query_req.action);
- return;
- }
-
- if (os_memcmp(mgmt->sa, wpa_s->bssid, ETH_ALEN) != 0) {
- wpa_printf(MSG_DEBUG, "MLME: Ignore SA Query from unknown "
- "source " MACSTR, MAC2STR(mgmt->sa));
- return;
- }
-
- if (wpa_s->mlme.state == IEEE80211_ASSOCIATE) {
- wpa_printf(MSG_DEBUG, "MLME: Ignore SA query request during "
- "association process");
- return;
- }
-
- wpa_printf(MSG_DEBUG, "MLME: Replying to SA Query request");
- ieee80211_sta_send_sa_query_resp(wpa_s, mgmt->sa, mgmt->u.action.u.
- sa_query_req.trans_id);
-}
-
-#endif /* CONFIG_IEEE80211W */
-
-
-static void dump_tspec(struct wmm_tspec_element *tspec)
-{
- int up, psb, dir, tid;
- u16 val;
-
- up = (tspec->ts_info[1] >> 3) & 0x07;
- psb = (tspec->ts_info[1] >> 2) & 0x01;
- dir = (tspec->ts_info[0] >> 5) & 0x03;
- tid = (tspec->ts_info[0] >> 1) & 0x0f;
- wpa_printf(MSG_DEBUG, "WMM: TS Info: UP=%d PSB=%d Direction=%d TID=%d",
- up, psb, dir, tid);
- val = le_to_host16(tspec->nominal_msdu_size);
- wpa_printf(MSG_DEBUG, "WMM: Nominal MSDU Size: %d%s",
- val & 0x7fff, val & 0x8000 ? " (fixed)" : "");
- wpa_printf(MSG_DEBUG, "WMM: Mean Data Rate: %u bps",
- le_to_host32(tspec->mean_data_rate));
- wpa_printf(MSG_DEBUG, "WMM: Minimum PHY Rate: %u bps",
- le_to_host32(tspec->minimum_phy_rate));
- val = le_to_host16(tspec->surplus_bandwidth_allowance);
- wpa_printf(MSG_DEBUG, "WMM: Surplus Bandwidth Allowance: %u.%04u",
- val >> 13, 10000 * (val & 0x1fff) / 0x2000);
- val = le_to_host16(tspec->medium_time);
- wpa_printf(MSG_DEBUG, "WMM: Medium Time: %u (= %u usec/sec)",
- val, 32 * val);
-}
-
-
-static int is_wmm_tspec(const u8 *ie, size_t len)
-{
- const struct wmm_tspec_element *tspec;
-
- if (len < sizeof(*tspec))
- return 0;
-
- tspec = (const struct wmm_tspec_element *) ie;
- if (tspec->eid != WLAN_EID_VENDOR_SPECIFIC ||
- tspec->length < sizeof(*tspec) - 2 ||
- tspec->oui[0] != 0x00 || tspec->oui[1] != 0x50 ||
- tspec->oui[2] != 0xf2 || tspec->oui_type != 2 ||
- tspec->oui_subtype != 2 || tspec->version != 1)
- return 0;
-
- return 1;
-}
-
-
-static void ieee80211_rx_addts_resp(
- struct wpa_supplicant *wpa_s, struct ieee80211_mgmt *mgmt, size_t len,
- size_t var_len)
-{
- struct wmm_tspec_element *tspec;
-
- wpa_printf(MSG_DEBUG, "WMM: Received ADDTS Response");
- wpa_hexdump(MSG_MSGDUMP, "WMM: ADDTS Response IE(s)",
- mgmt->u.action.u.wmm_action.variable, var_len);
- if (!is_wmm_tspec(mgmt->u.action.u.wmm_action.variable, var_len))
- return;
- tspec = (struct wmm_tspec_element *)
- mgmt->u.action.u.wmm_action.variable;
- dump_tspec(tspec);
-}
-
-
-static void ieee80211_rx_delts(
- struct wpa_supplicant *wpa_s, struct ieee80211_mgmt *mgmt, size_t len,
- size_t var_len)
-{
- struct wmm_tspec_element *tspec;
-
- wpa_printf(MSG_DEBUG, "WMM: Received DELTS");
- wpa_hexdump(MSG_MSGDUMP, "WMM: DELTS IE(s)",
- mgmt->u.action.u.wmm_action.variable, var_len);
- if (!is_wmm_tspec(mgmt->u.action.u.wmm_action.variable, var_len))
- return;
- tspec = (struct wmm_tspec_element *)
- mgmt->u.action.u.wmm_action.variable;
- dump_tspec(tspec);
-}
-
-
-static void ieee80211_rx_mgmt_wmm_action(
- struct wpa_supplicant *wpa_s, struct ieee80211_mgmt *mgmt, size_t len,
- struct ieee80211_rx_status *rx_status)
-{
- size_t alen;
-
- alen = mgmt->u.action.u.wmm_action.variable - (u8 *) mgmt;
- if (len < alen) {
- wpa_printf(MSG_DEBUG, "WMM: Received Action frame too short");
- return;
- }
-
- wpa_printf(MSG_DEBUG, "WMM: Received Action frame: Action Code %d, "
- "Dialog Token %d, Status Code %d",
- mgmt->u.action.u.wmm_action.action_code,
- mgmt->u.action.u.wmm_action.dialog_token,
- mgmt->u.action.u.wmm_action.status_code);
-
- switch (mgmt->u.action.u.wmm_action.action_code) {
- case WMM_ACTION_CODE_ADDTS_RESP:
- ieee80211_rx_addts_resp(wpa_s, mgmt, len, len - alen);
- break;
- case WMM_ACTION_CODE_DELTS:
- ieee80211_rx_delts(wpa_s, mgmt, len, len - alen);
- break;
- default:
- wpa_printf(MSG_DEBUG, "WMM: Unsupported Action Code %d",
- mgmt->u.action.u.wmm_action.action_code);
- break;
- }
-}
-
-
-static void ieee80211_rx_mgmt_action(struct wpa_supplicant *wpa_s,
- struct ieee80211_mgmt *mgmt,
- size_t len,
- struct ieee80211_rx_status *rx_status)
-{
- wpa_printf(MSG_DEBUG, "MLME: received Action frame");
-
- if (len < 25)
- return;
-
- switch (mgmt->u.action.category) {
-#ifdef CONFIG_IEEE80211R
- case WLAN_ACTION_FT:
- ieee80211_rx_mgmt_ft_action(wpa_s, mgmt, len, rx_status);
- break;
-#endif /* CONFIG_IEEE80211R */
-#ifdef CONFIG_IEEE80211W
- case WLAN_ACTION_SA_QUERY:
- ieee80211_rx_mgmt_sa_query_action(wpa_s, mgmt, len, rx_status);
- break;
-#endif /* CONFIG_IEEE80211W */
- case WLAN_ACTION_WMM:
- ieee80211_rx_mgmt_wmm_action(wpa_s, mgmt, len, rx_status);
- break;
- case WLAN_ACTION_PUBLIC:
- if (wpa_s->mlme.public_action_cb) {
- wpa_s->mlme.public_action_cb(
- wpa_s->mlme.public_action_cb_ctx,
- (u8 *) mgmt, len, rx_status->freq);
- return;
- }
- break;
- default:
- wpa_printf(MSG_DEBUG, "MLME: unknown Action Category %d",
- mgmt->u.action.category);
- break;
- }
-}
-
-
-static void ieee80211_sta_rx_mgmt(struct wpa_supplicant *wpa_s,
- const u8 *buf, size_t len,
- struct ieee80211_rx_status *rx_status)
-{
- struct ieee80211_mgmt *mgmt;
- u16 fc;
-
- if (len < 24)
- return;
-
- mgmt = (struct ieee80211_mgmt *) buf;
- fc = le_to_host16(mgmt->frame_control);
-
- switch (WLAN_FC_GET_STYPE(fc)) {
- case WLAN_FC_STYPE_PROBE_REQ:
- ieee80211_rx_mgmt_probe_req(wpa_s, mgmt, len, rx_status);
- break;
- case WLAN_FC_STYPE_PROBE_RESP:
- ieee80211_rx_mgmt_probe_resp(wpa_s, mgmt, len, rx_status);
- break;
- case WLAN_FC_STYPE_BEACON:
- ieee80211_rx_mgmt_beacon(wpa_s, mgmt, len, rx_status);
- break;
- case WLAN_FC_STYPE_AUTH:
- ieee80211_rx_mgmt_auth(wpa_s, mgmt, len, rx_status);
- break;
- case WLAN_FC_STYPE_ASSOC_RESP:
- ieee80211_rx_mgmt_assoc_resp(wpa_s, mgmt, len, rx_status, 0);
- break;
- case WLAN_FC_STYPE_REASSOC_RESP:
- ieee80211_rx_mgmt_assoc_resp(wpa_s, mgmt, len, rx_status, 1);
- break;
- case WLAN_FC_STYPE_DEAUTH:
- ieee80211_rx_mgmt_deauth(wpa_s, mgmt, len, rx_status);
- break;
- case WLAN_FC_STYPE_DISASSOC:
- ieee80211_rx_mgmt_disassoc(wpa_s, mgmt, len, rx_status);
- break;
- case WLAN_FC_STYPE_ACTION:
- ieee80211_rx_mgmt_action(wpa_s, mgmt, len, rx_status);
- break;
- default:
- wpa_printf(MSG_DEBUG, "MLME: received unknown management "
- "frame - stype=%d", WLAN_FC_GET_STYPE(fc));
- break;
- }
-}
-
-
-static void ieee80211_sta_rx_scan(struct wpa_supplicant *wpa_s,
- const u8 *buf, size_t len,
- struct ieee80211_rx_status *rx_status)
-{
- struct ieee80211_mgmt *mgmt;
- u16 fc;
-
- if (len < 24)
- return;
-
- mgmt = (struct ieee80211_mgmt *) buf;
- fc = le_to_host16(mgmt->frame_control);
-
- if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT) {
- if (WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_RESP) {
- ieee80211_rx_mgmt_probe_resp(wpa_s, mgmt,
- len, rx_status);
- } else if (WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON) {
- ieee80211_rx_mgmt_beacon(wpa_s, mgmt, len, rx_status);
- }
- }
-}
-
-
-static int ieee80211_sta_active_ibss(struct wpa_supplicant *wpa_s)
-{
- int active = 0;
-
-#if 0 /* FIX */
- list_for_each(ptr, &local->sta_list) {
- sta = list_entry(ptr, struct sta_info, list);
- if (sta->dev == dev &&
- time_after(sta->last_rx + IEEE80211_IBSS_MERGE_INTERVAL,
- jiffies)) {
- active++;
- break;
- }
- }
-#endif
-
- return active;
-}
-
-
-static void ieee80211_sta_expire(struct wpa_supplicant *wpa_s)
-{
-#if 0 /* FIX */
- list_for_each_safe(ptr, n, &local->sta_list) {
- sta = list_entry(ptr, struct sta_info, list);
- if (time_after(jiffies, sta->last_rx +
- IEEE80211_IBSS_INACTIVITY_LIMIT)) {
- wpa_printf(MSG_DEBUG, "MLME: expiring inactive STA "
- MACSTR, MAC2STR(sta->addr));
- sta_info_free(local, sta, 1);
- }
- }
-#endif
-}
-
-
-static void ieee80211_sta_merge_ibss(struct wpa_supplicant *wpa_s)
-{
- struct wpa_driver_scan_params params;
-
- ieee80211_reschedule_timer(wpa_s, IEEE80211_IBSS_MERGE_INTERVAL);
-
- ieee80211_sta_expire(wpa_s);
- if (ieee80211_sta_active_ibss(wpa_s))
- return;
-
- wpa_printf(MSG_DEBUG, "MLME: No active IBSS STAs - trying to scan for "
- "other IBSS networks with same SSID (merge)");
- os_memset(¶ms, 0, sizeof(params));
- params.ssids[0].ssid = wpa_s->mlme.ssid;
- params.ssids[0].ssid_len = wpa_s->mlme.ssid_len;
- params.num_ssids = wpa_s->mlme.ssid_len ? 1 : 0;
- ieee80211_sta_req_scan(wpa_s, ¶ms);
-}
-
-
-static void ieee80211_sta_timer(void *eloop_ctx, void *timeout_ctx)
-{
- struct wpa_supplicant *wpa_s = eloop_ctx;
-
- switch (wpa_s->mlme.state) {
- case IEEE80211_DISABLED:
- break;
- case IEEE80211_AUTHENTICATE:
- ieee80211_authenticate(wpa_s);
- break;
- case IEEE80211_ASSOCIATE:
- ieee80211_associate(wpa_s);
- break;
- case IEEE80211_ASSOCIATED:
- ieee80211_associated(wpa_s);
- break;
- case IEEE80211_IBSS_SEARCH:
- ieee80211_sta_find_ibss(wpa_s);
- break;
- case IEEE80211_IBSS_JOINED:
- ieee80211_sta_merge_ibss(wpa_s);
- break;
- default:
- wpa_printf(MSG_DEBUG, "ieee80211_sta_timer: Unknown state %d",
- wpa_s->mlme.state);
- break;
- }
-
- if (ieee80211_privacy_mismatch(wpa_s)) {
- wpa_printf(MSG_DEBUG, "MLME: privacy configuration mismatch "
- "and mixed-cell disabled - disassociate");
-
- ieee80211_send_disassoc(wpa_s, WLAN_REASON_UNSPECIFIED);
- ieee80211_set_associated(wpa_s, 0);
- }
-}
-
-
-static void ieee80211_sta_new_auth(struct wpa_supplicant *wpa_s)
-{
- struct wpa_ssid *ssid = wpa_s->current_ssid;
- if (ssid && ssid->mode != WPAS_MODE_INFRA)
- return;
-
-#if 0 /* FIX */
- if (local->hw->reset_tsf) {
- /* Reset own TSF to allow time synchronization work. */
- local->hw->reset_tsf(local->mdev);
- }
-#endif
-
- wpa_s->mlme.wmm_last_param_set = -1; /* allow any WMM update */
-
-
- if (wpa_s->mlme.auth_algs & WPA_AUTH_ALG_OPEN)
- wpa_s->mlme.auth_alg = WLAN_AUTH_OPEN;
- else if (wpa_s->mlme.auth_algs & WPA_AUTH_ALG_SHARED)
- wpa_s->mlme.auth_alg = WLAN_AUTH_SHARED_KEY;
- else if (wpa_s->mlme.auth_algs & WPA_AUTH_ALG_LEAP)
- wpa_s->mlme.auth_alg = WLAN_AUTH_LEAP;
- else
- wpa_s->mlme.auth_alg = WLAN_AUTH_OPEN;
- wpa_printf(MSG_DEBUG, "MLME: Initial auth_alg=%d",
- wpa_s->mlme.auth_alg);
- wpa_s->mlme.auth_transaction = -1;
- wpa_s->mlme.auth_tries = wpa_s->mlme.assoc_tries = 0;
- ieee80211_authenticate(wpa_s);
-}
-
-
-static int ieee80211_ibss_allowed(struct wpa_supplicant *wpa_s)
-{
-#if 0 /* FIX */
- int m, c;
-
- for (m = 0; m < local->hw->num_modes; m++) {
- struct ieee80211_hw_modes *mode = &local->hw->modes[m];
- if (mode->mode != local->conf.phymode)
- continue;
- for (c = 0; c < mode->num_channels; c++) {
- struct ieee80211_channel *chan = &mode->channels[c];
- if (chan->flag & IEEE80211_CHAN_W_SCAN &&
- chan->chan == local->conf.channel) {
- if (chan->flag & IEEE80211_CHAN_W_IBSS)
- return 1;
- break;
- }
- }
- }
-#endif
-
- return 0;
-}
-
-
-static int ieee80211_sta_join_ibss(struct wpa_supplicant *wpa_s,
- struct ieee80211_sta_bss *bss)
-{
- int res = 0, rates, done = 0, bssid_changed;
- struct ieee80211_mgmt *mgmt;
-#if 0 /* FIX */
- struct ieee80211_tx_control control;
- struct ieee80211_rate *rate;
- struct rate_control_extra extra;
-#endif
- u8 *pos, *buf;
- size_t len;
-
- /* Remove possible STA entries from other IBSS networks. */
-#if 0 /* FIX */
- sta_info_flush(local, NULL);
-
- if (local->hw->reset_tsf) {
- /* Reset own TSF to allow time synchronization work. */
- local->hw->reset_tsf(local->mdev);
- }
-#endif
- bssid_changed = os_memcmp(wpa_s->bssid, bss->bssid, ETH_ALEN);
- os_memcpy(wpa_s->bssid, bss->bssid, ETH_ALEN);
- if (bssid_changed)
- wpas_notify_bssid_changed(wpa_s);
-
-#if 0 /* FIX */
- local->conf.beacon_int = bss->beacon_int >= 10 ? bss->beacon_int : 10;
-
- sdata->drop_unencrypted = bss->capability &
- host_to_le16(WLAN_CAPABILITY_PRIVACY) ? 1 : 0;
-#endif
-
-#if 0 /* FIX */
- os_memset(&rq, 0, sizeof(rq));
- rq.m = bss->freq * 100000;
- rq.e = 1;
- res = ieee80211_ioctl_siwfreq(wpa_s, NULL, &rq, NULL);
-#endif
-
- if (!ieee80211_ibss_allowed(wpa_s)) {
-#if 0 /* FIX */
- wpa_printf(MSG_DEBUG, "MLME: IBSS not allowed on channel %d "
- "(%d MHz)", local->conf.channel,
- local->conf.freq);
-#endif
- return -1;
- }
-
- /* Set beacon template based on scan results */
- buf = os_malloc(400);
- len = 0;
- do {
- if (buf == NULL)
- break;
-
- mgmt = (struct ieee80211_mgmt *) buf;
- len += 24 + sizeof(mgmt->u.beacon);
- os_memset(mgmt, 0, 24 + sizeof(mgmt->u.beacon));
- mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
- WLAN_FC_STYPE_BEACON);
- os_memset(mgmt->da, 0xff, ETH_ALEN);
- os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
- os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
-#if 0 /* FIX */
- mgmt->u.beacon.beacon_int =
- host_to_le16(local->conf.beacon_int);
-#endif
- mgmt->u.beacon.capab_info = host_to_le16(bss->capability);
-
- pos = buf + len;
- len += 2 + wpa_s->mlme.ssid_len;
- *pos++ = WLAN_EID_SSID;
- *pos++ = wpa_s->mlme.ssid_len;
- os_memcpy(pos, wpa_s->mlme.ssid, wpa_s->mlme.ssid_len);
-
- rates = bss->supp_rates_len;
- if (rates > 8)
- rates = 8;
- pos = buf + len;
- len += 2 + rates;
- *pos++ = WLAN_EID_SUPP_RATES;
- *pos++ = rates;
- os_memcpy(pos, bss->supp_rates, rates);
-
- pos = buf + len;
- len += 2 + 1;
- *pos++ = WLAN_EID_DS_PARAMS;
- *pos++ = 1;
- *pos++ = bss->channel;
-
- pos = buf + len;
- len += 2 + 2;
- *pos++ = WLAN_EID_IBSS_PARAMS;
- *pos++ = 2;
- /* FIX: set ATIM window based on scan results */
- *pos++ = 0;
- *pos++ = 0;
-
- if (bss->supp_rates_len > 8) {
- rates = bss->supp_rates_len - 8;
- pos = buf + len;
- len += 2 + rates;
- *pos++ = WLAN_EID_EXT_SUPP_RATES;
- *pos++ = rates;
- os_memcpy(pos, &bss->supp_rates[8], rates);
- }
-
-#if 0 /* FIX */
- os_memset(&control, 0, sizeof(control));
- control.pkt_type = PKT_PROBE_RESP;
- os_memset(&extra, 0, sizeof(extra));
- extra.endidx = local->num_curr_rates;
- rate = rate_control_get_rate(wpa_s, skb, &extra);
- if (rate == NULL) {
- wpa_printf(MSG_DEBUG, "MLME: Failed to determine TX "
- "rate for IBSS beacon");
- break;
- }
- control.tx_rate = (wpa_s->mlme.short_preamble &&
- (rate->flags & IEEE80211_RATE_PREAMBLE2)) ?
- rate->val2 : rate->val;
- control.antenna_sel = local->conf.antenna_sel;
- control.power_level = local->conf.power_level;
- control.no_ack = 1;
- control.retry_limit = 1;
- control.rts_cts_duration = 0;
-#endif
-
-#if 0 /* FIX */
- wpa_s->mlme.probe_resp = skb_copy(skb, GFP_ATOMIC);
- if (wpa_s->mlme.probe_resp) {
- mgmt = (struct ieee80211_mgmt *)
- wpa_s->mlme.probe_resp->data;
- mgmt->frame_control =
- IEEE80211_FC(WLAN_FC_TYPE_MGMT,
- WLAN_FC_STYPE_PROBE_RESP);
- } else {
- wpa_printf(MSG_DEBUG, "MLME: Could not allocate "
- "ProbeResp template for IBSS");
- }
-
- if (local->hw->beacon_update &&
- local->hw->beacon_update(wpa_s, skb, &control) == 0) {
- wpa_printf(MSG_DEBUG, "MLME: Configured IBSS beacon "
- "template based on scan results");
- skb = NULL;
- }
-
- rates = 0;
- for (i = 0; i < bss->supp_rates_len; i++) {
- int rate = (bss->supp_rates[i] & 0x7f) * 5;
- if (local->conf.phymode == MODE_ATHEROS_TURBO)
- rate *= 2;
- for (j = 0; j < local->num_curr_rates; j++)
- if (local->curr_rates[j] == rate)
- rates |= BIT(j);
- }
- wpa_s->mlme.supp_rates_bits = rates;
-#endif
- done = 1;
- } while (0);
-
- os_free(buf);
- if (!done) {
- wpa_printf(MSG_DEBUG, "MLME: Failed to configure IBSS beacon "
- "template");
- }
-
- wpa_s->mlme.state = IEEE80211_IBSS_JOINED;
- ieee80211_reschedule_timer(wpa_s, IEEE80211_IBSS_MERGE_INTERVAL);
-
- return res;
-}
-
-
-#if 0 /* FIX */
-static int ieee80211_sta_create_ibss(struct wpa_supplicant *wpa_s)
-{
- struct ieee80211_sta_bss *bss;
- u8 bssid[ETH_ALEN], *pos;
- int i;
-
-#if 0
- /* Easier testing, use fixed BSSID. */
- os_memset(bssid, 0xfe, ETH_ALEN);
-#else
- /* Generate random, not broadcast, locally administered BSSID. Mix in
- * own MAC address to make sure that devices that do not have proper
- * random number generator get different BSSID. */
- os_get_random(bssid, ETH_ALEN);
- for (i = 0; i < ETH_ALEN; i++)
- bssid[i] ^= wpa_s->own_addr[i];
- bssid[0] &= ~0x01;
- bssid[0] |= 0x02;
-#endif
-
- wpa_printf(MSG_DEBUG, "MLME: Creating new IBSS network, BSSID "
- MACSTR "", MAC2STR(bssid));
-
- bss = ieee80211_bss_add(wpa_s, bssid);
- if (bss == NULL)
- return -ENOMEM;
-
-#if 0 /* FIX */
- if (local->conf.beacon_int == 0)
- local->conf.beacon_int = 100;
- bss->beacon_int = local->conf.beacon_int;
- bss->hw_mode = local->conf.phymode;
- bss->channel = local->conf.channel;
- bss->freq = local->conf.freq;
-#endif
- os_get_time(&bss->last_update);
- bss->capability = host_to_le16(WLAN_CAPABILITY_IBSS);
-#if 0 /* FIX */
- if (sdata->default_key) {
- bss->capability |= host_to_le16(WLAN_CAPABILITY_PRIVACY);
- } else
- sdata->drop_unencrypted = 0;
- bss->supp_rates_len = local->num_curr_rates;
-#endif
- pos = bss->supp_rates;
-#if 0 /* FIX */
- for (i = 0; i < local->num_curr_rates; i++) {
- int rate = local->curr_rates[i];
- if (local->conf.phymode == MODE_ATHEROS_TURBO)
- rate /= 2;
- *pos++ = (u8) (rate / 5);
- }
-#endif
-
- return ieee80211_sta_join_ibss(wpa_s, bss);
-}
-#endif
-
-
-static int ieee80211_sta_find_ibss(struct wpa_supplicant *wpa_s)
-{
- struct ieee80211_sta_bss *bss;
- int found = 0;
- u8 bssid[ETH_ALEN];
- int active_ibss;
- struct os_time now;
-
- if (wpa_s->mlme.ssid_len == 0)
- return -EINVAL;
-
- active_ibss = ieee80211_sta_active_ibss(wpa_s);
-#ifdef IEEE80211_IBSS_DEBUG
- wpa_printf(MSG_DEBUG, "MLME: sta_find_ibss (active_ibss=%d)",
- active_ibss);
-#endif /* IEEE80211_IBSS_DEBUG */
- for (bss = wpa_s->mlme.sta_bss_list; bss; bss = bss->next) {
- if (wpa_s->mlme.ssid_len != bss->ssid_len ||
- os_memcmp(wpa_s->mlme.ssid, bss->ssid, bss->ssid_len) != 0
- || !(bss->capability & WLAN_CAPABILITY_IBSS))
- continue;
-#ifdef IEEE80211_IBSS_DEBUG
- wpa_printf(MSG_DEBUG, " bssid=" MACSTR " found",
- MAC2STR(bss->bssid));
-#endif /* IEEE80211_IBSS_DEBUG */
- os_memcpy(bssid, bss->bssid, ETH_ALEN);
- found = 1;
- if (active_ibss ||
- os_memcmp(bssid, wpa_s->bssid, ETH_ALEN) != 0)
- break;
- }
-
-#ifdef IEEE80211_IBSS_DEBUG
- wpa_printf(MSG_DEBUG, " sta_find_ibss: selected " MACSTR " current "
- MACSTR, MAC2STR(bssid), MAC2STR(wpa_s->bssid));
-#endif /* IEEE80211_IBSS_DEBUG */
- if (found && os_memcmp(wpa_s->bssid, bssid, ETH_ALEN) != 0 &&
- (bss = ieee80211_bss_get(wpa_s, bssid))) {
- wpa_printf(MSG_DEBUG, "MLME: Selected IBSS BSSID " MACSTR
- " based on configured SSID",
- MAC2STR(bssid));
- return ieee80211_sta_join_ibss(wpa_s, bss);
- }
-#ifdef IEEE80211_IBSS_DEBUG
- wpa_printf(MSG_DEBUG, " did not try to join ibss");
-#endif /* IEEE80211_IBSS_DEBUG */
-
- /* Selected IBSS not found in current scan results - try to scan */
- os_get_time(&now);
-#if 0 /* FIX */
- if (wpa_s->mlme.state == IEEE80211_IBSS_JOINED &&
- !ieee80211_sta_active_ibss(wpa_s)) {
- ieee80211_reschedule_timer(wpa_s,
- IEEE80211_IBSS_MERGE_INTERVAL);
- } else if (time_after(jiffies, wpa_s->mlme.last_scan_completed +
- IEEE80211_SCAN_INTERVAL)) {
- wpa_printf(MSG_DEBUG, "MLME: Trigger new scan to find an IBSS "
- "to join");
- return ieee80211_sta_req_scan(wpa_s->mlme.ssid,
- wpa_s->mlme.ssid_len);
- } else if (wpa_s->mlme.state != IEEE80211_IBSS_JOINED) {
- int interval = IEEE80211_SCAN_INTERVAL;
-
- if (time_after(jiffies, wpa_s->mlme.ibss_join_req +
- IEEE80211_IBSS_JOIN_TIMEOUT)) {
- if (wpa_s->mlme.create_ibss &&
- ieee80211_ibss_allowed(wpa_s))
- return ieee80211_sta_create_ibss(wpa_s);
- if (wpa_s->mlme.create_ibss) {
- wpa_printf(MSG_DEBUG, "MLME: IBSS not allowed "
- "on the configured channel %d "
- "(%d MHz)",
- local->conf.channel,
- local->conf.freq);
- }
-
- /* No IBSS found - decrease scan interval and continue
- * scanning. */
- interval = IEEE80211_SCAN_INTERVAL_SLOW;
- }
-
- wpa_s->mlme.state = IEEE80211_IBSS_SEARCH;
- ieee80211_reschedule_timer(wpa_s, interval);
- return 0;
- }
-#endif
-
- return 0;
-}
-
-
-int ieee80211_sta_get_ssid(struct wpa_supplicant *wpa_s, u8 *ssid,
- size_t *len)
-{
- os_memcpy(ssid, wpa_s->mlme.ssid, wpa_s->mlme.ssid_len);
- *len = wpa_s->mlme.ssid_len;
- return 0;
-}
-
-
-int ieee80211_sta_associate(struct wpa_supplicant *wpa_s,
- struct wpa_driver_associate_params *params)
-{
- struct ieee80211_sta_bss *bss;
- int bssid_changed;
-
- wpa_s->mlme.bssid_set = 0;
- wpa_s->mlme.freq = params->freq;
- if (params->bssid) {
- bssid_changed = os_memcmp(wpa_s->bssid, params->bssid,
- ETH_ALEN);
- os_memcpy(wpa_s->bssid, params->bssid, ETH_ALEN);
- if (bssid_changed)
- wpas_notify_bssid_changed(wpa_s);
-
- if (!is_zero_ether_addr(params->bssid))
- wpa_s->mlme.bssid_set = 1;
- bss = ieee80211_bss_get(wpa_s, wpa_s->bssid);
- if (bss) {
- wpa_s->mlme.phymode = bss->hw_mode;
- wpa_s->mlme.channel = bss->channel;
- wpa_s->mlme.freq = bss->freq;
- }
- }
-
-#if 0 /* FIX */
- /* TODO: This should always be done for IBSS, even if IEEE80211_QOS is
- * not defined. */
- if (local->hw->conf_tx) {
- struct ieee80211_tx_queue_params qparam;
- int i;
-
- os_memset(&qparam, 0, sizeof(qparam));
- /* TODO: are these ok defaults for all hw_modes? */
- qparam.aifs = 2;
- qparam.cw_min =
- local->conf.phymode == MODE_IEEE80211B ? 31 : 15;
- qparam.cw_max = 1023;
- qparam.burst_time = 0;
- for (i = IEEE80211_TX_QUEUE_DATA0; i < NUM_TX_DATA_QUEUES; i++)
- {
- local->hw->conf_tx(wpa_s, i + IEEE80211_TX_QUEUE_DATA0,
- &qparam);
- }
- /* IBSS uses different parameters for Beacon sending */
- qparam.cw_min++;
- qparam.cw_min *= 2;
- qparam.cw_min--;
- local->hw->conf_tx(wpa_s, IEEE80211_TX_QUEUE_BEACON, &qparam);
- }
-#endif
-
- if (wpa_s->mlme.ssid_len != params->ssid_len ||
- os_memcmp(wpa_s->mlme.ssid, params->ssid, params->ssid_len) != 0)
- wpa_s->mlme.prev_bssid_set = 0;
- os_memcpy(wpa_s->mlme.ssid, params->ssid, params->ssid_len);
- os_memset(wpa_s->mlme.ssid + params->ssid_len, 0,
- MAX_SSID_LEN - params->ssid_len);
- wpa_s->mlme.ssid_len = params->ssid_len;
- wpa_s->mlme.ssid_set = 1;
-
- os_free(wpa_s->mlme.extra_ie);
- if (params->wpa_ie == NULL || params->wpa_ie_len == 0) {
- wpa_s->mlme.extra_ie = NULL;
- wpa_s->mlme.extra_ie_len = 0;
- } else {
- wpa_s->mlme.extra_ie = os_malloc(params->wpa_ie_len);
- if (wpa_s->mlme.extra_ie == NULL) {
- wpa_s->mlme.extra_ie_len = 0;
- return -1;
- }
- os_memcpy(wpa_s->mlme.extra_ie, params->wpa_ie,
- params->wpa_ie_len);
- wpa_s->mlme.extra_ie_len = params->wpa_ie_len;
- }
-
- wpa_s->mlme.key_mgmt = params->key_mgmt_suite;
-
- ieee80211_sta_set_channel(wpa_s, wpa_s->mlme.phymode,
- wpa_s->mlme.channel, wpa_s->mlme.freq);
-
- if (params->mode == WPAS_MODE_IBSS && !wpa_s->mlme.bssid_set) {
- os_get_time(&wpa_s->mlme.ibss_join_req);
- wpa_s->mlme.state = IEEE80211_IBSS_SEARCH;
- return ieee80211_sta_find_ibss(wpa_s);
- }
-
- if (wpa_s->mlme.bssid_set)
- ieee80211_sta_new_auth(wpa_s);
-
- return 0;
-}
-
-
-static void ieee80211_sta_save_oper_chan(struct wpa_supplicant *wpa_s)
-{
- wpa_s->mlme.scan_oper_channel = wpa_s->mlme.channel;
- wpa_s->mlme.scan_oper_freq = wpa_s->mlme.freq;
- wpa_s->mlme.scan_oper_phymode = wpa_s->mlme.phymode;
-}
-
-
-static int ieee80211_sta_restore_oper_chan(struct wpa_supplicant *wpa_s)
-{
- wpa_s->mlme.channel = wpa_s->mlme.scan_oper_channel;
- wpa_s->mlme.freq = wpa_s->mlme.scan_oper_freq;
- wpa_s->mlme.phymode = wpa_s->mlme.scan_oper_phymode;
- if (wpa_s->mlme.freq == 0)
- return 0;
- return ieee80211_sta_set_channel(wpa_s, wpa_s->mlme.phymode,
- wpa_s->mlme.channel,
- wpa_s->mlme.freq);
-}
-
-
-static int ieee80211_active_scan(struct wpa_supplicant *wpa_s)
-{
- size_t m;
- int c;
-
- for (m = 0; m < wpa_s->mlme.num_modes; m++) {
- struct hostapd_hw_modes *mode = &wpa_s->mlme.modes[m];
- if ((int) mode->mode != (int) wpa_s->mlme.phymode)
- continue;
- for (c = 0; c < mode->num_channels; c++) {
- struct hostapd_channel_data *chan = &mode->channels[c];
- if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
- chan->chan == wpa_s->mlme.channel) {
- if (!(chan->flag & HOSTAPD_CHAN_PASSIVE_SCAN))
- return 1;
- break;
- }
- }
- }
-
- return 0;
-}
-
-
-static void ieee80211_sta_scan_timer(void *eloop_ctx, void *timeout_ctx)
-{
- struct wpa_supplicant *wpa_s = eloop_ctx;
- struct hostapd_hw_modes *mode;
- struct hostapd_channel_data *chan;
- int skip = 0;
- int timeout = 0;
- struct wpa_ssid *ssid = wpa_s->current_ssid;
- int adhoc;
-
- if (!wpa_s->mlme.sta_scanning || wpa_s->mlme.modes == NULL)
- return;
-
- adhoc = ssid && ssid->mode == 1;
-
- switch (wpa_s->mlme.scan_state) {
- case SCAN_SET_CHANNEL:
- mode = &wpa_s->mlme.modes[wpa_s->mlme.scan_hw_mode_idx];
- if (wpa_s->mlme.scan_hw_mode_idx >=
- (int) wpa_s->mlme.num_modes ||
- (wpa_s->mlme.scan_hw_mode_idx + 1 ==
- (int) wpa_s->mlme.num_modes
- && wpa_s->mlme.scan_channel_idx >= mode->num_channels)) {
- if (ieee80211_sta_restore_oper_chan(wpa_s)) {
- wpa_printf(MSG_DEBUG, "MLME: failed to "
- "restore operational channel after "
- "scan");
- }
- wpa_printf(MSG_DEBUG, "MLME: scan completed");
- wpa_s->mlme.sta_scanning = 0;
- os_get_time(&wpa_s->mlme.last_scan_completed);
- wpa_supplicant_event(wpa_s, EVENT_SCAN_RESULTS, NULL);
- if (adhoc) {
- if (!wpa_s->mlme.bssid_set ||
- (wpa_s->mlme.state ==
- IEEE80211_IBSS_JOINED &&
- !ieee80211_sta_active_ibss(wpa_s)))
- ieee80211_sta_find_ibss(wpa_s);
- }
- return;
- }
- skip = !(wpa_s->mlme.hw_modes & (1 << mode->mode));
- chan = &mode->channels[wpa_s->mlme.scan_channel_idx];
- if ((chan->flag & HOSTAPD_CHAN_DISABLED) ||
- (adhoc && (chan->flag & HOSTAPD_CHAN_NO_IBSS)) ||
- (wpa_s->mlme.hw_modes & (1 << HOSTAPD_MODE_IEEE80211G) &&
- mode->mode == HOSTAPD_MODE_IEEE80211B &&
- wpa_s->mlme.scan_skip_11b))
- skip = 1;
- if (!skip && wpa_s->mlme.scan_freqs) {
- int i, found = 0;
- for (i = 0; wpa_s->mlme.scan_freqs[i]; i++) {
- if (wpa_s->mlme.scan_freqs[i] == chan->freq) {
- found = 1;
- break;
- }
- }
- if (!found)
- skip = 1;
- }
-
- if (!skip) {
- wpa_printf(MSG_MSGDUMP,
- "MLME: scan channel %d (%d MHz)",
- chan->chan, chan->freq);
-
- wpa_s->mlme.channel = chan->chan;
- wpa_s->mlme.freq = chan->freq;
- wpa_s->mlme.phymode = mode->mode;
- if (ieee80211_sta_set_channel(wpa_s, mode->mode,
- chan->chan, chan->freq))
- {
- wpa_printf(MSG_DEBUG, "MLME: failed to set "
- "channel %d (%d MHz) for scan",
- chan->chan, chan->freq);
- skip = 1;
- }
- }
-
- wpa_s->mlme.scan_channel_idx++;
- if (wpa_s->mlme.scan_channel_idx >=
- wpa_s->mlme.modes[wpa_s->mlme.scan_hw_mode_idx].
- num_channels) {
- wpa_s->mlme.scan_hw_mode_idx++;
- wpa_s->mlme.scan_channel_idx = 0;
- }
-
- if (skip) {
- timeout = 0;
- break;
- }
-
- timeout = IEEE80211_PROBE_DELAY;
- wpa_s->mlme.scan_state = SCAN_SEND_PROBE;
- break;
- case SCAN_SEND_PROBE:
- if (ieee80211_active_scan(wpa_s)) {
- ieee80211_send_probe_req(wpa_s, NULL,
- wpa_s->mlme.scan_ssid,
- wpa_s->mlme.scan_ssid_len);
- timeout = IEEE80211_CHANNEL_TIME;
- } else {
- timeout = IEEE80211_PASSIVE_CHANNEL_TIME;
- }
- wpa_s->mlme.scan_state = SCAN_SET_CHANNEL;
- break;
- }
-
- eloop_register_timeout(timeout / 1000, 1000 * (timeout % 1000),
- ieee80211_sta_scan_timer, wpa_s, NULL);
-}
-
-
-int ieee80211_sta_req_scan(struct wpa_supplicant *wpa_s,
- struct wpa_driver_scan_params *params)
-{
- const u8 *ssid = params->ssids[0].ssid;
- size_t ssid_len = params->ssids[0].ssid_len;
-
- if (ssid_len > MAX_SSID_LEN)
- return -1;
-
- /* MLME-SCAN.request (page 118) page 144 (11.1.3.1)
- * BSSType: INFRASTRUCTURE, INDEPENDENT, ANY_BSS
- * BSSID: MACAddress
- * SSID
- * ScanType: ACTIVE, PASSIVE
- * ProbeDelay: delay (in microseconds) to be used prior to transmitting
- * a Probe frame during active scanning
- * ChannelList
- * MinChannelTime (>= ProbeDelay), in TU
- * MaxChannelTime: (>= MinChannelTime), in TU
- */
-
- /* MLME-SCAN.confirm
- * BSSDescriptionSet
- * ResultCode: SUCCESS, INVALID_PARAMETERS
- */
-
- /* TODO: if assoc, move to power save mode for the duration of the
- * scan */
-
- if (wpa_s->mlme.sta_scanning)
- return -1;
-
- wpa_printf(MSG_DEBUG, "MLME: starting scan");
-
- ieee80211_sta_set_probe_req_ie(wpa_s, params->extra_ies,
- params->extra_ies_len);
-
- os_free(wpa_s->mlme.scan_freqs);
- if (params->freqs) {
- int i;
- for (i = 0; params->freqs[i]; i++)
- ;
- wpa_s->mlme.scan_freqs = os_malloc((i + 1) * sizeof(int));
- if (wpa_s->mlme.scan_freqs)
- os_memcpy(wpa_s->mlme.scan_freqs, params->freqs,
- (i + 1) * sizeof(int));
- } else
- wpa_s->mlme.scan_freqs = NULL;
-
- ieee80211_sta_save_oper_chan(wpa_s);
-
- wpa_s->mlme.sta_scanning = 1;
- /* TODO: stop TX queue? */
-
- if (ssid) {
- wpa_s->mlme.scan_ssid_len = ssid_len;
- os_memcpy(wpa_s->mlme.scan_ssid, ssid, ssid_len);
- } else
- wpa_s->mlme.scan_ssid_len = 0;
- wpa_s->mlme.scan_skip_11b = 1; /* FIX: clear this is 11g is not
- * supported */
- wpa_s->mlme.scan_state = SCAN_SET_CHANNEL;
- wpa_s->mlme.scan_hw_mode_idx = 0;
- wpa_s->mlme.scan_channel_idx = 0;
- eloop_register_timeout(0, 1, ieee80211_sta_scan_timer, wpa_s, NULL);
-
- return 0;
-}
-
-
-struct wpa_scan_results *
-ieee80211_sta_get_scan_results(struct wpa_supplicant *wpa_s)
-{
- size_t ap_num = 0;
- struct wpa_scan_results *res;
- struct wpa_scan_res *r;
- struct ieee80211_sta_bss *bss;
-
- res = os_zalloc(sizeof(*res));
- for (bss = wpa_s->mlme.sta_bss_list; bss; bss = bss->next)
- ap_num++;
- res->res = os_zalloc(ap_num * sizeof(struct wpa_scan_res *));
- if (res->res == NULL) {
- os_free(res);
- return NULL;
- }
-
- for (bss = wpa_s->mlme.sta_bss_list; bss; bss = bss->next) {
- r = os_zalloc(sizeof(*r) + bss->ie_len);
- if (r == NULL)
- break;
- os_memcpy(r->bssid, bss->bssid, ETH_ALEN);
- r->freq = bss->freq;
- r->beacon_int = bss->beacon_int;
- r->caps = bss->capability;
- r->level = bss->rssi;
- r->tsf = bss->timestamp;
- if (bss->ie) {
- r->ie_len = bss->ie_len;
- os_memcpy(r + 1, bss->ie, bss->ie_len);
- }
-
- res->res[res->num++] = r;
- }
-
- return res;
-}
-
-
-#if 0 /* FIX */
-struct sta_info * ieee80211_ibss_add_sta(struct wpa_supplicant *wpa_s,
- struct sk_buff *skb, u8 *bssid,
- u8 *addr)
-{
- struct ieee80211_local *local = dev->priv;
- struct list_head *ptr;
- struct sta_info *sta;
- struct wpa_supplicant *sta_dev = NULL;
-
- /* TODO: Could consider removing the least recently used entry and
- * allow new one to be added. */
- if (local->num_sta >= IEEE80211_IBSS_MAX_STA_ENTRIES) {
- if (net_ratelimit()) {
- wpa_printf(MSG_DEBUG, "MLME: No room for a new IBSS "
- "STA entry " MACSTR, MAC2STR(addr));
- }
- return NULL;
- }
-
- spin_lock_bh(&local->sub_if_lock);
- list_for_each(ptr, &local->sub_if_list) {
- sdata = list_entry(ptr, struct ieee80211_sub_if_data, list);
- if (sdata->type == IEEE80211_SUB_IF_TYPE_STA &&
- os_memcmp(bssid, sdata->u.sta.bssid, ETH_ALEN) == 0) {
- sta_dev = sdata->dev;
- break;
- }
- }
- spin_unlock_bh(&local->sub_if_lock);
-
- if (sta_dev == NULL)
- return NULL;
-
- wpa_printf(MSG_DEBUG, "MLME: Adding new IBSS station " MACSTR
- " (dev=%s)", MAC2STR(addr), sta_dev->name);
-
- sta = sta_info_add(wpa_s, addr);
- if (sta == NULL) {
- return NULL;
- }
-
- sta->dev = sta_dev;
- sta->supp_rates = wpa_s->mlme.supp_rates_bits;
-
- rate_control_rate_init(local, sta);
-
- return sta; /* caller will call sta_info_release() */
-}
-#endif
-
-
-int ieee80211_sta_deauthenticate(struct wpa_supplicant *wpa_s, u16 reason)
-{
- wpa_printf(MSG_DEBUG, "MLME: deauthenticate(reason=%d)", reason);
-
- ieee80211_send_deauth(wpa_s, reason);
- ieee80211_set_associated(wpa_s, 0);
- return 0;
-}
-
-
-int ieee80211_sta_disassociate(struct wpa_supplicant *wpa_s, u16 reason)
-{
- wpa_printf(MSG_DEBUG, "MLME: disassociate(reason=%d)", reason);
-
- if (!wpa_s->mlme.associated)
- return -1;
-
- ieee80211_send_disassoc(wpa_s, reason);
- ieee80211_set_associated(wpa_s, 0);
- return 0;
-}
-
-
-void ieee80211_sta_rx(struct wpa_supplicant *wpa_s, const u8 *buf, size_t len,
- struct ieee80211_rx_status *rx_status)
-{
- struct ieee80211_mgmt *mgmt;
- u16 fc;
- const u8 *pos;
-
- /* wpa_hexdump(MSG_MSGDUMP, "MLME: Received frame", buf, len); */
-
- if (wpa_s->mlme.sta_scanning) {
- ieee80211_sta_rx_scan(wpa_s, buf, len, rx_status);
- return;
- }
-
- if (len < 24)
- return;
-
- mgmt = (struct ieee80211_mgmt *) buf;
- fc = le_to_host16(mgmt->frame_control);
-
- if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT)
- ieee80211_sta_rx_mgmt(wpa_s, buf, len, rx_status);
- else if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA) {
- if ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) !=
- WLAN_FC_FROMDS)
- return;
- /* mgmt->sa is actually BSSID for FromDS data frames */
- if (os_memcmp(mgmt->sa, wpa_s->bssid, ETH_ALEN) != 0)
- return;
- /* Skip IEEE 802.11 and LLC headers */
- pos = buf + 24 + 6;
- if (WPA_GET_BE16(pos) != ETH_P_EAPOL)
- return;
- pos += 2;
- /* mgmt->bssid is actually BSSID for SA data frames */
- wpa_supplicant_rx_eapol(wpa_s, mgmt->bssid,
- pos, buf + len - pos);
- }
-}
-
-
-void ieee80211_sta_free_hw_features(struct hostapd_hw_modes *hw_features,
- size_t num_hw_features)
-{
- size_t i;
-
- if (hw_features == NULL)
- return;
-
- for (i = 0; i < num_hw_features; i++) {
- os_free(hw_features[i].channels);
- os_free(hw_features[i].rates);
- }
-
- os_free(hw_features);
-}
-
-
-int ieee80211_sta_init(struct wpa_supplicant *wpa_s)
-{
- u16 num_modes, flags;
-
- wpa_s->mlme.modes = wpa_drv_get_hw_feature_data(wpa_s, &num_modes,
- &flags);
- if (wpa_s->mlme.modes == NULL) {
- wpa_printf(MSG_ERROR, "MLME: Failed to read supported "
- "channels and rates from the driver");
- return -1;
- }
-
- wpa_s->mlme.num_modes = num_modes;
-
- wpa_s->mlme.hw_modes = 1 << HOSTAPD_MODE_IEEE80211A;
- wpa_s->mlme.hw_modes |= 1 << HOSTAPD_MODE_IEEE80211B;
- wpa_s->mlme.hw_modes |= 1 << HOSTAPD_MODE_IEEE80211G;
-
- wpa_s->mlme.wmm_enabled = 1;
-
- return 0;
-}
-
-
-void ieee80211_sta_deinit(struct wpa_supplicant *wpa_s)
-{
- eloop_cancel_timeout(ieee80211_sta_timer, wpa_s, NULL);
- eloop_cancel_timeout(ieee80211_sta_scan_timer, wpa_s, NULL);
- os_free(wpa_s->mlme.extra_ie);
- wpa_s->mlme.extra_ie = NULL;
- os_free(wpa_s->mlme.extra_probe_ie);
- wpa_s->mlme.extra_probe_ie = NULL;
- os_free(wpa_s->mlme.assocreq_ies);
- wpa_s->mlme.assocreq_ies = NULL;
- os_free(wpa_s->mlme.assocresp_ies);
- wpa_s->mlme.assocresp_ies = NULL;
- ieee80211_bss_list_deinit(wpa_s);
- ieee80211_sta_free_hw_features(wpa_s->mlme.modes,
- wpa_s->mlme.num_modes);
-#ifdef CONFIG_IEEE80211R
- os_free(wpa_s->mlme.ft_ies);
- wpa_s->mlme.ft_ies = NULL;
- wpa_s->mlme.ft_ies_len = 0;
-#endif /* CONFIG_IEEE80211R */
-
- os_free(wpa_s->mlme.scan_freqs);
- wpa_s->mlme.scan_freqs = NULL;
-}
-
-
-#ifdef CONFIG_IEEE80211R
-
-int ieee80211_sta_update_ft_ies(struct wpa_supplicant *wpa_s, const u8 *md,
- const u8 *ies, size_t ies_len)
-{
- if (md == NULL) {
- wpa_printf(MSG_DEBUG, "MLME: Clear FT mobility domain");
- os_memset(wpa_s->mlme.current_md, 0, MOBILITY_DOMAIN_ID_LEN);
- } else {
- wpa_printf(MSG_DEBUG, "MLME: Update FT IEs for MD " MACSTR,
- MAC2STR(md));
- os_memcpy(wpa_s->mlme.current_md, md, MOBILITY_DOMAIN_ID_LEN);
- }
-
- wpa_hexdump(MSG_DEBUG, "MLME: FT IEs", ies, ies_len);
- os_free(wpa_s->mlme.ft_ies);
- wpa_s->mlme.ft_ies = os_malloc(ies_len);
- if (wpa_s->mlme.ft_ies == NULL)
- return -1;
- os_memcpy(wpa_s->mlme.ft_ies, ies, ies_len);
- wpa_s->mlme.ft_ies_len = ies_len;
-
- return 0;
-}
-
-
-int ieee80211_sta_send_ft_action(struct wpa_supplicant *wpa_s, u8 action,
- const u8 *target_ap,
- const u8 *ies, size_t ies_len)
-{
- u8 *buf;
- size_t len;
- struct ieee80211_mgmt *mgmt;
- int res;
-
- /*
- * Action frame payload:
- * Category[1] = 6 (Fast BSS Transition)
- * Action[1] = 1 (Fast BSS Transition Request)
- * STA Address
- * Target AP Address
- * FT IEs
- */
-
- buf = os_zalloc(sizeof(*mgmt) + ies_len);
- if (buf == NULL) {
- wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for "
- "FT action frame");
- return -1;
- }
-
- mgmt = (struct ieee80211_mgmt *) buf;
- len = 24;
- os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
- os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
- os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
- mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
- WLAN_FC_STYPE_ACTION);
- mgmt->u.action.category = WLAN_ACTION_FT;
- mgmt->u.action.u.ft_action_req.action = action;
- os_memcpy(mgmt->u.action.u.ft_action_req.sta_addr, wpa_s->own_addr,
- ETH_ALEN);
- os_memcpy(mgmt->u.action.u.ft_action_req.target_ap_addr, target_ap,
- ETH_ALEN);
- os_memcpy(mgmt->u.action.u.ft_action_req.variable, ies, ies_len);
- len += 1 + sizeof(mgmt->u.action.u.ft_action_req) + ies_len;
-
- wpa_printf(MSG_DEBUG, "MLME: Send FT Action Frame: Action=%d "
- "Target AP=" MACSTR " body_len=%lu",
- action, MAC2STR(target_ap), (unsigned long) ies_len);
-
- res = ieee80211_sta_tx(wpa_s, buf, len);
- os_free(buf);
-
- return res;
-}
-
-#endif /* CONFIG_IEEE80211R */
-
-
-static int ieee80211_sta_set_probe_req_ie(struct wpa_supplicant *wpa_s,
- const u8 *ies, size_t ies_len)
-{
- os_free(wpa_s->mlme.extra_probe_ie);
- wpa_s->mlme.extra_probe_ie = NULL;
- wpa_s->mlme.extra_probe_ie_len = 0;
-
- if (ies == NULL)
- return 0;
-
- wpa_s->mlme.extra_probe_ie = os_malloc(ies_len);
- if (wpa_s->mlme.extra_probe_ie == NULL)
- return -1;
-
- os_memcpy(wpa_s->mlme.extra_probe_ie, ies, ies_len);
- wpa_s->mlme.extra_probe_ie_len = ies_len;
-
- return 0;
-}
+++ /dev/null
-/*
- * WPA Supplicant - Client mode MLME
- * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
- * Copyright (c) 2004, Instant802 Networks, Inc.
- * Copyright (c) 2005-2006, Devicescape Software, Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
- */
-
-#ifndef MLME_H
-#define MLME_H
-
-struct wpa_supplicant;
-
-struct ieee80211_rx_status {
- int freq;
- int channel;
- int ssi;
-};
-
-#ifdef CONFIG_CLIENT_MLME
-
-int ieee80211_sta_init(struct wpa_supplicant *wpa_s);
-void ieee80211_sta_deinit(struct wpa_supplicant *wpa_s);
-int ieee80211_sta_req_scan(struct wpa_supplicant *wpa_s,
- struct wpa_driver_scan_params *params);
-int ieee80211_sta_deauthenticate(struct wpa_supplicant *wpa_s, u16 reason);
-int ieee80211_sta_disassociate(struct wpa_supplicant *wpa_s, u16 reason);
-int ieee80211_sta_associate(struct wpa_supplicant *wpa_s,
- struct wpa_driver_associate_params *params);
-int ieee80211_sta_get_ssid(struct wpa_supplicant *wpa_s, u8 *ssid,
- size_t *len);
-void ieee80211_sta_free_hw_features(struct hostapd_hw_modes *hw_features,
- size_t num_hw_features);
-void ieee80211_sta_rx(struct wpa_supplicant *wpa_s, const u8 *buf, size_t len,
- struct ieee80211_rx_status *rx_status);
-struct wpa_scan_results *
-ieee80211_sta_get_scan_results(struct wpa_supplicant *wpa_s);
-int ieee80211_sta_update_ft_ies(struct wpa_supplicant *wpa_s, const u8 *md,
- const u8 *ies, size_t ies_len);
-int ieee80211_sta_send_ft_action(struct wpa_supplicant *wpa_s, u8 action,
- const u8 *target_ap,
- const u8 *ies, size_t ies_len);
-
-#else /* CONFIG_CLIENT_MLME */
-
-static inline int ieee80211_sta_init(struct wpa_supplicant *wpa_s)
-{
- return 0;
-}
-
-static inline void ieee80211_sta_deinit(struct wpa_supplicant *wpa_s)
-{
-}
-
-static inline int ieee80211_sta_req_scan(struct wpa_supplicant *wpa_s,
- struct wpa_driver_scan_params *params)
-{
- return -1;
-}
-
-static inline int ieee80211_sta_deauthenticate(struct wpa_supplicant *wpa_s,
- u16 reason)
-{
- return -1;
-}
-
-static inline int ieee80211_sta_disassociate(struct wpa_supplicant *wpa_s,
- u16 reason)
-{
- return -1;
-}
-
-static inline int
-ieee80211_sta_associate(struct wpa_supplicant *wpa_s,
- struct wpa_driver_associate_params *params)
-{
- return -1;
-}
-
-static inline int ieee80211_sta_get_ssid(struct wpa_supplicant *wpa_s,
- u8 *ssid, size_t *len)
-{
- return -1;
-}
-
-static inline void
-ieee80211_sta_free_hw_features(struct hostapd_hw_modes *hw_features,
- size_t num_hw_features)
-{
-}
-
-static inline void
-ieee80211_sta_rx(struct wpa_supplicant *wpa_s, const u8 *buf, size_t len,
- struct ieee80211_rx_status *rx_status)
-{
-}
-
-static inline struct wpa_scan_results *
-ieee80211_sta_get_scan_results(struct wpa_supplicant *wpa_s)
-{
- return NULL;
-}
-
-static inline int
-ieee80211_sta_update_ft_ies(struct wpa_supplicant *wpa_s, const u8 *md,
- const u8 *ies, size_t ies_len)
-{
- return -1;
-}
-
-static inline int
-ieee80211_sta_send_ft_action(struct wpa_supplicant *wpa_s, u8 action,
- const u8 *target_ap,
- const u8 *ies, size_t ies_len)
-{
- return -1;
-}
-
-#endif /* CONFIG_CLIENT_MLME */
-
-#endif /* MLME_H */
#include "dbus/dbus_common.h"
#include "dbus/dbus_old.h"
#include "dbus/dbus_new.h"
+#include "rsn_supp/wpa.h"
#include "driver_i.h"
#include "scan.h"
+#include "p2p_supplicant.h"
+#include "sme.h"
#include "notify.h"
int wpas_notify_supplicant_initialized(struct wpa_global *global)
/* notify the new DBus API */
wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_STATE);
+
+#ifdef CONFIG_P2P
+ if (new_state == WPA_COMPLETED)
+ wpas_p2p_notif_connected(wpa_s);
+ else if (old_state >= WPA_ASSOCIATED && new_state < WPA_ASSOCIATED)
+ wpas_p2p_notif_disconnected(wpa_s);
+#endif /* CONFIG_P2P */
+
+ sme_state_changed(wpa_s);
+
+#ifdef ANDROID
+ wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_STATE_CHANGE
+ "id=%d state=%d BSSID=" MACSTR,
+ wpa_s->current_ssid ? wpa_s->current_ssid->id : -1,
+ new_state, MAC2STR(wpa_s->pending_bssid));
+#endif /* ANDROID */
}
}
+void wpas_notify_auth_changed(struct wpa_supplicant *wpa_s)
+{
+ wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_CURRENT_AUTH_MODE);
+}
+
+
void wpas_notify_network_enabled_changed(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
}
+void wpas_notify_network_request(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid,
+ enum wpa_ctrl_req_type rtype,
+ const char *default_txt)
+{
+ wpas_dbus_signal_network_request(wpa_s, ssid, rtype, default_txt);
+}
+
+
void wpas_notify_scanning(struct wpa_supplicant *wpa_s)
{
/* notify the old DBus API */
void wpas_notify_network_added(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
- wpas_dbus_register_network(wpa_s, ssid);
+ /*
+ * Networks objects created during any P2P activities should not be
+ * exposed out. They might/will confuse certain non-P2P aware
+ * applications since these network objects won't behave like
+ * regular ones.
+ */
+ if (wpa_s->global->p2p_group_formation != wpa_s)
+ wpas_dbus_register_network(wpa_s, ssid);
+}
+
+
+void wpas_notify_persistent_group_added(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+#ifdef CONFIG_P2P
+ wpas_dbus_register_persistent_group(wpa_s, ssid);
+#endif /* CONFIG_P2P */
+}
+
+
+void wpas_notify_persistent_group_removed(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+#ifdef CONFIG_P2P
+ wpas_dbus_unregister_persistent_group(wpa_s, ssid->id);
+#endif /* CONFIG_P2P */
}
void wpas_notify_network_removed(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
- wpas_dbus_unregister_network(wpa_s, ssid->id);
+ if (wpa_s->wpa)
+ wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
+ if (wpa_s->global->p2p_group_formation != wpa_s)
+ wpas_dbus_unregister_network(wpa_s, ssid->id);
+#ifdef CONFIG_P2P
+ wpas_p2p_network_removed(wpa_s, ssid);
+#endif /* CONFIG_P2P */
}
wpa_supplicant_req_scan(wpa_s, 0, 100000);
}
}
+
+
+#ifdef CONFIG_P2P
+
+void wpas_notify_p2p_device_found(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr, int new_device)
+{
+ if (new_device) {
+ /* Create the new peer object */
+ wpas_dbus_register_peer(wpa_s, dev_addr);
+ }
+
+ /* Notify a new peer has been detected*/
+ wpas_dbus_signal_peer_device_found(wpa_s, dev_addr);
+}
+
+
+void wpas_notify_p2p_device_lost(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr)
+{
+ wpas_dbus_unregister_peer(wpa_s, dev_addr);
+
+ /* Create signal on interface object*/
+ wpas_dbus_signal_peer_device_lost(wpa_s, dev_addr);
+}
+
+
+void wpas_notify_p2p_group_removed(struct wpa_supplicant *wpa_s,
+ const struct wpa_ssid *ssid,
+ const char *role)
+{
+ wpas_dbus_unregister_p2p_group(wpa_s, ssid);
+
+ wpas_dbus_signal_p2p_group_removed(wpa_s, role);
+}
+
+
+void wpas_notify_p2p_go_neg_req(struct wpa_supplicant *wpa_s,
+ const u8 *src, u16 dev_passwd_id)
+{
+ wpas_dbus_signal_p2p_go_neg_req(wpa_s, src, dev_passwd_id);
+}
+
+
+void wpas_notify_p2p_go_neg_completed(struct wpa_supplicant *wpa_s,
+ struct p2p_go_neg_results *res)
+{
+ wpas_dbus_signal_p2p_go_neg_resp(wpa_s, res);
+}
+
+
+void wpas_notify_p2p_invitation_result(struct wpa_supplicant *wpa_s,
+ int status, const u8 *bssid)
+{
+ wpas_dbus_signal_p2p_invitation_result(wpa_s, status, bssid);
+}
+
+
+void wpas_notify_p2p_sd_request(struct wpa_supplicant *wpa_s,
+ int freq, const u8 *sa, u8 dialog_token,
+ u16 update_indic, const u8 *tlvs,
+ size_t tlvs_len)
+{
+ wpas_dbus_signal_p2p_sd_request(wpa_s, freq, sa, dialog_token,
+ update_indic, tlvs, tlvs_len);
+}
+
+
+void wpas_notify_p2p_sd_response(struct wpa_supplicant *wpa_s,
+ const u8 *sa, u16 update_indic,
+ const u8 *tlvs, size_t tlvs_len)
+{
+ wpas_dbus_signal_p2p_sd_response(wpa_s, sa, update_indic,
+ tlvs, tlvs_len);
+}
+
+
+/**
+ * wpas_notify_p2p_provision_discovery - Notification of provision discovery
+ * @dev_addr: Who sent the request or responded to our request.
+ * @request: Will be 1 if request, 0 for response.
+ * @status: Valid only in case of response (0 in case of success)
+ * @config_methods: WPS config methods
+ * @generated_pin: PIN to be displayed in case of WPS_CONFIG_DISPLAY method
+ *
+ * This can be used to notify:
+ * - Requests or responses
+ * - Various config methods
+ * - Failure condition in case of response
+ */
+void wpas_notify_p2p_provision_discovery(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr, int request,
+ enum p2p_prov_disc_status status,
+ u16 config_methods,
+ unsigned int generated_pin)
+{
+ wpas_dbus_signal_p2p_provision_discovery(wpa_s, dev_addr, request,
+ status, config_methods,
+ generated_pin);
+}
+
+
+void wpas_notify_p2p_group_started(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid, int network_id,
+ int client)
+{
+ /* Notify a group has been started */
+ wpas_dbus_register_p2p_group(wpa_s, ssid);
+
+ wpas_dbus_signal_p2p_group_started(wpa_s, ssid, client, network_id);
+}
+
+
+void wpas_notify_p2p_wps_failed(struct wpa_supplicant *wpa_s,
+ struct wps_event_fail *fail)
+{
+ wpas_dbus_signal_p2p_wps_failed(wpa_s, fail);
+}
+
+#endif /* CONFIG_P2P */
+
+
+static void wpas_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s,
+ const u8 *sta,
+ const u8 *p2p_dev_addr)
+{
+#ifdef CONFIG_P2P
+ wpas_p2p_notify_ap_sta_authorized(wpa_s, p2p_dev_addr);
+
+ /*
+ * Register a group member object corresponding to this peer and
+ * emit a PeerJoined signal. This will check if it really is a
+ * P2P group.
+ */
+ wpas_dbus_register_p2p_groupmember(wpa_s, sta);
+
+ /*
+ * Create 'peer-joined' signal on group object -- will also
+ * check P2P itself.
+ */
+ wpas_dbus_signal_p2p_peer_joined(wpa_s, sta);
+#endif /* CONFIG_P2P */
+}
+
+
+static void wpas_notify_ap_sta_deauthorized(struct wpa_supplicant *wpa_s,
+ const u8 *sta)
+{
+#ifdef CONFIG_P2P
+ /*
+ * Unregister a group member object corresponding to this peer
+ * if this is a P2P group.
+ */
+ wpas_dbus_unregister_p2p_groupmember(wpa_s, sta);
+
+ /*
+ * Create 'peer-disconnected' signal on group object if this
+ * is a P2P group.
+ */
+ wpas_dbus_signal_p2p_peer_disconnected(wpa_s, sta);
+#endif /* CONFIG_P2P */
+}
+
+
+void wpas_notify_sta_authorized(struct wpa_supplicant *wpa_s,
+ const u8 *mac_addr, int authorized,
+ const u8 *p2p_dev_addr)
+{
+ if (authorized)
+ wpas_notify_ap_sta_authorized(wpa_s, mac_addr, p2p_dev_addr);
+ else
+ wpas_notify_ap_sta_deauthorized(wpa_s, mac_addr);
+}
+
+
+void wpas_notify_certification(struct wpa_supplicant *wpa_s, int depth,
+ const char *subject, const char *cert_hash,
+ const struct wpabuf *cert)
+{
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_EAP_PEER_CERT
+ "depth=%d subject='%s'%s%s",
+ depth, subject,
+ cert_hash ? " hash=" : "",
+ cert_hash ? cert_hash : "");
+
+ if (cert) {
+ char *cert_hex;
+ size_t len = wpabuf_len(cert) * 2 + 1;
+ cert_hex = os_malloc(len);
+ if (cert_hex) {
+ wpa_snprintf_hex(cert_hex, len, wpabuf_head(cert),
+ wpabuf_len(cert));
+ wpa_msg_ctrl(wpa_s, MSG_INFO,
+ WPA_EVENT_EAP_PEER_CERT
+ "depth=%d subject='%s' cert=%s",
+ depth, subject, cert_hex);
+ os_free(cert_hex);
+ }
+ }
+
+ /* notify the old DBus API */
+ wpa_supplicant_dbus_notify_certification(wpa_s, depth, subject,
+ cert_hash, cert);
+ /* notify the new DBus API */
+ wpas_dbus_signal_certification(wpa_s, depth, subject, cert_hash, cert);
+}
#ifndef NOTIFY_H
#define NOTIFY_H
+#include "p2p/p2p.h"
+
struct wps_credential;
struct wps_event_m2d;
struct wps_event_fail;
void wpas_notify_network_changed(struct wpa_supplicant *wpa_s);
void wpas_notify_ap_scan_changed(struct wpa_supplicant *wpa_s);
void wpas_notify_bssid_changed(struct wpa_supplicant *wpa_s);
+void wpas_notify_auth_changed(struct wpa_supplicant *wpa_s);
void wpas_notify_network_enabled_changed(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid);
void wpas_notify_network_selected(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid);
+void wpas_notify_network_request(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid,
+ enum wpa_ctrl_req_type rtype,
+ const char *default_txt);
void wpas_notify_scanning(struct wpa_supplicant *wpa_s);
void wpas_notify_scan_done(struct wpa_supplicant *wpa_s, int success);
void wpas_notify_scan_results(struct wpa_supplicant *wpa_s);
void wpas_notify_suspend(struct wpa_global *global);
void wpas_notify_resume(struct wpa_global *global);
+void wpas_notify_sta_authorized(struct wpa_supplicant *wpa_s,
+ const u8 *mac_addr, int authorized,
+ const u8 *p2p_dev_addr);
+void wpas_notify_p2p_device_found(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr, int new_device);
+void wpas_notify_p2p_device_lost(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr);
+void wpas_notify_p2p_group_removed(struct wpa_supplicant *wpa_s,
+ const struct wpa_ssid *ssid,
+ const char *role);
+void wpas_notify_p2p_go_neg_req(struct wpa_supplicant *wpa_s,
+ const u8 *src, u16 dev_passwd_id);
+void wpas_notify_p2p_go_neg_completed(struct wpa_supplicant *wpa_s,
+ struct p2p_go_neg_results *res);
+void wpas_notify_p2p_invitation_result(struct wpa_supplicant *wpa_s,
+ int status, const u8 *bssid);
+void wpas_notify_p2p_sd_request(struct wpa_supplicant *wpa_s,
+ int freq, const u8 *sa, u8 dialog_token,
+ u16 update_indic, const u8 *tlvs,
+ size_t tlvs_len);
+void wpas_notify_p2p_sd_response(struct wpa_supplicant *wpa_s,
+ const u8 *sa, u16 update_indic,
+ const u8 *tlvs, size_t tlvs_len);
+void wpas_notify_p2p_provision_discovery(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr, int request,
+ enum p2p_prov_disc_status status,
+ u16 config_methods,
+ unsigned int generated_pin);
+void wpas_notify_p2p_group_started(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid, int network_id,
+ int client);
+void wpas_notify_persistent_group_added(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid);
+void wpas_notify_persistent_group_removed(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid);
+
+void wpas_notify_p2p_wps_failed(struct wpa_supplicant *wpa_s,
+ struct wps_event_fail *fail);
+
+void wpas_notify_certification(struct wpa_supplicant *wpa_s, int depth,
+ const char *subject, const char *cert_hash,
+ const struct wpabuf *cert);
+
#endif /* NOTIFY_H */
--- /dev/null
+/*
+ * wpa_supplicant - Off-channel Action frame TX/RX
+ * Copyright (c) 2009-2010, Atheros Communications
+ * Copyright (c) 2011, Qualcomm Atheros
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "utils/eloop.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "offchannel.h"
+
+
+
+static struct wpa_supplicant *
+wpas_get_tx_interface(struct wpa_supplicant *wpa_s, const u8 *src)
+{
+ struct wpa_supplicant *iface;
+
+ if (os_memcmp(src, wpa_s->own_addr, ETH_ALEN) == 0)
+ return wpa_s;
+
+ /*
+ * Try to find a group interface that matches with the source address.
+ */
+ iface = wpa_s->global->ifaces;
+ while (iface) {
+ if (os_memcmp(wpa_s->pending_action_src,
+ iface->own_addr, ETH_ALEN) == 0)
+ break;
+ iface = iface->next;
+ }
+ if (iface) {
+ wpa_printf(MSG_DEBUG, "P2P: Use group interface %s "
+ "instead of interface %s for Action TX",
+ iface->ifname, wpa_s->ifname);
+ return iface;
+ }
+
+ return wpa_s;
+}
+
+
+static void wpas_send_action_cb(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ struct wpa_supplicant *iface;
+ int res;
+ int without_roc;
+
+ without_roc = wpa_s->pending_action_without_roc;
+ wpa_s->pending_action_without_roc = 0;
+ wpa_printf(MSG_DEBUG, "Off-channel: Send Action callback "
+ "(without_roc=%d pending_action_tx=%p)",
+ without_roc, wpa_s->pending_action_tx);
+
+ if (wpa_s->pending_action_tx == NULL)
+ return;
+
+ /*
+ * This call is likely going to be on the P2P device instance if the
+ * driver uses a separate interface for that purpose. However, some
+ * Action frames are actually sent within a P2P Group and when that is
+ * the case, we need to follow power saving (e.g., GO buffering the
+ * frame for a client in PS mode or a client following the advertised
+ * NoA from its GO). To make that easier for the driver, select the
+ * correct group interface here.
+ */
+ iface = wpas_get_tx_interface(wpa_s, wpa_s->pending_action_src);
+
+ if (wpa_s->off_channel_freq != wpa_s->pending_action_freq &&
+ wpa_s->pending_action_freq != 0 &&
+ wpa_s->pending_action_freq != iface->assoc_freq) {
+ wpa_printf(MSG_DEBUG, "Off-channel: Pending Action frame TX "
+ "waiting for another freq=%u (off_channel_freq=%u "
+ "assoc_freq=%u)",
+ wpa_s->pending_action_freq,
+ wpa_s->off_channel_freq,
+ iface->assoc_freq);
+ if (without_roc && wpa_s->off_channel_freq == 0) {
+ /*
+ * We may get here if wpas_send_action() found us to be
+ * on the correct channel, but remain-on-channel cancel
+ * event was received before getting here.
+ */
+ wpa_printf(MSG_DEBUG, "Off-channel: Schedule "
+ "remain-on-channel to send Action frame");
+ if (wpa_drv_remain_on_channel(
+ wpa_s, wpa_s->pending_action_freq, 200) <
+ 0) {
+ wpa_printf(MSG_DEBUG, "Off-channel: Failed to "
+ "request driver to remain on "
+ "channel (%u MHz) for Action Frame "
+ "TX", wpa_s->pending_action_freq);
+ } else {
+ wpa_s->off_channel_freq = 0;
+ wpa_s->roc_waiting_drv_freq =
+ wpa_s->pending_action_freq;
+ }
+ }
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "Off-channel: Sending pending Action frame to "
+ MACSTR " using interface %s",
+ MAC2STR(wpa_s->pending_action_dst), iface->ifname);
+ res = wpa_drv_send_action(iface, wpa_s->pending_action_freq, 0,
+ wpa_s->pending_action_dst,
+ wpa_s->pending_action_src,
+ wpa_s->pending_action_bssid,
+ wpabuf_head(wpa_s->pending_action_tx),
+ wpabuf_len(wpa_s->pending_action_tx),
+ wpa_s->pending_action_no_cck);
+ if (res) {
+ wpa_printf(MSG_DEBUG, "Off-channel: Failed to send the "
+ "pending Action frame");
+ /*
+ * Use fake TX status event to allow state machines to
+ * continue.
+ */
+ offchannel_send_action_tx_status(
+ wpa_s, wpa_s->pending_action_dst,
+ wpabuf_head(wpa_s->pending_action_tx),
+ wpabuf_len(wpa_s->pending_action_tx),
+ OFFCHANNEL_SEND_ACTION_FAILED);
+ }
+}
+
+
+void offchannel_send_action_tx_status(
+ struct wpa_supplicant *wpa_s, const u8 *dst, const u8 *data,
+ size_t data_len, enum offchannel_send_action_result result)
+{
+ if (wpa_s->pending_action_tx == NULL) {
+ wpa_printf(MSG_DEBUG, "Off-channel: Ignore Action TX status - "
+ "no pending operation");
+ return;
+ }
+
+ if (os_memcmp(dst, wpa_s->pending_action_dst, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG, "Off-channel: Ignore Action TX status - "
+ "unknown destination address");
+ return;
+ }
+
+ wpabuf_free(wpa_s->pending_action_tx);
+ wpa_s->pending_action_tx = NULL;
+
+ wpa_printf(MSG_DEBUG, "Off-channel: TX status result=%d cb=%p",
+ result, wpa_s->pending_action_tx_status_cb);
+
+ if (wpa_s->pending_action_tx_status_cb) {
+ wpa_s->pending_action_tx_status_cb(
+ wpa_s, wpa_s->pending_action_freq,
+ wpa_s->pending_action_dst, wpa_s->pending_action_src,
+ wpa_s->pending_action_bssid,
+ data, data_len, result);
+ }
+}
+
+
+int offchannel_send_action(struct wpa_supplicant *wpa_s, unsigned int freq,
+ const u8 *dst, const u8 *src, const u8 *bssid,
+ const u8 *buf, size_t len, unsigned int wait_time,
+ void (*tx_cb)(struct wpa_supplicant *wpa_s,
+ unsigned int freq, const u8 *dst,
+ const u8 *src, const u8 *bssid,
+ const u8 *data, size_t data_len,
+ enum offchannel_send_action_result
+ result),
+ int no_cck)
+{
+ wpa_printf(MSG_DEBUG, "Off-channel: Send action frame: freq=%d dst="
+ MACSTR " src=" MACSTR " bssid=" MACSTR " len=%d",
+ freq, MAC2STR(dst), MAC2STR(src), MAC2STR(bssid),
+ (int) len);
+
+ wpa_s->pending_action_tx_status_cb = tx_cb;
+
+ if (wpa_s->pending_action_tx) {
+ wpa_printf(MSG_DEBUG, "Off-channel: Dropped pending Action "
+ "frame TX to " MACSTR,
+ MAC2STR(wpa_s->pending_action_dst));
+ wpabuf_free(wpa_s->pending_action_tx);
+ }
+ wpa_s->pending_action_tx = wpabuf_alloc(len);
+ if (wpa_s->pending_action_tx == NULL) {
+ wpa_printf(MSG_DEBUG, "Off-channel: Failed to allocate Action "
+ "frame TX buffer (len=%llu)",
+ (unsigned long long) len);
+ return -1;
+ }
+ wpabuf_put_data(wpa_s->pending_action_tx, buf, len);
+ os_memcpy(wpa_s->pending_action_src, src, ETH_ALEN);
+ os_memcpy(wpa_s->pending_action_dst, dst, ETH_ALEN);
+ os_memcpy(wpa_s->pending_action_bssid, bssid, ETH_ALEN);
+ wpa_s->pending_action_freq = freq;
+ wpa_s->pending_action_no_cck = no_cck;
+
+ if (freq != 0 && wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) {
+ struct wpa_supplicant *iface;
+
+ iface = wpas_get_tx_interface(wpa_s,
+ wpa_s->pending_action_src);
+ wpa_s->action_tx_wait_time = wait_time;
+
+ return wpa_drv_send_action(
+ iface, wpa_s->pending_action_freq,
+ wait_time, wpa_s->pending_action_dst,
+ wpa_s->pending_action_src, wpa_s->pending_action_bssid,
+ wpabuf_head(wpa_s->pending_action_tx),
+ wpabuf_len(wpa_s->pending_action_tx),
+ wpa_s->pending_action_no_cck);
+ }
+
+ if (freq) {
+ struct wpa_supplicant *tx_iface;
+ tx_iface = wpas_get_tx_interface(wpa_s, src);
+ if (tx_iface->assoc_freq == freq) {
+ wpa_printf(MSG_DEBUG, "Off-channel: Already on "
+ "requested channel (TX interface operating "
+ "channel)");
+ freq = 0;
+ }
+ }
+
+ if (wpa_s->off_channel_freq == freq || freq == 0) {
+ wpa_printf(MSG_DEBUG, "Off-channel: Already on requested "
+ "channel; send Action frame immediately");
+ /* TODO: Would there ever be need to extend the current
+ * duration on the channel? */
+ wpa_s->pending_action_without_roc = 1;
+ eloop_cancel_timeout(wpas_send_action_cb, wpa_s, NULL);
+ eloop_register_timeout(0, 0, wpas_send_action_cb, wpa_s, NULL);
+ return 0;
+ }
+ wpa_s->pending_action_without_roc = 0;
+
+ if (wpa_s->roc_waiting_drv_freq == freq) {
+ wpa_printf(MSG_DEBUG, "Off-channel: Already waiting for "
+ "driver to get to frequency %u MHz; continue "
+ "waiting to send the Action frame", freq);
+ return 0;
+ }
+
+ wpa_printf(MSG_DEBUG, "Off-channel: Schedule Action frame to be "
+ "transmitted once the driver gets to the requested "
+ "channel");
+ if (wait_time > wpa_s->max_remain_on_chan)
+ wait_time = wpa_s->max_remain_on_chan;
+ if (wpa_drv_remain_on_channel(wpa_s, freq, wait_time) < 0) {
+ wpa_printf(MSG_DEBUG, "Off-channel: Failed to request driver "
+ "to remain on channel (%u MHz) for Action "
+ "Frame TX", freq);
+ return -1;
+ }
+ wpa_s->off_channel_freq = 0;
+ wpa_s->roc_waiting_drv_freq = freq;
+
+ return 0;
+}
+
+
+void offchannel_send_action_done(struct wpa_supplicant *wpa_s)
+{
+ wpa_printf(MSG_DEBUG, "Off-channel: Action frame sequence done "
+ "notification");
+ wpabuf_free(wpa_s->pending_action_tx);
+ wpa_s->pending_action_tx = NULL;
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX &&
+ wpa_s->action_tx_wait_time)
+ wpa_drv_send_action_cancel_wait(wpa_s);
+
+ if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) {
+ wpa_drv_cancel_remain_on_channel(wpa_s);
+ wpa_s->off_channel_freq = 0;
+ wpa_s->roc_waiting_drv_freq = 0;
+ }
+}
+
+
+void offchannel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+ unsigned int freq, unsigned int duration)
+{
+ wpa_s->roc_waiting_drv_freq = 0;
+ wpa_s->off_channel_freq = freq;
+ wpas_send_action_cb(wpa_s, NULL);
+}
+
+
+void offchannel_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+ unsigned int freq)
+{
+ wpa_s->off_channel_freq = 0;
+}
+
+
+void offchannel_deinit(struct wpa_supplicant *wpa_s)
+{
+ wpabuf_free(wpa_s->pending_action_tx);
+ wpa_s->pending_action_tx = NULL;
+ eloop_cancel_timeout(wpas_send_action_cb, wpa_s, NULL);
+}
--- /dev/null
+/*
+ * wpa_supplicant - Off-channel Action frame TX/RX
+ * Copyright (c) 2009-2010, Atheros Communications
+ * Copyright (c) 2011, Qualcomm Atheros
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef OFFCHANNEL_H
+#define OFFCHANNEL_H
+
+int offchannel_send_action(struct wpa_supplicant *wpa_s, unsigned int freq,
+ const u8 *dst, const u8 *src, const u8 *bssid,
+ const u8 *buf, size_t len, unsigned int wait_time,
+ void (*tx_cb)(struct wpa_supplicant *wpa_s,
+ unsigned int freq, const u8 *dst,
+ const u8 *src, const u8 *bssid,
+ const u8 *data, size_t data_len,
+ enum offchannel_send_action_result
+ result),
+ int no_cck);
+void offchannel_send_action_done(struct wpa_supplicant *wpa_s);
+void offchannel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+ unsigned int freq, unsigned int duration);
+void offchannel_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+ unsigned int freq);
+void offchannel_deinit(struct wpa_supplicant *wpa_s);
+void offchannel_send_action_tx_status(
+ struct wpa_supplicant *wpa_s, const u8 *dst, const u8 *data,
+ size_t data_len, enum offchannel_send_action_result result);
+
+#endif /* OFFCHANNEL_H */
--- /dev/null
+/*
+ * wpa_supplicant - P2P
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eloop.h"
+#include "common/ieee802_11_common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
+#include "wps/wps_i.h"
+#include "p2p/p2p.h"
+#include "ap/hostapd.h"
+#include "ap/ap_config.h"
+#include "ap/p2p_hostapd.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "rsn_supp/wpa.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "ap.h"
+#include "config_ssid.h"
+#include "config.h"
+#include "notify.h"
+#include "scan.h"
+#include "bss.h"
+#include "offchannel.h"
+#include "wps_supplicant.h"
+#include "p2p_supplicant.h"
+
+
+/*
+ * How many times to try to scan to find the GO before giving up on join
+ * request.
+ */
+#define P2P_MAX_JOIN_SCAN_ATTEMPTS 10
+
+#ifndef P2P_MAX_CLIENT_IDLE
+/*
+ * How many seconds to try to reconnect to the GO when connection in P2P client
+ * role has been lost.
+ */
+#define P2P_MAX_CLIENT_IDLE 10
+#endif /* P2P_MAX_CLIENT_IDLE */
+
+
+static void wpas_p2p_long_listen_timeout(void *eloop_ctx, void *timeout_ctx);
+static struct wpa_supplicant *
+wpas_p2p_get_group_iface(struct wpa_supplicant *wpa_s, int addr_allocated,
+ int go);
+static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s);
+static void wpas_p2p_join_scan(void *eloop_ctx, void *timeout_ctx);
+static int wpas_p2p_join(struct wpa_supplicant *wpa_s, const u8 *iface_addr,
+ const u8 *dev_addr, enum p2p_wps_method wps_method);
+static void wpas_p2p_pd_before_join_timeout(void *eloop_ctx,
+ void *timeout_ctx);
+static int wpas_p2p_create_iface(struct wpa_supplicant *wpa_s);
+static void wpas_p2p_cross_connect_setup(struct wpa_supplicant *wpa_s);
+static void wpas_p2p_group_idle_timeout(void *eloop_ctx, void *timeout_ctx);
+static void wpas_p2p_set_group_idle_timeout(struct wpa_supplicant *wpa_s);
+
+
+static void wpas_p2p_scan_res_handler(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_results *scan_res)
+{
+ size_t i;
+
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+ return;
+
+ wpa_printf(MSG_DEBUG, "P2P: Scan results received (%d BSS)",
+ (int) scan_res->num);
+
+ for (i = 0; i < scan_res->num; i++) {
+ struct wpa_scan_res *bss = scan_res->res[i];
+ if (p2p_scan_res_handler(wpa_s->global->p2p, bss->bssid,
+ bss->freq, bss->level,
+ (const u8 *) (bss + 1),
+ bss->ie_len) > 0)
+ break;
+ }
+
+ p2p_scan_res_handled(wpa_s->global->p2p);
+}
+
+
+static int wpas_p2p_scan(void *ctx, enum p2p_scan_type type, int freq,
+ unsigned int num_req_dev_types,
+ const u8 *req_dev_types, const u8 *dev_id)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ struct wpa_driver_scan_params params;
+ int ret;
+ struct wpabuf *wps_ie, *ies;
+ int social_channels[] = { 2412, 2437, 2462, 0, 0 };
+ size_t ielen;
+ int was_in_p2p_scan;
+
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+ return -1;
+
+ os_memset(¶ms, 0, sizeof(params));
+
+ /* P2P Wildcard SSID */
+ params.num_ssids = 1;
+ params.ssids[0].ssid = (u8 *) P2P_WILDCARD_SSID;
+ params.ssids[0].ssid_len = P2P_WILDCARD_SSID_LEN;
+
+ wpa_s->wps->dev.p2p = 1;
+ wps_ie = wps_build_probe_req_ie(0, &wpa_s->wps->dev, wpa_s->wps->uuid,
+ WPS_REQ_ENROLLEE,
+ num_req_dev_types, req_dev_types);
+ if (wps_ie == NULL)
+ return -1;
+
+ ielen = p2p_scan_ie_buf_len(wpa_s->global->p2p);
+ ies = wpabuf_alloc(wpabuf_len(wps_ie) + ielen);
+ if (ies == NULL) {
+ wpabuf_free(wps_ie);
+ return -1;
+ }
+ wpabuf_put_buf(ies, wps_ie);
+ wpabuf_free(wps_ie);
+
+ p2p_scan_ie(wpa_s->global->p2p, ies, dev_id);
+
+ params.p2p_probe = 1;
+ params.extra_ies = wpabuf_head(ies);
+ params.extra_ies_len = wpabuf_len(ies);
+
+ switch (type) {
+ case P2P_SCAN_SOCIAL:
+ params.freqs = social_channels;
+ break;
+ case P2P_SCAN_FULL:
+ break;
+ case P2P_SCAN_SPECIFIC:
+ social_channels[0] = freq;
+ social_channels[1] = 0;
+ params.freqs = social_channels;
+ break;
+ case P2P_SCAN_SOCIAL_PLUS_ONE:
+ social_channels[3] = freq;
+ params.freqs = social_channels;
+ break;
+ }
+
+ was_in_p2p_scan = wpa_s->scan_res_handler == wpas_p2p_scan_res_handler;
+ wpa_s->scan_res_handler = wpas_p2p_scan_res_handler;
+ ret = wpa_drv_scan(wpa_s, ¶ms);
+
+ wpabuf_free(ies);
+
+ if (ret) {
+ wpa_s->scan_res_handler = NULL;
+ if (wpa_s->scanning || was_in_p2p_scan) {
+ wpa_s->p2p_cb_on_scan_complete = 1;
+ ret = 1;
+ }
+ }
+
+ return ret;
+}
+
+
+static enum wpa_driver_if_type wpas_p2p_if_type(int p2p_group_interface)
+{
+ switch (p2p_group_interface) {
+ case P2P_GROUP_INTERFACE_PENDING:
+ return WPA_IF_P2P_GROUP;
+ case P2P_GROUP_INTERFACE_GO:
+ return WPA_IF_P2P_GO;
+ case P2P_GROUP_INTERFACE_CLIENT:
+ return WPA_IF_P2P_CLIENT;
+ }
+
+ return WPA_IF_P2P_GROUP;
+}
+
+
+static struct wpa_supplicant * wpas_get_p2p_group(struct wpa_supplicant *wpa_s,
+ const u8 *ssid,
+ size_t ssid_len, int *go)
+{
+ struct wpa_ssid *s;
+
+ for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+ for (s = wpa_s->conf->ssid; s; s = s->next) {
+ if (s->disabled != 0 || !s->p2p_group ||
+ s->ssid_len != ssid_len ||
+ os_memcmp(ssid, s->ssid, ssid_len) != 0)
+ continue;
+ if (s->mode == WPAS_MODE_P2P_GO &&
+ s != wpa_s->current_ssid)
+ continue;
+ if (go)
+ *go = s->mode == WPAS_MODE_P2P_GO;
+ return wpa_s;
+ }
+ }
+
+ return NULL;
+}
+
+
+static void wpas_p2p_group_delete(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_ssid *ssid;
+ char *gtype;
+ const char *reason;
+
+ ssid = wpa_s->current_ssid;
+ if (ssid == NULL) {
+ /*
+ * The current SSID was not known, but there may still be a
+ * pending P2P group interface waiting for provisioning.
+ */
+ ssid = wpa_s->conf->ssid;
+ while (ssid) {
+ if (ssid->p2p_group &&
+ (ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION ||
+ (ssid->key_mgmt & WPA_KEY_MGMT_WPS)))
+ break;
+ ssid = ssid->next;
+ }
+ }
+ if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_GO)
+ gtype = "GO";
+ else if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT ||
+ (ssid && ssid->mode == WPAS_MODE_INFRA)) {
+ wpa_s->reassociate = 0;
+ wpa_s->disconnected = 1;
+ wpa_supplicant_deauthenticate(wpa_s,
+ WLAN_REASON_DEAUTH_LEAVING);
+ gtype = "client";
+ } else
+ gtype = "GO";
+ if (wpa_s->cross_connect_in_use) {
+ wpa_s->cross_connect_in_use = 0;
+ wpa_msg(wpa_s->parent, MSG_INFO,
+ P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s",
+ wpa_s->ifname, wpa_s->cross_connect_uplink);
+ }
+ switch (wpa_s->removal_reason) {
+ case P2P_GROUP_REMOVAL_REQUESTED:
+ reason = " reason=REQUESTED";
+ break;
+ case P2P_GROUP_REMOVAL_IDLE_TIMEOUT:
+ reason = " reason=IDLE";
+ break;
+ case P2P_GROUP_REMOVAL_UNAVAILABLE:
+ reason = " reason=UNAVAILABLE";
+ break;
+ default:
+ reason = "";
+ break;
+ }
+ wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_REMOVED "%s %s%s",
+ wpa_s->ifname, gtype, reason);
+
+ eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL);
+
+ if (ssid)
+ wpas_notify_p2p_group_removed(wpa_s, ssid, gtype);
+
+ if (wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) {
+ struct wpa_global *global;
+ char *ifname;
+ enum wpa_driver_if_type type;
+ wpa_printf(MSG_DEBUG, "P2P: Remove group interface %s",
+ wpa_s->ifname);
+ global = wpa_s->global;
+ ifname = os_strdup(wpa_s->ifname);
+ type = wpas_p2p_if_type(wpa_s->p2p_group_interface);
+ wpa_supplicant_remove_iface(wpa_s->global, wpa_s);
+ wpa_s = global->ifaces;
+ if (wpa_s && ifname)
+ wpa_drv_if_remove(wpa_s, type, ifname);
+ os_free(ifname);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "P2P: Remove temporary group network");
+ if (ssid && (ssid->p2p_group ||
+ ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION ||
+ (ssid->key_mgmt & WPA_KEY_MGMT_WPS))) {
+ int id = ssid->id;
+ if (ssid == wpa_s->current_ssid) {
+ wpa_sm_set_config(wpa_s->wpa, NULL);
+ eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
+ wpa_s->current_ssid = NULL;
+ }
+ /*
+ * Networks objects created during any P2P activities are not
+ * exposed out as they might/will confuse certain non-P2P aware
+ * applications since these network objects won't behave like
+ * regular ones.
+ *
+ * Likewise, we don't send out network removed signals for such
+ * network objects.
+ */
+ wpa_config_remove_network(wpa_s->conf, id);
+ wpa_supplicant_clear_status(wpa_s);
+ wpa_supplicant_cancel_sched_scan(wpa_s);
+ } else {
+ wpa_printf(MSG_DEBUG, "P2P: Temporary group network not "
+ "found");
+ }
+ if (wpa_s->ap_iface)
+ wpa_supplicant_ap_deinit(wpa_s);
+ else
+ wpa_drv_deinit_p2p_cli(wpa_s);
+}
+
+
+static int wpas_p2p_persistent_group(struct wpa_supplicant *wpa_s,
+ u8 *go_dev_addr,
+ const u8 *ssid, size_t ssid_len)
+{
+ struct wpa_bss *bss;
+ const u8 *bssid;
+ struct wpabuf *p2p;
+ u8 group_capab;
+ const u8 *addr;
+
+ if (wpa_s->go_params)
+ bssid = wpa_s->go_params->peer_interface_addr;
+ else
+ bssid = wpa_s->bssid;
+
+ bss = wpa_bss_get(wpa_s, bssid, ssid, ssid_len);
+ if (bss == NULL) {
+ u8 iface_addr[ETH_ALEN];
+ if (p2p_get_interface_addr(wpa_s->global->p2p, bssid,
+ iface_addr) == 0)
+ bss = wpa_bss_get(wpa_s, iface_addr, ssid, ssid_len);
+ }
+ if (bss == NULL) {
+ wpa_printf(MSG_DEBUG, "P2P: Could not figure out whether "
+ "group is persistent - BSS " MACSTR " not found",
+ MAC2STR(bssid));
+ return 0;
+ }
+
+ p2p = wpa_bss_get_vendor_ie_multi(bss, P2P_IE_VENDOR_TYPE);
+ if (p2p == NULL) {
+ wpa_printf(MSG_DEBUG, "P2P: Could not figure out whether "
+ "group is persistent - BSS " MACSTR
+ " did not include P2P IE", MAC2STR(bssid));
+ wpa_hexdump(MSG_DEBUG, "P2P: Probe Response IEs",
+ (u8 *) (bss + 1), bss->ie_len);
+ wpa_hexdump(MSG_DEBUG, "P2P: Beacon IEs",
+ ((u8 *) bss + 1) + bss->ie_len,
+ bss->beacon_ie_len);
+ return 0;
+ }
+
+ group_capab = p2p_get_group_capab(p2p);
+ addr = p2p_get_go_dev_addr(p2p);
+ wpa_printf(MSG_DEBUG, "P2P: Checking whether group is persistent: "
+ "group_capab=0x%x", group_capab);
+ if (addr) {
+ os_memcpy(go_dev_addr, addr, ETH_ALEN);
+ wpa_printf(MSG_DEBUG, "P2P: GO Device Address " MACSTR,
+ MAC2STR(addr));
+ } else
+ os_memset(go_dev_addr, 0, ETH_ALEN);
+ wpabuf_free(p2p);
+
+ wpa_printf(MSG_DEBUG, "P2P: BSS " MACSTR " group_capab=0x%x "
+ "go_dev_addr=" MACSTR,
+ MAC2STR(bssid), group_capab, MAC2STR(go_dev_addr));
+
+ return group_capab & P2P_GROUP_CAPAB_PERSISTENT_GROUP;
+}
+
+
+static int wpas_p2p_store_persistent_group(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid,
+ const u8 *go_dev_addr)
+{
+ struct wpa_ssid *s;
+ int changed = 0;
+
+ wpa_printf(MSG_DEBUG, "P2P: Storing credentials for a persistent "
+ "group (GO Dev Addr " MACSTR ")", MAC2STR(go_dev_addr));
+ for (s = wpa_s->conf->ssid; s; s = s->next) {
+ if (s->disabled == 2 &&
+ os_memcmp(go_dev_addr, s->bssid, ETH_ALEN) == 0 &&
+ s->ssid_len == ssid->ssid_len &&
+ os_memcmp(ssid->ssid, s->ssid, ssid->ssid_len) == 0)
+ break;
+ }
+
+ if (s) {
+ wpa_printf(MSG_DEBUG, "P2P: Update existing persistent group "
+ "entry");
+ if (ssid->passphrase && !s->passphrase)
+ changed = 1;
+ else if (ssid->passphrase && s->passphrase &&
+ os_strcmp(ssid->passphrase, s->passphrase) != 0)
+ changed = 1;
+ } else {
+ wpa_printf(MSG_DEBUG, "P2P: Create a new persistent group "
+ "entry");
+ changed = 1;
+ s = wpa_config_add_network(wpa_s->conf);
+ if (s == NULL)
+ return -1;
+
+ /*
+ * Instead of network_added we emit persistent_group_added
+ * notification. Also to keep the defense checks in
+ * persistent_group obj registration method, we set the
+ * relevant flags in s to designate it as a persistent group.
+ */
+ s->p2p_group = 1;
+ s->p2p_persistent_group = 1;
+ wpas_notify_persistent_group_added(wpa_s, s);
+ wpa_config_set_network_defaults(s);
+ }
+
+ s->p2p_group = 1;
+ s->p2p_persistent_group = 1;
+ s->disabled = 2;
+ s->bssid_set = 1;
+ os_memcpy(s->bssid, go_dev_addr, ETH_ALEN);
+ s->mode = ssid->mode;
+ s->auth_alg = WPA_AUTH_ALG_OPEN;
+ s->key_mgmt = WPA_KEY_MGMT_PSK;
+ s->proto = WPA_PROTO_RSN;
+ s->pairwise_cipher = WPA_CIPHER_CCMP;
+ s->export_keys = 1;
+ if (ssid->passphrase) {
+ os_free(s->passphrase);
+ s->passphrase = os_strdup(ssid->passphrase);
+ }
+ if (ssid->psk_set) {
+ s->psk_set = 1;
+ os_memcpy(s->psk, ssid->psk, 32);
+ }
+ if (s->passphrase && !s->psk_set)
+ wpa_config_update_psk(s);
+ if (s->ssid == NULL || s->ssid_len < ssid->ssid_len) {
+ os_free(s->ssid);
+ s->ssid = os_malloc(ssid->ssid_len);
+ }
+ if (s->ssid) {
+ s->ssid_len = ssid->ssid_len;
+ os_memcpy(s->ssid, ssid->ssid, s->ssid_len);
+ }
+
+#ifndef CONFIG_NO_CONFIG_WRITE
+ if (changed && wpa_s->conf->update_config &&
+ wpa_config_write(wpa_s->confname, wpa_s->conf)) {
+ wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
+ }
+#endif /* CONFIG_NO_CONFIG_WRITE */
+
+ return s->id;
+}
+
+
+static void wpas_p2p_add_persistent_group_client(struct wpa_supplicant *wpa_s,
+ const u8 *addr)
+{
+ struct wpa_ssid *ssid, *s;
+ u8 *n;
+ size_t i;
+
+ ssid = wpa_s->current_ssid;
+ if (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GO ||
+ !ssid->p2p_persistent_group)
+ return;
+
+ for (s = wpa_s->parent->conf->ssid; s; s = s->next) {
+ if (s->disabled != 2 || s->mode != WPAS_MODE_P2P_GO)
+ continue;
+
+ if (s->ssid_len == ssid->ssid_len &&
+ os_memcmp(s->ssid, ssid->ssid, s->ssid_len) == 0)
+ break;
+ }
+
+ if (s == NULL)
+ return;
+
+ for (i = 0; s->p2p_client_list && i < s->num_p2p_clients; i++) {
+ if (os_memcmp(s->p2p_client_list + i * ETH_ALEN, addr,
+ ETH_ALEN) == 0)
+ return; /* already in list */
+ }
+
+ n = os_realloc(s->p2p_client_list,
+ (s->num_p2p_clients + 1) * ETH_ALEN);
+ if (n == NULL)
+ return;
+ os_memcpy(n + s->num_p2p_clients * ETH_ALEN, addr, ETH_ALEN);
+ s->p2p_client_list = n;
+ s->num_p2p_clients++;
+
+#ifndef CONFIG_NO_CONFIG_WRITE
+ if (wpa_s->parent->conf->update_config &&
+ wpa_config_write(wpa_s->parent->confname, wpa_s->parent->conf))
+ wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
+#endif /* CONFIG_NO_CONFIG_WRITE */
+}
+
+
+static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s,
+ int success)
+{
+ struct wpa_ssid *ssid;
+ const char *ssid_txt;
+ int client;
+ int persistent;
+ u8 go_dev_addr[ETH_ALEN];
+ int network_id = -1;
+
+ /*
+ * This callback is likely called for the main interface. Update wpa_s
+ * to use the group interface if a new interface was created for the
+ * group.
+ */
+ if (wpa_s->global->p2p_group_formation)
+ wpa_s = wpa_s->global->p2p_group_formation;
+ wpa_s->global->p2p_group_formation = NULL;
+ wpa_s->p2p_in_provisioning = 0;
+
+ if (!success) {
+ wpa_msg(wpa_s->parent, MSG_INFO,
+ P2P_EVENT_GROUP_FORMATION_FAILURE);
+ wpas_p2p_group_delete(wpa_s);
+ return;
+ }
+
+ wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_FORMATION_SUCCESS);
+
+ ssid = wpa_s->current_ssid;
+ if (ssid && ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) {
+ ssid->mode = WPAS_MODE_P2P_GO;
+ p2p_group_notif_formation_done(wpa_s->p2p_group);
+ wpa_supplicant_ap_mac_addr_filter(wpa_s, NULL);
+ }
+
+ persistent = 0;
+ if (ssid) {
+ ssid_txt = wpa_ssid_txt(ssid->ssid, ssid->ssid_len);
+ client = ssid->mode == WPAS_MODE_INFRA;
+ if (ssid->mode == WPAS_MODE_P2P_GO) {
+ persistent = ssid->p2p_persistent_group;
+ os_memcpy(go_dev_addr, wpa_s->global->p2p_dev_addr,
+ ETH_ALEN);
+ } else
+ persistent = wpas_p2p_persistent_group(wpa_s,
+ go_dev_addr,
+ ssid->ssid,
+ ssid->ssid_len);
+ } else {
+ ssid_txt = "";
+ client = wpa_s->p2p_group_interface ==
+ P2P_GROUP_INTERFACE_CLIENT;
+ os_memset(go_dev_addr, 0, ETH_ALEN);
+ }
+
+ wpa_s->show_group_started = 0;
+ if (client) {
+ /*
+ * Indicate event only after successfully completed 4-way
+ * handshake, i.e., when the interface is ready for data
+ * packets.
+ */
+ wpa_s->show_group_started = 1;
+ } else if (ssid && ssid->passphrase == NULL && ssid->psk_set) {
+ char psk[65];
+ wpa_snprintf_hex(psk, sizeof(psk), ssid->psk, 32);
+ wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED
+ "%s GO ssid=\"%s\" freq=%d psk=%s go_dev_addr=" MACSTR
+ "%s",
+ wpa_s->ifname, ssid_txt, ssid->frequency, psk,
+ MAC2STR(go_dev_addr),
+ persistent ? " [PERSISTENT]" : "");
+ wpas_p2p_cross_connect_setup(wpa_s);
+ wpas_p2p_set_group_idle_timeout(wpa_s);
+ } else {
+ wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED
+ "%s GO ssid=\"%s\" freq=%d passphrase=\"%s\" "
+ "go_dev_addr=" MACSTR "%s",
+ wpa_s->ifname, ssid_txt, ssid ? ssid->frequency : 0,
+ ssid && ssid->passphrase ? ssid->passphrase : "",
+ MAC2STR(go_dev_addr),
+ persistent ? " [PERSISTENT]" : "");
+ wpas_p2p_cross_connect_setup(wpa_s);
+ wpas_p2p_set_group_idle_timeout(wpa_s);
+ }
+
+ if (persistent)
+ network_id = wpas_p2p_store_persistent_group(wpa_s->parent,
+ ssid, go_dev_addr);
+ if (network_id < 0 && ssid)
+ network_id = ssid->id;
+ if (!client)
+ wpas_notify_p2p_group_started(wpa_s, ssid, network_id, 0);
+}
+
+
+static void wpas_p2p_send_action_tx_status(struct wpa_supplicant *wpa_s,
+ unsigned int freq,
+ const u8 *dst, const u8 *src,
+ const u8 *bssid,
+ const u8 *data, size_t data_len,
+ enum offchannel_send_action_result
+ result)
+{
+ enum p2p_send_action_result res = P2P_SEND_ACTION_SUCCESS;
+
+ if (wpa_s->global->p2p == NULL || wpa_s->global->p2p_disabled)
+ return;
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+ return;
+
+ switch (result) {
+ case OFFCHANNEL_SEND_ACTION_SUCCESS:
+ res = P2P_SEND_ACTION_SUCCESS;
+ break;
+ case OFFCHANNEL_SEND_ACTION_NO_ACK:
+ res = P2P_SEND_ACTION_NO_ACK;
+ break;
+ case OFFCHANNEL_SEND_ACTION_FAILED:
+ res = P2P_SEND_ACTION_FAILED;
+ break;
+ }
+
+ p2p_send_action_cb(wpa_s->global->p2p, freq, dst, src, bssid, res);
+
+ if (result != OFFCHANNEL_SEND_ACTION_SUCCESS &&
+ wpa_s->pending_pd_before_join &&
+ (os_memcmp(dst, wpa_s->pending_join_dev_addr, ETH_ALEN) == 0 ||
+ os_memcmp(dst, wpa_s->pending_join_iface_addr, ETH_ALEN) == 0)) {
+ wpa_s->pending_pd_before_join = 0;
+ wpa_printf(MSG_DEBUG, "P2P: Starting pending "
+ "join-existing-group operation (no ACK for PD "
+ "Req)");
+ wpas_p2p_join_start(wpa_s);
+ }
+}
+
+
+static int wpas_send_action(void *ctx, unsigned int freq, const u8 *dst,
+ const u8 *src, const u8 *bssid, const u8 *buf,
+ size_t len, unsigned int wait_time)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ return offchannel_send_action(wpa_s, freq, dst, src, bssid, buf, len,
+ wait_time,
+ wpas_p2p_send_action_tx_status, 1);
+}
+
+
+static void wpas_send_action_done(void *ctx)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ offchannel_send_action_done(wpa_s);
+}
+
+
+static int wpas_copy_go_neg_results(struct wpa_supplicant *wpa_s,
+ struct p2p_go_neg_results *params)
+{
+ if (wpa_s->go_params == NULL) {
+ wpa_s->go_params = os_malloc(sizeof(*params));
+ if (wpa_s->go_params == NULL)
+ return -1;
+ }
+ os_memcpy(wpa_s->go_params, params, sizeof(*params));
+ return 0;
+}
+
+
+static void wpas_start_wps_enrollee(struct wpa_supplicant *wpa_s,
+ struct p2p_go_neg_results *res)
+{
+ wpa_printf(MSG_DEBUG, "P2P: Start WPS Enrollee for peer " MACSTR,
+ MAC2STR(res->peer_interface_addr));
+ wpa_hexdump_ascii(MSG_DEBUG, "P2P: Start WPS Enrollee for SSID",
+ res->ssid, res->ssid_len);
+ wpa_supplicant_ap_deinit(wpa_s);
+ wpas_copy_go_neg_results(wpa_s, res);
+ if (res->wps_method == WPS_PBC)
+ wpas_wps_start_pbc(wpa_s, res->peer_interface_addr, 1);
+ else {
+ u16 dev_pw_id = DEV_PW_DEFAULT;
+ if (wpa_s->p2p_wps_method == WPS_PIN_KEYPAD)
+ dev_pw_id = DEV_PW_REGISTRAR_SPECIFIED;
+ wpas_wps_start_pin(wpa_s, res->peer_interface_addr,
+ wpa_s->p2p_pin, 1, dev_pw_id);
+ }
+}
+
+
+static void p2p_go_configured(void *ctx, void *data)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ struct p2p_go_neg_results *params = data;
+ struct wpa_ssid *ssid;
+ int network_id = -1;
+
+ ssid = wpa_s->current_ssid;
+ if (ssid && ssid->mode == WPAS_MODE_P2P_GO) {
+ wpa_printf(MSG_DEBUG, "P2P: Group setup without provisioning");
+ if (wpa_s->global->p2p_group_formation == wpa_s)
+ wpa_s->global->p2p_group_formation = NULL;
+ wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED
+ "%s GO ssid=\"%s\" freq=%d passphrase=\"%s\" "
+ "go_dev_addr=" MACSTR "%s",
+ wpa_s->ifname,
+ wpa_ssid_txt(ssid->ssid, ssid->ssid_len),
+ ssid->frequency,
+ params->passphrase ? params->passphrase : "",
+ MAC2STR(wpa_s->global->p2p_dev_addr),
+ params->persistent_group ? " [PERSISTENT]" : "");
+
+ if (params->persistent_group)
+ network_id = wpas_p2p_store_persistent_group(
+ wpa_s->parent, ssid,
+ wpa_s->global->p2p_dev_addr);
+ if (network_id < 0)
+ network_id = ssid->id;
+ wpas_notify_p2p_group_started(wpa_s, ssid, network_id, 0);
+ wpas_p2p_cross_connect_setup(wpa_s);
+ wpas_p2p_set_group_idle_timeout(wpa_s);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "P2P: Setting up WPS for GO provisioning");
+ if (wpa_supplicant_ap_mac_addr_filter(wpa_s,
+ params->peer_interface_addr)) {
+ wpa_printf(MSG_DEBUG, "P2P: Failed to setup MAC address "
+ "filtering");
+ return;
+ }
+ if (params->wps_method == WPS_PBC)
+ wpa_supplicant_ap_wps_pbc(wpa_s, params->peer_interface_addr,
+ params->peer_device_addr);
+ else if (wpa_s->p2p_pin[0])
+ wpa_supplicant_ap_wps_pin(wpa_s, params->peer_interface_addr,
+ wpa_s->p2p_pin, NULL, 0);
+ os_free(wpa_s->go_params);
+ wpa_s->go_params = NULL;
+}
+
+
+static void wpas_start_wps_go(struct wpa_supplicant *wpa_s,
+ struct p2p_go_neg_results *params,
+ int group_formation)
+{
+ struct wpa_ssid *ssid;
+
+ if (wpas_copy_go_neg_results(wpa_s, params) < 0)
+ return;
+
+ ssid = wpa_config_add_network(wpa_s->conf);
+ if (ssid == NULL)
+ return;
+
+ wpa_s->show_group_started = 0;
+
+ wpa_config_set_network_defaults(ssid);
+ ssid->temporary = 1;
+ ssid->p2p_group = 1;
+ ssid->p2p_persistent_group = params->persistent_group;
+ ssid->mode = group_formation ? WPAS_MODE_P2P_GROUP_FORMATION :
+ WPAS_MODE_P2P_GO;
+ ssid->frequency = params->freq;
+ ssid->ssid = os_zalloc(params->ssid_len + 1);
+ if (ssid->ssid) {
+ os_memcpy(ssid->ssid, params->ssid, params->ssid_len);
+ ssid->ssid_len = params->ssid_len;
+ }
+ ssid->auth_alg = WPA_AUTH_ALG_OPEN;
+ ssid->key_mgmt = WPA_KEY_MGMT_PSK;
+ ssid->proto = WPA_PROTO_RSN;
+ ssid->pairwise_cipher = WPA_CIPHER_CCMP;
+ ssid->passphrase = os_strdup(params->passphrase);
+
+ wpa_s->ap_configured_cb = p2p_go_configured;
+ wpa_s->ap_configured_cb_ctx = wpa_s;
+ wpa_s->ap_configured_cb_data = wpa_s->go_params;
+ wpa_s->connect_without_scan = ssid;
+ wpa_s->reassociate = 1;
+ wpa_s->disconnected = 0;
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+}
+
+
+static void wpas_p2p_clone_config(struct wpa_supplicant *dst,
+ const struct wpa_supplicant *src)
+{
+ struct wpa_config *d;
+ const struct wpa_config *s;
+
+ d = dst->conf;
+ s = src->conf;
+
+#define C(n) if (s->n) d->n = os_strdup(s->n)
+ C(device_name);
+ C(manufacturer);
+ C(model_name);
+ C(model_number);
+ C(serial_number);
+ C(config_methods);
+#undef C
+
+ os_memcpy(d->device_type, s->device_type, WPS_DEV_TYPE_LEN);
+ os_memcpy(d->sec_device_type, s->sec_device_type,
+ sizeof(d->sec_device_type));
+ d->num_sec_device_types = s->num_sec_device_types;
+
+ d->p2p_group_idle = s->p2p_group_idle;
+ d->p2p_intra_bss = s->p2p_intra_bss;
+ d->persistent_reconnect = s->persistent_reconnect;
+}
+
+
+static int wpas_p2p_add_group_interface(struct wpa_supplicant *wpa_s,
+ enum wpa_driver_if_type type)
+{
+ char ifname[120], force_ifname[120];
+
+ if (wpa_s->pending_interface_name[0]) {
+ wpa_printf(MSG_DEBUG, "P2P: Pending virtual interface exists "
+ "- skip creation of a new one");
+ if (is_zero_ether_addr(wpa_s->pending_interface_addr)) {
+ wpa_printf(MSG_DEBUG, "P2P: Pending virtual address "
+ "unknown?! ifname='%s'",
+ wpa_s->pending_interface_name);
+ return -1;
+ }
+ return 0;
+ }
+
+ os_snprintf(ifname, sizeof(ifname), "p2p-%s-%d", wpa_s->ifname,
+ wpa_s->p2p_group_idx);
+ if (os_strlen(ifname) >= IFNAMSIZ &&
+ os_strlen(wpa_s->ifname) < IFNAMSIZ) {
+ /* Try to avoid going over the IFNAMSIZ length limit */
+ os_snprintf(ifname, sizeof(ifname), "p2p-%d",
+ wpa_s->p2p_group_idx);
+ }
+ force_ifname[0] = '\0';
+
+ wpa_printf(MSG_DEBUG, "P2P: Create a new interface %s for the group",
+ ifname);
+ wpa_s->p2p_group_idx++;
+
+ wpa_s->pending_interface_type = type;
+ if (wpa_drv_if_add(wpa_s, type, ifname, NULL, NULL, force_ifname,
+ wpa_s->pending_interface_addr, NULL) < 0) {
+ wpa_printf(MSG_ERROR, "P2P: Failed to create new group "
+ "interface");
+ return -1;
+ }
+
+ if (force_ifname[0]) {
+ wpa_printf(MSG_DEBUG, "P2P: Driver forced interface name %s",
+ force_ifname);
+ os_strlcpy(wpa_s->pending_interface_name, force_ifname,
+ sizeof(wpa_s->pending_interface_name));
+ } else
+ os_strlcpy(wpa_s->pending_interface_name, ifname,
+ sizeof(wpa_s->pending_interface_name));
+ wpa_printf(MSG_DEBUG, "P2P: Created pending virtual interface %s addr "
+ MACSTR, wpa_s->pending_interface_name,
+ MAC2STR(wpa_s->pending_interface_addr));
+
+ return 0;
+}
+
+
+static void wpas_p2p_remove_pending_group_interface(
+ struct wpa_supplicant *wpa_s)
+{
+ if (!wpa_s->pending_interface_name[0] ||
+ is_zero_ether_addr(wpa_s->pending_interface_addr))
+ return; /* No pending virtual interface */
+
+ wpa_printf(MSG_DEBUG, "P2P: Removing pending group interface %s",
+ wpa_s->pending_interface_name);
+ wpa_drv_if_remove(wpa_s, wpa_s->pending_interface_type,
+ wpa_s->pending_interface_name);
+ os_memset(wpa_s->pending_interface_addr, 0, ETH_ALEN);
+ wpa_s->pending_interface_name[0] = '\0';
+}
+
+
+static struct wpa_supplicant *
+wpas_p2p_init_group_interface(struct wpa_supplicant *wpa_s, int go)
+{
+ struct wpa_interface iface;
+ struct wpa_supplicant *group_wpa_s;
+
+ if (!wpa_s->pending_interface_name[0]) {
+ wpa_printf(MSG_ERROR, "P2P: No pending group interface");
+ if (!wpas_p2p_create_iface(wpa_s))
+ return NULL;
+ /*
+ * Something has forced us to remove the pending interface; try
+ * to create a new one and hope for the best that we will get
+ * the same local address.
+ */
+ if (wpas_p2p_add_group_interface(wpa_s, go ? WPA_IF_P2P_GO :
+ WPA_IF_P2P_CLIENT) < 0)
+ return NULL;
+ }
+
+ os_memset(&iface, 0, sizeof(iface));
+ iface.ifname = wpa_s->pending_interface_name;
+ iface.driver = wpa_s->driver->name;
+ iface.ctrl_interface = wpa_s->conf->ctrl_interface;
+ iface.driver_param = wpa_s->conf->driver_param;
+ group_wpa_s = wpa_supplicant_add_iface(wpa_s->global, &iface);
+ if (group_wpa_s == NULL) {
+ wpa_printf(MSG_ERROR, "P2P: Failed to create new "
+ "wpa_supplicant interface");
+ return NULL;
+ }
+ wpa_s->pending_interface_name[0] = '\0';
+ group_wpa_s->parent = wpa_s;
+ group_wpa_s->p2p_group_interface = go ? P2P_GROUP_INTERFACE_GO :
+ P2P_GROUP_INTERFACE_CLIENT;
+ wpa_s->global->p2p_group_formation = group_wpa_s;
+
+ wpas_p2p_clone_config(group_wpa_s, wpa_s);
+
+ return group_wpa_s;
+}
+
+
+static void wpas_p2p_group_formation_timeout(void *eloop_ctx,
+ void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ wpa_printf(MSG_DEBUG, "P2P: Group Formation timed out");
+ if (wpa_s->global->p2p)
+ p2p_group_formation_failed(wpa_s->global->p2p);
+ else if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+ wpa_drv_p2p_group_formation_failed(wpa_s);
+ wpas_group_formation_completed(wpa_s, 0);
+}
+
+
+void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) {
+ wpa_drv_cancel_remain_on_channel(wpa_s);
+ wpa_s->off_channel_freq = 0;
+ wpa_s->roc_waiting_drv_freq = 0;
+ }
+
+ if (res->status) {
+ wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_FAILURE "status=%d",
+ res->status);
+ wpas_notify_p2p_go_neg_completed(wpa_s, res);
+ wpas_p2p_remove_pending_group_interface(wpa_s);
+ return;
+ }
+
+ wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_SUCCESS);
+ wpas_notify_p2p_go_neg_completed(wpa_s, res);
+
+ if (wpa_s->create_p2p_iface) {
+ struct wpa_supplicant *group_wpa_s =
+ wpas_p2p_init_group_interface(wpa_s, res->role_go);
+ if (group_wpa_s == NULL) {
+ wpas_p2p_remove_pending_group_interface(wpa_s);
+ return;
+ }
+ if (group_wpa_s != wpa_s) {
+ os_memcpy(group_wpa_s->p2p_pin, wpa_s->p2p_pin,
+ sizeof(group_wpa_s->p2p_pin));
+ group_wpa_s->p2p_wps_method = wpa_s->p2p_wps_method;
+ }
+ os_memset(wpa_s->pending_interface_addr, 0, ETH_ALEN);
+ wpa_s->pending_interface_name[0] = '\0';
+ group_wpa_s->p2p_in_provisioning = 1;
+
+ if (res->role_go)
+ wpas_start_wps_go(group_wpa_s, res, 1);
+ else
+ wpas_start_wps_enrollee(group_wpa_s, res);
+ } else {
+ wpa_s->p2p_in_provisioning = 1;
+ wpa_s->global->p2p_group_formation = wpa_s;
+
+ if (res->role_go)
+ wpas_start_wps_go(wpa_s, res, 1);
+ else
+ wpas_start_wps_enrollee(ctx, res);
+ }
+
+ wpa_s->p2p_long_listen = 0;
+ eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL);
+
+ eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s, NULL);
+ eloop_register_timeout(15 + res->peer_config_timeout / 100,
+ (res->peer_config_timeout % 100) * 10000,
+ wpas_p2p_group_formation_timeout, wpa_s, NULL);
+}
+
+
+void wpas_go_neg_req_rx(void *ctx, const u8 *src, u16 dev_passwd_id)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_REQUEST MACSTR
+ " dev_passwd_id=%u", MAC2STR(src), dev_passwd_id);
+
+ wpas_notify_p2p_go_neg_req(wpa_s, src, dev_passwd_id);
+}
+
+
+void wpas_dev_found(void *ctx, const u8 *addr,
+ const struct p2p_peer_info *info,
+ int new_device)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ char devtype[WPS_DEV_TYPE_BUFSIZE];
+
+ wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_DEVICE_FOUND MACSTR
+ " p2p_dev_addr=" MACSTR
+ " pri_dev_type=%s name='%s' config_methods=0x%x "
+ "dev_capab=0x%x group_capab=0x%x",
+ MAC2STR(addr), MAC2STR(info->p2p_device_addr),
+ wps_dev_type_bin2str(info->pri_dev_type, devtype,
+ sizeof(devtype)),
+ info->device_name, info->config_methods,
+ info->dev_capab, info->group_capab);
+
+ wpas_notify_p2p_device_found(ctx, info->p2p_device_addr, new_device);
+}
+
+
+static void wpas_dev_lost(void *ctx, const u8 *dev_addr)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_DEVICE_LOST
+ "p2p_dev_addr=" MACSTR, MAC2STR(dev_addr));
+
+ wpas_notify_p2p_device_lost(wpa_s, dev_addr);
+}
+
+
+static int wpas_start_listen(void *ctx, unsigned int freq,
+ unsigned int duration,
+ const struct wpabuf *probe_resp_ie)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ wpa_drv_set_ap_wps_ie(wpa_s, NULL, probe_resp_ie, NULL);
+
+ if (wpa_drv_probe_req_report(wpa_s, 1) < 0) {
+ wpa_printf(MSG_DEBUG, "P2P: Failed to request the driver to "
+ "report received Probe Request frames");
+ return -1;
+ }
+
+ wpa_s->pending_listen_freq = freq;
+ wpa_s->pending_listen_duration = duration;
+
+ if (wpa_drv_remain_on_channel(wpa_s, freq, duration) < 0) {
+ wpa_printf(MSG_DEBUG, "P2P: Failed to request the driver "
+ "to remain on channel (%u MHz) for Listen "
+ "state", freq);
+ wpa_s->pending_listen_freq = 0;
+ return -1;
+ }
+ wpa_s->off_channel_freq = 0;
+ wpa_s->roc_waiting_drv_freq = freq;
+
+ return 0;
+}
+
+
+static void wpas_stop_listen(void *ctx)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) {
+ wpa_drv_cancel_remain_on_channel(wpa_s);
+ wpa_s->off_channel_freq = 0;
+ wpa_s->roc_waiting_drv_freq = 0;
+ }
+ wpa_drv_set_ap_wps_ie(wpa_s, NULL, NULL, NULL);
+ wpa_drv_probe_req_report(wpa_s, 0);
+}
+
+
+static int wpas_send_probe_resp(void *ctx, const struct wpabuf *buf)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ return wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf));
+}
+
+
+static struct p2p_srv_bonjour *
+wpas_p2p_service_get_bonjour(struct wpa_supplicant *wpa_s,
+ const struct wpabuf *query)
+{
+ struct p2p_srv_bonjour *bsrv;
+ size_t len;
+
+ len = wpabuf_len(query);
+ dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour,
+ struct p2p_srv_bonjour, list) {
+ if (len == wpabuf_len(bsrv->query) &&
+ os_memcmp(wpabuf_head(query), wpabuf_head(bsrv->query),
+ len) == 0)
+ return bsrv;
+ }
+ return NULL;
+}
+
+
+static struct p2p_srv_upnp *
+wpas_p2p_service_get_upnp(struct wpa_supplicant *wpa_s, u8 version,
+ const char *service)
+{
+ struct p2p_srv_upnp *usrv;
+
+ dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp,
+ struct p2p_srv_upnp, list) {
+ if (version == usrv->version &&
+ os_strcmp(service, usrv->service) == 0)
+ return usrv;
+ }
+ return NULL;
+}
+
+
+static void wpas_sd_add_proto_not_avail(struct wpabuf *resp, u8 srv_proto,
+ u8 srv_trans_id)
+{
+ u8 *len_pos;
+
+ if (wpabuf_tailroom(resp) < 5)
+ return;
+
+ /* Length (to be filled) */
+ len_pos = wpabuf_put(resp, 2);
+ wpabuf_put_u8(resp, srv_proto);
+ wpabuf_put_u8(resp, srv_trans_id);
+ /* Status Code */
+ wpabuf_put_u8(resp, P2P_SD_PROTO_NOT_AVAILABLE);
+ /* Response Data: empty */
+ WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
+}
+
+
+static void wpas_sd_all_bonjour(struct wpa_supplicant *wpa_s,
+ struct wpabuf *resp, u8 srv_trans_id)
+{
+ struct p2p_srv_bonjour *bsrv;
+ u8 *len_pos;
+
+ wpa_printf(MSG_DEBUG, "P2P: SD Request for all Bonjour services");
+
+ if (dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) {
+ wpa_printf(MSG_DEBUG, "P2P: Bonjour protocol not available");
+ return;
+ }
+
+ dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour,
+ struct p2p_srv_bonjour, list) {
+ if (wpabuf_tailroom(resp) <
+ 5 + wpabuf_len(bsrv->query) + wpabuf_len(bsrv->resp))
+ return;
+ /* Length (to be filled) */
+ len_pos = wpabuf_put(resp, 2);
+ wpabuf_put_u8(resp, P2P_SERV_BONJOUR);
+ wpabuf_put_u8(resp, srv_trans_id);
+ /* Status Code */
+ wpabuf_put_u8(resp, P2P_SD_SUCCESS);
+ wpa_hexdump_ascii(MSG_DEBUG, "P2P: Matching Bonjour service",
+ wpabuf_head(bsrv->resp),
+ wpabuf_len(bsrv->resp));
+ /* Response Data */
+ wpabuf_put_buf(resp, bsrv->query); /* Key */
+ wpabuf_put_buf(resp, bsrv->resp); /* Value */
+ WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos -
+ 2);
+ }
+}
+
+
+static void wpas_sd_req_bonjour(struct wpa_supplicant *wpa_s,
+ struct wpabuf *resp, u8 srv_trans_id,
+ const u8 *query, size_t query_len)
+{
+ struct p2p_srv_bonjour *bsrv;
+ struct wpabuf buf;
+ u8 *len_pos;
+
+ wpa_hexdump_ascii(MSG_DEBUG, "P2P: SD Request for Bonjour",
+ query, query_len);
+ if (dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) {
+ wpa_printf(MSG_DEBUG, "P2P: Bonjour protocol not available");
+ wpas_sd_add_proto_not_avail(resp, P2P_SERV_BONJOUR,
+ srv_trans_id);
+ return;
+ }
+
+ if (query_len == 0) {
+ wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id);
+ return;
+ }
+
+ if (wpabuf_tailroom(resp) < 5)
+ return;
+ /* Length (to be filled) */
+ len_pos = wpabuf_put(resp, 2);
+ wpabuf_put_u8(resp, P2P_SERV_BONJOUR);
+ wpabuf_put_u8(resp, srv_trans_id);
+
+ wpabuf_set(&buf, query, query_len);
+ bsrv = wpas_p2p_service_get_bonjour(wpa_s, &buf);
+ if (bsrv == NULL) {
+ wpa_printf(MSG_DEBUG, "P2P: Requested Bonjour service not "
+ "available");
+
+ /* Status Code */
+ wpabuf_put_u8(resp, P2P_SD_REQUESTED_INFO_NOT_AVAILABLE);
+ /* Response Data: empty */
+ WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos -
+ 2);
+ return;
+ }
+
+ /* Status Code */
+ wpabuf_put_u8(resp, P2P_SD_SUCCESS);
+ wpa_hexdump_ascii(MSG_DEBUG, "P2P: Matching Bonjour service",
+ wpabuf_head(bsrv->resp), wpabuf_len(bsrv->resp));
+
+ if (wpabuf_tailroom(resp) >=
+ wpabuf_len(bsrv->query) + wpabuf_len(bsrv->resp)) {
+ /* Response Data */
+ wpabuf_put_buf(resp, bsrv->query); /* Key */
+ wpabuf_put_buf(resp, bsrv->resp); /* Value */
+ }
+ WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
+}
+
+
+static void wpas_sd_all_upnp(struct wpa_supplicant *wpa_s,
+ struct wpabuf *resp, u8 srv_trans_id)
+{
+ struct p2p_srv_upnp *usrv;
+ u8 *len_pos;
+
+ wpa_printf(MSG_DEBUG, "P2P: SD Request for all UPnP services");
+
+ if (dl_list_empty(&wpa_s->global->p2p_srv_upnp)) {
+ wpa_printf(MSG_DEBUG, "P2P: UPnP protocol not available");
+ return;
+ }
+
+ dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp,
+ struct p2p_srv_upnp, list) {
+ if (wpabuf_tailroom(resp) < 5 + 1 + os_strlen(usrv->service))
+ return;
+
+ /* Length (to be filled) */
+ len_pos = wpabuf_put(resp, 2);
+ wpabuf_put_u8(resp, P2P_SERV_UPNP);
+ wpabuf_put_u8(resp, srv_trans_id);
+
+ /* Status Code */
+ wpabuf_put_u8(resp, P2P_SD_SUCCESS);
+ /* Response Data */
+ wpabuf_put_u8(resp, usrv->version);
+ wpa_printf(MSG_DEBUG, "P2P: Matching UPnP Service: %s",
+ usrv->service);
+ wpabuf_put_str(resp, usrv->service);
+ WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos -
+ 2);
+ }
+}
+
+
+static void wpas_sd_req_upnp(struct wpa_supplicant *wpa_s,
+ struct wpabuf *resp, u8 srv_trans_id,
+ const u8 *query, size_t query_len)
+{
+ struct p2p_srv_upnp *usrv;
+ u8 *len_pos;
+ u8 version;
+ char *str;
+ int count = 0;
+
+ wpa_hexdump_ascii(MSG_DEBUG, "P2P: SD Request for UPnP",
+ query, query_len);
+
+ if (dl_list_empty(&wpa_s->global->p2p_srv_upnp)) {
+ wpa_printf(MSG_DEBUG, "P2P: UPnP protocol not available");
+ wpas_sd_add_proto_not_avail(resp, P2P_SERV_UPNP,
+ srv_trans_id);
+ return;
+ }
+
+ if (query_len == 0) {
+ wpas_sd_all_upnp(wpa_s, resp, srv_trans_id);
+ return;
+ }
+
+ if (wpabuf_tailroom(resp) < 5)
+ return;
+
+ /* Length (to be filled) */
+ len_pos = wpabuf_put(resp, 2);
+ wpabuf_put_u8(resp, P2P_SERV_UPNP);
+ wpabuf_put_u8(resp, srv_trans_id);
+
+ version = query[0];
+ str = os_malloc(query_len);
+ if (str == NULL)
+ return;
+ os_memcpy(str, query + 1, query_len - 1);
+ str[query_len - 1] = '\0';
+
+ dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp,
+ struct p2p_srv_upnp, list) {
+ if (version != usrv->version)
+ continue;
+
+ if (os_strcmp(str, "ssdp:all") != 0 &&
+ os_strstr(usrv->service, str) == NULL)
+ continue;
+
+ if (wpabuf_tailroom(resp) < 2)
+ break;
+ if (count == 0) {
+ /* Status Code */
+ wpabuf_put_u8(resp, P2P_SD_SUCCESS);
+ /* Response Data */
+ wpabuf_put_u8(resp, version);
+ } else
+ wpabuf_put_u8(resp, ',');
+
+ count++;
+
+ wpa_printf(MSG_DEBUG, "P2P: Matching UPnP Service: %s",
+ usrv->service);
+ if (wpabuf_tailroom(resp) < os_strlen(usrv->service))
+ break;
+ wpabuf_put_str(resp, usrv->service);
+ }
+ os_free(str);
+
+ if (count == 0) {
+ wpa_printf(MSG_DEBUG, "P2P: Requested UPnP service not "
+ "available");
+ /* Status Code */
+ wpabuf_put_u8(resp, P2P_SD_REQUESTED_INFO_NOT_AVAILABLE);
+ /* Response Data: empty */
+ }
+
+ WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
+}
+
+
+void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token,
+ u16 update_indic, const u8 *tlvs, size_t tlvs_len)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ const u8 *pos = tlvs;
+ const u8 *end = tlvs + tlvs_len;
+ const u8 *tlv_end;
+ u16 slen;
+ struct wpabuf *resp;
+ u8 srv_proto, srv_trans_id;
+ size_t buf_len;
+ char *buf;
+
+ wpa_hexdump(MSG_MSGDUMP, "P2P: Service Discovery Request TLVs",
+ tlvs, tlvs_len);
+ buf_len = 2 * tlvs_len + 1;
+ buf = os_malloc(buf_len);
+ if (buf) {
+ wpa_snprintf_hex(buf, buf_len, tlvs, tlvs_len);
+ wpa_msg_ctrl(wpa_s, MSG_INFO, P2P_EVENT_SERV_DISC_REQ "%d "
+ MACSTR " %u %u %s",
+ freq, MAC2STR(sa), dialog_token, update_indic,
+ buf);
+ os_free(buf);
+ }
+
+ if (wpa_s->p2p_sd_over_ctrl_iface) {
+ wpas_notify_p2p_sd_request(wpa_s, freq, sa, dialog_token,
+ update_indic, tlvs, tlvs_len);
+ return; /* to be processed by an external program */
+ }
+
+ resp = wpabuf_alloc(10000);
+ if (resp == NULL)
+ return;
+
+ while (pos + 1 < end) {
+ wpa_printf(MSG_DEBUG, "P2P: Service Request TLV");
+ slen = WPA_GET_LE16(pos);
+ pos += 2;
+ if (pos + slen > end || slen < 2) {
+ wpa_printf(MSG_DEBUG, "P2P: Unexpected Query Data "
+ "length");
+ wpabuf_free(resp);
+ return;
+ }
+ tlv_end = pos + slen;
+
+ srv_proto = *pos++;
+ wpa_printf(MSG_DEBUG, "P2P: Service Protocol Type %u",
+ srv_proto);
+ srv_trans_id = *pos++;
+ wpa_printf(MSG_DEBUG, "P2P: Service Transaction ID %u",
+ srv_trans_id);
+
+ wpa_hexdump(MSG_MSGDUMP, "P2P: Query Data",
+ pos, tlv_end - pos);
+
+
+ if (wpa_s->force_long_sd) {
+ wpa_printf(MSG_DEBUG, "P2P: SD test - force long "
+ "response");
+ wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id);
+ wpas_sd_all_upnp(wpa_s, resp, srv_trans_id);
+ goto done;
+ }
+
+ switch (srv_proto) {
+ case P2P_SERV_ALL_SERVICES:
+ wpa_printf(MSG_DEBUG, "P2P: Service Discovery Request "
+ "for all services");
+ if (dl_list_empty(&wpa_s->global->p2p_srv_upnp) &&
+ dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) {
+ wpa_printf(MSG_DEBUG, "P2P: No service "
+ "discovery protocols available");
+ wpas_sd_add_proto_not_avail(
+ resp, P2P_SERV_ALL_SERVICES,
+ srv_trans_id);
+ break;
+ }
+ wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id);
+ wpas_sd_all_upnp(wpa_s, resp, srv_trans_id);
+ break;
+ case P2P_SERV_BONJOUR:
+ wpas_sd_req_bonjour(wpa_s, resp, srv_trans_id,
+ pos, tlv_end - pos);
+ break;
+ case P2P_SERV_UPNP:
+ wpas_sd_req_upnp(wpa_s, resp, srv_trans_id,
+ pos, tlv_end - pos);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "P2P: Unavailable service "
+ "protocol %u", srv_proto);
+ wpas_sd_add_proto_not_avail(resp, srv_proto,
+ srv_trans_id);
+ break;
+ }
+
+ pos = tlv_end;
+ }
+
+done:
+ wpas_notify_p2p_sd_request(wpa_s, freq, sa, dialog_token,
+ update_indic, tlvs, tlvs_len);
+
+ wpas_p2p_sd_response(wpa_s, freq, sa, dialog_token, resp);
+
+ wpabuf_free(resp);
+}
+
+
+void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic,
+ const u8 *tlvs, size_t tlvs_len)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ const u8 *pos = tlvs;
+ const u8 *end = tlvs + tlvs_len;
+ const u8 *tlv_end;
+ u16 slen;
+ size_t buf_len;
+ char *buf;
+
+ wpa_hexdump(MSG_MSGDUMP, "P2P: Service Discovery Response TLVs",
+ tlvs, tlvs_len);
+ if (tlvs_len > 1500) {
+ /* TODO: better way for handling this */
+ wpa_msg_ctrl(wpa_s, MSG_INFO,
+ P2P_EVENT_SERV_DISC_RESP MACSTR
+ " %u <long response: %u bytes>",
+ MAC2STR(sa), update_indic,
+ (unsigned int) tlvs_len);
+ } else {
+ buf_len = 2 * tlvs_len + 1;
+ buf = os_malloc(buf_len);
+ if (buf) {
+ wpa_snprintf_hex(buf, buf_len, tlvs, tlvs_len);
+ wpa_msg_ctrl(wpa_s, MSG_INFO,
+ P2P_EVENT_SERV_DISC_RESP MACSTR " %u %s",
+ MAC2STR(sa), update_indic, buf);
+ os_free(buf);
+ }
+ }
+
+ while (pos < end) {
+ u8 srv_proto, srv_trans_id, status;
+
+ wpa_printf(MSG_DEBUG, "P2P: Service Response TLV");
+ slen = WPA_GET_LE16(pos);
+ pos += 2;
+ if (pos + slen > end || slen < 3) {
+ wpa_printf(MSG_DEBUG, "P2P: Unexpected Response Data "
+ "length");
+ return;
+ }
+ tlv_end = pos + slen;
+
+ srv_proto = *pos++;
+ wpa_printf(MSG_DEBUG, "P2P: Service Protocol Type %u",
+ srv_proto);
+ srv_trans_id = *pos++;
+ wpa_printf(MSG_DEBUG, "P2P: Service Transaction ID %u",
+ srv_trans_id);
+ status = *pos++;
+ wpa_printf(MSG_DEBUG, "P2P: Status Code ID %u",
+ status);
+
+ wpa_hexdump(MSG_MSGDUMP, "P2P: Response Data",
+ pos, tlv_end - pos);
+
+ pos = tlv_end;
+ }
+
+ wpas_notify_p2p_sd_response(wpa_s, sa, update_indic, tlvs, tlvs_len);
+}
+
+
+void * wpas_p2p_sd_request(struct wpa_supplicant *wpa_s, const u8 *dst,
+ const struct wpabuf *tlvs)
+{
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+ return (void *) wpa_drv_p2p_sd_request(wpa_s, dst, tlvs);
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+ return NULL;
+ return p2p_sd_request(wpa_s->global->p2p, dst, tlvs);
+}
+
+
+void * wpas_p2p_sd_request_upnp(struct wpa_supplicant *wpa_s, const u8 *dst,
+ u8 version, const char *query)
+{
+ struct wpabuf *tlvs;
+ void *ret;
+
+ tlvs = wpabuf_alloc(2 + 1 + 1 + 1 + os_strlen(query));
+ if (tlvs == NULL)
+ return NULL;
+ wpabuf_put_le16(tlvs, 1 + 1 + 1 + os_strlen(query));
+ wpabuf_put_u8(tlvs, P2P_SERV_UPNP); /* Service Protocol Type */
+ wpabuf_put_u8(tlvs, 1); /* Service Transaction ID */
+ wpabuf_put_u8(tlvs, version);
+ wpabuf_put_str(tlvs, query);
+ ret = wpas_p2p_sd_request(wpa_s, dst, tlvs);
+ wpabuf_free(tlvs);
+ return ret;
+}
+
+
+int wpas_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s, void *req)
+{
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+ return wpa_drv_p2p_sd_cancel_request(wpa_s, (u64) req);
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+ return -1;
+ return p2p_sd_cancel_request(wpa_s->global->p2p, req);
+}
+
+
+void wpas_p2p_sd_response(struct wpa_supplicant *wpa_s, int freq,
+ const u8 *dst, u8 dialog_token,
+ const struct wpabuf *resp_tlvs)
+{
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) {
+ wpa_drv_p2p_sd_response(wpa_s, freq, dst, dialog_token,
+ resp_tlvs);
+ return;
+ }
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+ return;
+ p2p_sd_response(wpa_s->global->p2p, freq, dst, dialog_token,
+ resp_tlvs);
+}
+
+
+void wpas_p2p_sd_service_update(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) {
+ wpa_drv_p2p_service_update(wpa_s);
+ return;
+ }
+ if (wpa_s->global->p2p)
+ p2p_sd_service_update(wpa_s->global->p2p);
+}
+
+
+static void wpas_p2p_srv_bonjour_free(struct p2p_srv_bonjour *bsrv)
+{
+ dl_list_del(&bsrv->list);
+ wpabuf_free(bsrv->query);
+ wpabuf_free(bsrv->resp);
+ os_free(bsrv);
+}
+
+
+static void wpas_p2p_srv_upnp_free(struct p2p_srv_upnp *usrv)
+{
+ dl_list_del(&usrv->list);
+ os_free(usrv->service);
+ os_free(usrv);
+}
+
+
+void wpas_p2p_service_flush(struct wpa_supplicant *wpa_s)
+{
+ struct p2p_srv_bonjour *bsrv, *bn;
+ struct p2p_srv_upnp *usrv, *un;
+
+ dl_list_for_each_safe(bsrv, bn, &wpa_s->global->p2p_srv_bonjour,
+ struct p2p_srv_bonjour, list)
+ wpas_p2p_srv_bonjour_free(bsrv);
+
+ dl_list_for_each_safe(usrv, un, &wpa_s->global->p2p_srv_upnp,
+ struct p2p_srv_upnp, list)
+ wpas_p2p_srv_upnp_free(usrv);
+
+ wpas_p2p_sd_service_update(wpa_s);
+}
+
+
+int wpas_p2p_service_add_bonjour(struct wpa_supplicant *wpa_s,
+ struct wpabuf *query, struct wpabuf *resp)
+{
+ struct p2p_srv_bonjour *bsrv;
+
+ bsrv = wpas_p2p_service_get_bonjour(wpa_s, query);
+ if (bsrv) {
+ wpabuf_free(query);
+ wpabuf_free(bsrv->resp);
+ bsrv->resp = resp;
+ return 0;
+ }
+
+ bsrv = os_zalloc(sizeof(*bsrv));
+ if (bsrv == NULL)
+ return -1;
+ bsrv->query = query;
+ bsrv->resp = resp;
+ dl_list_add(&wpa_s->global->p2p_srv_bonjour, &bsrv->list);
+
+ wpas_p2p_sd_service_update(wpa_s);
+ return 0;
+}
+
+
+int wpas_p2p_service_del_bonjour(struct wpa_supplicant *wpa_s,
+ const struct wpabuf *query)
+{
+ struct p2p_srv_bonjour *bsrv;
+
+ bsrv = wpas_p2p_service_get_bonjour(wpa_s, query);
+ if (bsrv == NULL)
+ return -1;
+ wpas_p2p_srv_bonjour_free(bsrv);
+ wpas_p2p_sd_service_update(wpa_s);
+ return 0;
+}
+
+
+int wpas_p2p_service_add_upnp(struct wpa_supplicant *wpa_s, u8 version,
+ const char *service)
+{
+ struct p2p_srv_upnp *usrv;
+
+ if (wpas_p2p_service_get_upnp(wpa_s, version, service))
+ return 0; /* Already listed */
+ usrv = os_zalloc(sizeof(*usrv));
+ if (usrv == NULL)
+ return -1;
+ usrv->version = version;
+ usrv->service = os_strdup(service);
+ if (usrv->service == NULL) {
+ os_free(usrv);
+ return -1;
+ }
+ dl_list_add(&wpa_s->global->p2p_srv_upnp, &usrv->list);
+
+ wpas_p2p_sd_service_update(wpa_s);
+ return 0;
+}
+
+
+int wpas_p2p_service_del_upnp(struct wpa_supplicant *wpa_s, u8 version,
+ const char *service)
+{
+ struct p2p_srv_upnp *usrv;
+
+ usrv = wpas_p2p_service_get_upnp(wpa_s, version, service);
+ if (usrv == NULL)
+ return -1;
+ wpas_p2p_srv_upnp_free(usrv);
+ wpas_p2p_sd_service_update(wpa_s);
+ return 0;
+}
+
+
+static void wpas_prov_disc_local_display(struct wpa_supplicant *wpa_s,
+ const u8 *peer, const char *params,
+ unsigned int generated_pin)
+{
+ wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_SHOW_PIN MACSTR " %08d%s",
+ MAC2STR(peer), generated_pin, params);
+}
+
+
+static void wpas_prov_disc_local_keypad(struct wpa_supplicant *wpa_s,
+ const u8 *peer, const char *params)
+{
+ wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_ENTER_PIN MACSTR "%s",
+ MAC2STR(peer), params);
+}
+
+
+void wpas_prov_disc_req(void *ctx, const u8 *peer, u16 config_methods,
+ const u8 *dev_addr, const u8 *pri_dev_type,
+ const char *dev_name, u16 supp_config_methods,
+ u8 dev_capab, u8 group_capab, const u8 *group_id,
+ size_t group_id_len)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ char devtype[WPS_DEV_TYPE_BUFSIZE];
+ char params[300];
+ u8 empty_dev_type[8];
+ unsigned int generated_pin = 0;
+ struct wpa_supplicant *group = NULL;
+
+ if (group_id) {
+ for (group = wpa_s->global->ifaces; group; group = group->next)
+ {
+ struct wpa_ssid *s = group->current_ssid;
+ if (s != NULL &&
+ s->mode == WPAS_MODE_P2P_GO &&
+ group_id_len - ETH_ALEN == s->ssid_len &&
+ os_memcmp(group_id + ETH_ALEN, s->ssid,
+ s->ssid_len) == 0)
+ break;
+ }
+ }
+
+ if (pri_dev_type == NULL) {
+ os_memset(empty_dev_type, 0, sizeof(empty_dev_type));
+ pri_dev_type = empty_dev_type;
+ }
+ os_snprintf(params, sizeof(params), " p2p_dev_addr=" MACSTR
+ " pri_dev_type=%s name='%s' config_methods=0x%x "
+ "dev_capab=0x%x group_capab=0x%x%s%s",
+ MAC2STR(dev_addr),
+ wps_dev_type_bin2str(pri_dev_type, devtype,
+ sizeof(devtype)),
+ dev_name, supp_config_methods, dev_capab, group_capab,
+ group ? " group=" : "",
+ group ? group->ifname : "");
+ params[sizeof(params) - 1] = '\0';
+
+ if (config_methods & WPS_CONFIG_DISPLAY) {
+ generated_pin = wps_generate_pin();
+ wpas_prov_disc_local_display(wpa_s, peer, params,
+ generated_pin);
+ } else if (config_methods & WPS_CONFIG_KEYPAD)
+ wpas_prov_disc_local_keypad(wpa_s, peer, params);
+ else if (config_methods & WPS_CONFIG_PUSHBUTTON)
+ wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_PBC_REQ MACSTR
+ "%s", MAC2STR(peer), params);
+
+ wpas_notify_p2p_provision_discovery(wpa_s, peer, 1 /* request */,
+ P2P_PROV_DISC_SUCCESS,
+ config_methods, generated_pin);
+}
+
+
+void wpas_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ unsigned int generated_pin = 0;
+
+ if (wpa_s->pending_pd_before_join &&
+ (os_memcmp(peer, wpa_s->pending_join_dev_addr, ETH_ALEN) == 0 ||
+ os_memcmp(peer, wpa_s->pending_join_iface_addr, ETH_ALEN) == 0)) {
+ wpa_s->pending_pd_before_join = 0;
+ wpa_printf(MSG_DEBUG, "P2P: Starting pending "
+ "join-existing-group operation");
+ wpas_p2p_join_start(wpa_s);
+ return;
+ }
+
+ if (config_methods & WPS_CONFIG_DISPLAY)
+ wpas_prov_disc_local_keypad(wpa_s, peer, "");
+ else if (config_methods & WPS_CONFIG_KEYPAD) {
+ generated_pin = wps_generate_pin();
+ wpas_prov_disc_local_display(wpa_s, peer, "", generated_pin);
+ } else if (config_methods & WPS_CONFIG_PUSHBUTTON)
+ wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_PBC_RESP MACSTR,
+ MAC2STR(peer));
+
+ wpas_notify_p2p_provision_discovery(wpa_s, peer, 0 /* response */,
+ P2P_PROV_DISC_SUCCESS,
+ config_methods, generated_pin);
+}
+
+
+void wpas_prov_disc_fail(void *ctx, const u8 *peer,
+ enum p2p_prov_disc_status status)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ wpas_notify_p2p_provision_discovery(wpa_s, peer, 0 /* response */,
+ status, 0, 0);
+}
+
+
+static u8 wpas_invitation_process(void *ctx, const u8 *sa, const u8 *bssid,
+ const u8 *go_dev_addr, const u8 *ssid,
+ size_t ssid_len, int *go, u8 *group_bssid,
+ int *force_freq, int persistent_group)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ struct wpa_ssid *s;
+ u8 cur_bssid[ETH_ALEN];
+ int res;
+ struct wpa_supplicant *grp;
+
+ if (!persistent_group) {
+ wpa_printf(MSG_DEBUG, "P2P: Invitation from " MACSTR
+ " to join an active group", MAC2STR(sa));
+ if (!is_zero_ether_addr(wpa_s->p2p_auth_invite) &&
+ (os_memcmp(go_dev_addr, wpa_s->p2p_auth_invite, ETH_ALEN)
+ == 0 ||
+ os_memcmp(sa, wpa_s->p2p_auth_invite, ETH_ALEN) == 0)) {
+ wpa_printf(MSG_DEBUG, "P2P: Accept previously "
+ "authorized invitation");
+ goto accept_inv;
+ }
+ /*
+ * Do not accept the invitation automatically; notify user and
+ * request approval.
+ */
+ return P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
+ }
+
+ grp = wpas_get_p2p_group(wpa_s, ssid, ssid_len, go);
+ if (grp) {
+ wpa_printf(MSG_DEBUG, "P2P: Accept invitation to already "
+ "running persistent group");
+ if (*go)
+ os_memcpy(group_bssid, grp->own_addr, ETH_ALEN);
+ goto accept_inv;
+ }
+
+ if (!wpa_s->conf->persistent_reconnect)
+ return P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
+
+ for (s = wpa_s->conf->ssid; s; s = s->next) {
+ if (s->disabled == 2 &&
+ os_memcmp(s->bssid, go_dev_addr, ETH_ALEN) == 0 &&
+ s->ssid_len == ssid_len &&
+ os_memcmp(ssid, s->ssid, ssid_len) == 0)
+ break;
+ }
+
+ if (!s) {
+ wpa_printf(MSG_DEBUG, "P2P: Invitation from " MACSTR
+ " requested reinvocation of an unknown group",
+ MAC2STR(sa));
+ return P2P_SC_FAIL_UNKNOWN_GROUP;
+ }
+
+ if (s->mode == WPAS_MODE_P2P_GO && !wpas_p2p_create_iface(wpa_s)) {
+ *go = 1;
+ if (wpa_s->wpa_state >= WPA_AUTHENTICATING) {
+ wpa_printf(MSG_DEBUG, "P2P: The only available "
+ "interface is already in use - reject "
+ "invitation");
+ return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
+ }
+ os_memcpy(group_bssid, wpa_s->own_addr, ETH_ALEN);
+ } else if (s->mode == WPAS_MODE_P2P_GO) {
+ *go = 1;
+ if (wpas_p2p_add_group_interface(wpa_s, WPA_IF_P2P_GO) < 0)
+ {
+ wpa_printf(MSG_ERROR, "P2P: Failed to allocate a new "
+ "interface address for the group");
+ return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
+ }
+ os_memcpy(group_bssid, wpa_s->pending_interface_addr,
+ ETH_ALEN);
+ }
+
+accept_inv:
+ if (wpa_s->current_ssid && wpa_drv_get_bssid(wpa_s, cur_bssid) == 0 &&
+ wpa_s->assoc_freq) {
+ wpa_printf(MSG_DEBUG, "P2P: Trying to force channel to match "
+ "the channel we are already using");
+ *force_freq = wpa_s->assoc_freq;
+ }
+
+ res = wpa_drv_shared_freq(wpa_s);
+ if (res > 0) {
+ wpa_printf(MSG_DEBUG, "P2P: Trying to force channel to match "
+ "with the channel we are already using on a "
+ "shared interface");
+ *force_freq = res;
+ }
+
+ return P2P_SC_SUCCESS;
+}
+
+
+static void wpas_invitation_received(void *ctx, const u8 *sa, const u8 *bssid,
+ const u8 *ssid, size_t ssid_len,
+ const u8 *go_dev_addr, u8 status,
+ int op_freq)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ struct wpa_ssid *s;
+
+ for (s = wpa_s->conf->ssid; s; s = s->next) {
+ if (s->disabled == 2 &&
+ s->ssid_len == ssid_len &&
+ os_memcmp(ssid, s->ssid, ssid_len) == 0)
+ break;
+ }
+
+ if (status == P2P_SC_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "P2P: Invitation from peer " MACSTR
+ " was accepted; op_freq=%d MHz",
+ MAC2STR(sa), op_freq);
+ if (s) {
+ wpas_p2p_group_add_persistent(
+ wpa_s, s, s->mode == WPAS_MODE_P2P_GO, 0);
+ } else if (bssid) {
+ wpas_p2p_join(wpa_s, bssid, go_dev_addr,
+ wpa_s->p2p_wps_method);
+ }
+ return;
+ }
+
+ if (status != P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) {
+ wpa_printf(MSG_DEBUG, "P2P: Invitation from peer " MACSTR
+ " was rejected (status %u)", MAC2STR(sa), status);
+ return;
+ }
+
+ if (!s) {
+ if (bssid) {
+ wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RECEIVED
+ "sa=" MACSTR " go_dev_addr=" MACSTR
+ " bssid=" MACSTR " unknown-network",
+ MAC2STR(sa), MAC2STR(go_dev_addr),
+ MAC2STR(bssid));
+ } else {
+ wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RECEIVED
+ "sa=" MACSTR " go_dev_addr=" MACSTR
+ " unknown-network",
+ MAC2STR(sa), MAC2STR(go_dev_addr));
+ }
+ return;
+ }
+
+ wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RECEIVED "sa=" MACSTR
+ " persistent=%d", MAC2STR(sa), s->id);
+}
+
+
+static void wpas_invitation_result(void *ctx, int status, const u8 *bssid)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ struct wpa_ssid *ssid;
+
+ if (bssid) {
+ wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RESULT
+ "status=%d " MACSTR,
+ status, MAC2STR(bssid));
+ } else {
+ wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RESULT
+ "status=%d ", status);
+ }
+ wpas_notify_p2p_invitation_result(wpa_s, status, bssid);
+
+ if (wpa_s->pending_invite_ssid_id == -1)
+ return; /* Invitation to active group */
+
+ if (status != P2P_SC_SUCCESS) {
+ wpas_p2p_remove_pending_group_interface(wpa_s);
+ return;
+ }
+
+ ssid = wpa_config_get_network(wpa_s->conf,
+ wpa_s->pending_invite_ssid_id);
+ if (ssid == NULL) {
+ wpa_printf(MSG_ERROR, "P2P: Could not find persistent group "
+ "data matching with invitation");
+ return;
+ }
+
+ wpas_p2p_group_add_persistent(wpa_s, ssid,
+ ssid->mode == WPAS_MODE_P2P_GO, 0);
+}
+
+
+static int wpas_p2p_default_channels(struct wpa_supplicant *wpa_s,
+ struct p2p_channels *chan)
+{
+ int i, cla = 0;
+
+ wpa_printf(MSG_DEBUG, "P2P: Enable operating classes for 2.4 GHz "
+ "band");
+
+ /* Operating class 81 - 2.4 GHz band channels 1..13 */
+ chan->reg_class[cla].reg_class = 81;
+ chan->reg_class[cla].channels = 11;
+ for (i = 0; i < 11; i++)
+ chan->reg_class[cla].channel[i] = i + 1;
+ cla++;
+
+ wpa_printf(MSG_DEBUG, "P2P: Enable operating classes for lower 5 GHz "
+ "band");
+
+ /* Operating class 115 - 5 GHz, channels 36-48 */
+ chan->reg_class[cla].reg_class = 115;
+ chan->reg_class[cla].channels = 4;
+ chan->reg_class[cla].channel[0] = 36;
+ chan->reg_class[cla].channel[1] = 40;
+ chan->reg_class[cla].channel[2] = 44;
+ chan->reg_class[cla].channel[3] = 48;
+ cla++;
+
+ wpa_printf(MSG_DEBUG, "P2P: Enable operating classes for higher 5 GHz "
+ "band");
+
+ /* Operating class 124 - 5 GHz, channels 149,153,157,161 */
+ chan->reg_class[cla].reg_class = 124;
+ chan->reg_class[cla].channels = 4;
+ chan->reg_class[cla].channel[0] = 149;
+ chan->reg_class[cla].channel[1] = 153;
+ chan->reg_class[cla].channel[2] = 157;
+ chan->reg_class[cla].channel[3] = 161;
+ cla++;
+
+ chan->reg_classes = cla;
+ return 0;
+}
+
+
+static struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes,
+ u16 num_modes,
+ enum hostapd_hw_mode mode)
+{
+ u16 i;
+
+ for (i = 0; i < num_modes; i++) {
+ if (modes[i].mode == mode)
+ return &modes[i];
+ }
+
+ return NULL;
+}
+
+
+static int has_channel(struct hostapd_hw_modes *mode, u8 chan, int *flags)
+{
+ int i;
+
+ for (i = 0; i < mode->num_channels; i++) {
+ if (mode->channels[i].chan == chan) {
+ if (flags)
+ *flags = mode->channels[i].flag;
+ return !(mode->channels[i].flag &
+ (HOSTAPD_CHAN_DISABLED |
+ HOSTAPD_CHAN_PASSIVE_SCAN |
+ HOSTAPD_CHAN_NO_IBSS |
+ HOSTAPD_CHAN_RADAR));
+ }
+ }
+
+ return 0;
+}
+
+
+struct p2p_oper_class_map {
+ enum hostapd_hw_mode mode;
+ u8 op_class;
+ u8 min_chan;
+ u8 max_chan;
+ u8 inc;
+ enum { BW20, BW40PLUS, BW40MINUS } bw;
+};
+
+static int wpas_p2p_setup_channels(struct wpa_supplicant *wpa_s,
+ struct p2p_channels *chan)
+{
+ struct hostapd_hw_modes *mode;
+ int cla, op;
+ struct p2p_oper_class_map op_class[] = {
+ { HOSTAPD_MODE_IEEE80211G, 81, 1, 13, 1, BW20 },
+ { HOSTAPD_MODE_IEEE80211G, 82, 14, 14, 1, BW20 },
+#if 0 /* Do not enable HT40 on 2 GHz for now */
+ { HOSTAPD_MODE_IEEE80211G, 83, 1, 9, 1, BW40PLUS },
+ { HOSTAPD_MODE_IEEE80211G, 84, 5, 13, 1, BW40MINUS },
+#endif
+ { HOSTAPD_MODE_IEEE80211A, 115, 36, 48, 4, BW20 },
+ { HOSTAPD_MODE_IEEE80211A, 124, 149, 161, 4, BW20 },
+ { HOSTAPD_MODE_IEEE80211A, 116, 36, 44, 8, BW40PLUS },
+ { HOSTAPD_MODE_IEEE80211A, 117, 40, 48, 8, BW40MINUS },
+ { HOSTAPD_MODE_IEEE80211A, 126, 149, 157, 8, BW40PLUS },
+ { HOSTAPD_MODE_IEEE80211A, 127, 153, 161, 8, BW40MINUS },
+ { -1, 0, 0, 0, 0, BW20 }
+ };
+
+ if (wpa_s->hw.modes == NULL) {
+ wpa_printf(MSG_DEBUG, "P2P: Driver did not support fetching "
+ "of all supported channels; assume dualband "
+ "support");
+ return wpas_p2p_default_channels(wpa_s, chan);
+ }
+
+ cla = 0;
+
+ for (op = 0; op_class[op].op_class; op++) {
+ struct p2p_oper_class_map *o = &op_class[op];
+ u8 ch;
+ struct p2p_reg_class *reg = NULL;
+
+ mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, o->mode);
+ if (mode == NULL)
+ continue;
+ for (ch = o->min_chan; ch <= o->max_chan; ch += o->inc) {
+ int flag;
+ if (!has_channel(mode, ch, &flag))
+ continue;
+ if (o->bw == BW40MINUS &&
+ (!(flag & HOSTAPD_CHAN_HT40MINUS) ||
+ !has_channel(mode, ch - 4, NULL)))
+ continue;
+ if (o->bw == BW40PLUS &&
+ (!(flag & HOSTAPD_CHAN_HT40PLUS) ||
+ !has_channel(mode, ch + 4, NULL)))
+ continue;
+ if (reg == NULL) {
+ wpa_printf(MSG_DEBUG, "P2P: Add operating "
+ "class %u", o->op_class);
+ reg = &chan->reg_class[cla];
+ cla++;
+ reg->reg_class = o->op_class;
+ }
+ reg->channel[reg->channels] = ch;
+ reg->channels++;
+ }
+ if (reg) {
+ wpa_hexdump(MSG_DEBUG, "P2P: Channels",
+ reg->channel, reg->channels);
+ }
+ }
+
+ chan->reg_classes = cla;
+
+ return 0;
+}
+
+
+static int wpas_get_noa(void *ctx, const u8 *interface_addr, u8 *buf,
+ size_t buf_len)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+ if (os_memcmp(wpa_s->own_addr, interface_addr, ETH_ALEN) == 0)
+ break;
+ }
+ if (wpa_s == NULL)
+ return -1;
+
+ return wpa_drv_get_noa(wpa_s, buf, buf_len);
+}
+
+
+static int wpas_go_connected(void *ctx, const u8 *dev_addr)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+ struct wpa_ssid *ssid = wpa_s->current_ssid;
+ if (ssid == NULL)
+ continue;
+ if (ssid->mode != WPAS_MODE_INFRA)
+ continue;
+ if (wpa_s->wpa_state != WPA_COMPLETED &&
+ wpa_s->wpa_state != WPA_GROUP_HANDSHAKE)
+ continue;
+ if (os_memcmp(wpa_s->go_dev_addr, dev_addr, ETH_ALEN) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * wpas_p2p_init - Initialize P2P module for %wpa_supplicant
+ * @global: Pointer to global data from wpa_supplicant_init()
+ * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
+ * Returns: 0 on success, -1 on failure
+ */
+int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s)
+{
+ struct p2p_config p2p;
+ unsigned int r;
+ int i;
+
+ if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE))
+ return 0;
+
+ if (global->p2p)
+ return 0;
+
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) {
+ struct p2p_params params;
+
+ wpa_printf(MSG_DEBUG, "P2P: Use driver-based P2P management");
+ os_memset(¶ms, 0, sizeof(params));
+ params.dev_name = wpa_s->conf->device_name;
+ os_memcpy(params.pri_dev_type, wpa_s->conf->device_type,
+ WPS_DEV_TYPE_LEN);
+ params.num_sec_dev_types = wpa_s->conf->num_sec_device_types;
+ os_memcpy(params.sec_dev_type,
+ wpa_s->conf->sec_device_type,
+ params.num_sec_dev_types * WPS_DEV_TYPE_LEN);
+
+ if (wpa_drv_p2p_set_params(wpa_s, ¶ms) < 0)
+ return -1;
+
+ return 0;
+ }
+
+ os_memset(&p2p, 0, sizeof(p2p));
+ p2p.msg_ctx = wpa_s;
+ p2p.cb_ctx = wpa_s;
+ p2p.p2p_scan = wpas_p2p_scan;
+ p2p.send_action = wpas_send_action;
+ p2p.send_action_done = wpas_send_action_done;
+ p2p.go_neg_completed = wpas_go_neg_completed;
+ p2p.go_neg_req_rx = wpas_go_neg_req_rx;
+ p2p.dev_found = wpas_dev_found;
+ p2p.dev_lost = wpas_dev_lost;
+ p2p.start_listen = wpas_start_listen;
+ p2p.stop_listen = wpas_stop_listen;
+ p2p.send_probe_resp = wpas_send_probe_resp;
+ p2p.sd_request = wpas_sd_request;
+ p2p.sd_response = wpas_sd_response;
+ p2p.prov_disc_req = wpas_prov_disc_req;
+ p2p.prov_disc_resp = wpas_prov_disc_resp;
+ p2p.prov_disc_fail = wpas_prov_disc_fail;
+ p2p.invitation_process = wpas_invitation_process;
+ p2p.invitation_received = wpas_invitation_received;
+ p2p.invitation_result = wpas_invitation_result;
+ p2p.get_noa = wpas_get_noa;
+ p2p.go_connected = wpas_go_connected;
+
+ os_memcpy(wpa_s->global->p2p_dev_addr, wpa_s->own_addr, ETH_ALEN);
+ os_memcpy(p2p.dev_addr, wpa_s->global->p2p_dev_addr, ETH_ALEN);
+ p2p.dev_name = wpa_s->conf->device_name;
+ p2p.manufacturer = wpa_s->conf->manufacturer;
+ p2p.model_name = wpa_s->conf->model_name;
+ p2p.model_number = wpa_s->conf->model_number;
+ p2p.serial_number = wpa_s->conf->serial_number;
+ if (wpa_s->wps) {
+ os_memcpy(p2p.uuid, wpa_s->wps->uuid, 16);
+ p2p.config_methods = wpa_s->wps->config_methods;
+ }
+
+ if (wpa_s->conf->p2p_listen_reg_class &&
+ wpa_s->conf->p2p_listen_channel) {
+ p2p.reg_class = wpa_s->conf->p2p_listen_reg_class;
+ p2p.channel = wpa_s->conf->p2p_listen_channel;
+ } else {
+ p2p.reg_class = 81;
+ /*
+ * Pick one of the social channels randomly as the listen
+ * channel.
+ */
+ os_get_random((u8 *) &r, sizeof(r));
+ p2p.channel = 1 + (r % 3) * 5;
+ }
+ wpa_printf(MSG_DEBUG, "P2P: Own listen channel: %d", p2p.channel);
+
+ if (wpa_s->conf->p2p_oper_reg_class &&
+ wpa_s->conf->p2p_oper_channel) {
+ p2p.op_reg_class = wpa_s->conf->p2p_oper_reg_class;
+ p2p.op_channel = wpa_s->conf->p2p_oper_channel;
+ p2p.cfg_op_channel = 1;
+ wpa_printf(MSG_DEBUG, "P2P: Configured operating channel: "
+ "%d:%d", p2p.op_reg_class, p2p.op_channel);
+
+ } else {
+ p2p.op_reg_class = 81;
+ /*
+ * Use random operation channel from (1, 6, 11) if no other
+ * preference is indicated.
+ */
+ os_get_random((u8 *) &r, sizeof(r));
+ p2p.op_channel = 1 + (r % 3) * 5;
+ p2p.cfg_op_channel = 0;
+ wpa_printf(MSG_DEBUG, "P2P: Random operating channel: "
+ "%d:%d", p2p.op_reg_class, p2p.op_channel);
+ }
+ if (wpa_s->conf->country[0] && wpa_s->conf->country[1]) {
+ os_memcpy(p2p.country, wpa_s->conf->country, 2);
+ p2p.country[2] = 0x04;
+ } else
+ os_memcpy(p2p.country, "XX\x04", 3);
+
+ if (wpas_p2p_setup_channels(wpa_s, &p2p.channels)) {
+ wpa_printf(MSG_ERROR, "P2P: Failed to configure supported "
+ "channel list");
+ return -1;
+ }
+
+ os_memcpy(p2p.pri_dev_type, wpa_s->conf->device_type,
+ WPS_DEV_TYPE_LEN);
+
+ p2p.num_sec_dev_types = wpa_s->conf->num_sec_device_types;
+ os_memcpy(p2p.sec_dev_type, wpa_s->conf->sec_device_type,
+ p2p.num_sec_dev_types * WPS_DEV_TYPE_LEN);
+
+ p2p.concurrent_operations = !!(wpa_s->drv_flags &
+ WPA_DRIVER_FLAGS_P2P_CONCURRENT);
+
+ p2p.max_peers = 100;
+
+ if (wpa_s->conf->p2p_ssid_postfix) {
+ p2p.ssid_postfix_len =
+ os_strlen(wpa_s->conf->p2p_ssid_postfix);
+ if (p2p.ssid_postfix_len > sizeof(p2p.ssid_postfix))
+ p2p.ssid_postfix_len = sizeof(p2p.ssid_postfix);
+ os_memcpy(p2p.ssid_postfix, wpa_s->conf->p2p_ssid_postfix,
+ p2p.ssid_postfix_len);
+ }
+
+ p2p.p2p_intra_bss = wpa_s->conf->p2p_intra_bss;
+
+ global->p2p = p2p_init(&p2p);
+ if (global->p2p == NULL)
+ return -1;
+
+ for (i = 0; i < MAX_WPS_VENDOR_EXT; i++) {
+ if (wpa_s->conf->wps_vendor_ext[i] == NULL)
+ continue;
+ p2p_add_wps_vendor_extension(
+ global->p2p, wpa_s->conf->wps_vendor_ext[i]);
+ }
+
+ return 0;
+}
+
+
+/**
+ * wpas_p2p_deinit - Deinitialize per-interface P2P data
+ * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
+ *
+ * This function deinitialize per-interface P2P data.
+ */
+void wpas_p2p_deinit(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->driver && wpa_s->drv_priv)
+ wpa_drv_probe_req_report(wpa_s, 0);
+
+ if (wpa_s->go_params) {
+ /* Clear any stored provisioning info */
+ p2p_clear_provisioning_info(
+ wpa_s->global->p2p,
+ wpa_s->go_params->peer_device_addr);
+ }
+
+ os_free(wpa_s->go_params);
+ wpa_s->go_params = NULL;
+ eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s, NULL);
+ eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
+ eloop_cancel_timeout(wpas_p2p_pd_before_join_timeout, wpa_s, NULL);
+ wpa_s->p2p_long_listen = 0;
+ eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL);
+ eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL);
+ wpas_p2p_remove_pending_group_interface(wpa_s);
+
+ /* TODO: remove group interface from the driver if this wpa_s instance
+ * is on top of a P2P group interface */
+}
+
+
+/**
+ * wpas_p2p_deinit_global - Deinitialize global P2P module
+ * @global: Pointer to global data from wpa_supplicant_init()
+ *
+ * This function deinitializes the global (per device) P2P module.
+ */
+void wpas_p2p_deinit_global(struct wpa_global *global)
+{
+ struct wpa_supplicant *wpa_s, *tmp;
+
+ if (global->p2p == NULL)
+ return;
+
+ /* Remove remaining P2P group interfaces */
+ wpa_s = global->ifaces;
+ if (wpa_s)
+ wpas_p2p_service_flush(wpa_s);
+ while (wpa_s && wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE)
+ wpa_s = wpa_s->next;
+ while (wpa_s) {
+ enum wpa_driver_if_type type;
+ tmp = global->ifaces;
+ while (tmp &&
+ (tmp == wpa_s ||
+ tmp->p2p_group_interface == NOT_P2P_GROUP_INTERFACE)) {
+ tmp = tmp->next;
+ }
+ if (tmp == NULL)
+ break;
+ type = wpas_p2p_if_type(tmp->p2p_group_interface);
+ /* Disconnect from the P2P group and deinit the interface */
+ wpas_p2p_disconnect(tmp);
+ }
+
+ /*
+ * Deinit GO data on any possibly remaining interface (if main
+ * interface is used as GO).
+ */
+ for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+ if (wpa_s->ap_iface)
+ wpas_p2p_group_deinit(wpa_s);
+ }
+
+ p2p_deinit(global->p2p);
+ global->p2p = NULL;
+}
+
+
+static int wpas_p2p_create_iface(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->drv_flags &
+ (WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE |
+ WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P))
+ return 1; /* P2P group requires a new interface in every case
+ */
+ if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_CONCURRENT))
+ return 0; /* driver does not support concurrent operations */
+ if (wpa_s->global->ifaces->next)
+ return 1; /* more that one interface already in use */
+ if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
+ return 1; /* this interface is already in use */
+ return 0;
+}
+
+
+static int wpas_p2p_start_go_neg(struct wpa_supplicant *wpa_s,
+ const u8 *peer_addr,
+ enum p2p_wps_method wps_method,
+ int go_intent, const u8 *own_interface_addr,
+ unsigned int force_freq, int persistent_group)
+{
+ if (persistent_group && wpa_s->conf->persistent_reconnect)
+ persistent_group = 2;
+
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) {
+ return wpa_drv_p2p_connect(wpa_s, peer_addr, wps_method,
+ go_intent, own_interface_addr,
+ force_freq, persistent_group);
+ }
+
+ return p2p_connect(wpa_s->global->p2p, peer_addr, wps_method,
+ go_intent, own_interface_addr, force_freq,
+ persistent_group);
+}
+
+
+static int wpas_p2p_auth_go_neg(struct wpa_supplicant *wpa_s,
+ const u8 *peer_addr,
+ enum p2p_wps_method wps_method,
+ int go_intent, const u8 *own_interface_addr,
+ unsigned int force_freq, int persistent_group)
+{
+ if (persistent_group && wpa_s->conf->persistent_reconnect)
+ persistent_group = 2;
+
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+ return -1;
+
+ return p2p_authorize(wpa_s->global->p2p, peer_addr, wps_method,
+ go_intent, own_interface_addr, force_freq,
+ persistent_group);
+}
+
+
+static void wpas_p2p_check_join_scan_limit(struct wpa_supplicant *wpa_s)
+{
+ wpa_s->p2p_join_scan_count++;
+ wpa_printf(MSG_DEBUG, "P2P: Join scan attempt %d",
+ wpa_s->p2p_join_scan_count);
+ if (wpa_s->p2p_join_scan_count > P2P_MAX_JOIN_SCAN_ATTEMPTS) {
+ wpa_printf(MSG_DEBUG, "P2P: Failed to find GO " MACSTR
+ " for join operationg - stop join attempt",
+ MAC2STR(wpa_s->pending_join_iface_addr));
+ eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
+ wpa_msg(wpa_s->parent, MSG_INFO,
+ P2P_EVENT_GROUP_FORMATION_FAILURE);
+ }
+}
+
+
+static void wpas_p2p_pd_before_join_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ if (!wpa_s->pending_pd_before_join)
+ return;
+ /*
+ * Provision Discovery Response may have been lost - try to connect
+ * anyway since we do not need any information from this PD.
+ */
+ wpa_printf(MSG_DEBUG, "P2P: PD timeout for join-existing-group - "
+ "try to connect anyway");
+ wpas_p2p_join_start(wpa_s);
+}
+
+
+static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_results *scan_res)
+{
+ struct wpa_bss *bss;
+ int freq;
+ u8 iface_addr[ETH_ALEN];
+
+ eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
+
+ if (wpa_s->global->p2p_disabled)
+ return;
+
+ wpa_printf(MSG_DEBUG, "P2P: Scan results received (%d BSS) for join",
+ scan_res ? (int) scan_res->num : -1);
+
+ if (scan_res)
+ wpas_p2p_scan_res_handler(wpa_s, scan_res);
+
+ freq = p2p_get_oper_freq(wpa_s->global->p2p,
+ wpa_s->pending_join_iface_addr);
+ if (freq < 0 &&
+ p2p_get_interface_addr(wpa_s->global->p2p,
+ wpa_s->pending_join_dev_addr,
+ iface_addr) == 0 &&
+ os_memcmp(iface_addr, wpa_s->pending_join_dev_addr, ETH_ALEN) != 0)
+ {
+ wpa_printf(MSG_DEBUG, "P2P: Overwrite pending interface "
+ "address for join from " MACSTR " to " MACSTR
+ " based on newly discovered P2P peer entry",
+ MAC2STR(wpa_s->pending_join_iface_addr),
+ MAC2STR(iface_addr));
+ os_memcpy(wpa_s->pending_join_iface_addr, iface_addr,
+ ETH_ALEN);
+
+ freq = p2p_get_oper_freq(wpa_s->global->p2p,
+ wpa_s->pending_join_iface_addr);
+ }
+ if (freq >= 0) {
+ wpa_printf(MSG_DEBUG, "P2P: Target GO operating frequency "
+ "from P2P peer table: %d MHz", freq);
+ }
+ bss = wpa_bss_get_bssid(wpa_s, wpa_s->pending_join_iface_addr);
+ if (bss) {
+ freq = bss->freq;
+ wpa_printf(MSG_DEBUG, "P2P: Target GO operating frequency "
+ "from BSS table: %d MHz", freq);
+ }
+ if (freq > 0) {
+ u16 method;
+
+ wpa_printf(MSG_DEBUG, "P2P: Send Provision Discovery Request "
+ "prior to joining an existing group (GO " MACSTR
+ " freq=%u MHz)",
+ MAC2STR(wpa_s->pending_join_dev_addr), freq);
+ wpa_s->pending_pd_before_join = 1;
+
+ switch (wpa_s->pending_join_wps_method) {
+ case WPS_PIN_DISPLAY:
+ method = WPS_CONFIG_KEYPAD;
+ break;
+ case WPS_PIN_KEYPAD:
+ method = WPS_CONFIG_DISPLAY;
+ break;
+ case WPS_PBC:
+ method = WPS_CONFIG_PUSHBUTTON;
+ break;
+ default:
+ method = 0;
+ break;
+ }
+
+ if ((p2p_get_provisioning_info(wpa_s->global->p2p,
+ wpa_s->pending_join_dev_addr) ==
+ method)) {
+ /*
+ * We have already performed provision discovery for
+ * joining the group. Proceed directly to join
+ * operation without duplicated provision discovery. */
+ wpa_printf(MSG_DEBUG, "P2P: Provisioning discovery "
+ "with " MACSTR " already done - proceed to "
+ "join",
+ MAC2STR(wpa_s->pending_join_dev_addr));
+ wpa_s->pending_pd_before_join = 0;
+ goto start;
+ }
+
+ if (p2p_prov_disc_req(wpa_s->global->p2p,
+ wpa_s->pending_join_dev_addr, method, 1,
+ freq) < 0) {
+ wpa_printf(MSG_DEBUG, "P2P: Failed to send Provision "
+ "Discovery Request before joining an "
+ "existing group");
+ wpa_s->pending_pd_before_join = 0;
+ goto start;
+ }
+
+ /*
+ * Actual join operation will be started from the Action frame
+ * TX status callback (if no ACK is received) or when the
+ * Provision Discovery Response is received. Use a short
+ * timeout as a backup mechanism should the Provision Discovery
+ * Response be lost for any reason.
+ */
+ eloop_cancel_timeout(wpas_p2p_pd_before_join_timeout, wpa_s,
+ NULL);
+ eloop_register_timeout(2, 0, wpas_p2p_pd_before_join_timeout,
+ wpa_s, NULL);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "P2P: Failed to find BSS/GO - try again later");
+ eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
+ eloop_register_timeout(1, 0, wpas_p2p_join_scan, wpa_s, NULL);
+ wpas_p2p_check_join_scan_limit(wpa_s);
+ return;
+
+start:
+ /* Start join operation immediately */
+ wpas_p2p_join_start(wpa_s);
+}
+
+
+static void wpas_p2p_join_scan(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ int ret;
+ struct wpa_driver_scan_params params;
+ struct wpabuf *wps_ie, *ies;
+ size_t ielen;
+
+ os_memset(¶ms, 0, sizeof(params));
+
+ /* P2P Wildcard SSID */
+ params.num_ssids = 1;
+ params.ssids[0].ssid = (u8 *) P2P_WILDCARD_SSID;
+ params.ssids[0].ssid_len = P2P_WILDCARD_SSID_LEN;
+
+ wpa_s->wps->dev.p2p = 1;
+ wps_ie = wps_build_probe_req_ie(0, &wpa_s->wps->dev, wpa_s->wps->uuid,
+ WPS_REQ_ENROLLEE, 0, NULL);
+ if (wps_ie == NULL) {
+ wpas_p2p_scan_res_join(wpa_s, NULL);
+ return;
+ }
+
+ ielen = p2p_scan_ie_buf_len(wpa_s->global->p2p);
+ ies = wpabuf_alloc(wpabuf_len(wps_ie) + ielen);
+ if (ies == NULL) {
+ wpabuf_free(wps_ie);
+ wpas_p2p_scan_res_join(wpa_s, NULL);
+ return;
+ }
+ wpabuf_put_buf(ies, wps_ie);
+ wpabuf_free(wps_ie);
+
+ p2p_scan_ie(wpa_s->global->p2p, ies, NULL);
+
+ params.p2p_probe = 1;
+ params.extra_ies = wpabuf_head(ies);
+ params.extra_ies_len = wpabuf_len(ies);
+
+ /*
+ * Run a scan to update BSS table and start Provision Discovery once
+ * the new scan results become available.
+ */
+ wpa_s->scan_res_handler = wpas_p2p_scan_res_join;
+ ret = wpa_drv_scan(wpa_s, ¶ms);
+
+ wpabuf_free(ies);
+
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "P2P: Failed to start scan for join - "
+ "try again later");
+ eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
+ eloop_register_timeout(1, 0, wpas_p2p_join_scan, wpa_s, NULL);
+ wpas_p2p_check_join_scan_limit(wpa_s);
+ }
+}
+
+
+static int wpas_p2p_join(struct wpa_supplicant *wpa_s, const u8 *iface_addr,
+ const u8 *dev_addr, enum p2p_wps_method wps_method)
+{
+ wpa_printf(MSG_DEBUG, "P2P: Request to join existing group (iface "
+ MACSTR " dev " MACSTR ")",
+ MAC2STR(iface_addr), MAC2STR(dev_addr));
+
+ os_memcpy(wpa_s->pending_join_iface_addr, iface_addr, ETH_ALEN);
+ os_memcpy(wpa_s->pending_join_dev_addr, dev_addr, ETH_ALEN);
+ wpa_s->pending_join_wps_method = wps_method;
+
+ /* Make sure we are not running find during connection establishment */
+ wpas_p2p_stop_find(wpa_s);
+
+ wpa_s->p2p_join_scan_count = 0;
+ wpas_p2p_join_scan(wpa_s, NULL);
+ return 0;
+}
+
+
+static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_supplicant *group;
+ struct p2p_go_neg_results res;
+
+ eloop_cancel_timeout(wpas_p2p_pd_before_join_timeout, wpa_s, NULL);
+ group = wpas_p2p_get_group_iface(wpa_s, 0, 0);
+ if (group == NULL)
+ return -1;
+ if (group != wpa_s) {
+ os_memcpy(group->p2p_pin, wpa_s->p2p_pin,
+ sizeof(group->p2p_pin));
+ group->p2p_wps_method = wpa_s->p2p_wps_method;
+ }
+
+ group->p2p_in_provisioning = 1;
+
+ os_memset(&res, 0, sizeof(res));
+ os_memcpy(res.peer_interface_addr, wpa_s->pending_join_iface_addr,
+ ETH_ALEN);
+ res.wps_method = wpa_s->pending_join_wps_method;
+ if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) {
+ wpa_printf(MSG_DEBUG, "P2P: Cancel remain-on-channel prior to "
+ "starting client");
+ wpa_drv_cancel_remain_on_channel(wpa_s);
+ wpa_s->off_channel_freq = 0;
+ wpa_s->roc_waiting_drv_freq = 0;
+ }
+ wpas_start_wps_enrollee(group, &res);
+
+ /*
+ * Allow a longer timeout for join-a-running-group than normal 15
+ * second group formation timeout since the GO may not have authorized
+ * our connection yet.
+ */
+ eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s, NULL);
+ eloop_register_timeout(60, 0, wpas_p2p_group_formation_timeout,
+ wpa_s, NULL);
+
+ return 0;
+}
+
+
+/**
+ * wpas_p2p_connect - Request P2P Group Formation to be started
+ * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
+ * @peer_addr: Address of the peer P2P Device
+ * @pin: PIN to use during provisioning or %NULL to indicate PBC mode
+ * @persistent_group: Whether to create a persistent group
+ * @join: Whether to join an existing group (as a client) instead of starting
+ * Group Owner negotiation; @peer_addr is BSSID in that case
+ * @auth: Whether to only authorize the connection instead of doing that and
+ * initiating Group Owner negotiation
+ * @go_intent: GO Intent or -1 to use default
+ * @freq: Frequency for the group or 0 for auto-selection
+ * Returns: 0 or new PIN (if pin was %NULL) on success, -1 on unspecified
+ * failure, -2 on failure due to channel not currently available,
+ * -3 if forced channel is not supported
+ */
+int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
+ const char *pin, enum p2p_wps_method wps_method,
+ int persistent_group, int join, int auth, int go_intent,
+ int freq)
+{
+ int force_freq = 0, oper_freq = 0;
+ u8 bssid[ETH_ALEN];
+ int ret = 0;
+ enum wpa_driver_if_type iftype;
+
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+ return -1;
+
+ if (go_intent < 0)
+ go_intent = wpa_s->conf->p2p_go_intent;
+
+ if (!auth)
+ wpa_s->p2p_long_listen = 0;
+
+ wpa_s->p2p_wps_method = wps_method;
+
+ if (pin)
+ os_strlcpy(wpa_s->p2p_pin, pin, sizeof(wpa_s->p2p_pin));
+ else if (wps_method == WPS_PIN_DISPLAY) {
+ ret = wps_generate_pin();
+ os_snprintf(wpa_s->p2p_pin, sizeof(wpa_s->p2p_pin), "%08d",
+ ret);
+ wpa_printf(MSG_DEBUG, "P2P: Randomly generated PIN: %s",
+ wpa_s->p2p_pin);
+ } else
+ wpa_s->p2p_pin[0] = '\0';
+
+ if (join) {
+ u8 iface_addr[ETH_ALEN], dev_addr[ETH_ALEN];
+ if (auth) {
+ wpa_printf(MSG_DEBUG, "P2P: Authorize invitation to "
+ "connect a running group from " MACSTR,
+ MAC2STR(peer_addr));
+ os_memcpy(wpa_s->p2p_auth_invite, peer_addr, ETH_ALEN);
+ return ret;
+ }
+ os_memcpy(dev_addr, peer_addr, ETH_ALEN);
+ if (p2p_get_interface_addr(wpa_s->global->p2p, peer_addr,
+ iface_addr) < 0) {
+ os_memcpy(iface_addr, peer_addr, ETH_ALEN);
+ p2p_get_dev_addr(wpa_s->global->p2p, peer_addr,
+ dev_addr);
+ }
+ if (wpas_p2p_join(wpa_s, iface_addr, dev_addr, wps_method) <
+ 0)
+ return -1;
+ return ret;
+ }
+
+ if (wpa_s->current_ssid && wpa_drv_get_bssid(wpa_s, bssid) == 0 &&
+ wpa_s->assoc_freq)
+ oper_freq = wpa_s->assoc_freq;
+ else {
+ oper_freq = wpa_drv_shared_freq(wpa_s);
+ if (oper_freq < 0)
+ oper_freq = 0;
+ }
+
+ if (freq > 0) {
+ if (!p2p_supported_freq(wpa_s->global->p2p, freq)) {
+ wpa_printf(MSG_DEBUG, "P2P: The forced channel "
+ "(%u MHz) is not supported for P2P uses",
+ freq);
+ return -3;
+ }
+
+ if (oper_freq > 0 && freq != oper_freq &&
+ !(wpa_s->drv_flags &
+ WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT)) {
+ wpa_printf(MSG_DEBUG, "P2P: Cannot start P2P group "
+ "on %u MHz while connected on another "
+ "channel (%u MHz)", freq, oper_freq);
+ return -2;
+ }
+ wpa_printf(MSG_DEBUG, "P2P: Trying to force us to use the "
+ "requested channel (%u MHz)", freq);
+ force_freq = freq;
+ } else if (oper_freq > 0 &&
+ !p2p_supported_freq(wpa_s->global->p2p, oper_freq)) {
+ if (!(wpa_s->drv_flags &
+ WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT)) {
+ wpa_printf(MSG_DEBUG, "P2P: Cannot start P2P group "
+ "while connected on non-P2P supported "
+ "channel (%u MHz)", oper_freq);
+ return -2;
+ }
+ wpa_printf(MSG_DEBUG, "P2P: Current operating channel "
+ "(%u MHz) not available for P2P - try to use "
+ "another channel", oper_freq);
+ force_freq = 0;
+ } else if (oper_freq > 0) {
+ wpa_printf(MSG_DEBUG, "P2P: Trying to force us to use the "
+ "channel we are already using (%u MHz) on another "
+ "interface", oper_freq);
+ force_freq = oper_freq;
+ }
+
+ wpa_s->create_p2p_iface = wpas_p2p_create_iface(wpa_s);
+
+ if (!wpa_s->create_p2p_iface) {
+ if (auth) {
+ if (wpas_p2p_auth_go_neg(wpa_s, peer_addr, wps_method,
+ go_intent, wpa_s->own_addr,
+ force_freq, persistent_group)
+ < 0)
+ return -1;
+ return ret;
+ }
+ if (wpas_p2p_start_go_neg(wpa_s, peer_addr, wps_method,
+ go_intent, wpa_s->own_addr,
+ force_freq, persistent_group) < 0)
+ return -1;
+ return ret;
+ }
+
+ /* Prepare to add a new interface for the group */
+ iftype = WPA_IF_P2P_GROUP;
+ if (go_intent == 15)
+ iftype = WPA_IF_P2P_GO;
+ if (wpas_p2p_add_group_interface(wpa_s, iftype) < 0) {
+ wpa_printf(MSG_ERROR, "P2P: Failed to allocate a new "
+ "interface for the group");
+ return -1;
+ }
+
+ if (auth) {
+ if (wpas_p2p_auth_go_neg(wpa_s, peer_addr, wps_method,
+ go_intent,
+ wpa_s->pending_interface_addr,
+ force_freq, persistent_group) < 0)
+ return -1;
+ return ret;
+ }
+ if (wpas_p2p_start_go_neg(wpa_s, peer_addr, wps_method, go_intent,
+ wpa_s->pending_interface_addr,
+ force_freq, persistent_group) < 0) {
+ wpas_p2p_remove_pending_group_interface(wpa_s);
+ return -1;
+ }
+ return ret;
+}
+
+
+/**
+ * wpas_p2p_remain_on_channel_cb - Indication of remain-on-channel start
+ * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
+ * @freq: Frequency of the channel in MHz
+ * @duration: Duration of the stay on the channel in milliseconds
+ *
+ * This callback is called when the driver indicates that it has started the
+ * requested remain-on-channel duration.
+ */
+void wpas_p2p_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+ unsigned int freq, unsigned int duration)
+{
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+ return;
+ if (wpa_s->off_channel_freq == wpa_s->pending_listen_freq) {
+ p2p_listen_cb(wpa_s->global->p2p, wpa_s->pending_listen_freq,
+ wpa_s->pending_listen_duration);
+ wpa_s->pending_listen_freq = 0;
+ }
+}
+
+
+static int wpas_p2p_listen_start(struct wpa_supplicant *wpa_s,
+ unsigned int timeout)
+{
+ /* Limit maximum Listen state time based on driver limitation. */
+ if (timeout > wpa_s->max_remain_on_chan)
+ timeout = wpa_s->max_remain_on_chan;
+
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+ return wpa_drv_p2p_listen(wpa_s, timeout);
+
+ return p2p_listen(wpa_s->global->p2p, timeout);
+}
+
+
+/**
+ * wpas_p2p_cancel_remain_on_channel_cb - Remain-on-channel timeout
+ * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
+ * @freq: Frequency of the channel in MHz
+ *
+ * This callback is called when the driver indicates that a remain-on-channel
+ * operation has been completed, i.e., the duration on the requested channel
+ * has timed out.
+ */
+void wpas_p2p_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+ unsigned int freq)
+{
+ wpa_printf(MSG_DEBUG, "P2P: Cancel remain-on-channel callback "
+ "(p2p_long_listen=%d ms pending_action_tx=%p)",
+ wpa_s->p2p_long_listen, wpa_s->pending_action_tx);
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+ return;
+ if (p2p_listen_end(wpa_s->global->p2p, freq) > 0)
+ return; /* P2P module started a new operation */
+ if (wpa_s->pending_action_tx)
+ return;
+ if (wpa_s->p2p_long_listen > 0)
+ wpa_s->p2p_long_listen -= wpa_s->max_remain_on_chan;
+ if (wpa_s->p2p_long_listen > 0) {
+ wpa_printf(MSG_DEBUG, "P2P: Continuing long Listen state");
+ wpas_p2p_listen_start(wpa_s, wpa_s->p2p_long_listen);
+ }
+}
+
+
+/**
+ * wpas_p2p_group_remove - Remove a P2P group
+ * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
+ * @ifname: Network interface name of the group interface or "*" to remove all
+ * groups
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to remove a P2P group. This can be used to disconnect
+ * from a group in which the local end is a P2P Client or to end a P2P Group in
+ * case the local end is the Group Owner. If a virtual network interface was
+ * created for this group, that interface will be removed. Otherwise, only the
+ * configured P2P group network will be removed from the interface.
+ */
+int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname)
+{
+ struct wpa_global *global = wpa_s->global;
+
+ if (os_strcmp(ifname, "*") == 0) {
+ struct wpa_supplicant *prev;
+ wpa_s = global->ifaces;
+ while (wpa_s) {
+ prev = wpa_s;
+ wpa_s = wpa_s->next;
+ wpas_p2p_disconnect(prev);
+ }
+ return 0;
+ }
+
+ for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+ if (os_strcmp(wpa_s->ifname, ifname) == 0)
+ break;
+ }
+
+ return wpas_p2p_disconnect(wpa_s);
+}
+
+
+static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s,
+ struct p2p_go_neg_results *params,
+ int freq)
+{
+ u8 bssid[ETH_ALEN];
+ int res;
+
+ os_memset(params, 0, sizeof(*params));
+ params->role_go = 1;
+ if (freq) {
+ wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on forced "
+ "frequency %d MHz", freq);
+ params->freq = freq;
+ } else if (wpa_s->conf->p2p_oper_reg_class == 81 &&
+ wpa_s->conf->p2p_oper_channel >= 1 &&
+ wpa_s->conf->p2p_oper_channel <= 11) {
+ params->freq = 2407 + 5 * wpa_s->conf->p2p_oper_channel;
+ wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on configured "
+ "frequency %d MHz", params->freq);
+ } else if (wpa_s->conf->p2p_oper_reg_class == 115 ||
+ wpa_s->conf->p2p_oper_reg_class == 124) {
+ params->freq = 5000 + 5 * wpa_s->conf->p2p_oper_channel;
+ wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on configured "
+ "frequency %d MHz", params->freq);
+ } else if (wpa_s->conf->p2p_oper_channel == 0 &&
+ wpa_s->best_overall_freq > 0 &&
+ p2p_supported_freq(wpa_s->global->p2p,
+ wpa_s->best_overall_freq)) {
+ params->freq = wpa_s->best_overall_freq;
+ wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best overall "
+ "channel %d MHz", params->freq);
+ } else if (wpa_s->conf->p2p_oper_channel == 0 &&
+ wpa_s->best_24_freq > 0 &&
+ p2p_supported_freq(wpa_s->global->p2p,
+ wpa_s->best_24_freq)) {
+ params->freq = wpa_s->best_24_freq;
+ wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best 2.4 GHz "
+ "channel %d MHz", params->freq);
+ } else if (wpa_s->conf->p2p_oper_channel == 0 &&
+ wpa_s->best_5_freq > 0 &&
+ p2p_supported_freq(wpa_s->global->p2p,
+ wpa_s->best_5_freq)) {
+ params->freq = wpa_s->best_5_freq;
+ wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best 5 GHz "
+ "channel %d MHz", params->freq);
+ } else {
+ params->freq = 2412;
+ wpa_printf(MSG_DEBUG, "P2P: Set GO freq %d MHz (no preference "
+ "known)", params->freq);
+ }
+
+ if (wpa_s->current_ssid && wpa_drv_get_bssid(wpa_s, bssid) == 0 &&
+ wpa_s->assoc_freq && !freq) {
+ wpa_printf(MSG_DEBUG, "P2P: Force GO on the channel we are "
+ "already using");
+ params->freq = wpa_s->assoc_freq;
+ }
+
+ res = wpa_drv_shared_freq(wpa_s);
+ if (res > 0 && !freq) {
+ wpa_printf(MSG_DEBUG, "P2P: Force GO on the channel we are "
+ "already using on a shared interface");
+ params->freq = res;
+ } else if (res > 0 && freq != res &&
+ !(wpa_s->drv_flags &
+ WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT)) {
+ wpa_printf(MSG_DEBUG, "P2P: Cannot start P2P group on %u MHz "
+ "while connected on another channel (%u MHz)",
+ freq, res);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static struct wpa_supplicant *
+wpas_p2p_get_group_iface(struct wpa_supplicant *wpa_s, int addr_allocated,
+ int go)
+{
+ struct wpa_supplicant *group_wpa_s;
+
+ if (!wpas_p2p_create_iface(wpa_s))
+ return wpa_s;
+
+ if (wpas_p2p_add_group_interface(wpa_s, go ? WPA_IF_P2P_GO :
+ WPA_IF_P2P_CLIENT) < 0)
+ return NULL;
+ group_wpa_s = wpas_p2p_init_group_interface(wpa_s, go);
+ if (group_wpa_s == NULL) {
+ wpas_p2p_remove_pending_group_interface(wpa_s);
+ return NULL;
+ }
+
+ return group_wpa_s;
+}
+
+
+/**
+ * wpas_p2p_group_add - Add a new P2P group with local end as Group Owner
+ * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
+ * @persistent_group: Whether to create a persistent group
+ * @freq: Frequency for the group or 0 to indicate no hardcoding
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function creates a new P2P group with the local end as the Group Owner,
+ * i.e., without using Group Owner Negotiation.
+ */
+int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group,
+ int freq)
+{
+ struct p2p_go_neg_results params;
+ unsigned int r;
+
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+ return -1;
+
+ /* Make sure we are not running find during connection establishment */
+ wpa_printf(MSG_DEBUG, "P2P: Stop any on-going P2P FIND");
+ wpas_p2p_stop_find(wpa_s);
+
+ if (freq == 2) {
+ wpa_printf(MSG_DEBUG, "P2P: Request to start GO on 2.4 GHz "
+ "band");
+ if (wpa_s->best_24_freq > 0 &&
+ p2p_supported_freq(wpa_s->global->p2p,
+ wpa_s->best_24_freq)) {
+ freq = wpa_s->best_24_freq;
+ wpa_printf(MSG_DEBUG, "P2P: Use best 2.4 GHz band "
+ "channel: %d MHz", freq);
+ } else {
+ os_get_random((u8 *) &r, sizeof(r));
+ freq = 2412 + (r % 3) * 25;
+ wpa_printf(MSG_DEBUG, "P2P: Use random 2.4 GHz band "
+ "channel: %d MHz", freq);
+ }
+ }
+
+ if (freq == 5) {
+ wpa_printf(MSG_DEBUG, "P2P: Request to start GO on 5 GHz "
+ "band");
+ if (wpa_s->best_5_freq > 0 &&
+ p2p_supported_freq(wpa_s->global->p2p,
+ wpa_s->best_5_freq)) {
+ freq = wpa_s->best_5_freq;
+ wpa_printf(MSG_DEBUG, "P2P: Use best 5 GHz band "
+ "channel: %d MHz", freq);
+ } else {
+ os_get_random((u8 *) &r, sizeof(r));
+ freq = 5180 + (r % 4) * 20;
+ if (!p2p_supported_freq(wpa_s->global->p2p, freq)) {
+ wpa_printf(MSG_DEBUG, "P2P: Could not select "
+ "5 GHz channel for P2P group");
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "P2P: Use random 5 GHz band "
+ "channel: %d MHz", freq);
+ }
+ }
+
+ if (freq > 0 && !p2p_supported_freq(wpa_s->global->p2p, freq)) {
+ wpa_printf(MSG_DEBUG, "P2P: The forced channel for GO "
+ "(%u MHz) is not supported for P2P uses",
+ freq);
+ return -1;
+ }
+
+ if (wpas_p2p_init_go_params(wpa_s, ¶ms, freq))
+ return -1;
+ if (params.freq &&
+ !p2p_supported_freq(wpa_s->global->p2p, params.freq)) {
+ wpa_printf(MSG_DEBUG, "P2P: The selected channel for GO "
+ "(%u MHz) is not supported for P2P uses",
+ params.freq);
+ return -1;
+ }
+ p2p_go_params(wpa_s->global->p2p, ¶ms);
+ params.persistent_group = persistent_group;
+
+ wpa_s = wpas_p2p_get_group_iface(wpa_s, 0, 1);
+ if (wpa_s == NULL)
+ return -1;
+ wpas_start_wps_go(wpa_s, ¶ms, 0);
+
+ return 0;
+}
+
+
+static int wpas_start_p2p_client(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *params, int addr_allocated)
+{
+ struct wpa_ssid *ssid;
+
+ wpa_s = wpas_p2p_get_group_iface(wpa_s, addr_allocated, 0);
+ if (wpa_s == NULL)
+ return -1;
+
+ wpa_supplicant_ap_deinit(wpa_s);
+
+ ssid = wpa_config_add_network(wpa_s->conf);
+ if (ssid == NULL)
+ return -1;
+ wpa_config_set_network_defaults(ssid);
+ ssid->temporary = 1;
+ ssid->proto = WPA_PROTO_RSN;
+ ssid->pairwise_cipher = WPA_CIPHER_CCMP;
+ ssid->group_cipher = WPA_CIPHER_CCMP;
+ ssid->key_mgmt = WPA_KEY_MGMT_PSK;
+ ssid->ssid = os_malloc(params->ssid_len);
+ if (ssid->ssid == NULL) {
+ wpa_config_remove_network(wpa_s->conf, ssid->id);
+ return -1;
+ }
+ os_memcpy(ssid->ssid, params->ssid, params->ssid_len);
+ ssid->ssid_len = params->ssid_len;
+ ssid->p2p_group = 1;
+ ssid->export_keys = 1;
+ if (params->psk_set) {
+ os_memcpy(ssid->psk, params->psk, 32);
+ ssid->psk_set = 1;
+ }
+ if (params->passphrase)
+ ssid->passphrase = os_strdup(params->passphrase);
+
+ wpa_supplicant_select_network(wpa_s, ssid);
+
+ wpa_s->show_group_started = 1;
+
+ return 0;
+}
+
+
+int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid, int addr_allocated,
+ int freq)
+{
+ struct p2p_go_neg_results params;
+ int go = 0;
+
+ if (ssid->disabled != 2 || ssid->ssid == NULL)
+ return -1;
+
+ if (wpas_get_p2p_group(wpa_s, ssid->ssid, ssid->ssid_len, &go) &&
+ go == (ssid->mode == WPAS_MODE_P2P_GO)) {
+ wpa_printf(MSG_DEBUG, "P2P: Requested persistent group is "
+ "already running");
+ return 0;
+ }
+
+ /* Make sure we are not running find during connection establishment */
+ wpas_p2p_stop_find(wpa_s);
+
+ if (ssid->mode == WPAS_MODE_INFRA)
+ return wpas_start_p2p_client(wpa_s, ssid, addr_allocated);
+
+ if (ssid->mode != WPAS_MODE_P2P_GO)
+ return -1;
+
+ if (wpas_p2p_init_go_params(wpa_s, ¶ms, freq))
+ return -1;
+
+ params.role_go = 1;
+ if (ssid->passphrase == NULL ||
+ os_strlen(ssid->passphrase) >= sizeof(params.passphrase)) {
+ wpa_printf(MSG_DEBUG, "P2P: Invalid passphrase in persistent "
+ "group");
+ return -1;
+ }
+ os_strlcpy(params.passphrase, ssid->passphrase,
+ sizeof(params.passphrase));
+ os_memcpy(params.ssid, ssid->ssid, ssid->ssid_len);
+ params.ssid_len = ssid->ssid_len;
+ params.persistent_group = 1;
+
+ wpa_s = wpas_p2p_get_group_iface(wpa_s, addr_allocated, 1);
+ if (wpa_s == NULL)
+ return -1;
+
+ wpas_start_wps_go(wpa_s, ¶ms, 0);
+
+ return 0;
+}
+
+
+static void wpas_p2p_ie_update(void *ctx, struct wpabuf *beacon_ies,
+ struct wpabuf *proberesp_ies)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ if (wpa_s->ap_iface) {
+ struct hostapd_data *hapd = wpa_s->ap_iface->bss[0];
+ if (!(hapd->conf->p2p & P2P_GROUP_OWNER)) {
+ wpabuf_free(beacon_ies);
+ wpabuf_free(proberesp_ies);
+ return;
+ }
+ if (beacon_ies) {
+ wpabuf_free(hapd->p2p_beacon_ie);
+ hapd->p2p_beacon_ie = beacon_ies;
+ }
+ wpabuf_free(hapd->p2p_probe_resp_ie);
+ hapd->p2p_probe_resp_ie = proberesp_ies;
+ } else {
+ wpabuf_free(beacon_ies);
+ wpabuf_free(proberesp_ies);
+ }
+ wpa_supplicant_ap_update_beacon(wpa_s);
+}
+
+
+static void wpas_p2p_idle_update(void *ctx, int idle)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ if (!wpa_s->ap_iface)
+ return;
+ wpa_printf(MSG_DEBUG, "P2P: GO - group %sidle", idle ? "" : "not ");
+ if (idle)
+ wpas_p2p_set_group_idle_timeout(wpa_s);
+ else
+ eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL);
+}
+
+
+struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s,
+ int persistent_group,
+ int group_formation)
+{
+ struct p2p_group *group;
+ struct p2p_group_config *cfg;
+
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+ return NULL;
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+ return NULL;
+
+ cfg = os_zalloc(sizeof(*cfg));
+ if (cfg == NULL)
+ return NULL;
+
+ if (persistent_group && wpa_s->conf->persistent_reconnect)
+ cfg->persistent_group = 2;
+ else if (persistent_group)
+ cfg->persistent_group = 1;
+ os_memcpy(cfg->interface_addr, wpa_s->own_addr, ETH_ALEN);
+ if (wpa_s->max_stations &&
+ wpa_s->max_stations < wpa_s->conf->max_num_sta)
+ cfg->max_clients = wpa_s->max_stations;
+ else
+ cfg->max_clients = wpa_s->conf->max_num_sta;
+ cfg->cb_ctx = wpa_s;
+ cfg->ie_update = wpas_p2p_ie_update;
+ cfg->idle_update = wpas_p2p_idle_update;
+
+ group = p2p_group_init(wpa_s->global->p2p, cfg);
+ if (group == NULL)
+ os_free(cfg);
+ if (!group_formation)
+ p2p_group_notif_formation_done(group);
+ wpa_s->p2p_group = group;
+ return group;
+}
+
+
+void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
+ int registrar)
+{
+ struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+ if (!wpa_s->p2p_in_provisioning) {
+ wpa_printf(MSG_DEBUG, "P2P: Ignore WPS success event - P2P "
+ "provisioning not in progress");
+ return;
+ }
+
+ if (ssid && ssid->mode == WPAS_MODE_INFRA) {
+ u8 go_dev_addr[ETH_ALEN];
+ os_memcpy(go_dev_addr, wpa_s->bssid, ETH_ALEN);
+ wpas_p2p_persistent_group(wpa_s, go_dev_addr, ssid->ssid,
+ ssid->ssid_len);
+ /* Clear any stored provisioning info */
+ p2p_clear_provisioning_info(wpa_s->global->p2p, go_dev_addr);
+ }
+
+ eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->parent,
+ NULL);
+ if (wpa_s->global->p2p)
+ p2p_wps_success_cb(wpa_s->global->p2p, peer_addr);
+ else if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+ wpa_drv_wps_success_cb(wpa_s, peer_addr);
+ wpas_group_formation_completed(wpa_s, 1);
+}
+
+
+void wpas_p2p_wps_failed(struct wpa_supplicant *wpa_s,
+ struct wps_event_fail *fail)
+{
+ if (!wpa_s->p2p_in_provisioning) {
+ wpa_printf(MSG_DEBUG, "P2P: Ignore WPS fail event - P2P "
+ "provisioning not in progress");
+ return;
+ }
+
+ if (wpa_s->go_params) {
+ p2p_clear_provisioning_info(
+ wpa_s->global->p2p,
+ wpa_s->go_params->peer_device_addr);
+ }
+
+ wpas_notify_p2p_wps_failed(wpa_s, fail);
+}
+
+
+int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
+ const char *config_method, int join)
+{
+ u16 config_methods;
+
+ if (os_strncmp(config_method, "display", 7) == 0)
+ config_methods = WPS_CONFIG_DISPLAY;
+ else if (os_strncmp(config_method, "keypad", 6) == 0)
+ config_methods = WPS_CONFIG_KEYPAD;
+ else if (os_strncmp(config_method, "pbc", 3) == 0 ||
+ os_strncmp(config_method, "pushbutton", 10) == 0)
+ config_methods = WPS_CONFIG_PUSHBUTTON;
+ else {
+ wpa_printf(MSG_DEBUG, "P2P: Unknown config method");
+ return -1;
+ }
+
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) {
+ return wpa_drv_p2p_prov_disc_req(wpa_s, peer_addr,
+ config_methods, join);
+ }
+
+ if (wpa_s->global->p2p == NULL || wpa_s->global->p2p_disabled)
+ return -1;
+
+ return p2p_prov_disc_req(wpa_s->global->p2p, peer_addr,
+ config_methods, join, 0);
+}
+
+
+int wpas_p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf,
+ char *end)
+{
+ return p2p_scan_result_text(ies, ies_len, buf, end);
+}
+
+
+static void wpas_p2p_clear_pending_action_tx(struct wpa_supplicant *wpa_s)
+{
+ if (!wpa_s->pending_action_tx)
+ return;
+
+ wpa_printf(MSG_DEBUG, "P2P: Drop pending Action TX due to new "
+ "operation request");
+ wpabuf_free(wpa_s->pending_action_tx);
+ wpa_s->pending_action_tx = NULL;
+}
+
+
+int wpas_p2p_find(struct wpa_supplicant *wpa_s, unsigned int timeout,
+ enum p2p_discovery_type type,
+ unsigned int num_req_dev_types, const u8 *req_dev_types,
+ const u8 *dev_id)
+{
+ wpas_p2p_clear_pending_action_tx(wpa_s);
+ wpa_s->p2p_long_listen = 0;
+
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+ return wpa_drv_p2p_find(wpa_s, timeout, type);
+
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+ return -1;
+
+ wpa_supplicant_cancel_sched_scan(wpa_s);
+
+ return p2p_find(wpa_s->global->p2p, timeout, type,
+ num_req_dev_types, req_dev_types, dev_id);
+}
+
+
+void wpas_p2p_stop_find(struct wpa_supplicant *wpa_s)
+{
+ wpas_p2p_clear_pending_action_tx(wpa_s);
+ wpa_s->p2p_long_listen = 0;
+ eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL);
+ eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
+ wpa_s->p2p_cb_on_scan_complete = 0;
+
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) {
+ wpa_drv_p2p_stop_find(wpa_s);
+ return;
+ }
+
+ if (wpa_s->global->p2p)
+ p2p_stop_find(wpa_s->global->p2p);
+
+ wpas_p2p_remove_pending_group_interface(wpa_s);
+}
+
+
+static void wpas_p2p_long_listen_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ wpa_s->p2p_long_listen = 0;
+}
+
+
+int wpas_p2p_listen(struct wpa_supplicant *wpa_s, unsigned int timeout)
+{
+ int res;
+
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+ return -1;
+
+ wpa_supplicant_cancel_sched_scan(wpa_s);
+ wpas_p2p_clear_pending_action_tx(wpa_s);
+
+ if (timeout == 0) {
+ /*
+ * This is a request for unlimited Listen state. However, at
+ * least for now, this is mapped to a Listen state for one
+ * hour.
+ */
+ timeout = 3600;
+ }
+ eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL);
+ wpa_s->p2p_long_listen = 0;
+
+ /*
+ * Stop previous find/listen operation to avoid trying to request a new
+ * remain-on-channel operation while the driver is still running the
+ * previous one.
+ */
+ if (wpa_s->global->p2p)
+ p2p_stop_find(wpa_s->global->p2p);
+
+ res = wpas_p2p_listen_start(wpa_s, timeout * 1000);
+ if (res == 0 && timeout * 1000 > wpa_s->max_remain_on_chan) {
+ wpa_s->p2p_long_listen = timeout * 1000;
+ eloop_register_timeout(timeout, 0,
+ wpas_p2p_long_listen_timeout,
+ wpa_s, NULL);
+ }
+
+ return res;
+}
+
+
+int wpas_p2p_assoc_req_ie(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
+ u8 *buf, size_t len, int p2p_group)
+{
+ struct wpabuf *p2p_ie;
+ int ret;
+
+ if (wpa_s->global->p2p_disabled)
+ return -1;
+ if (wpa_s->global->p2p == NULL)
+ return -1;
+ if (bss == NULL)
+ return -1;
+
+ p2p_ie = wpa_bss_get_vendor_ie_multi(bss, P2P_IE_VENDOR_TYPE);
+ ret = p2p_assoc_req_ie(wpa_s->global->p2p, bss->bssid, buf, len,
+ p2p_group, p2p_ie);
+ wpabuf_free(p2p_ie);
+
+ return ret;
+}
+
+
+int wpas_p2p_probe_req_rx(struct wpa_supplicant *wpa_s, const u8 *addr,
+ const u8 *dst, const u8 *bssid,
+ const u8 *ie, size_t ie_len)
+{
+ if (wpa_s->global->p2p_disabled)
+ return 0;
+ if (wpa_s->global->p2p == NULL)
+ return 0;
+
+ return p2p_probe_req_rx(wpa_s->global->p2p, addr, dst, bssid,
+ ie, ie_len);
+}
+
+
+void wpas_p2p_rx_action(struct wpa_supplicant *wpa_s, const u8 *da,
+ const u8 *sa, const u8 *bssid,
+ u8 category, const u8 *data, size_t len, int freq)
+{
+ if (wpa_s->global->p2p_disabled)
+ return;
+ if (wpa_s->global->p2p == NULL)
+ return;
+
+ p2p_rx_action(wpa_s->global->p2p, da, sa, bssid, category, data, len,
+ freq);
+}
+
+
+void wpas_p2p_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ies)
+{
+ if (wpa_s->global->p2p_disabled)
+ return;
+ if (wpa_s->global->p2p == NULL)
+ return;
+
+ p2p_scan_ie(wpa_s->global->p2p, ies, NULL);
+}
+
+
+void wpas_p2p_group_deinit(struct wpa_supplicant *wpa_s)
+{
+ p2p_group_deinit(wpa_s->p2p_group);
+ wpa_s->p2p_group = NULL;
+
+ wpa_s->ap_configured_cb = NULL;
+ wpa_s->ap_configured_cb_ctx = NULL;
+ wpa_s->ap_configured_cb_data = NULL;
+ wpa_s->connect_without_scan = NULL;
+}
+
+
+int wpas_p2p_reject(struct wpa_supplicant *wpa_s, const u8 *addr)
+{
+ wpa_s->p2p_long_listen = 0;
+
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+ return wpa_drv_p2p_reject(wpa_s, addr);
+
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+ return -1;
+
+ return p2p_reject(wpa_s->global->p2p, addr);
+}
+
+
+/* Invite to reinvoke a persistent group */
+int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
+ struct wpa_ssid *ssid, const u8 *go_dev_addr)
+{
+ enum p2p_invite_role role;
+ u8 *bssid = NULL;
+
+ if (ssid->mode == WPAS_MODE_P2P_GO) {
+ role = P2P_INVITE_ROLE_GO;
+ if (peer_addr == NULL) {
+ wpa_printf(MSG_DEBUG, "P2P: Missing peer "
+ "address in invitation command");
+ return -1;
+ }
+ if (wpas_p2p_create_iface(wpa_s)) {
+ if (wpas_p2p_add_group_interface(wpa_s,
+ WPA_IF_P2P_GO) < 0) {
+ wpa_printf(MSG_ERROR, "P2P: Failed to "
+ "allocate a new interface for the "
+ "group");
+ return -1;
+ }
+ bssid = wpa_s->pending_interface_addr;
+ } else
+ bssid = wpa_s->own_addr;
+ } else {
+ role = P2P_INVITE_ROLE_CLIENT;
+ peer_addr = ssid->bssid;
+ }
+ wpa_s->pending_invite_ssid_id = ssid->id;
+
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+ return wpa_drv_p2p_invite(wpa_s, peer_addr, role, bssid,
+ ssid->ssid, ssid->ssid_len,
+ go_dev_addr, 1);
+
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+ return -1;
+
+ return p2p_invite(wpa_s->global->p2p, peer_addr, role, bssid,
+ ssid->ssid, ssid->ssid_len, 0, go_dev_addr, 1);
+}
+
+
+/* Invite to join an active group */
+int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname,
+ const u8 *peer_addr, const u8 *go_dev_addr)
+{
+ struct wpa_global *global = wpa_s->global;
+ enum p2p_invite_role role;
+ u8 *bssid = NULL;
+ struct wpa_ssid *ssid;
+ int persistent;
+
+ for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+ if (os_strcmp(wpa_s->ifname, ifname) == 0)
+ break;
+ }
+ if (wpa_s == NULL) {
+ wpa_printf(MSG_DEBUG, "P2P: Interface '%s' not found", ifname);
+ return -1;
+ }
+
+ ssid = wpa_s->current_ssid;
+ if (ssid == NULL) {
+ wpa_printf(MSG_DEBUG, "P2P: No current SSID to use for "
+ "invitation");
+ return -1;
+ }
+
+ persistent = ssid->p2p_persistent_group &&
+ wpas_p2p_get_persistent(wpa_s->parent, peer_addr,
+ ssid->ssid, ssid->ssid_len);
+
+ if (ssid->mode == WPAS_MODE_P2P_GO) {
+ role = P2P_INVITE_ROLE_ACTIVE_GO;
+ bssid = wpa_s->own_addr;
+ if (go_dev_addr == NULL)
+ go_dev_addr = wpa_s->global->p2p_dev_addr;
+ } else {
+ role = P2P_INVITE_ROLE_CLIENT;
+ if (wpa_s->wpa_state < WPA_ASSOCIATED) {
+ wpa_printf(MSG_DEBUG, "P2P: Not associated - cannot "
+ "invite to current group");
+ return -1;
+ }
+ bssid = wpa_s->bssid;
+ if (go_dev_addr == NULL &&
+ !is_zero_ether_addr(wpa_s->go_dev_addr))
+ go_dev_addr = wpa_s->go_dev_addr;
+ }
+ wpa_s->parent->pending_invite_ssid_id = -1;
+
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+ return wpa_drv_p2p_invite(wpa_s, peer_addr, role, bssid,
+ ssid->ssid, ssid->ssid_len,
+ go_dev_addr, persistent);
+
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+ return -1;
+
+ return p2p_invite(wpa_s->global->p2p, peer_addr, role, bssid,
+ ssid->ssid, ssid->ssid_len, wpa_s->assoc_freq,
+ go_dev_addr, persistent);
+}
+
+
+void wpas_p2p_completed(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_ssid *ssid = wpa_s->current_ssid;
+ const char *ssid_txt;
+ u8 go_dev_addr[ETH_ALEN];
+ int network_id = -1;
+ int persistent;
+ int freq;
+
+ if (!wpa_s->show_group_started || !ssid)
+ return;
+
+ wpa_s->show_group_started = 0;
+
+ ssid_txt = wpa_ssid_txt(ssid->ssid, ssid->ssid_len);
+ os_memset(go_dev_addr, 0, ETH_ALEN);
+ if (ssid->bssid_set)
+ os_memcpy(go_dev_addr, ssid->bssid, ETH_ALEN);
+ persistent = wpas_p2p_persistent_group(wpa_s, go_dev_addr, ssid->ssid,
+ ssid->ssid_len);
+ os_memcpy(wpa_s->go_dev_addr, go_dev_addr, ETH_ALEN);
+
+ if (wpa_s->global->p2p_group_formation == wpa_s)
+ wpa_s->global->p2p_group_formation = NULL;
+
+ freq = wpa_s->current_bss ? wpa_s->current_bss->freq :
+ (int) wpa_s->assoc_freq;
+ if (ssid->passphrase == NULL && ssid->psk_set) {
+ char psk[65];
+ wpa_snprintf_hex(psk, sizeof(psk), ssid->psk, 32);
+ wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED
+ "%s client ssid=\"%s\" freq=%d psk=%s go_dev_addr="
+ MACSTR "%s",
+ wpa_s->ifname, ssid_txt, freq, psk,
+ MAC2STR(go_dev_addr),
+ persistent ? " [PERSISTENT]" : "");
+ } else {
+ wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED
+ "%s client ssid=\"%s\" freq=%d passphrase=\"%s\" "
+ "go_dev_addr=" MACSTR "%s",
+ wpa_s->ifname, ssid_txt, freq,
+ ssid->passphrase ? ssid->passphrase : "",
+ MAC2STR(go_dev_addr),
+ persistent ? " [PERSISTENT]" : "");
+ }
+
+ if (persistent)
+ network_id = wpas_p2p_store_persistent_group(wpa_s->parent,
+ ssid, go_dev_addr);
+ if (network_id < 0)
+ network_id = ssid->id;
+ wpas_notify_p2p_group_started(wpa_s, ssid, network_id, 1);
+}
+
+
+int wpas_p2p_presence_req(struct wpa_supplicant *wpa_s, u32 duration1,
+ u32 interval1, u32 duration2, u32 interval2)
+{
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+ return -1;
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+ return -1;
+
+ if (wpa_s->wpa_state < WPA_ASSOCIATED ||
+ wpa_s->current_ssid == NULL ||
+ wpa_s->current_ssid->mode != WPAS_MODE_INFRA)
+ return -1;
+
+ return p2p_presence_req(wpa_s->global->p2p, wpa_s->bssid,
+ wpa_s->own_addr, wpa_s->assoc_freq,
+ duration1, interval1, duration2, interval2);
+}
+
+
+int wpas_p2p_ext_listen(struct wpa_supplicant *wpa_s, unsigned int period,
+ unsigned int interval)
+{
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+ return -1;
+
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+ return -1;
+
+ return p2p_ext_listen(wpa_s->global->p2p, period, interval);
+}
+
+
+static int wpas_p2p_is_client(struct wpa_supplicant *wpa_s)
+{
+ return wpa_s->current_ssid != NULL &&
+ wpa_s->current_ssid->p2p_group &&
+ wpa_s->current_ssid->mode == WPAS_MODE_INFRA;
+}
+
+
+static void wpas_p2p_group_idle_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+
+ if (wpa_s->conf->p2p_group_idle == 0 && !wpas_p2p_is_client(wpa_s)) {
+ wpa_printf(MSG_DEBUG, "P2P: Ignore group idle timeout - "
+ "disabled");
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "P2P: Group idle timeout reached - terminate "
+ "group");
+ wpa_s->removal_reason = P2P_GROUP_REMOVAL_IDLE_TIMEOUT;
+ wpas_p2p_group_delete(wpa_s);
+}
+
+
+static void wpas_p2p_set_group_idle_timeout(struct wpa_supplicant *wpa_s)
+{
+ unsigned int timeout;
+
+ eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL);
+ if (wpa_s->current_ssid == NULL || !wpa_s->current_ssid->p2p_group)
+ return;
+
+ timeout = wpa_s->conf->p2p_group_idle;
+ if (wpa_s->current_ssid->mode == WPAS_MODE_INFRA &&
+ (timeout == 0 || timeout > P2P_MAX_CLIENT_IDLE))
+ timeout = P2P_MAX_CLIENT_IDLE;
+
+ if (timeout == 0)
+ return;
+
+ if (wpa_s->p2p_in_provisioning) {
+ /*
+ * Use the normal group formation timeout during the
+ * provisioning phase to avoid terminating this process too
+ * early due to group idle timeout.
+ */
+ wpa_printf(MSG_DEBUG, "P2P: Do not use P2P group idle timeout "
+ "during provisioning");
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "P2P: Set P2P group idle timeout to %u seconds",
+ timeout);
+ eloop_register_timeout(timeout, 0, wpas_p2p_group_idle_timeout,
+ wpa_s, NULL);
+}
+
+
+void wpas_p2p_deauth_notif(struct wpa_supplicant *wpa_s, const u8 *bssid,
+ u16 reason_code, const u8 *ie, size_t ie_len)
+{
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+ return;
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+ return;
+
+ p2p_deauth_notif(wpa_s->global->p2p, bssid, reason_code, ie, ie_len);
+}
+
+
+void wpas_p2p_disassoc_notif(struct wpa_supplicant *wpa_s, const u8 *bssid,
+ u16 reason_code, const u8 *ie, size_t ie_len)
+{
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+ return;
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+ return;
+
+ p2p_disassoc_notif(wpa_s->global->p2p, bssid, reason_code, ie, ie_len);
+}
+
+
+void wpas_p2p_update_config(struct wpa_supplicant *wpa_s)
+{
+ struct p2p_data *p2p = wpa_s->global->p2p;
+
+ if (p2p == NULL)
+ return;
+
+ if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE))
+ return;
+
+ if (wpa_s->conf->changed_parameters & CFG_CHANGED_DEVICE_NAME)
+ p2p_set_dev_name(p2p, wpa_s->conf->device_name);
+
+ if (wpa_s->conf->changed_parameters & CFG_CHANGED_DEVICE_TYPE)
+ p2p_set_pri_dev_type(p2p, wpa_s->conf->device_type);
+
+ if (wpa_s->wps &&
+ (wpa_s->conf->changed_parameters & CFG_CHANGED_CONFIG_METHODS))
+ p2p_set_config_methods(p2p, wpa_s->wps->config_methods);
+
+ if (wpa_s->wps && (wpa_s->conf->changed_parameters & CFG_CHANGED_UUID))
+ p2p_set_uuid(p2p, wpa_s->wps->uuid);
+
+ if (wpa_s->conf->changed_parameters & CFG_CHANGED_WPS_STRING) {
+ p2p_set_manufacturer(p2p, wpa_s->conf->manufacturer);
+ p2p_set_model_name(p2p, wpa_s->conf->model_name);
+ p2p_set_model_number(p2p, wpa_s->conf->model_number);
+ p2p_set_serial_number(p2p, wpa_s->conf->serial_number);
+ }
+
+ if (wpa_s->conf->changed_parameters & CFG_CHANGED_SEC_DEVICE_TYPE)
+ p2p_set_sec_dev_types(p2p,
+ (void *) wpa_s->conf->sec_device_type,
+ wpa_s->conf->num_sec_device_types);
+
+ if (wpa_s->conf->changed_parameters & CFG_CHANGED_VENDOR_EXTENSION) {
+ int i;
+ p2p_remove_wps_vendor_extensions(p2p);
+ for (i = 0; i < MAX_WPS_VENDOR_EXT; i++) {
+ if (wpa_s->conf->wps_vendor_ext[i] == NULL)
+ continue;
+ p2p_add_wps_vendor_extension(
+ p2p, wpa_s->conf->wps_vendor_ext[i]);
+ }
+ }
+
+ if ((wpa_s->conf->changed_parameters & CFG_CHANGED_COUNTRY) &&
+ wpa_s->conf->country[0] && wpa_s->conf->country[1]) {
+ char country[3];
+ country[0] = wpa_s->conf->country[0];
+ country[1] = wpa_s->conf->country[1];
+ country[2] = 0x04;
+ p2p_set_country(p2p, country);
+ }
+
+ if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_SSID_POSTFIX) {
+ p2p_set_ssid_postfix(p2p, (u8 *) wpa_s->conf->p2p_ssid_postfix,
+ wpa_s->conf->p2p_ssid_postfix ?
+ os_strlen(wpa_s->conf->p2p_ssid_postfix) :
+ 0);
+ }
+
+ if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_INTRA_BSS)
+ p2p_set_intra_bss_dist(p2p, wpa_s->conf->p2p_intra_bss);
+
+ if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_LISTEN_CHANNEL) {
+ u8 reg_class, channel;
+ int ret;
+ unsigned int r;
+ if (wpa_s->conf->p2p_listen_reg_class &&
+ wpa_s->conf->p2p_listen_channel) {
+ reg_class = wpa_s->conf->p2p_listen_reg_class;
+ channel = wpa_s->conf->p2p_listen_channel;
+ } else {
+ reg_class = 81;
+ /*
+ * Pick one of the social channels randomly as the
+ * listen channel.
+ */
+ os_get_random((u8 *) &r, sizeof(r));
+ channel = 1 + (r % 3) * 5;
+ }
+ ret = p2p_set_listen_channel(p2p, reg_class, channel);
+ if (ret)
+ wpa_printf(MSG_ERROR, "P2P: Own listen channel update "
+ "failed: %d", ret);
+ }
+ if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_OPER_CHANNEL) {
+ u8 op_reg_class, op_channel, cfg_op_channel;
+ int ret = 0;
+ unsigned int r;
+ if (wpa_s->conf->p2p_oper_reg_class &&
+ wpa_s->conf->p2p_oper_channel) {
+ op_reg_class = wpa_s->conf->p2p_oper_reg_class;
+ op_channel = wpa_s->conf->p2p_oper_channel;
+ cfg_op_channel = 1;
+ } else {
+ op_reg_class = 81;
+ /*
+ * Use random operation channel from (1, 6, 11)
+ *if no other preference is indicated.
+ */
+ os_get_random((u8 *) &r, sizeof(r));
+ op_channel = 1 + (r % 3) * 5;
+ cfg_op_channel = 0;
+ }
+ ret = p2p_set_oper_channel(p2p, op_reg_class, op_channel,
+ cfg_op_channel);
+ if (ret)
+ wpa_printf(MSG_ERROR, "P2P: Own oper channel update "
+ "failed: %d", ret);
+ }
+
+ if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_PREF_CHAN) {
+ if (p2p_set_pref_chan(p2p, wpa_s->conf->num_p2p_pref_chan,
+ wpa_s->conf->p2p_pref_chan) < 0) {
+ wpa_printf(MSG_ERROR, "P2P: Preferred channel list "
+ "update failed");
+ }
+ }
+}
+
+
+int wpas_p2p_set_noa(struct wpa_supplicant *wpa_s, u8 count, int start,
+ int duration)
+{
+ if (!wpa_s->ap_iface)
+ return -1;
+ return hostapd_p2p_set_noa(wpa_s->ap_iface->bss[0], count, start,
+ duration);
+}
+
+
+int wpas_p2p_set_cross_connect(struct wpa_supplicant *wpa_s, int enabled)
+{
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+ return -1;
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+ return -1;
+
+ wpa_s->global->cross_connection = enabled;
+ p2p_set_cross_connect(wpa_s->global->p2p, enabled);
+
+ if (!enabled) {
+ struct wpa_supplicant *iface;
+
+ for (iface = wpa_s->global->ifaces; iface; iface = iface->next)
+ {
+ if (iface->cross_connect_enabled == 0)
+ continue;
+
+ iface->cross_connect_enabled = 0;
+ iface->cross_connect_in_use = 0;
+ wpa_msg(iface->parent, MSG_INFO,
+ P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s",
+ iface->ifname, iface->cross_connect_uplink);
+ }
+ }
+
+ return 0;
+}
+
+
+static void wpas_p2p_enable_cross_connect(struct wpa_supplicant *uplink)
+{
+ struct wpa_supplicant *iface;
+
+ if (!uplink->global->cross_connection)
+ return;
+
+ for (iface = uplink->global->ifaces; iface; iface = iface->next) {
+ if (!iface->cross_connect_enabled)
+ continue;
+ if (os_strcmp(uplink->ifname, iface->cross_connect_uplink) !=
+ 0)
+ continue;
+ if (iface->ap_iface == NULL)
+ continue;
+ if (iface->cross_connect_in_use)
+ continue;
+
+ iface->cross_connect_in_use = 1;
+ wpa_msg(iface->parent, MSG_INFO,
+ P2P_EVENT_CROSS_CONNECT_ENABLE "%s %s",
+ iface->ifname, iface->cross_connect_uplink);
+ }
+}
+
+
+static void wpas_p2p_disable_cross_connect(struct wpa_supplicant *uplink)
+{
+ struct wpa_supplicant *iface;
+
+ for (iface = uplink->global->ifaces; iface; iface = iface->next) {
+ if (!iface->cross_connect_enabled)
+ continue;
+ if (os_strcmp(uplink->ifname, iface->cross_connect_uplink) !=
+ 0)
+ continue;
+ if (!iface->cross_connect_in_use)
+ continue;
+
+ wpa_msg(iface->parent, MSG_INFO,
+ P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s",
+ iface->ifname, iface->cross_connect_uplink);
+ iface->cross_connect_in_use = 0;
+ }
+}
+
+
+void wpas_p2p_notif_connected(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->ap_iface || wpa_s->current_ssid == NULL ||
+ wpa_s->current_ssid->mode != WPAS_MODE_INFRA ||
+ wpa_s->cross_connect_disallowed)
+ wpas_p2p_disable_cross_connect(wpa_s);
+ else
+ wpas_p2p_enable_cross_connect(wpa_s);
+ if (!wpa_s->ap_iface)
+ eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL);
+}
+
+
+void wpas_p2p_notif_disconnected(struct wpa_supplicant *wpa_s)
+{
+ wpas_p2p_disable_cross_connect(wpa_s);
+ if (!wpa_s->ap_iface &&
+ !eloop_is_timeout_registered(wpas_p2p_group_idle_timeout,
+ wpa_s, NULL))
+ wpas_p2p_set_group_idle_timeout(wpa_s);
+}
+
+
+static void wpas_p2p_cross_connect_setup(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_supplicant *iface;
+
+ if (!wpa_s->global->cross_connection)
+ return;
+
+ for (iface = wpa_s->global->ifaces; iface; iface = iface->next) {
+ if (iface == wpa_s)
+ continue;
+ if (iface->drv_flags &
+ WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE)
+ continue;
+ if (iface->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE)
+ continue;
+
+ wpa_s->cross_connect_enabled = 1;
+ os_strlcpy(wpa_s->cross_connect_uplink, iface->ifname,
+ sizeof(wpa_s->cross_connect_uplink));
+ wpa_printf(MSG_DEBUG, "P2P: Enable cross connection from "
+ "%s to %s whenever uplink is available",
+ wpa_s->ifname, wpa_s->cross_connect_uplink);
+
+ if (iface->ap_iface || iface->current_ssid == NULL ||
+ iface->current_ssid->mode != WPAS_MODE_INFRA ||
+ iface->cross_connect_disallowed ||
+ iface->wpa_state != WPA_COMPLETED)
+ break;
+
+ wpa_s->cross_connect_in_use = 1;
+ wpa_msg(wpa_s->parent, MSG_INFO,
+ P2P_EVENT_CROSS_CONNECT_ENABLE "%s %s",
+ wpa_s->ifname, wpa_s->cross_connect_uplink);
+ break;
+ }
+}
+
+
+int wpas_p2p_notif_pbc_overlap(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->p2p_group_interface != P2P_GROUP_INTERFACE_CLIENT &&
+ !wpa_s->p2p_in_provisioning)
+ return 0; /* not P2P client operation */
+
+ wpa_printf(MSG_DEBUG, "P2P: Terminate connection due to WPS PBC "
+ "session overlap");
+ if (wpa_s != wpa_s->parent)
+ wpa_msg_ctrl(wpa_s->parent, MSG_INFO, WPS_EVENT_OVERLAP);
+
+ if (wpa_s->global->p2p)
+ p2p_group_formation_failed(wpa_s->global->p2p);
+
+ eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
+ wpa_s->parent, NULL);
+
+ wpas_group_formation_completed(wpa_s, 0);
+ return 1;
+}
+
+
+void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s)
+{
+ struct p2p_channels chan;
+
+ if (wpa_s->global == NULL || wpa_s->global->p2p == NULL)
+ return;
+
+ os_memset(&chan, 0, sizeof(chan));
+ if (wpas_p2p_setup_channels(wpa_s, &chan)) {
+ wpa_printf(MSG_ERROR, "P2P: Failed to update supported "
+ "channel list");
+ return;
+ }
+
+ p2p_update_channel_list(wpa_s->global->p2p, &chan);
+}
+
+
+int wpas_p2p_cancel(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_global *global = wpa_s->global;
+ int found = 0;
+ const u8 *peer;
+
+ if (global->p2p == NULL)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "P2P: Request to cancel group formation");
+
+ if (wpa_s->pending_interface_name[0] &&
+ !is_zero_ether_addr(wpa_s->pending_interface_addr))
+ found = 1;
+
+ peer = p2p_get_go_neg_peer(global->p2p);
+ if (peer) {
+ wpa_printf(MSG_DEBUG, "P2P: Unauthorize pending GO Neg peer "
+ MACSTR, MAC2STR(peer));
+ p2p_unauthorize(global->p2p, peer);
+ found = 1;
+ }
+
+ wpas_p2p_stop_find(wpa_s);
+
+ for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+ if (wpa_s == global->p2p_group_formation &&
+ (wpa_s->p2p_in_provisioning ||
+ wpa_s->parent->pending_interface_type ==
+ WPA_IF_P2P_CLIENT)) {
+ wpa_printf(MSG_DEBUG, "P2P: Interface %s in group "
+ "formation found - cancelling",
+ wpa_s->ifname);
+ found = 1;
+ eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
+ wpa_s->parent, NULL);
+ wpas_p2p_group_delete(wpa_s);
+ break;
+ }
+ }
+
+ if (!found) {
+ wpa_printf(MSG_DEBUG, "P2P: No ongoing group formation found");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+void wpas_p2p_interface_unavailable(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->current_ssid == NULL || !wpa_s->current_ssid->p2p_group)
+ return;
+
+ wpa_printf(MSG_DEBUG, "P2P: Remove group due to driver resource not "
+ "being available anymore");
+ wpa_s->removal_reason = P2P_GROUP_REMOVAL_UNAVAILABLE;
+ wpas_p2p_group_delete(wpa_s);
+}
+
+
+void wpas_p2p_update_best_channels(struct wpa_supplicant *wpa_s,
+ int freq_24, int freq_5, int freq_overall)
+{
+ struct p2p_data *p2p = wpa_s->global->p2p;
+ if (p2p == NULL || (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT))
+ return;
+ p2p_set_best_channels(p2p, freq_24, freq_5, freq_overall);
+}
+
+
+int wpas_p2p_unauthorize(struct wpa_supplicant *wpa_s, const char *addr)
+{
+ u8 peer[ETH_ALEN];
+ struct p2p_data *p2p = wpa_s->global->p2p;
+
+ if (p2p == NULL || (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT))
+ return -1;
+
+ if (hwaddr_aton(addr, peer))
+ return -1;
+
+ return p2p_unauthorize(p2p, peer);
+}
+
+
+/**
+ * wpas_p2p_disconnect - Disconnect from a P2P Group
+ * @wpa_s: Pointer to wpa_supplicant data
+ * Returns: 0 on success, -1 on failure
+ *
+ * This can be used to disconnect from a group in which the local end is a P2P
+ * Client or to end a P2P Group in case the local end is the Group Owner. If a
+ * virtual network interface was created for this group, that interface will be
+ * removed. Otherwise, only the configured P2P group network will be removed
+ * from the interface.
+ */
+int wpas_p2p_disconnect(struct wpa_supplicant *wpa_s)
+{
+
+ if (wpa_s == NULL)
+ return -1;
+
+ wpa_s->removal_reason = P2P_GROUP_REMOVAL_REQUESTED;
+ wpas_p2p_group_delete(wpa_s);
+
+ return 0;
+}
+
+
+int wpas_p2p_in_progress(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+ return 0;
+
+ return p2p_in_progress(wpa_s->global->p2p);
+}
+
+
+void wpas_p2p_network_removed(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+ if (wpa_s->p2p_in_provisioning && ssid->p2p_group &&
+ eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
+ wpa_s->parent, NULL) > 0) {
+ /**
+ * Remove the network by scheduling the group formation
+ * timeout to happen immediately. The teardown code
+ * needs to be scheduled to run asynch later so that we
+ * don't delete data from under ourselves unexpectedly.
+ * Calling wpas_p2p_group_formation_timeout directly
+ * causes a series of crashes in WPS failure scenarios.
+ */
+ wpa_printf(MSG_DEBUG, "P2P: Canceled group formation due to "
+ "P2P group network getting removed");
+ eloop_register_timeout(0, 0, wpas_p2p_group_formation_timeout,
+ wpa_s->parent, NULL);
+ }
+}
+
+
+struct wpa_ssid * wpas_p2p_get_persistent(struct wpa_supplicant *wpa_s,
+ const u8 *addr, const u8 *ssid,
+ size_t ssid_len)
+{
+ struct wpa_ssid *s;
+ size_t i;
+
+ for (s = wpa_s->conf->ssid; s; s = s->next) {
+ if (s->disabled != 2)
+ continue;
+ if (ssid &&
+ (ssid_len != s->ssid_len ||
+ os_memcmp(ssid, s->ssid, ssid_len) != 0))
+ continue;
+ if (os_memcmp(s->bssid, addr, ETH_ALEN) == 0)
+ return s; /* peer is GO in the persistent group */
+ if (s->mode != WPAS_MODE_P2P_GO || s->p2p_client_list == NULL)
+ continue;
+ for (i = 0; i < s->num_p2p_clients; i++) {
+ if (os_memcmp(s->p2p_client_list + i * ETH_ALEN,
+ addr, ETH_ALEN) == 0)
+ return s; /* peer is P2P client in persistent
+ * group */
+ }
+ }
+
+ return NULL;
+}
+
+
+void wpas_p2p_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s,
+ const u8 *addr)
+{
+ if (addr == NULL)
+ return;
+ wpas_p2p_add_persistent_group_client(wpa_s, addr);
+}
--- /dev/null
+/*
+ * wpa_supplicant - P2P
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef P2P_SUPPLICANT_H
+#define P2P_SUPPLICANT_H
+
+enum p2p_wps_method;
+struct p2p_go_neg_results;
+enum p2p_send_action_result;
+struct p2p_peer_info;
+
+int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s);
+void wpas_p2p_deinit(struct wpa_supplicant *wpa_s);
+void wpas_p2p_deinit_global(struct wpa_global *global);
+int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
+ const char *pin, enum p2p_wps_method wps_method,
+ int persistent_group, int join, int auth, int go_intent,
+ int freq);
+void wpas_p2p_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+ unsigned int freq, unsigned int duration);
+void wpas_p2p_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+ unsigned int freq);
+int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname);
+int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group,
+ int freq);
+int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid, int addr_allocated,
+ int freq);
+struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s,
+ int persistent_group,
+ int group_formation);
+void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
+ int registrar);
+int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
+ const char *config_method, int join);
+void wpas_send_action_tx_status(struct wpa_supplicant *wpa_s, const u8 *dst,
+ const u8 *data, size_t data_len,
+ enum p2p_send_action_result result);
+int wpas_p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf,
+ char *end);
+enum p2p_discovery_type;
+int wpas_p2p_find(struct wpa_supplicant *wpa_s, unsigned int timeout,
+ enum p2p_discovery_type type,
+ unsigned int num_req_dev_types, const u8 *req_dev_types,
+ const u8 *dev_id);
+void wpas_p2p_stop_find(struct wpa_supplicant *wpa_s);
+int wpas_p2p_listen(struct wpa_supplicant *wpa_s, unsigned int timeout);
+int wpas_p2p_assoc_req_ie(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
+ u8 *buf, size_t len, int p2p_group);
+int wpas_p2p_probe_req_rx(struct wpa_supplicant *wpa_s, const u8 *addr,
+ const u8 *dst, const u8 *bssid,
+ const u8 *ie, size_t ie_len);
+void wpas_p2p_rx_action(struct wpa_supplicant *wpa_s, const u8 *da,
+ const u8 *sa, const u8 *bssid,
+ u8 category, const u8 *data, size_t len, int freq);
+void wpas_p2p_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ies);
+void wpas_p2p_group_deinit(struct wpa_supplicant *wpa_s);
+void wpas_dev_found(void *ctx, const u8 *addr,
+ const struct p2p_peer_info *info,
+ int new_device);
+void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res);
+void wpas_go_neg_req_rx(void *ctx, const u8 *src, u16 dev_passwd_id);
+void wpas_prov_disc_req(void *ctx, const u8 *peer, u16 config_methods,
+ const u8 *dev_addr, const u8 *pri_dev_type,
+ const char *dev_name, u16 supp_config_methods,
+ u8 dev_capab, u8 group_capab, const u8 *group_id,
+ size_t group_id_len);
+void wpas_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods);
+void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token,
+ u16 update_indic, const u8 *tlvs, size_t tlvs_len);
+void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic,
+ const u8 *tlvs, size_t tlvs_len);
+void * wpas_p2p_sd_request(struct wpa_supplicant *wpa_s, const u8 *dst,
+ const struct wpabuf *tlvs);
+void * wpas_p2p_sd_request_upnp(struct wpa_supplicant *wpa_s, const u8 *dst,
+ u8 version, const char *query);
+int wpas_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s, void *req);
+void wpas_p2p_sd_response(struct wpa_supplicant *wpa_s, int freq,
+ const u8 *dst, u8 dialog_token,
+ const struct wpabuf *resp_tlvs);
+void wpas_p2p_sd_service_update(struct wpa_supplicant *wpa_s);
+void wpas_p2p_service_flush(struct wpa_supplicant *wpa_s);
+int wpas_p2p_service_add_bonjour(struct wpa_supplicant *wpa_s,
+ struct wpabuf *query, struct wpabuf *resp);
+int wpas_p2p_service_del_bonjour(struct wpa_supplicant *wpa_s,
+ const struct wpabuf *query);
+int wpas_p2p_service_add_upnp(struct wpa_supplicant *wpa_s, u8 version,
+ const char *service);
+int wpas_p2p_service_del_upnp(struct wpa_supplicant *wpa_s, u8 version,
+ const char *service);
+int wpas_p2p_reject(struct wpa_supplicant *wpa_s, const u8 *addr);
+int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
+ struct wpa_ssid *ssid, const u8 *go_dev_addr);
+int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname,
+ const u8 *peer_addr, const u8 *go_dev_addr);
+void wpas_p2p_completed(struct wpa_supplicant *wpa_s);
+int wpas_p2p_presence_req(struct wpa_supplicant *wpa_s, u32 duration1,
+ u32 interval1, u32 duration2, u32 interval2);
+int wpas_p2p_ext_listen(struct wpa_supplicant *wpa_s, unsigned int period,
+ unsigned int interval);
+void wpas_p2p_deauth_notif(struct wpa_supplicant *wpa_s, const u8 *bssid,
+ u16 reason_code, const u8 *ie, size_t ie_len);
+void wpas_p2p_disassoc_notif(struct wpa_supplicant *wpa_s, const u8 *bssid,
+ u16 reason_code, const u8 *ie, size_t ie_len);
+void wpas_p2p_update_config(struct wpa_supplicant *wpa_s);
+int wpas_p2p_set_noa(struct wpa_supplicant *wpa_s, u8 count, int start,
+ int duration);
+int wpas_p2p_set_cross_connect(struct wpa_supplicant *wpa_s, int enabled);
+void wpas_p2p_notif_connected(struct wpa_supplicant *wpa_s);
+void wpas_p2p_notif_disconnected(struct wpa_supplicant *wpa_s);
+int wpas_p2p_notif_pbc_overlap(struct wpa_supplicant *wpa_s);
+void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s);
+int wpas_p2p_cancel(struct wpa_supplicant *wpa_s);
+void wpas_p2p_interface_unavailable(struct wpa_supplicant *wpa_s);
+void wpas_p2p_update_best_channels(struct wpa_supplicant *wpa_s,
+ int freq_24, int freq_5, int freq_overall);
+int wpas_p2p_unauthorize(struct wpa_supplicant *wpa_s, const char *addr);
+int wpas_p2p_disconnect(struct wpa_supplicant *wpa_s);
+void wpas_p2p_wps_failed(struct wpa_supplicant *wpa_s,
+ struct wps_event_fail *fail);
+int wpas_p2p_in_progress(struct wpa_supplicant *wpa_s);
+void wpas_p2p_network_removed(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid);
+struct wpa_ssid * wpas_p2p_get_persistent(struct wpa_supplicant *wpa_s,
+ const u8 *addr, const u8 *ssid,
+ size_t ssid_len);
+void wpas_p2p_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s,
+ const u8 *addr);
+
+#endif /* P2P_SUPPLICANT_H */
#include "config.h"
#include "wpa_supplicant_i.h"
#include "driver_i.h"
-#include "mlme.h"
#include "wps_supplicant.h"
+#include "p2p_supplicant.h"
+#include "p2p/p2p.h"
#include "notify.h"
#include "bss.h"
#include "scan.h"
wpas_notify_network_changed(wpa_s);
}
wpa_supplicant_initiate_eapol(wpa_s);
- wpa_printf(MSG_DEBUG, "Already associated with a configured network - "
- "generating associated event");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Already associated with a configured "
+ "network - generating associated event");
os_memset(&data, 0, sizeof(data));
wpa_supplicant_event(wpa_s, EVENT_ASSOC, &data);
}
#ifdef CONFIG_WPS
-static int wpas_wps_in_use(struct wpa_config *conf,
+static int wpas_wps_in_use(struct wpa_supplicant *wpa_s,
enum wps_request_type *req_type)
{
struct wpa_ssid *ssid;
int wps = 0;
- for (ssid = conf->ssid; ssid; ssid = ssid->next) {
+ for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
if (!(ssid->key_mgmt & WPA_KEY_MGMT_WPS))
continue;
return 2;
}
+#ifdef CONFIG_P2P
+ if (!wpa_s->global->p2p_disabled && wpa_s->global->p2p) {
+ wpa_s->wps->dev.p2p = 1;
+ if (!wps) {
+ wps = 1;
+ *req_type = WPS_REQ_ENROLLEE_INFO;
+ }
+ }
+#endif /* CONFIG_P2P */
+
return wps;
}
#endif /* CONFIG_WPS */
int wpa_supplicant_enabled_networks(struct wpa_config *conf)
{
struct wpa_ssid *ssid = conf->ssid;
+ int count = 0;
while (ssid) {
if (!ssid->disabled)
- return 1;
+ count++;
ssid = ssid->next;
}
- return 0;
+ return count;
}
/* ap_scan=2 mode - try to associate with each SSID. */
if (ssid == NULL) {
- wpa_printf(MSG_DEBUG, "wpa_supplicant_scan: Reached "
- "end of scan list - go back to beginning");
+ wpa_dbg(wpa_s, MSG_DEBUG, "wpa_supplicant_assoc_try: Reached "
+ "end of scan list - go back to beginning");
wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN;
wpa_supplicant_req_scan(wpa_s, 0, 0);
return;
wpa_supplicant_notify_scanning(wpa_s, 1);
- if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)
- ret = ieee80211_sta_req_scan(wpa_s, params);
- else
- ret = wpa_drv_scan(wpa_s, params);
-
+ ret = wpa_drv_scan(wpa_s, params);
if (ret) {
wpa_supplicant_notify_scanning(wpa_s, 0);
wpas_notify_scan_done(wpa_s, 0);
}
+static void
+wpa_supplicant_delayed_sched_scan_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "Starting delayed sched scan");
+
+ if (wpa_supplicant_req_sched_scan(wpa_s))
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+}
+
+
+static void
+wpa_supplicant_sched_scan_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "Sched scan timeout - stopping it");
+
+ wpa_s->sched_scan_timed_out = 1;
+ wpa_supplicant_cancel_sched_scan(wpa_s);
+}
+
+
+static int
+wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s,
+ struct wpa_driver_scan_params *params,
+ int interval)
+{
+ int ret;
+
+ wpa_supplicant_notify_scanning(wpa_s, 1);
+ ret = wpa_drv_sched_scan(wpa_s, params, interval * 1000);
+ if (ret)
+ wpa_supplicant_notify_scanning(wpa_s, 0);
+ else
+ wpa_s->sched_scanning = 1;
+
+ return ret;
+}
+
+
+static int wpa_supplicant_stop_sched_scan(struct wpa_supplicant *wpa_s)
+{
+ int ret;
+
+ ret = wpa_drv_stop_sched_scan(wpa_s);
+ if (ret) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "stopping sched_scan failed!");
+ /* TODO: what to do if stopping fails? */
+ return -1;
+ }
+
+ return ret;
+}
+
+
static struct wpa_driver_scan_filter *
wpa_supplicant_build_filter_ssids(struct wpa_config *conf, size_t *num_ssids)
{
}
-static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
+static void wpa_supplicant_optimize_freqs(
+ struct wpa_supplicant *wpa_s, struct wpa_driver_scan_params *params)
{
- struct wpa_supplicant *wpa_s = eloop_ctx;
- struct wpa_ssid *ssid;
- int scan_req = 0, ret;
- struct wpabuf *wps_ie = NULL;
+#ifdef CONFIG_P2P
+ if (params->freqs == NULL && wpa_s->p2p_in_provisioning &&
+ wpa_s->go_params) {
+ /* Optimize provisioning state scan based on GO information */
+ if (wpa_s->p2p_in_provisioning < 5 &&
+ wpa_s->go_params->freq > 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Scan only GO "
+ "preferred frequency %d MHz",
+ wpa_s->go_params->freq);
+ params->freqs = os_zalloc(2 * sizeof(int));
+ if (params->freqs)
+ params->freqs[0] = wpa_s->go_params->freq;
+ } else if (wpa_s->p2p_in_provisioning < 8 &&
+ wpa_s->go_params->freq_list[0]) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Scan only common "
+ "channels");
+ int_array_concat(¶ms->freqs,
+ wpa_s->go_params->freq_list);
+ if (params->freqs)
+ int_array_sort_unique(params->freqs);
+ }
+ wpa_s->p2p_in_provisioning++;
+ }
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_WPS
+ if (params->freqs == NULL && wpa_s->after_wps && wpa_s->wps_freq) {
+ /*
+ * Optimize post-provisioning scan based on channel used
+ * during provisioning.
+ */
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPS: Scan only frequency %u MHz "
+ "that was used during provisioning", wpa_s->wps_freq);
+ params->freqs = os_zalloc(2 * sizeof(int));
+ if (params->freqs)
+ params->freqs[0] = wpa_s->wps_freq;
+ wpa_s->after_wps--;
+ }
+
+#endif /* CONFIG_WPS */
+}
+
+
+#ifdef CONFIG_INTERWORKING
+static void wpas_add_interworking_elements(struct wpa_supplicant *wpa_s,
+ struct wpabuf *buf)
+{
+ if (wpa_s->conf->interworking == 0)
+ return;
+
+ wpabuf_put_u8(buf, WLAN_EID_EXT_CAPAB);
+ wpabuf_put_u8(buf, 4);
+ wpabuf_put_u8(buf, 0x00);
+ wpabuf_put_u8(buf, 0x00);
+ wpabuf_put_u8(buf, 0x00);
+ wpabuf_put_u8(buf, 0x80); /* Bit 31 - Interworking */
+
+ wpabuf_put_u8(buf, WLAN_EID_INTERWORKING);
+ wpabuf_put_u8(buf, is_zero_ether_addr(wpa_s->conf->hessid) ? 1 :
+ 1 + ETH_ALEN);
+ wpabuf_put_u8(buf, wpa_s->conf->access_network_type);
+ /* No Venue Info */
+ if (!is_zero_ether_addr(wpa_s->conf->hessid))
+ wpabuf_put_data(buf, wpa_s->conf->hessid, ETH_ALEN);
+}
+#endif /* CONFIG_INTERWORKING */
+
+
+static struct wpabuf *
+wpa_supplicant_extra_ies(struct wpa_supplicant *wpa_s,
+ struct wpa_driver_scan_params *params)
+{
+ struct wpabuf *extra_ie = NULL;
#ifdef CONFIG_WPS
int wps = 0;
enum wps_request_type req_type = WPS_REQ_ENROLLEE_INFO;
#endif /* CONFIG_WPS */
+
+#ifdef CONFIG_INTERWORKING
+ if (wpa_s->conf->interworking &&
+ wpabuf_resize(&extra_ie, 100) == 0)
+ wpas_add_interworking_elements(wpa_s, extra_ie);
+#endif /* CONFIG_INTERWORKING */
+
+#ifdef CONFIG_WPS
+ wps = wpas_wps_in_use(wpa_s, &req_type);
+
+ if (wps) {
+ struct wpabuf *wps_ie;
+ wps_ie = wps_build_probe_req_ie(wps == 2, &wpa_s->wps->dev,
+ wpa_s->wps->uuid, req_type,
+ 0, NULL);
+ if (wps_ie) {
+ if (wpabuf_resize(&extra_ie, wpabuf_len(wps_ie)) == 0)
+ wpabuf_put_buf(extra_ie, wps_ie);
+ wpabuf_free(wps_ie);
+ }
+ }
+
+#ifdef CONFIG_P2P
+ if (wps) {
+ size_t ielen = p2p_scan_ie_buf_len(wpa_s->global->p2p);
+ if (wpabuf_resize(&extra_ie, ielen) == 0)
+ wpas_p2p_scan_ie(wpa_s, extra_ie);
+ }
+#endif /* CONFIG_P2P */
+
+#endif /* CONFIG_WPS */
+
+ return extra_ie;
+}
+
+
+static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ struct wpa_ssid *ssid;
+ int scan_req = 0, ret;
+ struct wpabuf *extra_ie;
struct wpa_driver_scan_params params;
size_t max_ssids;
enum wpa_states prev_state;
+ if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Skip scan - interface disabled");
+ return;
+ }
+
if (wpa_s->disconnected && !wpa_s->scan_req) {
wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
return;
if (!wpa_supplicant_enabled_networks(wpa_s->conf) &&
!wpa_s->scan_req) {
- wpa_printf(MSG_DEBUG, "No enabled networks - do not scan");
+ wpa_dbg(wpa_s, MSG_DEBUG, "No enabled networks - do not scan");
wpa_supplicant_set_state(wpa_s, WPA_INACTIVE);
return;
}
if (wpa_s->conf->ap_scan != 0 &&
(wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED)) {
- wpa_printf(MSG_DEBUG, "Using wired authentication - "
- "overriding ap_scan configuration");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Using wired authentication - "
+ "overriding ap_scan configuration");
wpa_s->conf->ap_scan = 0;
wpas_notify_ap_scan_changed(wpa_s);
}
return;
}
- if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) ||
- wpa_s->conf->ap_scan == 2)
+#ifdef CONFIG_P2P
+ if (wpas_p2p_in_progress(wpa_s)) {
+ if (wpa_s->wpa_state == WPA_SCANNING) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Delay station mode scan "
+ "while P2P operation is in progress");
+ wpa_supplicant_req_scan(wpa_s, 5, 0);
+ } else {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Do not request scan while "
+ "P2P operation is in progress");
+ }
+ return;
+ }
+#endif /* CONFIG_P2P */
+
+ if (wpa_s->conf->ap_scan == 2)
max_ssids = 1;
else {
max_ssids = wpa_s->max_scan_ssids;
max_ssids = WPAS_MAX_SCAN_SSIDS;
}
-#ifdef CONFIG_WPS
- wps = wpas_wps_in_use(wpa_s->conf, &req_type);
-#endif /* CONFIG_WPS */
-
scan_req = wpa_s->scan_req;
wpa_s->scan_req = 0;
wpa_s->wpa_state == WPA_INACTIVE)
wpa_supplicant_set_state(wpa_s, WPA_SCANNING);
+ if (scan_req != 2 && wpa_s->connect_without_scan) {
+ for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+ if (ssid == wpa_s->connect_without_scan)
+ break;
+ }
+ wpa_s->connect_without_scan = NULL;
+ if (ssid) {
+ wpa_printf(MSG_DEBUG, "Start a pre-selected network "
+ "without scan step");
+ wpa_supplicant_associate(wpa_s, NULL, ssid);
+ return;
+ }
+ }
+
/* Find the starting point from which to continue scanning */
ssid = wpa_s->conf->ssid;
if (wpa_s->prev_scan_ssid != WILDCARD_SSID_SCAN) {
}
}
- if (scan_req != 2 && (wpa_s->conf->ap_scan == 2 ||
- wpa_s->connect_without_scan)) {
- wpa_s->connect_without_scan = 0;
+ if (scan_req != 2 && wpa_s->conf->ap_scan == 2) {
+ wpa_s->connect_without_scan = NULL;
wpa_supplicant_assoc_try(wpa_s, ssid);
return;
} else if (wpa_s->conf->ap_scan == 2) {
if (ssid) {
wpa_s->prev_scan_ssid = ssid;
if (max_ssids > 1) {
- wpa_printf(MSG_DEBUG, "Include wildcard SSID in the "
- "scan request");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Include wildcard SSID in "
+ "the scan request");
params.num_ssids++;
}
- wpa_printf(MSG_DEBUG, "Starting AP scan for specific SSID(s)");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Starting AP scan for specific "
+ "SSID(s)");
} else {
wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN;
params.num_ssids++;
- wpa_printf(MSG_DEBUG, "Starting AP scan for wildcard SSID");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Starting AP scan for wildcard "
+ "SSID");
}
-#ifdef CONFIG_WPS
- if (params.freqs == NULL && wpa_s->after_wps && wpa_s->wps_freq) {
- /*
- * Optimize post-provisioning scan based on channel used
- * during provisioning.
- */
- wpa_printf(MSG_DEBUG, "WPS: Scan only frequency %u MHz that "
- "was used during provisioning", wpa_s->wps_freq);
- params.freqs = os_zalloc(2 * sizeof(int));
- if (params.freqs)
- params.freqs[0] = wpa_s->wps_freq;
- wpa_s->after_wps--;
- }
+ wpa_supplicant_optimize_freqs(wpa_s, ¶ms);
+ extra_ie = wpa_supplicant_extra_ies(wpa_s, ¶ms);
- if (wps) {
- wps_ie = wps_build_probe_req_ie(wps == 2, &wpa_s->wps->dev,
- wpa_s->wps->uuid, req_type);
- if (wps_ie) {
- params.extra_ies = wpabuf_head(wps_ie);
- params.extra_ies_len = wpabuf_len(wps_ie);
- }
- }
-#endif /* CONFIG_WPS */
+ if (params.freqs == NULL && wpa_s->next_scan_freqs) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Optimize scan based on previously "
+ "generated frequency list");
+ params.freqs = wpa_s->next_scan_freqs;
+ } else
+ os_free(wpa_s->next_scan_freqs);
+ wpa_s->next_scan_freqs = NULL;
params.filter_ssids = wpa_supplicant_build_filter_ssids(
wpa_s->conf, ¶ms.num_filter_ssids);
+ if (extra_ie) {
+ params.extra_ies = wpabuf_head(extra_ie);
+ params.extra_ies_len = wpabuf_len(extra_ie);
+ }
+
+#ifdef CONFIG_P2P
+ if (wpa_s->p2p_in_provisioning) {
+ /*
+ * The interface may not yet be in P2P mode, so we have to
+ * explicitly request P2P probe to disable CCK rates.
+ */
+ params.p2p_probe = 1;
+ }
+#endif /* CONFIG_P2P */
ret = wpa_supplicant_trigger_scan(wpa_s, ¶ms);
- wpabuf_free(wps_ie);
+ wpabuf_free(extra_ie);
os_free(params.freqs);
os_free(params.filter_ssids);
if (ret) {
- wpa_printf(MSG_WARNING, "Failed to initiate AP scan.");
+ wpa_msg(wpa_s, MSG_WARNING, "Failed to initiate AP scan");
if (prev_state != wpa_s->wpa_state)
wpa_supplicant_set_state(wpa_s, prev_state);
wpa_supplicant_req_scan(wpa_s, 1, 0);
ssid = ssid->next;
}
if (ssid) {
- wpa_msg(wpa_s, MSG_DEBUG, "Not rescheduling scan to "
+ wpa_dbg(wpa_s, MSG_DEBUG, "Not rescheduling scan to "
"ensure that specific SSID scans occur");
return;
}
}
- wpa_msg(wpa_s, MSG_DEBUG, "Setting scan request: %d sec %d usec",
+ wpa_dbg(wpa_s, MSG_DEBUG, "Setting scan request: %d sec %d usec",
sec, usec);
eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL);
eloop_register_timeout(sec, usec, wpa_supplicant_scan, wpa_s, NULL);
/**
+ * wpa_supplicant_delayed_sched_scan - Request a delayed scheduled scan
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @sec: Number of seconds after which to scan
+ * @usec: Number of microseconds after which to scan
+ *
+ * This function is used to schedule periodic scans for neighboring
+ * access points after the specified time.
+ */
+int wpa_supplicant_delayed_sched_scan(struct wpa_supplicant *wpa_s,
+ int sec, int usec)
+{
+ if (!wpa_s->sched_scan_supported)
+ return -1;
+
+ eloop_register_timeout(sec, usec,
+ wpa_supplicant_delayed_sched_scan_timeout,
+ wpa_s, NULL);
+
+ return 0;
+}
+
+
+/**
+ * wpa_supplicant_req_sched_scan - Start a periodic scheduled scan
+ * @wpa_s: Pointer to wpa_supplicant data
+ *
+ * This function is used to schedule periodic scans for neighboring
+ * access points repeating the scan continuously.
+ */
+int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_driver_scan_params params;
+ enum wpa_states prev_state;
+ struct wpa_ssid *ssid;
+ struct wpabuf *wps_ie = NULL;
+ int ret;
+ unsigned int max_sched_scan_ssids;
+
+ if (!wpa_s->sched_scan_supported)
+ return -1;
+
+ if (wpa_s->max_sched_scan_ssids > WPAS_MAX_SCAN_SSIDS)
+ max_sched_scan_ssids = WPAS_MAX_SCAN_SSIDS;
+ else
+ max_sched_scan_ssids = wpa_s->max_sched_scan_ssids;
+
+ if (wpa_s->sched_scanning)
+ return 0;
+
+ os_memset(¶ms, 0, sizeof(params));
+
+ /* If we can't allocate space for the filters, we just don't filter */
+ params.filter_ssids = os_zalloc(wpa_s->max_match_sets *
+ sizeof(struct wpa_driver_scan_filter));
+
+ prev_state = wpa_s->wpa_state;
+ if (wpa_s->wpa_state == WPA_DISCONNECTED ||
+ wpa_s->wpa_state == WPA_INACTIVE)
+ wpa_supplicant_set_state(wpa_s, WPA_SCANNING);
+
+ /* Find the starting point from which to continue scanning */
+ ssid = wpa_s->conf->ssid;
+ if (wpa_s->prev_sched_ssid) {
+ while (ssid) {
+ if (ssid == wpa_s->prev_sched_ssid) {
+ ssid = ssid->next;
+ break;
+ }
+ ssid = ssid->next;
+ }
+ }
+
+ if (!ssid || !wpa_s->prev_sched_ssid) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Beginning of SSID list");
+
+ wpa_s->sched_scan_interval = 2;
+ wpa_s->sched_scan_timeout = max_sched_scan_ssids * 2;
+ wpa_s->first_sched_scan = 1;
+ ssid = wpa_s->conf->ssid;
+ wpa_s->prev_sched_ssid = ssid;
+ }
+
+ while (ssid) {
+ if (ssid->disabled) {
+ wpa_s->prev_sched_ssid = ssid;
+ ssid = ssid->next;
+ continue;
+ }
+
+ if (params.filter_ssids && ssid->ssid && ssid->ssid_len) {
+ os_memcpy(params.filter_ssids[params.num_filter_ssids].ssid,
+ ssid->ssid, ssid->ssid_len);
+ params.filter_ssids[params.num_filter_ssids].ssid_len =
+ ssid->ssid_len;
+ params.num_filter_ssids++;
+ }
+
+ if (ssid->scan_ssid) {
+ params.ssids[params.num_ssids].ssid =
+ ssid->ssid;
+ params.ssids[params.num_ssids].ssid_len =
+ ssid->ssid_len;
+ params.num_ssids++;
+ if (params.num_ssids >= max_sched_scan_ssids) {
+ wpa_s->prev_sched_ssid = ssid;
+ break;
+ }
+ }
+
+ if (params.num_filter_ssids >= wpa_s->max_match_sets)
+ break;
+ wpa_s->prev_sched_ssid = ssid;
+ ssid = ssid->next;
+ }
+
+ if (!params.num_ssids) {
+ os_free(params.filter_ssids);
+ return 0;
+ }
+
+ if (wpa_s->wps)
+ wps_ie = wpa_supplicant_extra_ies(wpa_s, ¶ms);
+
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Starting sched scan: interval %d timeout %d",
+ wpa_s->sched_scan_interval, wpa_s->sched_scan_timeout);
+
+ ret = wpa_supplicant_start_sched_scan(wpa_s, ¶ms,
+ wpa_s->sched_scan_interval);
+ wpabuf_free(wps_ie);
+ os_free(params.filter_ssids);
+ if (ret) {
+ wpa_msg(wpa_s, MSG_WARNING, "Failed to initiate sched scan");
+ if (prev_state != wpa_s->wpa_state)
+ wpa_supplicant_set_state(wpa_s, prev_state);
+ return ret;
+ }
+
+ /* If we have more SSIDs to scan, add a timeout so we scan them too */
+ if (ssid || !wpa_s->first_sched_scan) {
+ wpa_s->sched_scan_timed_out = 0;
+ eloop_register_timeout(wpa_s->sched_scan_timeout, 0,
+ wpa_supplicant_sched_scan_timeout,
+ wpa_s, NULL);
+ wpa_s->first_sched_scan = 0;
+ wpa_s->sched_scan_timeout /= 2;
+ wpa_s->sched_scan_interval *= 2;
+ }
+
+ return 0;
+}
+
+
+/**
* wpa_supplicant_cancel_scan - Cancel a scheduled scan request
* @wpa_s: Pointer to wpa_supplicant data
*
*/
void wpa_supplicant_cancel_scan(struct wpa_supplicant *wpa_s)
{
- wpa_msg(wpa_s, MSG_DEBUG, "Cancelling scan request");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling scan request");
eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL);
}
+/**
+ * wpa_supplicant_cancel_sched_scan - Stop running scheduled scans
+ * @wpa_s: Pointer to wpa_supplicant data
+ *
+ * This function is used to stop a periodic scheduled scan.
+ */
+void wpa_supplicant_cancel_sched_scan(struct wpa_supplicant *wpa_s)
+{
+ if (!wpa_s->sched_scanning)
+ return;
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling sched scan");
+ eloop_cancel_timeout(wpa_supplicant_sched_scan_timeout, wpa_s, NULL);
+ wpa_supplicant_stop_sched_scan(wpa_s);
+}
+
+
void wpa_supplicant_notify_scanning(struct wpa_supplicant *wpa_s,
int scanning)
{
}
+struct wpabuf * wpa_scan_get_vendor_ie_multi_beacon(
+ const struct wpa_scan_res *res, u32 vendor_type)
+{
+ struct wpabuf *buf;
+ const u8 *end, *pos;
+
+ if (res->beacon_ie_len == 0)
+ return NULL;
+ buf = wpabuf_alloc(res->beacon_ie_len);
+ if (buf == NULL)
+ return NULL;
+
+ pos = (const u8 *) (res + 1);
+ pos += res->ie_len;
+ end = pos + res->beacon_ie_len;
+
+ while (pos + 1 < end) {
+ if (pos + 2 + pos[1] > end)
+ break;
+ if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
+ vendor_type == WPA_GET_BE32(&pos[2]))
+ wpabuf_put_data(buf, pos + 2 + 4, pos[1] - 4);
+ pos += 2 + pos[1];
+ }
+
+ if (wpabuf_len(buf) == 0) {
+ wpabuf_free(buf);
+ buf = NULL;
+ }
+
+ return buf;
+}
+
+
+/*
+ * Channels with a great SNR can operate at full rate. What is a great SNR?
+ * This doc https://supportforums.cisco.com/docs/DOC-12954 says, "the general
+ * rule of thumb is that any SNR above 20 is good." This one
+ * http://www.cisco.com/en/US/tech/tk722/tk809/technologies_q_and_a_item09186a00805e9a96.shtml#qa23
+ * recommends 25 as a minimum SNR for 54 Mbps data rate. 30 is chosen here as a
+ * conservative value.
+ */
+#define GREAT_SNR 30
+
/* Compare function for sorting scan results. Return >0 if @b is considered
* better. */
static int wpa_scan_result_compar(const void *a, const void *b)
{
+#define IS_5GHZ(n) (n > 4000)
+#define MIN(a,b) a < b ? a : b
struct wpa_scan_res **_wa = (void *) a;
struct wpa_scan_res **_wb = (void *) b;
struct wpa_scan_res *wa = *_wa;
struct wpa_scan_res *wb = *_wb;
int wpa_a, wpa_b, maxrate_a, maxrate_b;
+ int snr_a, snr_b;
/* WPA/WPA2 support preferred */
wpa_a = wpa_scan_get_vendor_ie(wa, WPA_IE_VENDOR_TYPE) != NULL ||
(wb->caps & IEEE80211_CAP_PRIVACY) == 0)
return -1;
- /* best/max rate preferred if signal level close enough XXX */
- if ((wa->level && wb->level && abs(wb->level - wa->level) < 5) ||
+ if ((wa->flags & wb->flags & WPA_SCAN_LEVEL_DBM) &&
+ !((wa->flags | wb->flags) & WPA_SCAN_NOISE_INVALID)) {
+ snr_a = MIN(wa->level - wa->noise, GREAT_SNR);
+ snr_b = MIN(wb->level - wb->noise, GREAT_SNR);
+ } else {
+ /* Not suitable information to calculate SNR, so use level */
+ snr_a = wa->level;
+ snr_b = wb->level;
+ }
+
+ /* best/max rate preferred if SNR close enough */
+ if ((snr_a && snr_b && abs(snr_b - snr_a) < 5) ||
(wa->qual && wb->qual && abs(wb->qual - wa->qual) < 10)) {
maxrate_a = wpa_scan_get_max_rate(wa);
maxrate_b = wpa_scan_get_max_rate(wb);
if (maxrate_a != maxrate_b)
return maxrate_b - maxrate_a;
+ if (IS_5GHZ(wa->freq) ^ IS_5GHZ(wb->freq))
+ return IS_5GHZ(wa->freq) ? -1 : 1;
}
/* use freq for channel preference */
+ /* all things being equal, use SNR; if SNRs are
+ * identical, use quality values since some drivers may only report
+ * that value and leave the signal level zero */
+ if (snr_b == snr_a)
+ return wb->qual - wa->qual;
+ return snr_b - snr_a;
+#undef MIN
+#undef IS_5GHZ
+}
+
+
+#ifdef CONFIG_WPS
+/* Compare function for sorting scan results when searching a WPS AP for
+ * provisioning. Return >0 if @b is considered better. */
+static int wpa_scan_result_wps_compar(const void *a, const void *b)
+{
+ struct wpa_scan_res **_wa = (void *) a;
+ struct wpa_scan_res **_wb = (void *) b;
+ struct wpa_scan_res *wa = *_wa;
+ struct wpa_scan_res *wb = *_wb;
+ int uses_wps_a, uses_wps_b;
+ struct wpabuf *wps_a, *wps_b;
+ int res;
+
+ /* Optimization - check WPS IE existence before allocated memory and
+ * doing full reassembly. */
+ uses_wps_a = wpa_scan_get_vendor_ie(wa, WPS_IE_VENDOR_TYPE) != NULL;
+ uses_wps_b = wpa_scan_get_vendor_ie(wb, WPS_IE_VENDOR_TYPE) != NULL;
+ if (uses_wps_a && !uses_wps_b)
+ return -1;
+ if (!uses_wps_a && uses_wps_b)
+ return 1;
+
+ if (uses_wps_a && uses_wps_b) {
+ wps_a = wpa_scan_get_vendor_ie_multi(wa, WPS_IE_VENDOR_TYPE);
+ wps_b = wpa_scan_get_vendor_ie_multi(wb, WPS_IE_VENDOR_TYPE);
+ res = wps_ap_priority_compar(wps_a, wps_b);
+ wpabuf_free(wps_a);
+ wpabuf_free(wps_b);
+ if (res)
+ return res;
+ }
+
+ /*
+ * Do not use current AP security policy as a sorting criteria during
+ * WPS provisioning step since the AP may get reconfigured at the
+ * completion of provisioning.
+ */
+
/* all things being equal, use signal level; if signal levels are
* identical, use quality values since some drivers may only report
* that value and leave the signal level zero */
return wb->qual - wa->qual;
return wb->level - wa->level;
}
+#endif /* CONFIG_WPS */
+
+
+static void dump_scan_res(struct wpa_scan_results *scan_res)
+{
+ size_t i;
+
+ if (scan_res->res == NULL || scan_res->num == 0)
+ return;
+
+ wpa_printf(MSG_EXCESSIVE, "Sorted scan results");
+
+ for (i = 0; i < scan_res->num; i++) {
+ struct wpa_scan_res *r = scan_res->res[i];
+ if ((r->flags & (WPA_SCAN_LEVEL_DBM | WPA_SCAN_NOISE_INVALID))
+ == WPA_SCAN_LEVEL_DBM) {
+ int snr = r->level - r->noise;
+ wpa_printf(MSG_EXCESSIVE, MACSTR " freq=%d qual=%d "
+ "noise=%d level=%d snr=%d%s flags=0x%x",
+ MAC2STR(r->bssid), r->freq, r->qual,
+ r->noise, r->level, snr,
+ snr >= GREAT_SNR ? "*" : "", r->flags);
+ } else {
+ wpa_printf(MSG_EXCESSIVE, MACSTR " freq=%d qual=%d "
+ "noise=%d level=%d flags=0x%x",
+ MAC2STR(r->bssid), r->freq, r->qual,
+ r->noise, r->level, r->flags);
+ }
+ }
+}
/**
{
struct wpa_scan_results *scan_res;
size_t i;
+ int (*compar)(const void *, const void *) = wpa_scan_result_compar;
- if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)
- scan_res = ieee80211_sta_get_scan_results(wpa_s);
- else
- scan_res = wpa_drv_get_scan_results2(wpa_s);
+ scan_res = wpa_drv_get_scan_results2(wpa_s);
if (scan_res == NULL) {
- wpa_printf(MSG_DEBUG, "Failed to get scan results");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Failed to get scan results");
return NULL;
}
+#ifdef CONFIG_WPS
+ if (wpas_wps_in_progress(wpa_s)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPS: Order scan results with WPS "
+ "provisioning rules");
+ compar = wpa_scan_result_wps_compar;
+ }
+#endif /* CONFIG_WPS */
+
qsort(scan_res->res, scan_res->num, sizeof(struct wpa_scan_res *),
- wpa_scan_result_compar);
+ compar);
+ dump_scan_res(scan_res);
wpa_bss_update_start(wpa_s);
for (i = 0; i < scan_res->num; i++)
return 0;
}
-
-
-void wpa_scan_results_free(struct wpa_scan_results *res)
-{
- size_t i;
-
- if (res == NULL)
- return;
-
- for (i = 0; i < res->num; i++)
- os_free(res->res[i]);
- os_free(res->res);
- os_free(res);
-}
int wpa_supplicant_enabled_networks(struct wpa_config *conf);
void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec);
+int wpa_supplicant_delayed_sched_scan(struct wpa_supplicant *wpa_s,
+ int sec, int usec);
+int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s);
void wpa_supplicant_cancel_scan(struct wpa_supplicant *wpa_s);
+void wpa_supplicant_cancel_sched_scan(struct wpa_supplicant *wpa_s);
void wpa_supplicant_notify_scanning(struct wpa_supplicant *wpa_s,
int scanning);
struct wpa_driver_scan_params;
u32 vendor_type);
struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res,
u32 vendor_type);
-void wpa_scan_results_free(struct wpa_scan_results *res);
+struct wpabuf * wpa_scan_get_vendor_ie_multi_beacon(
+ const struct wpa_scan_res *res, u32 vendor_type);
#endif /* SCAN_H */
#include "includes.h"
#include "common.h"
+#include "utils/eloop.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "eapol_supp/eapol_supp_sm.h"
#include "driver_i.h"
#include "wpas_glue.h"
#include "wps_supplicant.h"
+#include "p2p_supplicant.h"
#include "notify.h"
#include "blacklist.h"
#include "bss.h"
#include "scan.h"
#include "sme.h"
+#define SME_AUTH_TIMEOUT 5
+#define SME_ASSOC_TIMEOUT 5
+
+static void sme_auth_timer(void *eloop_ctx, void *timeout_ctx);
+static void sme_assoc_timer(void *eloop_ctx, void *timeout_ctx);
+#ifdef CONFIG_IEEE80211W
+static void sme_stop_sa_query(struct wpa_supplicant *wpa_s);
+#endif /* CONFIG_IEEE80211W */
+
+
void sme_authenticate(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss, struct wpa_ssid *ssid)
{
int i, bssid_changed;
if (bss == NULL) {
- wpa_printf(MSG_ERROR, "SME: No scan result available for the "
- "network");
+ wpa_msg(wpa_s, MSG_ERROR, "SME: No scan result available for "
+ "the network");
return;
}
params.bssid = bss->bssid;
params.ssid = bss->ssid;
params.ssid_len = bss->ssid_len;
+ params.p2p = ssid->p2p_group;
if (wpa_s->sme.ssid_len != params.ssid_len ||
os_memcmp(wpa_s->sme.ssid, params.ssid, params.ssid_len) != 0)
}
}
#endif /* IEEE8021X_EAPOL */
- wpa_printf(MSG_DEBUG, "Automatic auth_alg selection: 0x%x",
- params.auth_alg);
+ wpa_dbg(wpa_s, MSG_DEBUG, "Automatic auth_alg selection: 0x%x",
+ params.auth_alg);
if (ssid->auth_alg) {
params.auth_alg = ssid->auth_alg;
- wpa_printf(MSG_DEBUG, "Overriding auth_alg selection: 0x%x",
- params.auth_alg);
+ wpa_dbg(wpa_s, MSG_DEBUG, "Overriding auth_alg selection: "
+ "0x%x", params.auth_alg);
}
for (i = 0; i < NUM_WEP_KEYS; i++) {
if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
wpa_s->sme.assoc_req_ie,
&wpa_s->sme.assoc_req_ie_len)) {
- wpa_printf(MSG_WARNING, "SME: Failed to set WPA key "
- "management and encryption suites");
+ wpa_msg(wpa_s, MSG_WARNING, "SME: Failed to set WPA "
+ "key management and encryption suites");
return;
}
} else if (ssid->key_mgmt &
if (wpa_supplicant_set_suites(wpa_s, NULL, ssid,
wpa_s->sme.assoc_req_ie,
&wpa_s->sme.assoc_req_ie_len)) {
- wpa_printf(MSG_WARNING, "SME: Failed to set WPA key "
- "management and encryption suites (no scan "
- "results)");
+ wpa_msg(wpa_s, MSG_WARNING, "SME: Failed to set WPA "
+ "key management and encryption suites (no "
+ "scan results)");
return;
}
#ifdef CONFIG_WPS
if (wpa_s->sme.ft_used &&
os_memcmp(md, wpa_s->sme.mobility_domain, 2) == 0 &&
wpa_sm_has_ptk(wpa_s->wpa)) {
- wpa_printf(MSG_DEBUG, "SME: Trying to use FT "
- "over-the-air");
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: Trying to use FT "
+ "over-the-air");
params.auth_alg = WPA_AUTH_ALG_FT;
params.ie = wpa_s->sme.ft_ies;
params.ie_len = wpa_s->sme.ft_ies_len;
if (rsn && wpa_parse_wpa_ie(rsn, 2 + rsn[1], &_ie) == 0 &&
_ie.capabilities &
(WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR)) {
- wpa_printf(MSG_DEBUG, "WPA: Selected AP supports MFP: "
- "require MFP");
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: Selected AP supports "
+ "MFP: require MFP");
wpa_s->sme.mfp = MGMT_FRAME_PROTECTION_REQUIRED;
}
}
#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_P2P
+ if (wpa_s->global->p2p) {
+ u8 *pos;
+ size_t len;
+ int res;
+ pos = wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len;
+ len = sizeof(wpa_s->sme.assoc_req_ie) -
+ wpa_s->sme.assoc_req_ie_len;
+ res = wpas_p2p_assoc_req_ie(wpa_s, bss, pos, len,
+ ssid->p2p_group);
+ if (res >= 0)
+ wpa_s->sme.assoc_req_ie_len += res;
+ }
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_INTERWORKING
+ if (wpa_s->conf->interworking) {
+ u8 *pos = wpa_s->sme.assoc_req_ie;
+ if (wpa_s->sme.assoc_req_ie_len > 0 && pos[0] == WLAN_EID_RSN)
+ pos += 2 + pos[1];
+ os_memmove(pos + 6, pos,
+ wpa_s->sme.assoc_req_ie_len -
+ (pos - wpa_s->sme.assoc_req_ie));
+ wpa_s->sme.assoc_req_ie_len += 6;
+ *pos++ = WLAN_EID_EXT_CAPAB;
+ *pos++ = 4;
+ *pos++ = 0x00;
+ *pos++ = 0x00;
+ *pos++ = 0x00;
+ *pos++ = 0x80; /* Bit 31 - Interworking */
+ }
+#endif /* CONFIG_INTERWORKING */
+
+ wpa_supplicant_cancel_sched_scan(wpa_s);
wpa_supplicant_cancel_scan(wpa_s);
- wpa_msg(wpa_s, MSG_INFO, "Trying to authenticate with " MACSTR
+ wpa_msg(wpa_s, MSG_INFO, "SME: Trying to authenticate with " MACSTR
" (SSID='%s' freq=%d MHz)", MAC2STR(params.bssid),
wpa_ssid_txt(params.ssid, params.ssid_len), params.freq);
wpa_s->sme.auth_alg = params.auth_alg;
if (wpa_drv_authenticate(wpa_s, ¶ms) < 0) {
- wpa_msg(wpa_s, MSG_INFO, "Authentication request to the "
+ wpa_msg(wpa_s, MSG_INFO, "SME: Authentication request to the "
"driver failed");
- wpa_supplicant_req_scan(wpa_s, 1, 0);
+ wpas_connection_failed(wpa_s, bss->bssid);
+ wpa_supplicant_mark_disassoc(wpa_s);
return;
}
- /* TODO: add timeout on authentication */
+ eloop_register_timeout(SME_AUTH_TIMEOUT, 0, sme_auth_timer, wpa_s,
+ NULL);
/*
* Association will be started based on the authentication event from
struct wpa_ssid *ssid = wpa_s->current_ssid;
if (ssid == NULL) {
- wpa_printf(MSG_DEBUG, "SME: Ignore authentication event when "
- "network is not selected");
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: Ignore authentication event "
+ "when network is not selected");
return;
}
if (wpa_s->wpa_state != WPA_AUTHENTICATING) {
- wpa_printf(MSG_DEBUG, "SME: Ignore authentication event when "
- "not in authenticating state");
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: Ignore authentication event "
+ "when not in authenticating state");
return;
}
if (os_memcmp(wpa_s->pending_bssid, data->auth.peer, ETH_ALEN) != 0) {
- wpa_printf(MSG_DEBUG, "SME: Ignore authentication with "
- "unexpected peer " MACSTR,
- MAC2STR(data->auth.peer));
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: Ignore authentication with "
+ "unexpected peer " MACSTR,
+ MAC2STR(data->auth.peer));
return;
}
- wpa_printf(MSG_DEBUG, "SME: Authentication response: peer=" MACSTR
- " auth_type=%d status_code=%d",
- MAC2STR(data->auth.peer), data->auth.auth_type,
- data->auth.status_code);
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: Authentication response: peer=" MACSTR
+ " auth_type=%d status_code=%d",
+ MAC2STR(data->auth.peer), data->auth.auth_type,
+ data->auth.status_code);
wpa_hexdump(MSG_MSGDUMP, "SME: Authentication response IEs",
data->auth.ies, data->auth.ies_len);
+ eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL);
+
if (data->auth.status_code != WLAN_STATUS_SUCCESS) {
- wpa_printf(MSG_DEBUG, "SME: Authentication failed (status "
- "code %d)", data->auth.status_code);
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: Authentication failed (status "
+ "code %d)", data->auth.status_code);
if (data->auth.status_code !=
WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG ||
wpa_s->sme.auth_alg == data->auth.auth_type ||
- wpa_s->current_ssid->auth_alg == WPA_AUTH_ALG_LEAP)
+ wpa_s->current_ssid->auth_alg == WPA_AUTH_ALG_LEAP) {
+ wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
return;
+ }
switch (data->auth.auth_type) {
case WLAN_AUTH_OPEN:
wpa_s->current_ssid->auth_alg = WPA_AUTH_ALG_SHARED;
- wpa_printf(MSG_DEBUG, "SME: Trying SHARED auth");
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: Trying SHARED auth");
wpa_supplicant_associate(wpa_s, wpa_s->current_bss,
wpa_s->current_ssid);
return;
case WLAN_AUTH_SHARED_KEY:
wpa_s->current_ssid->auth_alg = WPA_AUTH_ALG_LEAP;
- wpa_printf(MSG_DEBUG, "SME: Trying LEAP auth");
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: Trying LEAP auth");
wpa_supplicant_associate(wpa_s, wpa_s->current_bss,
wpa_s->current_ssid);
return;
params.wpa_ie = wpa_s->sme.assoc_req_ie_len ?
wpa_s->sme.assoc_req_ie : NULL;
params.wpa_ie_len = wpa_s->sme.assoc_req_ie_len;
+ params.pairwise_suite = cipher_suite2driver(wpa_s->pairwise_cipher);
+ params.group_suite = cipher_suite2driver(wpa_s->group_cipher);
#ifdef CONFIG_IEEE80211R
if (auth_type == WLAN_AUTH_FT && wpa_s->sme.ft_ies) {
params.wpa_ie = wpa_s->sme.ft_ies;
if (params.wpa_ie == NULL ||
ieee802_11_parse_elems(params.wpa_ie, params.wpa_ie_len, &elems, 0)
< 0) {
- wpa_printf(MSG_DEBUG, "SME: Could not parse own IEs?!");
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: Could not parse own IEs?!");
os_memset(&elems, 0, sizeof(elems));
}
- if (elems.rsn_ie)
+ if (elems.rsn_ie) {
+ params.wpa_proto = WPA_PROTO_RSN;
wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, elems.rsn_ie - 2,
elems.rsn_ie_len + 2);
- else if (elems.wpa_ie)
+ } else if (elems.wpa_ie) {
+ params.wpa_proto = WPA_PROTO_WPA;
wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, elems.wpa_ie - 2,
elems.wpa_ie_len + 2);
- else
+ } else
wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
+ if (wpa_s->current_ssid && wpa_s->current_ssid->p2p_group)
+ params.p2p = 1;
+
+ if (wpa_s->parent->set_sta_uapsd)
+ params.uapsd = wpa_s->parent->sta_uapsd;
+ else
+ params.uapsd = -1;
if (wpa_drv_associate(wpa_s, ¶ms) < 0) {
- wpa_msg(wpa_s, MSG_INFO, "Association request to the driver "
- "failed");
- wpa_supplicant_req_scan(wpa_s, 5, 0);
+ wpa_msg(wpa_s, MSG_INFO, "SME: Association request to the "
+ "driver failed");
+ wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
+ os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
return;
}
- /* TODO: add timeout on association */
+ eloop_register_timeout(SME_ASSOC_TIMEOUT, 0, sme_assoc_timer, wpa_s,
+ NULL);
}
const u8 *ies, size_t ies_len)
{
if (md == NULL || ies == NULL) {
- wpa_printf(MSG_DEBUG, "SME: Remove mobility domain");
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: Remove mobility domain");
os_free(wpa_s->sme.ft_ies);
wpa_s->sme.ft_ies = NULL;
wpa_s->sme.ft_ies_len = 0;
}
-void sme_event_assoc_reject(struct wpa_supplicant *wpa_s,
- union wpa_event_data *data)
+static void sme_deauth(struct wpa_supplicant *wpa_s)
{
int bssid_changed;
- int timeout = 5000;
-
- wpa_printf(MSG_DEBUG, "SME: Association with " MACSTR " failed: "
- "status code %d", MAC2STR(wpa_s->pending_bssid),
- data->assoc_reject.status_code);
bssid_changed = !is_zero_ether_addr(wpa_s->bssid);
- /*
- * For now, unconditionally terminate the previous authentication. In
- * theory, this should not be needed, but mac80211 gets quite confused
- * if the authentication is left pending.. Some roaming cases might
- * benefit from using the previous authentication, so this could be
- * optimized in the future.
- */
if (wpa_drv_deauthenticate(wpa_s, wpa_s->pending_bssid,
WLAN_REASON_DEAUTH_LEAVING) < 0) {
- wpa_msg(wpa_s, MSG_INFO,
- "Deauth request to the driver failed");
+ wpa_msg(wpa_s, MSG_INFO, "SME: Deauth request to the driver "
+ "failed");
}
wpa_s->sme.prev_bssid_set = 0;
- if (wpa_blacklist_add(wpa_s, wpa_s->pending_bssid) == 0) {
- struct wpa_blacklist *b;
- b = wpa_blacklist_get(wpa_s, wpa_s->pending_bssid);
- if (b && b->count < 3) {
- /*
- * Speed up next attempt if there could be other APs
- * that could accept association.
- */
- timeout = 100;
- }
- }
+ wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
os_memset(wpa_s->bssid, 0, ETH_ALEN);
os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
if (bssid_changed)
wpas_notify_bssid_changed(wpa_s);
+}
+
+
+void sme_event_assoc_reject(struct wpa_supplicant *wpa_s,
+ union wpa_event_data *data)
+{
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: Association with " MACSTR " failed: "
+ "status code %d", MAC2STR(wpa_s->pending_bssid),
+ data->assoc_reject.status_code);
+
+ eloop_cancel_timeout(sme_assoc_timer, wpa_s, NULL);
/*
- * TODO: if more than one possible AP is available in scan results,
- * could try the other ones before requesting a new scan.
+ * For now, unconditionally terminate the previous authentication. In
+ * theory, this should not be needed, but mac80211 gets quite confused
+ * if the authentication is left pending.. Some roaming cases might
+ * benefit from using the previous authentication, so this could be
+ * optimized in the future.
*/
- wpa_supplicant_req_scan(wpa_s, timeout / 1000,
- 1000 * (timeout % 1000));
+ sme_deauth(wpa_s);
}
void sme_event_auth_timed_out(struct wpa_supplicant *wpa_s,
union wpa_event_data *data)
{
- wpa_printf(MSG_DEBUG, "SME: Authentication timed out");
- wpa_supplicant_req_scan(wpa_s, 5, 0);
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: Authentication timed out");
+ wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
+ wpa_supplicant_mark_disassoc(wpa_s);
}
void sme_event_assoc_timed_out(struct wpa_supplicant *wpa_s,
union wpa_event_data *data)
{
- wpa_printf(MSG_DEBUG, "SME: Association timed out");
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: Association timed out");
+ wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
wpa_supplicant_mark_disassoc(wpa_s);
- wpa_supplicant_req_scan(wpa_s, 5, 0);
}
void sme_event_disassoc(struct wpa_supplicant *wpa_s,
union wpa_event_data *data)
{
- wpa_printf(MSG_DEBUG, "SME: Disassociation event received");
- if (wpa_s->sme.prev_bssid_set &&
- !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: Disassociation event received");
+ if (wpa_s->sme.prev_bssid_set) {
/*
* cfg80211/mac80211 can get into somewhat confused state if
* the AP only disassociates us and leaves us in authenticated
* state. For now, force the state to be cleared to avoid
* confusing errors if we try to associate with the AP again.
*/
- wpa_printf(MSG_DEBUG, "SME: Deauthenticate to clear driver "
- "state");
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: Deauthenticate to clear "
+ "driver state");
wpa_drv_deauthenticate(wpa_s, wpa_s->sme.prev_bssid,
WLAN_REASON_DEAUTH_LEAVING);
}
}
+
+
+static void sme_auth_timer(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ if (wpa_s->wpa_state == WPA_AUTHENTICATING) {
+ wpa_msg(wpa_s, MSG_DEBUG, "SME: Authentication timeout");
+ sme_deauth(wpa_s);
+ }
+}
+
+
+static void sme_assoc_timer(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ if (wpa_s->wpa_state == WPA_ASSOCIATING) {
+ wpa_msg(wpa_s, MSG_DEBUG, "SME: Association timeout");
+ sme_deauth(wpa_s);
+ }
+}
+
+
+void sme_state_changed(struct wpa_supplicant *wpa_s)
+{
+ /* Make sure timers are cleaned up appropriately. */
+ if (wpa_s->wpa_state != WPA_ASSOCIATING)
+ eloop_cancel_timeout(sme_assoc_timer, wpa_s, NULL);
+ if (wpa_s->wpa_state != WPA_AUTHENTICATING)
+ eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL);
+}
+
+
+void sme_disassoc_while_authenticating(struct wpa_supplicant *wpa_s,
+ const u8 *prev_pending_bssid)
+{
+ /*
+ * mac80211-workaround to force deauth on failed auth cmd,
+ * requires us to remain in authenticating state to allow the
+ * second authentication attempt to be continued properly.
+ */
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: Allow pending authentication "
+ "to proceed after disconnection event");
+ wpa_supplicant_set_state(wpa_s, WPA_AUTHENTICATING);
+ os_memcpy(wpa_s->pending_bssid, prev_pending_bssid, ETH_ALEN);
+
+ /*
+ * Re-arm authentication timer in case auth fails for whatever reason.
+ */
+ eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL);
+ eloop_register_timeout(SME_AUTH_TIMEOUT, 0, sme_auth_timer, wpa_s,
+ NULL);
+}
+
+
+void sme_deinit(struct wpa_supplicant *wpa_s)
+{
+ os_free(wpa_s->sme.ft_ies);
+ wpa_s->sme.ft_ies = NULL;
+ wpa_s->sme.ft_ies_len = 0;
+#ifdef CONFIG_IEEE80211W
+ sme_stop_sa_query(wpa_s);
+#endif /* CONFIG_IEEE80211W */
+
+ eloop_cancel_timeout(sme_assoc_timer, wpa_s, NULL);
+ eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL);
+}
+
+
+#ifdef CONFIG_IEEE80211W
+
+static const unsigned int sa_query_max_timeout = 1000;
+static const unsigned int sa_query_retry_timeout = 201;
+
+static int sme_check_sa_query_timeout(struct wpa_supplicant *wpa_s)
+{
+ u32 tu;
+ struct os_time now, passed;
+ os_get_time(&now);
+ os_time_sub(&now, &wpa_s->sme.sa_query_start, &passed);
+ tu = (passed.sec * 1000000 + passed.usec) / 1024;
+ if (sa_query_max_timeout < tu) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: SA Query timed out");
+ sme_stop_sa_query(wpa_s);
+ wpa_supplicant_deauthenticate(
+ wpa_s, WLAN_REASON_PREV_AUTH_NOT_VALID);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static void sme_send_sa_query_req(struct wpa_supplicant *wpa_s,
+ const u8 *trans_id)
+{
+ u8 req[2 + WLAN_SA_QUERY_TR_ID_LEN];
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: Sending SA Query Request to "
+ MACSTR, MAC2STR(wpa_s->bssid));
+ wpa_hexdump(MSG_DEBUG, "SME: SA Query Transaction ID",
+ trans_id, WLAN_SA_QUERY_TR_ID_LEN);
+ req[0] = WLAN_ACTION_SA_QUERY;
+ req[1] = WLAN_SA_QUERY_REQUEST;
+ os_memcpy(req + 2, trans_id, WLAN_SA_QUERY_TR_ID_LEN);
+ if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
+ wpa_s->own_addr, wpa_s->bssid,
+ req, sizeof(req), 0) < 0)
+ wpa_msg(wpa_s, MSG_INFO, "SME: Failed to send SA Query "
+ "Request");
+}
+
+
+static void sme_sa_query_timer(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ unsigned int timeout, sec, usec;
+ u8 *trans_id, *nbuf;
+
+ if (wpa_s->sme.sa_query_count > 0 &&
+ sme_check_sa_query_timeout(wpa_s))
+ return;
+
+ nbuf = os_realloc(wpa_s->sme.sa_query_trans_id,
+ (wpa_s->sme.sa_query_count + 1) *
+ WLAN_SA_QUERY_TR_ID_LEN);
+ if (nbuf == NULL)
+ return;
+ if (wpa_s->sme.sa_query_count == 0) {
+ /* Starting a new SA Query procedure */
+ os_get_time(&wpa_s->sme.sa_query_start);
+ }
+ trans_id = nbuf + wpa_s->sme.sa_query_count * WLAN_SA_QUERY_TR_ID_LEN;
+ wpa_s->sme.sa_query_trans_id = nbuf;
+ wpa_s->sme.sa_query_count++;
+
+ os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN);
+
+ timeout = sa_query_retry_timeout;
+ sec = ((timeout / 1000) * 1024) / 1000;
+ usec = (timeout % 1000) * 1024;
+ eloop_register_timeout(sec, usec, sme_sa_query_timer, wpa_s, NULL);
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: Association SA Query attempt %d",
+ wpa_s->sme.sa_query_count);
+
+ sme_send_sa_query_req(wpa_s, trans_id);
+}
+
+
+static void sme_start_sa_query(struct wpa_supplicant *wpa_s)
+{
+ sme_sa_query_timer(wpa_s, NULL);
+}
+
+
+void sme_stop_sa_query(struct wpa_supplicant *wpa_s)
+{
+ eloop_cancel_timeout(sme_sa_query_timer, wpa_s, NULL);
+ os_free(wpa_s->sme.sa_query_trans_id);
+ wpa_s->sme.sa_query_trans_id = NULL;
+ wpa_s->sme.sa_query_count = 0;
+}
+
+
+void sme_event_unprot_disconnect(struct wpa_supplicant *wpa_s, const u8 *sa,
+ const u8 *da, u16 reason_code)
+{
+ struct wpa_ssid *ssid;
+
+ if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME))
+ return;
+ if (wpa_s->wpa_state != WPA_COMPLETED)
+ return;
+ ssid = wpa_s->current_ssid;
+ if (ssid == NULL || ssid->ieee80211w == 0)
+ return;
+ if (os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0)
+ return;
+ if (reason_code != WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA &&
+ reason_code != WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA)
+ return;
+ if (wpa_s->sme.sa_query_count > 0)
+ return;
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: Unprotected disconnect dropped - "
+ "possible AP/STA state mismatch - trigger SA Query");
+ sme_start_sa_query(wpa_s);
+}
+
+
+void sme_sa_query_rx(struct wpa_supplicant *wpa_s, const u8 *sa,
+ const u8 *data, size_t len)
+{
+ int i;
+
+ if (wpa_s->sme.sa_query_trans_id == NULL ||
+ len < 1 + WLAN_SA_QUERY_TR_ID_LEN ||
+ data[0] != WLAN_SA_QUERY_RESPONSE)
+ return;
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: Received SA Query response from "
+ MACSTR " (trans_id %02x%02x)", MAC2STR(sa), data[1], data[2]);
+
+ if (os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0)
+ return;
+
+ for (i = 0; i < wpa_s->sme.sa_query_count; i++) {
+ if (os_memcmp(wpa_s->sme.sa_query_trans_id +
+ i * WLAN_SA_QUERY_TR_ID_LEN,
+ data + 1, WLAN_SA_QUERY_TR_ID_LEN) == 0)
+ break;
+ }
+
+ if (i >= wpa_s->sme.sa_query_count) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: No matching SA Query "
+ "transaction identifier found");
+ return;
+ }
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: Reply to pending SA Query received "
+ "from " MACSTR, MAC2STR(sa));
+ sme_stop_sa_query(wpa_s);
+}
+
+#endif /* CONFIG_IEEE80211W */
union wpa_event_data *data);
void sme_event_disassoc(struct wpa_supplicant *wpa_s,
union wpa_event_data *data);
+void sme_event_unprot_disconnect(struct wpa_supplicant *wpa_s, const u8 *sa,
+ const u8 *da, u16 reason_code);
+void sme_sa_query_rx(struct wpa_supplicant *wpa_s, const u8 *sa,
+ const u8 *data, size_t len);
+void sme_state_changed(struct wpa_supplicant *wpa_s);
+void sme_disassoc_while_authenticating(struct wpa_supplicant *wpa_s,
+ const u8 *prev_pending_bssid);
+void sme_deinit(struct wpa_supplicant *wpa_s);
#else /* CONFIG_SME */
{
}
+static inline void sme_event_unprot_disconnect(struct wpa_supplicant *wpa_s,
+ const u8 *sa, const u8 *da,
+ u16 reason_code)
+{
+}
+
+static inline void sme_state_changed(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void
+sme_disassoc_while_authenticating(struct wpa_supplicant *wpa_s,
+ const u8 *prev_pending_bssid)
+{
+}
+
+static inline void sme_deinit(struct wpa_supplicant *wpa_s)
+{
+}
+
#endif /* CONFIG_SME */
#endif /* SME_H */
SOURCEPATH ..\..\src\rsn_supp
SOURCE wpa.c preauth.c pmksa_cache.c peerkey.c wpa_ie.c
SOURCEPATH ..\..\src\drivers
-SOURCE drivers.c
+SOURCE drivers.c driver_common.c
SOURCEPATH ..\..\src\common
SOURCE wpa_common.c
SOURCEPATH ..\..\src\utils
--- /dev/null
+[Unit]
+Description=WPA supplicant daemon (interface- and nl80211 driver-specific version)
+Requires=sys-subsystem-net-devices-%i.device
+After=sys-subsystem-net-devices-%i.device
+
+# NetworkManager users will probably want the dbus version instead.
+
+[Service]
+Type=simple
+ExecStart=@BINDIR@/wpa_supplicant -c/etc/wpa_supplicant/wpa_supplicant-nl80211-%I.conf -Dnl80211 -i%I
+
+[Install]
+Alias=multi-user.target.wants/wpa_supplicant-nl80211@wlan0.service
--- /dev/null
+[Unit]
+Description=WPA supplicant daemon (interface- and wired driver-specific version)
+Requires=sys-subsystem-net-devices-%i.device
+After=sys-subsystem-net-devices-%i.device
+
+# NetworkManager users will probably want the dbus version instead.
+
+[Service]
+Type=simple
+ExecStart=@BINDIR@/wpa_supplicant -c/etc/wpa_supplicant/wpa_supplicant-wired-%I.conf -Dwired -i%I
+
+[Install]
+Alias=multi-user.target.wants/wpa_supplicant-wired@wlan0.service
--- /dev/null
+[Unit]
+Description=WPA supplicant
+
+[Service]
+Type=dbus
+BusName=fi.epitest.hostap.WPASupplicant
+ExecStart=@BINDIR@/wpa_supplicant -u
+
+[Install]
+WantedBy=multi-user.target
+Alias=dbus-fi.epitest.hostap.WPASupplicant.service
--- /dev/null
+[Unit]
+Description=WPA supplicant daemon (interface-specific version)
+Requires=sys-subsystem-net-devices-%i.device
+After=sys-subsystem-net-devices-%i.device
+
+# NetworkManager users will probably want the dbus version instead.
+
+[Service]
+Type=simple
+ExecStart=@BINDIR@/wpa_supplicant -c/etc/wpa_supplicant/wpa_supplicant-%I.conf -i%I
+
+[Install]
+Alias=multi-user.target.wants/wpa_supplicant@wlan0.service
--- /dev/null
+<?xml version="1.0" encoding="Windows-1252"?>\r
+<VisualStudioProject\r
+ ProjectType="Visual C++"\r
+ Version="8.00"\r
+ Name="eapol_test"\r
+ ProjectGUID="{0E3F2C6D-1372-48D6-BCAB-E584917C4DE3}"\r
+ RootNamespace="eapol_test"\r
+ Keyword="Win32Proj"\r
+ >\r
+ <Platforms>\r
+ <Platform\r
+ Name="Win32"\r
+ />\r
+ </Platforms>\r
+ <ToolFiles>\r
+ </ToolFiles>\r
+ <Configurations>\r
+ <Configuration\r
+ Name="Debug|Win32"\r
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"\r
+ IntermediateDirectory="$(ConfigurationName)"\r
+ ConfigurationType="1"\r
+ CharacterSet="1"\r
+ >\r
+ <Tool\r
+ Name="VCPreBuildEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCCustomBuildTool"\r
+ />\r
+ <Tool\r
+ Name="VCXMLDataGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebServiceProxyGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCMIDLTool"\r
+ />\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ Optimization="0"\r
+ AdditionalIncludeDirectories="..\..;..\..\..\src;..\..\..\src\utils;C:\dev\WpdPack\include;C:\dev\openssl\include"\r
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS"\r
+ MinimalRebuild="true"\r
+ BasicRuntimeChecks="3"\r
+ RuntimeLibrary="3"\r
+ UsePrecompiledHeader="0"\r
+ WarningLevel="3"\r
+ Detect64BitPortabilityProblems="true"\r
+ DebugInformationFormat="4"\r
+ DisableSpecificWarnings="4244;4267;4311"\r
+ />\r
+ <Tool\r
+ Name="VCManagedResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCPreLinkEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCLinkerTool"\r
+ AdditionalDependencies="ws2_32.lib Crypt32.lib Winscard.lib Packet.lib wpcap.lib libeay32MT.lib ssleay32Mt.lib"\r
+ LinkIncremental="2"\r
+ AdditionalLibraryDirectories="C:\dev\WpdPack\lib;C:\dev\openssl\lib"\r
+ GenerateDebugInformation="true"\r
+ SubSystem="1"\r
+ TargetMachine="1"\r
+ />\r
+ <Tool\r
+ Name="VCALinkTool"\r
+ />\r
+ <Tool\r
+ Name="VCManifestTool"\r
+ />\r
+ <Tool\r
+ Name="VCXDCMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCBscMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCFxCopTool"\r
+ />\r
+ <Tool\r
+ Name="VCAppVerifierTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebDeploymentTool"\r
+ />\r
+ <Tool\r
+ Name="VCPostBuildEventTool"\r
+ />\r
+ </Configuration>\r
+ <Configuration\r
+ Name="Release|Win32"\r
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"\r
+ IntermediateDirectory="$(ConfigurationName)"\r
+ ConfigurationType="1"\r
+ CharacterSet="1"\r
+ WholeProgramOptimization="1"\r
+ >\r
+ <Tool\r
+ Name="VCPreBuildEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCCustomBuildTool"\r
+ />\r
+ <Tool\r
+ Name="VCXMLDataGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebServiceProxyGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCMIDLTool"\r
+ />\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ AdditionalIncludeDirectories="..\..;..\..\..\src;..\..\..\src\utils;C:\dev\WpdPack\include;C:\dev\openssl\include"\r
+ PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS"\r
+ RuntimeLibrary="2"\r
+ UsePrecompiledHeader="0"\r
+ WarningLevel="3"\r
+ Detect64BitPortabilityProblems="true"\r
+ DebugInformationFormat="3"\r
+ DisableSpecificWarnings="4244;4267;4311"\r
+ />\r
+ <Tool\r
+ Name="VCManagedResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCPreLinkEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCLinkerTool"\r
+ AdditionalDependencies="ws2_32.lib Crypt32.lib Winscard.lib Packet.lib wpcap.lib libeay32MT.lib ssleay32Mt.lib"\r
+ LinkIncremental="1"\r
+ AdditionalLibraryDirectories="C:\dev\WpdPack\lib;C:\dev\openssl\lib"\r
+ GenerateDebugInformation="true"\r
+ SubSystem="1"\r
+ OptimizeReferences="2"\r
+ EnableCOMDATFolding="2"\r
+ TargetMachine="1"\r
+ />\r
+ <Tool\r
+ Name="VCALinkTool"\r
+ />\r
+ <Tool\r
+ Name="VCManifestTool"\r
+ />\r
+ <Tool\r
+ Name="VCXDCMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCBscMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCFxCopTool"\r
+ />\r
+ <Tool\r
+ Name="VCAppVerifierTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebDeploymentTool"\r
+ />\r
+ <Tool\r
+ Name="VCPostBuildEventTool"\r
+ />\r
+ </Configuration>\r
+ </Configurations>\r
+ <References>\r
+ </References>\r
+ <Files>\r
+ <Filter\r
+ Name="Source Files"\r
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"\r
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"\r
+ >\r
+ <File\r
+ RelativePath="..\..\..\src\crypto\aes-cbc.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\crypto\aes-ctr.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\crypto\aes-eax.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\crypto\aes-encblock.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\crypto\aes-omac1.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\crypto\aes-unwrap.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\crypto\aes-wrap.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\utils\base64.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\blacklist.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\bss.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\eap_common\chap.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\utils\common.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\config.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\config_file.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\crypto\crypto_openssl.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\ctrl_iface.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\ctrl_iface_named_pipe.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\drivers\driver_common.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\eap_peer\eap.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\eap_peer\eap_aka.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\eap_common\eap_common.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\eap_peer\eap_gtc.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\eap_peer\eap_leap.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\eap_peer\eap_md5.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\eap_peer\eap_methods.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\eap_peer\eap_mschapv2.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\eap_peer\eap_otp.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\eap_peer\eap_peap.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\eap_common\eap_peap_common.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\eap_register.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\eap_peer\eap_sim.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\eap_common\eap_sim_common.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\eap_peer\eap_tls.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\eap_peer\eap_tls_common.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\eap_peer\eap_tnc.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\eap_peer\eap_ttls.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\eapol_supp\eapol_supp_sm.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\eapol_test.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\utils\eloop_win.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\events.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\crypto\fips_prf_openssl.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\utils\ip_addr.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\l2_packet\l2_packet_winpcap.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\crypto\md5.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\crypto\ms_funcs.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\eap_peer\mschapv2.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\notify.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\utils\os_win32.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\utils\pcsc_funcs.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\rsn_supp\peerkey.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\rsn_supp\pmksa_cache.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\rsn_supp\preauth.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\radius\radius.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\radius\radius_client.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\scan.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\crypto\sha1.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\crypto\sha1-pbkdf2.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\crypto\sha1-tlsprf.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\crypto\tls_openssl.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\eap_peer\tncc.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\rsn_supp\wpa.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\common\wpa_common.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\utils\wpa_debug.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\rsn_supp\wpa_ie.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\wpa_supplicant.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\utils\wpabuf.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\wpas_glue.c"\r
+ >\r
+ </File>\r
+ </Filter>\r
+ <Filter\r
+ Name="Header Files"\r
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"\r
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"\r
+ >\r
+ </Filter>\r
+ <Filter\r
+ Name="Resource Files"\r
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"\r
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"\r
+ >\r
+ </Filter>\r
+ </Files>\r
+ <Globals>\r
+ </Globals>\r
+</VisualStudioProject>\r
--- /dev/null
+<?xml version="1.0" encoding="Windows-1252"?>\r
+<VisualStudioProject\r
+ ProjectType="Visual C++"\r
+ Version="8.00"\r
+ Name="wpa_cli"\r
+ ProjectGUID="{E3A7B181-22CC-4DA3-8410-6AD69879A9EC}"\r
+ RootNamespace="wpa_cli"\r
+ Keyword="Win32Proj"\r
+ >\r
+ <Platforms>\r
+ <Platform\r
+ Name="Win32"\r
+ />\r
+ </Platforms>\r
+ <ToolFiles>\r
+ </ToolFiles>\r
+ <Configurations>\r
+ <Configuration\r
+ Name="Debug|Win32"\r
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"\r
+ IntermediateDirectory="$(ConfigurationName)"\r
+ ConfigurationType="1"\r
+ CharacterSet="0"\r
+ >\r
+ <Tool\r
+ Name="VCPreBuildEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCCustomBuildTool"\r
+ />\r
+ <Tool\r
+ Name="VCXMLDataGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebServiceProxyGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCMIDLTool"\r
+ />\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ Optimization="0"\r
+ AdditionalIncludeDirectories="..\..\..\src;..\..\..\src\utils"\r
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS"\r
+ MinimalRebuild="true"\r
+ BasicRuntimeChecks="3"\r
+ RuntimeLibrary="3"\r
+ UsePrecompiledHeader="0"\r
+ WarningLevel="3"\r
+ Detect64BitPortabilityProblems="true"\r
+ DebugInformationFormat="4"\r
+ DisableSpecificWarnings="4244;4267"\r
+ />\r
+ <Tool\r
+ Name="VCManagedResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCPreLinkEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCLinkerTool"\r
+ AdditionalDependencies="ws2_32.lib"\r
+ LinkIncremental="2"\r
+ GenerateDebugInformation="true"\r
+ SubSystem="1"\r
+ TargetMachine="1"\r
+ />\r
+ <Tool\r
+ Name="VCALinkTool"\r
+ />\r
+ <Tool\r
+ Name="VCManifestTool"\r
+ />\r
+ <Tool\r
+ Name="VCXDCMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCBscMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCFxCopTool"\r
+ />\r
+ <Tool\r
+ Name="VCAppVerifierTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebDeploymentTool"\r
+ />\r
+ <Tool\r
+ Name="VCPostBuildEventTool"\r
+ />\r
+ </Configuration>\r
+ <Configuration\r
+ Name="Release|Win32"\r
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"\r
+ IntermediateDirectory="$(ConfigurationName)"\r
+ ConfigurationType="1"\r
+ CharacterSet="0"\r
+ WholeProgramOptimization="1"\r
+ >\r
+ <Tool\r
+ Name="VCPreBuildEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCCustomBuildTool"\r
+ />\r
+ <Tool\r
+ Name="VCXMLDataGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebServiceProxyGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCMIDLTool"\r
+ />\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ AdditionalIncludeDirectories="..\..\..\src;..\..\..\src\utils"\r
+ PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS"\r
+ RuntimeLibrary="2"\r
+ UsePrecompiledHeader="0"\r
+ WarningLevel="3"\r
+ Detect64BitPortabilityProblems="true"\r
+ DebugInformationFormat="3"\r
+ DisableSpecificWarnings="4244;4267"\r
+ />\r
+ <Tool\r
+ Name="VCManagedResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCPreLinkEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCLinkerTool"\r
+ AdditionalDependencies="ws2_32.lib"\r
+ LinkIncremental="1"\r
+ GenerateDebugInformation="true"\r
+ SubSystem="1"\r
+ OptimizeReferences="2"\r
+ EnableCOMDATFolding="2"\r
+ TargetMachine="1"\r
+ />\r
+ <Tool\r
+ Name="VCALinkTool"\r
+ />\r
+ <Tool\r
+ Name="VCManifestTool"\r
+ />\r
+ <Tool\r
+ Name="VCXDCMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCBscMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCFxCopTool"\r
+ />\r
+ <Tool\r
+ Name="VCAppVerifierTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebDeploymentTool"\r
+ />\r
+ <Tool\r
+ Name="VCPostBuildEventTool"\r
+ />\r
+ </Configuration>\r
+ </Configurations>\r
+ <References>\r
+ </References>\r
+ <Files>\r
+ <Filter\r
+ Name="Source Files"\r
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"\r
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"\r
+ >\r
+ <File\r
+ RelativePath="..\..\..\src\utils\common.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\utils\os_win32.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\wpa_cli.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\common\wpa_ctrl.c"\r
+ >\r
+ </File>\r
+ </Filter>\r
+ <Filter\r
+ Name="Header Files"\r
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"\r
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"\r
+ >\r
+ </Filter>\r
+ <Filter\r
+ Name="Resource Files"\r
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"\r
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"\r
+ >\r
+ </Filter>\r
+ </Files>\r
+ <Globals>\r
+ </Globals>\r
+</VisualStudioProject>\r
--- /dev/null
+<?xml version="1.0" encoding="Windows-1252"?>\r
+<VisualStudioProject\r
+ ProjectType="Visual C++"\r
+ Version="8.00"\r
+ Name="wpa_passphrase"\r
+ ProjectGUID="{ADBE4EA8-F0C5-40C2-AE89-C56D0F2EC1DF}"\r
+ RootNamespace="wpa_passphrase"\r
+ Keyword="Win32Proj"\r
+ >\r
+ <Platforms>\r
+ <Platform\r
+ Name="Win32"\r
+ />\r
+ </Platforms>\r
+ <ToolFiles>\r
+ </ToolFiles>\r
+ <Configurations>\r
+ <Configuration\r
+ Name="Debug|Win32"\r
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"\r
+ IntermediateDirectory="$(ConfigurationName)"\r
+ ConfigurationType="1"\r
+ CharacterSet="0"\r
+ >\r
+ <Tool\r
+ Name="VCPreBuildEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCCustomBuildTool"\r
+ />\r
+ <Tool\r
+ Name="VCXMLDataGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebServiceProxyGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCMIDLTool"\r
+ />\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ Optimization="0"\r
+ AdditionalIncludeDirectories="..\..\..\src;..\..\..\src\utils;C:\dev\openssl\include"\r
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS"\r
+ MinimalRebuild="true"\r
+ BasicRuntimeChecks="3"\r
+ RuntimeLibrary="3"\r
+ UsePrecompiledHeader="0"\r
+ WarningLevel="3"\r
+ Detect64BitPortabilityProblems="true"\r
+ DebugInformationFormat="4"\r
+ DisableSpecificWarnings="4244;4267"\r
+ />\r
+ <Tool\r
+ Name="VCManagedResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCPreLinkEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCLinkerTool"\r
+ AdditionalDependencies="ws2_32.lib"\r
+ LinkIncremental="2"\r
+ AdditionalLibraryDirectories=""\r
+ GenerateDebugInformation="true"\r
+ SubSystem="1"\r
+ TargetMachine="1"\r
+ />\r
+ <Tool\r
+ Name="VCALinkTool"\r
+ />\r
+ <Tool\r
+ Name="VCManifestTool"\r
+ />\r
+ <Tool\r
+ Name="VCXDCMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCBscMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCFxCopTool"\r
+ />\r
+ <Tool\r
+ Name="VCAppVerifierTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebDeploymentTool"\r
+ />\r
+ <Tool\r
+ Name="VCPostBuildEventTool"\r
+ />\r
+ </Configuration>\r
+ <Configuration\r
+ Name="Release|Win32"\r
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"\r
+ IntermediateDirectory="$(ConfigurationName)"\r
+ ConfigurationType="1"\r
+ CharacterSet="0"\r
+ WholeProgramOptimization="1"\r
+ >\r
+ <Tool\r
+ Name="VCPreBuildEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCCustomBuildTool"\r
+ />\r
+ <Tool\r
+ Name="VCXMLDataGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebServiceProxyGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCMIDLTool"\r
+ />\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ AdditionalIncludeDirectories="..\..\..\src;..\..\..\src\utils;C:\dev\openssl\include"\r
+ PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS"\r
+ RuntimeLibrary="2"\r
+ UsePrecompiledHeader="0"\r
+ WarningLevel="3"\r
+ Detect64BitPortabilityProblems="true"\r
+ DebugInformationFormat="3"\r
+ DisableSpecificWarnings="4244;4267"\r
+ />\r
+ <Tool\r
+ Name="VCManagedResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCPreLinkEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCLinkerTool"\r
+ AdditionalDependencies="ws2_32.lib"\r
+ LinkIncremental="1"\r
+ GenerateDebugInformation="true"\r
+ SubSystem="1"\r
+ OptimizeReferences="2"\r
+ EnableCOMDATFolding="2"\r
+ TargetMachine="1"\r
+ />\r
+ <Tool\r
+ Name="VCALinkTool"\r
+ />\r
+ <Tool\r
+ Name="VCManifestTool"\r
+ />\r
+ <Tool\r
+ Name="VCXDCMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCBscMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCFxCopTool"\r
+ />\r
+ <Tool\r
+ Name="VCAppVerifierTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebDeploymentTool"\r
+ />\r
+ <Tool\r
+ Name="VCPostBuildEventTool"\r
+ />\r
+ </Configuration>\r
+ </Configurations>\r
+ <References>\r
+ </References>\r
+ <Files>\r
+ <Filter\r
+ Name="Source Files"\r
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"\r
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"\r
+ >\r
+ <File\r
+ RelativePath="..\..\..\src\utils\common.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\crypto\md5.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\crypto\md5-internal.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\utils\os_win32.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\crypto\sha1.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\crypto\sha1-internal.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\crypto\sha1-pbkdf2.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\wpa_passphrase.c"\r
+ >\r
+ </File>\r
+ </Filter>\r
+ <Filter\r
+ Name="Header Files"\r
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"\r
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"\r
+ >\r
+ </Filter>\r
+ <Filter\r
+ Name="Resource Files"\r
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"\r
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"\r
+ >\r
+ </Filter>\r
+ </Files>\r
+ <Globals>\r
+ </Globals>\r
+</VisualStudioProject>\r
--- /dev/null
+<?xml version="1.0" encoding="Windows-1252"?>\r
+<VisualStudioProject\r
+ ProjectType="Visual C++"\r
+ Version="8.00"\r
+ Name="wpa_supplicant"\r
+ ProjectGUID="{8BCFDA77-AEDC-4168-8897-5B73105BBB87}"\r
+ RootNamespace="wpa_supplicant"\r
+ Keyword="Win32Proj"\r
+ >\r
+ <Platforms>\r
+ <Platform\r
+ Name="Win32"\r
+ />\r
+ </Platforms>\r
+ <ToolFiles>\r
+ </ToolFiles>\r
+ <Configurations>\r
+ <Configuration\r
+ Name="Debug|Win32"\r
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"\r
+ IntermediateDirectory="$(ConfigurationName)"\r
+ ConfigurationType="1"\r
+ CharacterSet="0"\r
+ >\r
+ <Tool\r
+ Name="VCPreBuildEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCCustomBuildTool"\r
+ />\r
+ <Tool\r
+ Name="VCXMLDataGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebServiceProxyGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCMIDLTool"\r
+ />\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ Optimization="0"\r
+ AdditionalIncludeDirectories="..\..;..\..\..\src;..\..\..\src\utils;C:\dev\WpdPack\include;C:\dev\openssl\include"\r
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS"\r
+ MinimalRebuild="true"\r
+ BasicRuntimeChecks="3"\r
+ RuntimeLibrary="3"\r
+ UsePrecompiledHeader="0"\r
+ WarningLevel="3"\r
+ Detect64BitPortabilityProblems="true"\r
+ DebugInformationFormat="4"\r
+ DisableSpecificWarnings="4244;4267;4311"\r
+ />\r
+ <Tool\r
+ Name="VCManagedResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCPreLinkEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCLinkerTool"\r
+ AdditionalDependencies="wbemuuid.lib ws2_32.lib Crypt32.lib Winscard.lib Packet.lib wpcap.lib libeay32MT.lib ssleay32Mt.lib"\r
+ LinkIncremental="2"\r
+ AdditionalLibraryDirectories="C:\dev\WpdPack\lib;C:\dev\openssl\lib"\r
+ GenerateDebugInformation="true"\r
+ SubSystem="1"\r
+ TargetMachine="1"\r
+ />\r
+ <Tool\r
+ Name="VCALinkTool"\r
+ />\r
+ <Tool\r
+ Name="VCManifestTool"\r
+ />\r
+ <Tool\r
+ Name="VCXDCMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCBscMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCFxCopTool"\r
+ />\r
+ <Tool\r
+ Name="VCAppVerifierTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebDeploymentTool"\r
+ />\r
+ <Tool\r
+ Name="VCPostBuildEventTool"\r
+ />\r
+ </Configuration>\r
+ <Configuration\r
+ Name="Release|Win32"\r
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"\r
+ IntermediateDirectory="$(ConfigurationName)"\r
+ ConfigurationType="1"\r
+ CharacterSet="0"\r
+ WholeProgramOptimization="1"\r
+ >\r
+ <Tool\r
+ Name="VCPreBuildEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCCustomBuildTool"\r
+ />\r
+ <Tool\r
+ Name="VCXMLDataGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebServiceProxyGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCMIDLTool"\r
+ />\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ AdditionalIncludeDirectories="..\..;..\..\..\src;..\..\..\src\utils;C:\dev\WpdPack\include;C:\dev\openssl\include"\r
+ PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS"\r
+ RuntimeLibrary="2"\r
+ UsePrecompiledHeader="0"\r
+ WarningLevel="3"\r
+ Detect64BitPortabilityProblems="true"\r
+ DebugInformationFormat="3"\r
+ DisableSpecificWarnings="4244;4267;4311"\r
+ />\r
+ <Tool\r
+ Name="VCManagedResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCPreLinkEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCLinkerTool"\r
+ AdditionalDependencies="wbemuuid.lib ws2_32.lib Crypt32.lib Winscard.lib Packet.lib wpcap.lib libeay32MT.lib ssleay32Mt.lib"\r
+ LinkIncremental="1"\r
+ AdditionalLibraryDirectories="C:\dev\WpdPack\lib;C:\dev\openssl\lib"\r
+ GenerateDebugInformation="true"\r
+ SubSystem="1"\r
+ OptimizeReferences="2"\r
+ EnableCOMDATFolding="2"\r
+ TargetMachine="1"\r
+ />\r
+ <Tool\r
+ Name="VCALinkTool"\r
+ />\r
+ <Tool\r
+ Name="VCManifestTool"\r
+ />\r
+ <Tool\r
+ Name="VCXDCMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCBscMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCFxCopTool"\r
+ />\r
+ <Tool\r
+ Name="VCAppVerifierTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebDeploymentTool"\r
+ />\r
+ <Tool\r
+ Name="VCPostBuildEventTool"\r
+ />\r
+ </Configuration>\r
+ </Configurations>\r
+ <References>\r
+ </References>\r
+ <Files>\r
+ <Filter\r
+ Name="Source Files"\r
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"\r
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"\r
+ >\r
+ <File\r
+ RelativePath="..\..\..\src\crypto\aes-cbc.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\crypto\aes-ctr.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\crypto\aes-eax.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\crypto\aes-encblock.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\crypto\aes-omac1.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\crypto\aes-unwrap.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\crypto\aes-wrap.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\utils\base64.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\blacklist.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\bss.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\eap_common\chap.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\utils\common.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\config.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\config_file.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\crypto\crypto_openssl.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\ctrl_iface.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\ctrl_iface_named_pipe.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\drivers\driver_common.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\drivers\driver_ndis.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\drivers\driver_ndis_.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\drivers\drivers.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\eap_peer\eap.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\eap_common\eap_common.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\eap_peer\eap_gtc.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\eap_peer\eap_leap.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\eap_peer\eap_md5.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\eap_peer\eap_methods.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\eap_peer\eap_mschapv2.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\eap_peer\eap_otp.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\eap_peer\eap_peap.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\eap_common\eap_peap_common.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\eap_register.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\eap_peer\eap_tls.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\eap_peer\eap_tls_common.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\eap_peer\eap_tnc.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\eap_peer\eap_ttls.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\eapol_supp\eapol_supp_sm.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\utils\eloop_win.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\events.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\l2_packet\l2_packet_winpcap.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\main.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\crypto\md5.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\crypto\ms_funcs.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\eap_peer\mschapv2.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\drivers\ndis_events.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\notify.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\utils\os_win32.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\utils\pcsc_funcs.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\rsn_supp\peerkey.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\rsn_supp\pmksa_cache.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\rsn_supp\preauth.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\scan.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\crypto\sha1.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\crypto\sha1-pbkdf2.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\crypto\sha1-tlsprf.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\crypto\tls_openssl.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\eap_peer\tncc.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\rsn_supp\wpa.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\common\wpa_common.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\utils\wpa_debug.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\rsn_supp\wpa_ie.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\wpa_supplicant.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\src\utils\wpabuf.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\wpas_glue.c"\r
+ >\r
+ </File>\r
+ </Filter>\r
+ <Filter\r
+ Name="Header Files"\r
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"\r
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"\r
+ >\r
+ </Filter>\r
+ <Filter\r
+ Name="Resource Files"\r
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"\r
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"\r
+ >\r
+ </Filter>\r
+ </Files>\r
+ <Globals>\r
+ </Globals>\r
+</VisualStudioProject>\r
>\r
</File>\r
<File\r
+ RelativePath="..\..\..\src\drivers\driver_common.c"\r
+ >\r
+ </File>\r
+ <File\r
RelativePath="..\..\..\src\drivers\driver_ndis.c"\r
>\r
</File>\r
/*
* WPA Supplicant - command line interface for wpa_supplicant daemon
- * Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
#ifdef CONFIG_CTRL_IFACE_UNIX
#include <dirent.h>
#endif /* CONFIG_CTRL_IFACE_UNIX */
-#ifdef CONFIG_READLINE
-#include <readline/readline.h>
-#include <readline/history.h>
-#endif /* CONFIG_READLINE */
-#ifdef CONFIG_WPA_CLI_FORK
-#include <sys/wait.h>
-#endif /* CONFIG_WPA_CLI_FORK */
#include "common/wpa_ctrl.h"
-#include "common.h"
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/edit.h"
+#include "utils/list.h"
#include "common/version.h"
+#ifdef ANDROID
+#include <cutils/properties.h>
+#endif /* ANDROID */
static const char *wpa_cli_version =
"wpa_cli v" VERSION_STR "\n"
-"Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi> and contributors";
+"Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi> and contributors";
static const char *wpa_cli_license =
static struct wpa_ctrl *ctrl_conn;
static struct wpa_ctrl *mon_conn;
-#ifdef CONFIG_WPA_CLI_FORK
-static pid_t mon_pid = 0;
-#endif /* CONFIG_WPA_CLI_FORK */
static int wpa_cli_quit = 0;
static int wpa_cli_attached = 0;
static int wpa_cli_connected = 0;
static int wpa_cli_last_id = 0;
-static const char *ctrl_iface_dir = "/var/run/wpa_supplicant";
+#ifndef CONFIG_CTRL_IFACE_DIR
+#define CONFIG_CTRL_IFACE_DIR "/var/run/wpa_supplicant"
+#endif /* CONFIG_CTRL_IFACE_DIR */
+static const char *ctrl_iface_dir = CONFIG_CTRL_IFACE_DIR;
static char *ctrl_ifname = NULL;
static const char *pid_file = NULL;
static const char *action_file = NULL;
static int ping_interval = 5;
static int interactive = 0;
+struct cli_txt_entry {
+ struct dl_list list;
+ char *txt;
+};
+
+static DEFINE_DL_LIST(bsses); /* struct cli_txt_entry */
+static DEFINE_DL_LIST(p2p_peers); /* struct cli_txt_entry */
+static DEFINE_DL_LIST(p2p_groups); /* struct cli_txt_entry */
-static void print_help();
+
+static void print_help(void);
+static void wpa_cli_mon_receive(int sock, void *eloop_ctx, void *sock_ctx);
static void usage(void)
"events from\n"
" wpa_supplicant\n"
" -B = run a daemon in the background\n"
- " default path: /var/run/wpa_supplicant\n"
+ " default path: " CONFIG_CTRL_IFACE_DIR "\n"
" default interface: first interface found in socket path\n");
print_help();
}
-#ifdef CONFIG_WPA_CLI_FORK
-static int in_query = 0;
+static void cli_txt_list_free(struct cli_txt_entry *e)
+{
+ dl_list_del(&e->list);
+ os_free(e->txt);
+ os_free(e);
+}
+
-static void wpa_cli_monitor_sig(int sig)
+static void cli_txt_list_flush(struct dl_list *list)
{
- if (sig == SIGUSR1)
- in_query = 1;
- else if (sig == SIGUSR2)
- in_query = 0;
+ struct cli_txt_entry *e;
+ while ((e = dl_list_first(list, struct cli_txt_entry, list)))
+ cli_txt_list_free(e);
}
-static void wpa_cli_monitor(void)
+
+static struct cli_txt_entry * cli_txt_list_get(struct dl_list *txt_list,
+ const char *txt)
{
- char buf[256];
- size_t len = sizeof(buf) - 1;
- struct timeval tv;
- fd_set rfds;
+ struct cli_txt_entry *e;
+ dl_list_for_each(e, txt_list, struct cli_txt_entry, list) {
+ if (os_strcmp(e->txt, txt) == 0)
+ return e;
+ }
+ return NULL;
+}
- signal(SIGUSR1, wpa_cli_monitor_sig);
- signal(SIGUSR2, wpa_cli_monitor_sig);
- while (mon_conn) {
- int s = wpa_ctrl_get_fd(mon_conn);
- tv.tv_sec = 5;
- tv.tv_usec = 0;
- FD_ZERO(&rfds);
- FD_SET(s, &rfds);
- if (select(s + 1, &rfds, NULL, NULL, &tv) < 0) {
- if (errno == EINTR)
- continue;
- perror("select");
- break;
- }
- if (mon_conn == NULL)
+static void cli_txt_list_del(struct dl_list *txt_list, const char *txt)
+{
+ struct cli_txt_entry *e;
+ e = cli_txt_list_get(txt_list, txt);
+ if (e)
+ cli_txt_list_free(e);
+}
+
+
+static void cli_txt_list_del_addr(struct dl_list *txt_list, const char *txt)
+{
+ u8 addr[ETH_ALEN];
+ char buf[18];
+ if (hwaddr_aton(txt, addr) < 0)
+ return;
+ os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr));
+ cli_txt_list_del(txt_list, buf);
+}
+
+
+#ifdef CONFIG_P2P
+static void cli_txt_list_del_word(struct dl_list *txt_list, const char *txt)
+{
+ const char *end;
+ char *buf;
+ end = os_strchr(txt, ' ');
+ if (end == NULL)
+ end = txt + os_strlen(txt);
+ buf = os_malloc(end - txt + 1);
+ if (buf == NULL)
+ return;
+ os_memcpy(buf, txt, end - txt);
+ buf[end - txt] = '\0';
+ cli_txt_list_del(txt_list, buf);
+ os_free(buf);
+}
+#endif /* CONFIG_P2P */
+
+
+static int cli_txt_list_add(struct dl_list *txt_list, const char *txt)
+{
+ struct cli_txt_entry *e;
+ e = cli_txt_list_get(txt_list, txt);
+ if (e)
+ return 0;
+ e = os_zalloc(sizeof(*e));
+ if (e == NULL)
+ return -1;
+ e->txt = os_strdup(txt);
+ if (e->txt == NULL) {
+ os_free(e);
+ return -1;
+ }
+ dl_list_add(txt_list, &e->list);
+ return 0;
+}
+
+
+#ifdef CONFIG_P2P
+static int cli_txt_list_add_addr(struct dl_list *txt_list, const char *txt)
+{
+ u8 addr[ETH_ALEN];
+ char buf[18];
+ if (hwaddr_aton(txt, addr) < 0)
+ return -1;
+ os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr));
+ return cli_txt_list_add(txt_list, buf);
+}
+
+
+static int cli_txt_list_add_word(struct dl_list *txt_list, const char *txt)
+{
+ const char *end;
+ char *buf;
+ int ret;
+ end = os_strchr(txt, ' ');
+ if (end == NULL)
+ end = txt + os_strlen(txt);
+ buf = os_malloc(end - txt + 1);
+ if (buf == NULL)
+ return -1;
+ os_memcpy(buf, txt, end - txt);
+ buf[end - txt] = '\0';
+ ret = cli_txt_list_add(txt_list, buf);
+ os_free(buf);
+ return ret;
+}
+#endif /* CONFIG_P2P */
+
+
+static char ** cli_txt_list_array(struct dl_list *txt_list)
+{
+ unsigned int i, count = dl_list_len(txt_list);
+ char **res;
+ struct cli_txt_entry *e;
+
+ res = os_zalloc((count + 1) * sizeof(char *));
+ if (res == NULL)
+ return NULL;
+
+ i = 0;
+ dl_list_for_each(e, txt_list, struct cli_txt_entry, list) {
+ res[i] = os_strdup(e->txt);
+ if (res[i] == NULL)
break;
- if (FD_ISSET(s, &rfds)) {
- len = sizeof(buf) - 1;
- int res = wpa_ctrl_recv(mon_conn, buf, &len);
- if (res < 0) {
- perror("wpa_ctrl_recv");
- break;
- }
- buf[len] = '\0';
- if (in_query)
- printf("\r");
- printf("%s\n", buf);
- kill(getppid(), SIGUSR1);
+ i++;
+ }
+
+ return res;
+}
+
+
+static int get_cmd_arg_num(const char *str, int pos)
+{
+ int arg = 0, i;
+
+ for (i = 0; i <= pos; i++) {
+ if (str[i] != ' ') {
+ arg++;
+ while (i <= pos && str[i] != ' ')
+ i++;
}
}
+
+ if (arg > 0)
+ arg--;
+ return arg;
+}
+
+
+static int str_starts(const char *src, const char *match)
+{
+ return os_strncmp(src, match, os_strlen(match)) == 0;
+}
+
+
+static int wpa_cli_show_event(const char *event)
+{
+ const char *start;
+
+ start = os_strchr(event, '>');
+ if (start == NULL)
+ return 1;
+
+ start++;
+ /*
+ * Skip BSS added/removed events since they can be relatively frequent
+ * and are likely of not much use for an interactive user.
+ */
+ if (str_starts(start, WPA_EVENT_BSS_ADDED) ||
+ str_starts(start, WPA_EVENT_BSS_REMOVED))
+ return 0;
+
+ return 1;
}
-#endif /* CONFIG_WPA_CLI_FORK */
static int wpa_cli_open_connection(const char *ifname, int attach)
else
mon_conn = NULL;
#else /* CONFIG_CTRL_IFACE_UDP || CONFIG_CTRL_IFACE_NAMED_PIPE */
- char *cfile;
+ char *cfile = NULL;
int flen, res;
if (ifname == NULL)
return -1;
- flen = os_strlen(ctrl_iface_dir) + os_strlen(ifname) + 2;
- cfile = os_malloc(flen);
- if (cfile == NULL)
- return -1L;
- res = os_snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ifname);
- if (res < 0 || res >= flen) {
- os_free(cfile);
- return -1;
+#ifdef ANDROID
+ if (access(ctrl_iface_dir, F_OK) < 0) {
+ cfile = os_strdup(ifname);
+ if (cfile == NULL)
+ return -1;
+ }
+#endif /* ANDROID */
+
+ if (cfile == NULL) {
+ flen = os_strlen(ctrl_iface_dir) + os_strlen(ifname) + 2;
+ cfile = os_malloc(flen);
+ if (cfile == NULL)
+ return -1;
+ res = os_snprintf(cfile, flen, "%s/%s", ctrl_iface_dir,
+ ifname);
+ if (res < 0 || res >= flen) {
+ os_free(cfile);
+ return -1;
+ }
}
ctrl_conn = wpa_ctrl_open(cfile);
if (mon_conn) {
if (wpa_ctrl_attach(mon_conn) == 0) {
wpa_cli_attached = 1;
+ if (interactive)
+ eloop_register_read_sock(
+ wpa_ctrl_get_fd(mon_conn),
+ wpa_cli_mon_receive, NULL, NULL);
} else {
printf("Warning: Failed to attach to "
"wpa_supplicant.\n");
return -1;
}
-
-#ifdef CONFIG_WPA_CLI_FORK
- {
- pid_t p = fork();
- if (p < 0) {
- perror("fork");
- return -1;
- }
- if (p == 0) {
- wpa_cli_monitor();
- exit(0);
- } else
- mon_pid = p;
- }
-#endif /* CONFIG_WPA_CLI_FORK */
}
return 0;
if (ctrl_conn == NULL)
return;
-#ifdef CONFIG_WPA_CLI_FORK
- if (mon_pid) {
- int status;
- kill(mon_pid, SIGPIPE);
- wait(&status);
- mon_pid = 0;
- }
-#endif /* CONFIG_WPA_CLI_FORK */
-
if (wpa_cli_attached) {
wpa_ctrl_detach(interactive ? mon_conn : ctrl_conn);
wpa_cli_attached = 0;
wpa_ctrl_close(ctrl_conn);
ctrl_conn = NULL;
if (mon_conn) {
+ eloop_unregister_read_sock(wpa_ctrl_get_fd(mon_conn));
wpa_ctrl_close(mon_conn);
mon_conn = NULL;
}
if (print) {
buf[len] = '\0';
printf("%s", buf);
+ if (interactive && len > 0 && buf[len - 1] != '\n')
+ printf("\n");
}
return 0;
}
}
+static int wpa_cli_cmd_relog(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "RELOG");
+}
+
+
+static int wpa_cli_cmd_note(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ char cmd[256];
+ int ret;
+ if (argc == 0)
+ return -1;
+ ret = os_snprintf(cmd, sizeof(cmd), "NOTE %s", argv[0]);
+ if (ret < 0 || (size_t) ret >= sizeof(cmd))
+ return -1;
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
+
static int wpa_cli_cmd_mib(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_ctrl_command(ctrl, "MIB");
static int wpa_cli_cmd_quit(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
wpa_cli_quit = 1;
+ if (interactive)
+ eloop_terminate();
return 0;
}
return 0;
}
- if (argc != 2) {
+ if (argc != 1 && argc != 2) {
printf("Invalid SET command: needs two arguments (variable "
"name and value)\n");
return -1;
}
- res = os_snprintf(cmd, sizeof(cmd), "SET %s %s", argv[0], argv[1]);
+ if (argc == 1)
+ res = os_snprintf(cmd, sizeof(cmd), "SET %s ", argv[0]);
+ else
+ res = os_snprintf(cmd, sizeof(cmd), "SET %s %s",
+ argv[0], argv[1]);
if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
printf("Too long SET command.\n");
return -1;
}
+static int wpa_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ char cmd[256];
+ int res;
+
+ if (argc != 1) {
+ printf("Invalid GET command: need one argument (variable "
+ "name)\n");
+ return -1;
+ }
+
+ res = os_snprintf(cmd, sizeof(cmd), "GET %s", argv[0]);
+ if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+ printf("Too long GET command.\n");
+ return -1;
+ }
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
+
static int wpa_cli_cmd_logoff(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_ctrl_command(ctrl, "LOGOFF");
}
+static int wpa_cli_cmd_scan_interval(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ char cmd[256];
+ int res;
+
+ if (argc != 1) {
+ printf("Invalid SCAN_INTERVAL command: needs one argument "
+ "scan_interval value)\n");
+ return -1;
+ }
+ res = os_snprintf(cmd, sizeof(cmd), "SCAN_INTERVAL %s", argv[0]);
+ if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+ printf("Too long SCAN_INTERVAL command.\n");
+ return -1;
+ }
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_bss_expire_age(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ char cmd[256];
+ int res;
+
+ if (argc != 1) {
+ printf("Invalid BSS_EXPIRE_AGE command: needs one argument "
+ "(bss_expire_age value)\n");
+ return -1;
+ }
+ res = os_snprintf(cmd, sizeof(cmd), "BSS_EXPIRE_AGE %s", argv[0]);
+ if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+ printf("Too long BSS_EXPIRE_AGE command.\n");
+ return -1;
+ }
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_bss_expire_count(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ char cmd[256];
+ int res;
+
+ if (argc != 1) {
+ printf("Invalid BSS_EXPIRE_COUNT command: needs one argument "
+ "(bss_expire_count value)\n");
+ return -1;
+ }
+ res = os_snprintf(cmd, sizeof(cmd), "BSS_EXPIRE_COUNT %s", argv[0]);
+ if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+ printf("Too long BSS_EXPIRE_COUNT command.\n");
+ return -1;
+ }
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
+
static int wpa_cli_cmd_stkstart(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
}
+static int wpa_cli_cmd_wps_check_pin(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ char cmd[256];
+ int res;
+
+ if (argc != 1 && argc != 2) {
+ printf("Invalid WPS_CHECK_PIN command: needs one argument:\n"
+ "- PIN to be verified\n");
+ return -1;
+ }
+
+ if (argc == 2)
+ res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s %s",
+ argv[0], argv[1]);
+ else
+ res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s",
+ argv[0]);
+ if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+ printf("Too long WPS_CHECK_PIN command.\n");
+ return -1;
+ }
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_wps_cancel(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "WPS_CANCEL");
+}
+
+
#ifdef CONFIG_WPS_OOB
static int wpa_cli_cmd_wps_oob(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
if (argc == 2)
res = os_snprintf(cmd, sizeof(cmd), "WPS_REG %s %s",
argv[0], argv[1]);
- else if (argc == 6) {
+ else if (argc == 5 || argc == 6) {
char ssid_hex[2 * 32 + 1];
char key_hex[2 * 64 + 1];
int i;
}
key_hex[0] = '\0';
- for (i = 0; i < 64; i++) {
- if (argv[5][i] == '\0')
- break;
- os_snprintf(&key_hex[i * 2], 3, "%02x", argv[5][i]);
+ if (argc == 6) {
+ for (i = 0; i < 64; i++) {
+ if (argv[5][i] == '\0')
+ break;
+ os_snprintf(&key_hex[i * 2], 3, "%02x",
+ argv[5][i]);
+ }
}
res = os_snprintf(cmd, sizeof(cmd),
key_hex);
} else {
printf("Invalid WPS_REG command: need two arguments:\n"
- "- BSSID: use 'any' to select any\n"
+ "- BSSID of the target AP\n"
"- AP PIN\n");
printf("Alternatively, six arguments can be used to "
"reconfigure the AP:\n"
- "- BSSID: use 'any' to select any\n"
+ "- BSSID of the target AP\n"
"- AP PIN\n"
"- new SSID\n"
"- new auth (OPEN, WPAPSK, WPA2PSK)\n"
}
+static int wpa_cli_cmd_wps_ap_pin(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ char cmd[256];
+ int res;
+
+ if (argc < 1) {
+ printf("Invalid WPS_AP_PIN command: needs at least one "
+ "argument\n");
+ return -1;
+ }
+
+ if (argc > 2)
+ res = os_snprintf(cmd, sizeof(cmd), "WPS_AP_PIN %s %s %s",
+ argv[0], argv[1], argv[2]);
+ else if (argc > 1)
+ res = os_snprintf(cmd, sizeof(cmd), "WPS_AP_PIN %s %s",
+ argv[0], argv[1]);
+ else
+ res = os_snprintf(cmd, sizeof(cmd), "WPS_AP_PIN %s",
+ argv[0]);
+ if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+ printf("Too long WPS_AP_PIN command.\n");
+ return -1;
+ }
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
+
static int wpa_cli_cmd_wps_er_start(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
+ char cmd[100];
+ if (argc > 0) {
+ os_snprintf(cmd, sizeof(cmd), "WPS_ER_START %s", argv[0]);
+ return wpa_ctrl_command(ctrl, cmd);
+ }
return wpa_ctrl_command(ctrl, "WPS_ER_START");
-
}
char cmd[256];
int res;
- if (argc != 2) {
- printf("Invalid WPS_ER_PIN command: need two arguments:\n"
+ if (argc < 2) {
+ printf("Invalid WPS_ER_PIN command: need at least two "
+ "arguments:\n"
"- UUID: use 'any' to select any\n"
- "- PIN: Enrollee PIN\n");
+ "- PIN: Enrollee PIN\n"
+ "optional: - Enrollee MAC address\n");
return -1;
}
- res = os_snprintf(cmd, sizeof(cmd), "WPS_ER_PIN %s %s",
- argv[0], argv[1]);
+ if (argc > 2)
+ res = os_snprintf(cmd, sizeof(cmd), "WPS_ER_PIN %s %s %s",
+ argv[0], argv[1], argv[2]);
+ else
+ res = os_snprintf(cmd, sizeof(cmd), "WPS_ER_PIN %s %s",
+ argv[0], argv[1]);
if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
printf("Too long WPS_ER_PIN command.\n");
return -1;
}
-static int wpa_cli_cmd_ibss_rsn(struct wpa_ctrl *ctrl, int argc, char *argv[])
+static int wpa_cli_cmd_wps_er_set_config(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
{
char cmd[256];
int res;
- if (argc != 1) {
- printf("Invalid IBSS_RSN command: needs one argument "
- "(Peer STA MAC address)\n");
+ if (argc != 2) {
+ printf("Invalid WPS_ER_SET_CONFIG command: need two "
+ "arguments:\n"
+ "- UUID: specify which AP to use\n"
+ "- Network configuration id\n");
return -1;
}
- res = os_snprintf(cmd, sizeof(cmd), "IBSS_RSN %s", argv[0]);
+ res = os_snprintf(cmd, sizeof(cmd), "WPS_ER_SET_CONFIG %s %s",
+ argv[0], argv[1]);
if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
- printf("Too long IBSS_RSN command.\n");
+ printf("Too long WPS_ER_SET_CONFIG command.\n");
return -1;
}
return wpa_ctrl_command(ctrl, cmd);
}
-static int wpa_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[])
+static int wpa_cli_cmd_wps_er_config(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
{
char cmd[256];
int res;
- if (argc != 1) {
- printf("Invalid LEVEL command: needs one argument (debug "
- "level)\n");
- return -1;
- }
- res = os_snprintf(cmd, sizeof(cmd), "LEVEL %s", argv[0]);
- if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
- printf("Too long LEVEL command.\n");
- return -1;
- }
- return wpa_ctrl_command(ctrl, cmd);
-}
+ if (argc == 5 || argc == 6) {
+ char ssid_hex[2 * 32 + 1];
+ char key_hex[2 * 64 + 1];
+ int i;
+
+ ssid_hex[0] = '\0';
+ for (i = 0; i < 32; i++) {
+ if (argv[2][i] == '\0')
+ break;
+ os_snprintf(&ssid_hex[i * 2], 3, "%02x", argv[2][i]);
+ }
+
+ key_hex[0] = '\0';
+ if (argc == 6) {
+ for (i = 0; i < 64; i++) {
+ if (argv[5][i] == '\0')
+ break;
+ os_snprintf(&key_hex[i * 2], 3, "%02x",
+ argv[5][i]);
+ }
+ }
+
+ res = os_snprintf(cmd, sizeof(cmd),
+ "WPS_ER_CONFIG %s %s %s %s %s %s",
+ argv[0], argv[1], ssid_hex, argv[3], argv[4],
+ key_hex);
+ } else {
+ printf("Invalid WPS_ER_CONFIG command: need six arguments:\n"
+ "- AP UUID\n"
+ "- AP PIN\n"
+ "- new SSID\n"
+ "- new auth (OPEN, WPAPSK, WPA2PSK)\n"
+ "- new encr (NONE, WEP, TKIP, CCMP)\n"
+ "- new key\n");
+ return -1;
+ }
+
+ if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+ printf("Too long WPS_ER_CONFIG command.\n");
+ return -1;
+ }
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_ibss_rsn(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ char cmd[256];
+ int res;
+
+ if (argc != 1) {
+ printf("Invalid IBSS_RSN command: needs one argument "
+ "(Peer STA MAC address)\n");
+ return -1;
+ }
+
+ res = os_snprintf(cmd, sizeof(cmd), "IBSS_RSN %s", argv[0]);
+ if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+ printf("Too long IBSS_RSN command.\n");
+ return -1;
+ }
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ char cmd[256];
+ int res;
+
+ if (argc != 1) {
+ printf("Invalid LEVEL command: needs one argument (debug "
+ "level)\n");
+ return -1;
+ }
+ res = os_snprintf(cmd, sizeof(cmd), "LEVEL %s", argv[0]);
+ if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+ printf("Too long LEVEL command.\n");
+ return -1;
+ }
+ return wpa_ctrl_command(ctrl, cmd);
+}
static int wpa_cli_cmd_identity(struct wpa_ctrl *ctrl, int argc, char *argv[])
}
+static int wpa_cli_cmd_blacklist(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ char cmd[256], *pos, *end;
+ int i, ret;
+
+ end = cmd + sizeof(cmd);
+ pos = cmd;
+ ret = os_snprintf(pos, end - pos, "BLACKLIST");
+ if (ret < 0 || ret >= end - pos) {
+ printf("Too long BLACKLIST command.\n");
+ return -1;
+ }
+ pos += ret;
+ for (i = 0; i < argc; i++) {
+ ret = os_snprintf(pos, end - pos, " %s", argv[i]);
+ if (ret < 0 || ret >= end - pos) {
+ printf("Too long BLACKLIST command.\n");
+ return -1;
+ }
+ pos += ret;
+ }
+
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_log_level(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ char cmd[256], *pos, *end;
+ int i, ret;
+
+ end = cmd + sizeof(cmd);
+ pos = cmd;
+ ret = os_snprintf(pos, end - pos, "LOG_LEVEL");
+ if (ret < 0 || ret >= end - pos) {
+ printf("Too long LOG_LEVEL command.\n");
+ return -1;
+ }
+ pos += ret;
+ for (i = 0; i < argc; i++) {
+ ret = os_snprintf(pos, end - pos, " %s", argv[i]);
+ if (ret < 0 || ret >= end - pos) {
+ printf("Too long LOG_LEVEL command.\n");
+ return -1;
+ }
+ pos += ret;
+ }
+
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
+
static int wpa_cli_cmd_list_networks(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
}
+static char ** wpa_cli_complete_bss(const char *str, int pos)
+{
+ int arg = get_cmd_arg_num(str, pos);
+ char **res = NULL;
+
+ switch (arg) {
+ case 1:
+ res = cli_txt_list_array(&bsses);
+ break;
+ }
+
+ return res;
+}
+
+
static int wpa_cli_cmd_get_capability(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return -1;
}
- buf[len] = '\0';
- if (memcmp(buf, "FAIL", 4) == 0)
+ buf[len] = '\0';
+ if (memcmp(buf, "FAIL", 4) == 0)
+ return -1;
+ printf("%s", buf);
+
+ pos = buf;
+ while (*pos != '\0' && *pos != '\n')
+ pos++;
+ *pos = '\0';
+ os_strlcpy(addr, buf, addr_len);
+ return 0;
+}
+
+
+static int wpa_cli_cmd_all_sta(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ char addr[32], cmd[64];
+
+ if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr)))
+ return 0;
+ do {
+ os_snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr);
+ } while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr)) == 0);
+
+ return -1;
+}
+#endif /* CONFIG_AP */
+
+
+static int wpa_cli_cmd_suspend(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "SUSPEND");
+}
+
+
+static int wpa_cli_cmd_resume(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "RESUME");
+}
+
+
+static int wpa_cli_cmd_drop_sa(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "DROP_SA");
+}
+
+
+static int wpa_cli_cmd_roam(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ char cmd[128];
+ int res;
+
+ if (argc != 1) {
+ printf("Invalid ROAM command: needs one argument "
+ "(target AP's BSSID)\n");
+ return -1;
+ }
+
+ res = os_snprintf(cmd, sizeof(cmd), "ROAM %s", argv[0]);
+ if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+ printf("Too long ROAM command.\n");
+ return -1;
+ }
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+#ifdef CONFIG_P2P
+
+static int wpa_cli_cmd_p2p_find(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ char cmd[128];
+ int res;
+
+ if (argc == 0)
+ return wpa_ctrl_command(ctrl, "P2P_FIND");
+
+ if (argc > 2)
+ res = os_snprintf(cmd, sizeof(cmd), "P2P_FIND %s %s %s",
+ argv[0], argv[1], argv[2]);
+ else if (argc > 1)
+ res = os_snprintf(cmd, sizeof(cmd), "P2P_FIND %s %s",
+ argv[0], argv[1]);
+ else
+ res = os_snprintf(cmd, sizeof(cmd), "P2P_FIND %s", argv[0]);
+ if (res < 0 || (size_t) res >= sizeof(cmd))
+ return -1;
+ cmd[sizeof(cmd) - 1] = '\0';
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_p2p_stop_find(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "P2P_STOP_FIND");
+}
+
+
+static int wpa_cli_cmd_p2p_connect(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ char cmd[128];
+ int res;
+
+ if (argc < 2) {
+ printf("Invalid P2P_CONNECT command: needs at least two "
+ "arguments (address and pbc/PIN)\n");
+ return -1;
+ }
+
+ if (argc > 4)
+ res = os_snprintf(cmd, sizeof(cmd),
+ "P2P_CONNECT %s %s %s %s %s",
+ argv[0], argv[1], argv[2], argv[3],
+ argv[4]);
+ else if (argc > 3)
+ res = os_snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s %s %s",
+ argv[0], argv[1], argv[2], argv[3]);
+ else if (argc > 2)
+ res = os_snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s %s",
+ argv[0], argv[1], argv[2]);
+ else
+ res = os_snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s",
+ argv[0], argv[1]);
+ if (res < 0 || (size_t) res >= sizeof(cmd))
+ return -1;
+ cmd[sizeof(cmd) - 1] = '\0';
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static char ** wpa_cli_complete_p2p_connect(const char *str, int pos)
+{
+ int arg = get_cmd_arg_num(str, pos);
+ char **res = NULL;
+
+ switch (arg) {
+ case 1:
+ res = cli_txt_list_array(&p2p_peers);
+ break;
+ }
+
+ return res;
+}
+
+
+static int wpa_cli_cmd_p2p_listen(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ char cmd[128];
+ int res;
+
+ if (argc == 0)
+ return wpa_ctrl_command(ctrl, "P2P_LISTEN");
+
+ res = os_snprintf(cmd, sizeof(cmd), "P2P_LISTEN %s", argv[0]);
+ if (res < 0 || (size_t) res >= sizeof(cmd))
+ return -1;
+ cmd[sizeof(cmd) - 1] = '\0';
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_p2p_group_remove(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ char cmd[128];
+ int res;
+
+ if (argc != 1) {
+ printf("Invalid P2P_GROUP_REMOVE command: needs one argument "
+ "(interface name)\n");
+ return -1;
+ }
+
+ res = os_snprintf(cmd, sizeof(cmd), "P2P_GROUP_REMOVE %s", argv[0]);
+ if (res < 0 || (size_t) res >= sizeof(cmd))
+ return -1;
+ cmd[sizeof(cmd) - 1] = '\0';
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static char ** wpa_cli_complete_p2p_group_remove(const char *str, int pos)
+{
+ int arg = get_cmd_arg_num(str, pos);
+ char **res = NULL;
+
+ switch (arg) {
+ case 1:
+ res = cli_txt_list_array(&p2p_groups);
+ break;
+ }
+
+ return res;
+}
+
+
+static int wpa_cli_cmd_p2p_group_add(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ char cmd[128];
+ int res;
+
+ if (argc == 0)
+ return wpa_ctrl_command(ctrl, "P2P_GROUP_ADD");
+
+ if (argc > 1)
+ res = os_snprintf(cmd, sizeof(cmd), "P2P_GROUP_ADD %s %s",
+ argv[0], argv[1]);
+ else
+ res = os_snprintf(cmd, sizeof(cmd), "P2P_GROUP_ADD %s",
+ argv[0]);
+ if (res < 0 || (size_t) res >= sizeof(cmd))
+ return -1;
+ cmd[sizeof(cmd) - 1] = '\0';
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_p2p_prov_disc(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ char cmd[128];
+ int res;
+
+ if (argc != 2 && argc != 3) {
+ printf("Invalid P2P_PROV_DISC command: needs at least "
+ "two arguments, address and config method\n"
+ "(display, keypad, or pbc) and an optional join\n");
+ return -1;
+ }
+
+ if (argc == 3)
+ res = os_snprintf(cmd, sizeof(cmd), "P2P_PROV_DISC %s %s %s",
+ argv[0], argv[1], argv[2]);
+ else
+ res = os_snprintf(cmd, sizeof(cmd), "P2P_PROV_DISC %s %s",
+ argv[0], argv[1]);
+ if (res < 0 || (size_t) res >= sizeof(cmd))
+ return -1;
+ cmd[sizeof(cmd) - 1] = '\0';
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_p2p_get_passphrase(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "P2P_GET_PASSPHRASE");
+}
+
+
+static int wpa_cli_cmd_p2p_serv_disc_req(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ char cmd[4096];
+ int res;
+
+ if (argc != 2 && argc != 4) {
+ printf("Invalid P2P_SERV_DISC_REQ command: needs two "
+ "arguments (address and TLVs) or four arguments "
+ "(address, \"upnp\", version, search target "
+ "(SSDP ST:)\n");
+ return -1;
+ }
+
+ if (argc == 4)
+ res = os_snprintf(cmd, sizeof(cmd),
+ "P2P_SERV_DISC_REQ %s %s %s %s",
+ argv[0], argv[1], argv[2], argv[3]);
+ else
+ res = os_snprintf(cmd, sizeof(cmd), "P2P_SERV_DISC_REQ %s %s",
+ argv[0], argv[1]);
+ if (res < 0 || (size_t) res >= sizeof(cmd))
+ return -1;
+ cmd[sizeof(cmd) - 1] = '\0';
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_p2p_serv_disc_cancel_req(struct wpa_ctrl *ctrl,
+ int argc, char *argv[])
+{
+ char cmd[128];
+ int res;
+
+ if (argc != 1) {
+ printf("Invalid P2P_SERV_DISC_CANCEL_REQ command: needs one "
+ "argument (pending request identifier)\n");
+ return -1;
+ }
+
+ res = os_snprintf(cmd, sizeof(cmd), "P2P_SERV_DISC_CANCEL_REQ %s",
+ argv[0]);
+ if (res < 0 || (size_t) res >= sizeof(cmd))
+ return -1;
+ cmd[sizeof(cmd) - 1] = '\0';
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_p2p_serv_disc_resp(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ char cmd[4096];
+ int res;
+
+ if (argc != 4) {
+ printf("Invalid P2P_SERV_DISC_RESP command: needs four "
+ "arguments (freq, address, dialog token, and TLVs)\n");
+ return -1;
+ }
+
+ res = os_snprintf(cmd, sizeof(cmd), "P2P_SERV_DISC_RESP %s %s %s %s",
+ argv[0], argv[1], argv[2], argv[3]);
+ if (res < 0 || (size_t) res >= sizeof(cmd))
+ return -1;
+ cmd[sizeof(cmd) - 1] = '\0';
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_p2p_service_update(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "P2P_SERVICE_UPDATE");
+}
+
+
+static int wpa_cli_cmd_p2p_serv_disc_external(struct wpa_ctrl *ctrl,
+ int argc, char *argv[])
+{
+ char cmd[128];
+ int res;
+
+ if (argc != 1) {
+ printf("Invalid P2P_SERV_DISC_EXTERNAL command: needs one "
+ "argument (external processing: 0/1)\n");
+ return -1;
+ }
+
+ res = os_snprintf(cmd, sizeof(cmd), "P2P_SERV_DISC_EXTERNAL %s",
+ argv[0]);
+ if (res < 0 || (size_t) res >= sizeof(cmd))
+ return -1;
+ cmd[sizeof(cmd) - 1] = '\0';
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_p2p_service_flush(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "P2P_SERVICE_FLUSH");
+}
+
+
+static int wpa_cli_cmd_p2p_service_add(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ char cmd[4096];
+ int res;
+
+ if (argc != 3 && argc != 4) {
+ printf("Invalid P2P_SERVICE_ADD command: needs three or four "
+ "arguments\n");
+ return -1;
+ }
+
+ if (argc == 4)
+ res = os_snprintf(cmd, sizeof(cmd),
+ "P2P_SERVICE_ADD %s %s %s %s",
+ argv[0], argv[1], argv[2], argv[3]);
+ else
+ res = os_snprintf(cmd, sizeof(cmd),
+ "P2P_SERVICE_ADD %s %s %s",
+ argv[0], argv[1], argv[2]);
+ if (res < 0 || (size_t) res >= sizeof(cmd))
+ return -1;
+ cmd[sizeof(cmd) - 1] = '\0';
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_p2p_service_del(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ char cmd[4096];
+ int res;
+
+ if (argc != 2 && argc != 3) {
+ printf("Invalid P2P_SERVICE_DEL command: needs two or three "
+ "arguments\n");
+ return -1;
+ }
+
+ if (argc == 3)
+ res = os_snprintf(cmd, sizeof(cmd),
+ "P2P_SERVICE_DEL %s %s %s",
+ argv[0], argv[1], argv[2]);
+ else
+ res = os_snprintf(cmd, sizeof(cmd),
+ "P2P_SERVICE_DEL %s %s",
+ argv[0], argv[1]);
+ if (res < 0 || (size_t) res >= sizeof(cmd))
+ return -1;
+ cmd[sizeof(cmd) - 1] = '\0';
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_p2p_reject(struct wpa_ctrl *ctrl,
+ int argc, char *argv[])
+{
+ char cmd[128];
+ int res;
+
+ if (argc != 1) {
+ printf("Invalid P2P_REJECT command: needs one argument "
+ "(peer address)\n");
+ return -1;
+ }
+
+ res = os_snprintf(cmd, sizeof(cmd), "P2P_REJECT %s", argv[0]);
+ if (res < 0 || (size_t) res >= sizeof(cmd))
+ return -1;
+ cmd[sizeof(cmd) - 1] = '\0';
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_p2p_invite(struct wpa_ctrl *ctrl,
+ int argc, char *argv[])
+{
+ char cmd[128];
+ int res;
+
+ if (argc < 1) {
+ printf("Invalid P2P_INVITE command: needs at least one "
+ "argument\n");
+ return -1;
+ }
+
+ if (argc > 2)
+ res = os_snprintf(cmd, sizeof(cmd), "P2P_INVITE %s %s %s",
+ argv[0], argv[1], argv[2]);
+ else if (argc > 1)
+ res = os_snprintf(cmd, sizeof(cmd), "P2P_INVITE %s %s",
+ argv[0], argv[1]);
+ else
+ res = os_snprintf(cmd, sizeof(cmd), "P2P_INVITE %s", argv[0]);
+ if (res < 0 || (size_t) res >= sizeof(cmd))
+ return -1;
+ cmd[sizeof(cmd) - 1] = '\0';
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_p2p_peer(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ char buf[64];
+ if (argc != 1) {
+ printf("Invalid 'p2p_peer' command - exactly one argument, "
+ "P2P peer device address, is required.\n");
+ return -1;
+ }
+ os_snprintf(buf, sizeof(buf), "P2P_PEER %s", argv[0]);
+ return wpa_ctrl_command(ctrl, buf);
+}
+
+
+static char ** wpa_cli_complete_p2p_peer(const char *str, int pos)
+{
+ int arg = get_cmd_arg_num(str, pos);
+ char **res = NULL;
+
+ switch (arg) {
+ case 1:
+ res = cli_txt_list_array(&p2p_peers);
+ break;
+ }
+
+ return res;
+}
+
+
+static int wpa_ctrl_command_p2p_peer(struct wpa_ctrl *ctrl, char *cmd,
+ char *addr, size_t addr_len,
+ int discovered)
+{
+ char buf[4096], *pos;
+ size_t len;
+ int ret;
+
+ if (ctrl_conn == NULL)
+ return -1;
+ len = sizeof(buf) - 1;
+ ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len,
+ wpa_cli_msg_cb);
+ if (ret == -2) {
+ printf("'%s' command timed out.\n", cmd);
+ return -2;
+ } else if (ret < 0) {
+ printf("'%s' command failed.\n", cmd);
+ return -1;
+ }
+
+ buf[len] = '\0';
+ if (memcmp(buf, "FAIL", 4) == 0)
+ return -1;
+
+ pos = buf;
+ while (*pos != '\0' && *pos != '\n')
+ pos++;
+ *pos++ = '\0';
+ os_strlcpy(addr, buf, addr_len);
+ if (!discovered || os_strstr(pos, "[PROBE_REQ_ONLY]") == NULL)
+ printf("%s\n", addr);
+ return 0;
+}
+
+
+static int wpa_cli_cmd_p2p_peers(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ char addr[32], cmd[64];
+ int discovered;
+
+ discovered = argc > 0 && os_strcmp(argv[0], "discovered") == 0;
+
+ if (wpa_ctrl_command_p2p_peer(ctrl, "P2P_PEER FIRST",
+ addr, sizeof(addr), discovered))
+ return -1;
+ do {
+ os_snprintf(cmd, sizeof(cmd), "P2P_PEER NEXT-%s", addr);
+ } while (wpa_ctrl_command_p2p_peer(ctrl, cmd, addr, sizeof(addr),
+ discovered) == 0);
+
+ return 0;
+}
+
+
+static int wpa_cli_cmd_p2p_set(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ char cmd[100];
+ int res;
+
+ if (argc != 2) {
+ printf("Invalid P2P_SET command: needs two arguments (field, "
+ "value)\n");
+ return -1;
+ }
+
+ res = os_snprintf(cmd, sizeof(cmd), "P2P_SET %s %s", argv[0], argv[1]);
+ if (res < 0 || (size_t) res >= sizeof(cmd))
+ return -1;
+ cmd[sizeof(cmd) - 1] = '\0';
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_p2p_flush(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "P2P_FLUSH");
+}
+
+
+static int wpa_cli_cmd_p2p_cancel(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "P2P_CANCEL");
+}
+
+
+static int wpa_cli_cmd_p2p_unauthorize(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ char cmd[100];
+ int res;
+
+ if (argc != 1) {
+ printf("Invalid P2P_UNAUTHORIZE command: needs one argument "
+ "(peer address)\n");
+ return -1;
+ }
+
+ res = os_snprintf(cmd, sizeof(cmd), "P2P_UNAUTHORIZE %s", argv[0]);
+
+ if (res < 0 || (size_t) res >= sizeof(cmd))
+ return -1;
+
+ cmd[sizeof(cmd) - 1] = '\0';
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_p2p_presence_req(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ char cmd[100];
+ int res;
+
+ if (argc != 0 && argc != 2 && argc != 4) {
+ printf("Invalid P2P_PRESENCE_REQ command: needs two arguments "
+ "(preferred duration, interval; in microsecods).\n"
+ "Optional second pair can be used to provide "
+ "acceptable values.\n");
+ return -1;
+ }
+
+ if (argc == 4)
+ res = os_snprintf(cmd, sizeof(cmd),
+ "P2P_PRESENCE_REQ %s %s %s %s",
+ argv[0], argv[1], argv[2], argv[3]);
+ else if (argc == 2)
+ res = os_snprintf(cmd, sizeof(cmd), "P2P_PRESENCE_REQ %s %s",
+ argv[0], argv[1]);
+ else
+ res = os_snprintf(cmd, sizeof(cmd), "P2P_PRESENCE_REQ");
+ if (res < 0 || (size_t) res >= sizeof(cmd))
+ return -1;
+ cmd[sizeof(cmd) - 1] = '\0';
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_p2p_ext_listen(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ char cmd[100];
+ int res;
+
+ if (argc != 0 && argc != 2) {
+ printf("Invalid P2P_EXT_LISTEN command: needs two arguments "
+ "(availability period, availability interval; in "
+ "millisecods).\n"
+ "Extended Listen Timing can be cancelled with this "
+ "command when used without parameters.\n");
+ return -1;
+ }
+
+ if (argc == 2)
+ res = os_snprintf(cmd, sizeof(cmd), "P2P_EXT_LISTEN %s %s",
+ argv[0], argv[1]);
+ else
+ res = os_snprintf(cmd, sizeof(cmd), "P2P_EXT_LISTEN");
+ if (res < 0 || (size_t) res >= sizeof(cmd))
+ return -1;
+ cmd[sizeof(cmd) - 1] = '\0';
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
+#endif /* CONFIG_P2P */
+
+
+#ifdef CONFIG_INTERWORKING
+static int wpa_cli_cmd_fetch_anqp(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "FETCH_ANQP");
+}
+
+
+static int wpa_cli_cmd_stop_fetch_anqp(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "STOP_FETCH_ANQP");
+}
+
+
+static int wpa_cli_cmd_interworking_select(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ char cmd[100];
+ int res;
+
+ if (argc == 0)
+ return wpa_ctrl_command(ctrl, "INTERWORKING_SELECT");
+
+ res = os_snprintf(cmd, sizeof(cmd), "INTERWORKING_SELECT %s", argv[0]);
+ if (res < 0 || (size_t) res >= sizeof(cmd))
+ return -1;
+ cmd[sizeof(cmd) - 1] = '\0';
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_interworking_connect(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ char cmd[100];
+ int res;
+
+ if (argc != 1) {
+ printf("Invalid INTERWORKING_CONNECT commands: needs one "
+ "argument (BSSID)\n");
+ return -1;
+ }
+
+ res = os_snprintf(cmd, sizeof(cmd), "INTERWORKING_CONNECT %s",
+ argv[0]);
+ if (res < 0 || (size_t) res >= sizeof(cmd))
return -1;
- printf("%s", buf);
-
- pos = buf;
- while (*pos != '\0' && *pos != '\n')
- pos++;
- *pos = '\0';
- os_strlcpy(addr, buf, addr_len);
- return 0;
+ cmd[sizeof(cmd) - 1] = '\0';
+ return wpa_ctrl_command(ctrl, cmd);
}
-static int wpa_cli_cmd_all_sta(struct wpa_ctrl *ctrl, int argc, char *argv[])
+static int wpa_cli_cmd_anqp_get(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
- char addr[32], cmd[64];
+ char cmd[100];
+ int res;
- if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr)))
- return 0;
- do {
- os_snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr);
- } while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr)) == 0);
+ if (argc != 2) {
+ printf("Invalid ANQP_GET command: needs two arguments "
+ "(addr and info id list)\n");
+ return -1;
+ }
- return -1;
+ res = os_snprintf(cmd, sizeof(cmd), "ANQP_GET %s %s",
+ argv[0], argv[1]);
+ if (res < 0 || (size_t) res >= sizeof(cmd))
+ return -1;
+ cmd[sizeof(cmd) - 1] = '\0';
+ return wpa_ctrl_command(ctrl, cmd);
}
-#endif /* CONFIG_AP */
+#endif /* CONFIG_INTERWORKING */
-static int wpa_cli_cmd_suspend(struct wpa_ctrl *ctrl, int argc, char *argv[])
+static int wpa_cli_cmd_sta_autoconnect(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
{
- return wpa_ctrl_command(ctrl, "SUSPEND");
+ char cmd[256];
+ int res;
+
+ if (argc != 1) {
+ printf("Invalid STA_AUTOCONNECT command: needs one argument "
+ "(0/1 = disable/enable automatic reconnection)\n");
+ return -1;
+ }
+ res = os_snprintf(cmd, sizeof(cmd), "STA_AUTOCONNECT %s", argv[0]);
+ if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+ printf("Too long STA_AUTOCONNECT command.\n");
+ return -1;
+ }
+ return wpa_ctrl_command(ctrl, cmd);
}
-static int wpa_cli_cmd_resume(struct wpa_ctrl *ctrl, int argc, char *argv[])
+static int wpa_cli_cmd_tdls_discover(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
{
- return wpa_ctrl_command(ctrl, "RESUME");
+ char cmd[256];
+ int res;
+
+ if (argc != 1) {
+ printf("Invalid TDLS_DISCOVER command: needs one argument "
+ "(Peer STA MAC address)\n");
+ return -1;
+ }
+
+ res = os_snprintf(cmd, sizeof(cmd), "TDLS_DISCOVER %s", argv[0]);
+ if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+ printf("Too long TDLS_DISCOVER command.\n");
+ return -1;
+ }
+ return wpa_ctrl_command(ctrl, cmd);
}
-static int wpa_cli_cmd_drop_sa(struct wpa_ctrl *ctrl, int argc, char *argv[])
+static int wpa_cli_cmd_tdls_setup(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
{
- return wpa_ctrl_command(ctrl, "DROP_SA");
+ char cmd[256];
+ int res;
+
+ if (argc != 1) {
+ printf("Invalid TDLS_SETUP command: needs one argument "
+ "(Peer STA MAC address)\n");
+ return -1;
+ }
+
+ res = os_snprintf(cmd, sizeof(cmd), "TDLS_SETUP %s", argv[0]);
+ if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+ printf("Too long TDLS_SETUP command.\n");
+ return -1;
+ }
+ return wpa_ctrl_command(ctrl, cmd);
}
-static int wpa_cli_cmd_roam(struct wpa_ctrl *ctrl, int argc, char *argv[])
+static int wpa_cli_cmd_tdls_teardown(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
{
- char cmd[128];
+ char cmd[256];
int res;
if (argc != 1) {
- printf("Invalid ROAM command: needs one argument "
- "(target AP's BSSID)\n");
+ printf("Invalid TDLS_TEARDOWN command: needs one argument "
+ "(Peer STA MAC address)\n");
return -1;
}
- res = os_snprintf(cmd, sizeof(cmd), "ROAM %s", argv[0]);
+ res = os_snprintf(cmd, sizeof(cmd), "TDLS_TEARDOWN %s", argv[0]);
if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
- printf("Too long ROAM command.\n");
+ printf("Too long TDLS_TEARDOWN command.\n");
return -1;
}
return wpa_ctrl_command(ctrl, cmd);
}
+static int wpa_cli_cmd_signal_poll(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "SIGNAL_POLL");
+}
+
+
enum wpa_cli_cmd_flags {
cli_cmd_flag_none = 0x00,
cli_cmd_flag_sensitive = 0x01
{ "ping", wpa_cli_cmd_ping,
cli_cmd_flag_none,
"= pings wpa_supplicant" },
+ { "relog", wpa_cli_cmd_relog,
+ cli_cmd_flag_none,
+ "= re-open log-file (allow rolling logs)" },
+ { "note", wpa_cli_cmd_note,
+ cli_cmd_flag_none,
+ "<text> = add a note to wpa_supplicant debug log" },
{ "mib", wpa_cli_cmd_mib,
cli_cmd_flag_none,
"= get MIB variables (dot1x, dot11)" },
cli_cmd_flag_none,
"= set variables (shows list of variables when run without "
"arguments)" },
+ { "get", wpa_cli_cmd_get,
+ cli_cmd_flag_none,
+ "<name> = get information" },
{ "logon", wpa_cli_cmd_logon,
cli_cmd_flag_none,
"= IEEE 802.1X EAPOL state machine logon" },
{ "bssid", wpa_cli_cmd_bssid,
cli_cmd_flag_none,
"<network id> <BSSID> = set preferred BSSID for an SSID" },
+ { "blacklist", wpa_cli_cmd_blacklist,
+ cli_cmd_flag_none,
+ "<BSSID> = add a BSSID to the blacklist\n"
+ "blacklist clear = clear the blacklist\n"
+ "blacklist = display the blacklist" },
+ { "log_level", wpa_cli_cmd_log_level,
+ cli_cmd_flag_none,
+ "<level> [<timestamp>] = update the log level/timestamp\n"
+ "log_level = display the current log level and log options" },
{ "list_networks", wpa_cli_cmd_list_networks,
cli_cmd_flag_none,
"= list configured networks" },
{ "ap_scan", wpa_cli_cmd_ap_scan,
cli_cmd_flag_none,
"<value> = set ap_scan parameter" },
+ { "scan_interval", wpa_cli_cmd_scan_interval,
+ cli_cmd_flag_none,
+ "<value> = set scan_interval parameter (in seconds)" },
+ { "bss_expire_age", wpa_cli_cmd_bss_expire_age,
+ cli_cmd_flag_none,
+ "<value> = set BSS expiration age parameter" },
+ { "bss_expire_count", wpa_cli_cmd_bss_expire_count,
+ cli_cmd_flag_none,
+ "<value> = set BSS expiration scan count parameter" },
{ "stkstart", wpa_cli_cmd_stkstart,
cli_cmd_flag_none,
"<addr> = request STK negotiation with <addr>" },
cli_cmd_flag_sensitive,
"<BSSID> [PIN] = start WPS PIN method (returns PIN, if not "
"hardcoded)" },
+ { "wps_check_pin", wpa_cli_cmd_wps_check_pin,
+ cli_cmd_flag_sensitive,
+ "<PIN> = verify PIN checksum" },
+ { "wps_cancel", wpa_cli_cmd_wps_cancel, cli_cmd_flag_none,
+ "Cancels the pending WPS operation" },
#ifdef CONFIG_WPS_OOB
{ "wps_oob", wpa_cli_cmd_wps_oob,
cli_cmd_flag_sensitive,
{ "wps_reg", wpa_cli_cmd_wps_reg,
cli_cmd_flag_sensitive,
"<BSSID> <AP PIN> = start WPS Registrar to configure an AP" },
+ { "wps_ap_pin", wpa_cli_cmd_wps_ap_pin,
+ cli_cmd_flag_sensitive,
+ "[params..] = enable/disable AP PIN" },
{ "wps_er_start", wpa_cli_cmd_wps_er_start,
cli_cmd_flag_none,
- "= start Wi-Fi Protected Setup External Registrar" },
+ "[IP address] = start Wi-Fi Protected Setup External Registrar" },
{ "wps_er_stop", wpa_cli_cmd_wps_er_stop,
cli_cmd_flag_none,
"= stop Wi-Fi Protected Setup External Registrar" },
{ "wps_er_learn", wpa_cli_cmd_wps_er_learn,
cli_cmd_flag_sensitive,
"<UUID> <PIN> = learn AP configuration" },
+ { "wps_er_set_config", wpa_cli_cmd_wps_er_set_config,
+ cli_cmd_flag_none,
+ "<UUID> <network id> = set AP configuration for enrolling" },
+ { "wps_er_config", wpa_cli_cmd_wps_er_config,
+ cli_cmd_flag_sensitive,
+ "<UUID> <PIN> <SSID> <auth> <encr> <key> = configure AP" },
{ "ibss_rsn", wpa_cli_cmd_ibss_rsn,
cli_cmd_flag_none,
"<addr> = request RSN authentication with <addr> in IBSS" },
{ "roam", wpa_cli_cmd_roam,
cli_cmd_flag_none,
"<addr> = roam to the specified BSS" },
+#ifdef CONFIG_P2P
+ { "p2p_find", wpa_cli_cmd_p2p_find, cli_cmd_flag_none,
+ "[timeout] [type=*] = find P2P Devices for up-to timeout seconds" },
+ { "p2p_stop_find", wpa_cli_cmd_p2p_stop_find, cli_cmd_flag_none,
+ "= stop P2P Devices search" },
+ { "p2p_connect", wpa_cli_cmd_p2p_connect, cli_cmd_flag_none,
+ "<addr> <\"pbc\"|PIN> = connect to a P2P Devices" },
+ { "p2p_listen", wpa_cli_cmd_p2p_listen, cli_cmd_flag_none,
+ "[timeout] = listen for P2P Devices for up-to timeout seconds" },
+ { "p2p_group_remove", wpa_cli_cmd_p2p_group_remove, cli_cmd_flag_none,
+ "<ifname> = remove P2P group interface (terminate group if GO)" },
+ { "p2p_group_add", wpa_cli_cmd_p2p_group_add, cli_cmd_flag_none,
+ "= add a new P2P group (local end as GO)" },
+ { "p2p_prov_disc", wpa_cli_cmd_p2p_prov_disc, cli_cmd_flag_none,
+ "<addr> <method> = request provisioning discovery" },
+ { "p2p_get_passphrase", wpa_cli_cmd_p2p_get_passphrase,
+ cli_cmd_flag_none,
+ "= get the passphrase for a group (GO only)" },
+ { "p2p_serv_disc_req", wpa_cli_cmd_p2p_serv_disc_req,
+ cli_cmd_flag_none,
+ "<addr> <TLVs> = schedule service discovery request" },
+ { "p2p_serv_disc_cancel_req", wpa_cli_cmd_p2p_serv_disc_cancel_req,
+ cli_cmd_flag_none,
+ "<id> = cancel pending service discovery request" },
+ { "p2p_serv_disc_resp", wpa_cli_cmd_p2p_serv_disc_resp,
+ cli_cmd_flag_none,
+ "<freq> <addr> <dialog token> <TLVs> = service discovery response" },
+ { "p2p_service_update", wpa_cli_cmd_p2p_service_update,
+ cli_cmd_flag_none,
+ "= indicate change in local services" },
+ { "p2p_serv_disc_external", wpa_cli_cmd_p2p_serv_disc_external,
+ cli_cmd_flag_none,
+ "<external> = set external processing of service discovery" },
+ { "p2p_service_flush", wpa_cli_cmd_p2p_service_flush,
+ cli_cmd_flag_none,
+ "= remove all stored service entries" },
+ { "p2p_service_add", wpa_cli_cmd_p2p_service_add,
+ cli_cmd_flag_none,
+ "<bonjour|upnp> <query|version> <response|service> = add a local "
+ "service" },
+ { "p2p_service_del", wpa_cli_cmd_p2p_service_del,
+ cli_cmd_flag_none,
+ "<bonjour|upnp> <query|version> [|service] = remove a local "
+ "service" },
+ { "p2p_reject", wpa_cli_cmd_p2p_reject,
+ cli_cmd_flag_none,
+ "<addr> = reject connection attempts from a specific peer" },
+ { "p2p_invite", wpa_cli_cmd_p2p_invite,
+ cli_cmd_flag_none,
+ "<cmd> [peer=addr] = invite peer" },
+ { "p2p_peers", wpa_cli_cmd_p2p_peers, cli_cmd_flag_none,
+ "[discovered] = list known (optionally, only fully discovered) P2P "
+ "peers" },
+ { "p2p_peer", wpa_cli_cmd_p2p_peer, cli_cmd_flag_none,
+ "<address> = show information about known P2P peer" },
+ { "p2p_set", wpa_cli_cmd_p2p_set, cli_cmd_flag_none,
+ "<field> <value> = set a P2P parameter" },
+ { "p2p_flush", wpa_cli_cmd_p2p_flush, cli_cmd_flag_none,
+ "= flush P2P state" },
+ { "p2p_cancel", wpa_cli_cmd_p2p_cancel, cli_cmd_flag_none,
+ "= cancel P2P group formation" },
+ { "p2p_unauthorize", wpa_cli_cmd_p2p_unauthorize, cli_cmd_flag_none,
+ "<address> = unauthorize a peer" },
+ { "p2p_presence_req", wpa_cli_cmd_p2p_presence_req, cli_cmd_flag_none,
+ "[<duration> <interval>] [<duration> <interval>] = request GO "
+ "presence" },
+ { "p2p_ext_listen", wpa_cli_cmd_p2p_ext_listen, cli_cmd_flag_none,
+ "[<period> <interval>] = set extended listen timing" },
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_INTERWORKING
+ { "fetch_anqp", wpa_cli_cmd_fetch_anqp, cli_cmd_flag_none,
+ "= fetch ANQP information for all APs" },
+ { "stop_fetch_anqp", wpa_cli_cmd_stop_fetch_anqp, cli_cmd_flag_none,
+ "= stop fetch_anqp operation" },
+ { "interworking_select", wpa_cli_cmd_interworking_select,
+ cli_cmd_flag_none,
+ "[auto] = perform Interworking network selection" },
+ { "interworking_connect", wpa_cli_cmd_interworking_connect,
+ cli_cmd_flag_none,
+ "<BSSID> = connect using Interworking credentials" },
+ { "anqp_get", wpa_cli_cmd_anqp_get, cli_cmd_flag_none,
+ "<addr> <info id>[,<info id>]... = request ANQP information" },
+#endif /* CONFIG_INTERWORKING */
+ { "sta_autoconnect", wpa_cli_cmd_sta_autoconnect, cli_cmd_flag_none,
+ "<0/1> = disable/enable automatic reconnection" },
+ { "tdls_discover", wpa_cli_cmd_tdls_discover,
+ cli_cmd_flag_none,
+ "<addr> = request TDLS discovery with <addr>" },
+ { "tdls_setup", wpa_cli_cmd_tdls_setup,
+ cli_cmd_flag_none,
+ "<addr> = request TDLS setup with <addr>" },
+ { "tdls_teardown", wpa_cli_cmd_tdls_teardown,
+ cli_cmd_flag_none,
+ "<addr> = tear down TDLS with <addr>" },
+ { "signal_poll", wpa_cli_cmd_signal_poll,
+ cli_cmd_flag_none,
+ "= get signal parameters" },
{ NULL, NULL, cli_cmd_flag_none, NULL }
};
}
-#ifdef CONFIG_READLINE
-static int cmd_has_sensitive_data(const char *cmd)
+static int wpa_cli_edit_filter_history_cb(void *ctx, const char *cmd)
{
const char *c, *delim;
int n;
}
return 0;
}
-#endif /* CONFIG_READLINE */
+
+
+static char ** wpa_list_cmd_list(void)
+{
+ char **res;
+ int i, count;
+
+ count = sizeof(wpa_cli_commands) / sizeof(wpa_cli_commands[0]);
+ res = os_zalloc(count * sizeof(char *));
+ if (res == NULL)
+ return NULL;
+
+ for (i = 0; wpa_cli_commands[i].cmd; i++) {
+ res[i] = os_strdup(wpa_cli_commands[i].cmd);
+ if (res[i] == NULL)
+ break;
+ }
+
+ return res;
+}
+
+
+static char ** wpa_cli_cmd_completion(const char *cmd, const char *str,
+ int pos)
+{
+ int i;
+
+ if (os_strcasecmp(cmd, "bss") == 0)
+ return wpa_cli_complete_bss(str, pos);
+#ifdef CONFIG_P2P
+ if (os_strcasecmp(cmd, "p2p_connect") == 0)
+ return wpa_cli_complete_p2p_connect(str, pos);
+ if (os_strcasecmp(cmd, "p2p_peer") == 0)
+ return wpa_cli_complete_p2p_peer(str, pos);
+ if (os_strcasecmp(cmd, "p2p_group_remove") == 0)
+ return wpa_cli_complete_p2p_group_remove(str, pos);
+#endif /* CONFIG_P2P */
+
+ for (i = 0; wpa_cli_commands[i].cmd; i++) {
+ if (os_strcasecmp(wpa_cli_commands[i].cmd, cmd) == 0) {
+ edit_clear_line();
+ printf("\r%s\n", wpa_cli_commands[i].usage);
+ edit_redraw();
+ break;
+ }
+ }
+
+ return NULL;
+}
+
+
+static char ** wpa_cli_edit_completion_cb(void *ctx, const char *str, int pos)
+{
+ char **res;
+ const char *end;
+ char *cmd;
+
+ end = os_strchr(str, ' ');
+ if (end == NULL || str + pos < end)
+ return wpa_list_cmd_list();
+
+ cmd = os_malloc(pos + 1);
+ if (cmd == NULL)
+ return NULL;
+ os_memcpy(cmd, str, pos);
+ cmd[end - str] = '\0';
+ res = wpa_cli_cmd_completion(cmd, str, pos);
+ os_free(cmd);
+ return res;
+}
static int wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[])
wpa_cli_connected = 0;
wpa_cli_exec(action_file, ctrl_ifname, "DISCONNECTED");
}
+ } else if (str_match(pos, P2P_EVENT_GROUP_STARTED)) {
+ wpa_cli_exec(action_file, ctrl_ifname, pos);
+ } else if (str_match(pos, P2P_EVENT_GROUP_REMOVED)) {
+ wpa_cli_exec(action_file, ctrl_ifname, pos);
+ } else if (str_match(pos, P2P_EVENT_CROSS_CONNECT_ENABLE)) {
+ wpa_cli_exec(action_file, ctrl_ifname, pos);
+ } else if (str_match(pos, P2P_EVENT_CROSS_CONNECT_DISABLE)) {
+ wpa_cli_exec(action_file, ctrl_ifname, pos);
+ } else if (str_match(pos, WPS_EVENT_SUCCESS)) {
+ wpa_cli_exec(action_file, ctrl_ifname, pos);
+ } else if (str_match(pos, WPS_EVENT_FAIL)) {
+ wpa_cli_exec(action_file, ctrl_ifname, pos);
} else if (str_match(pos, WPA_EVENT_TERMINATING)) {
printf("wpa_supplicant is terminating - stop monitoring\n");
wpa_cli_quit = 1;
}
-static void wpa_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read,
- int action_monitor)
+static void cli_event(const char *str)
+{
+ const char *start, *s;
+
+ start = os_strchr(str, '>');
+ if (start == NULL)
+ return;
+
+ start++;
+
+ if (str_starts(start, WPA_EVENT_BSS_ADDED)) {
+ s = os_strchr(start, ' ');
+ if (s == NULL)
+ return;
+ s = os_strchr(s + 1, ' ');
+ if (s == NULL)
+ return;
+ cli_txt_list_add(&bsses, s + 1);
+ return;
+ }
+
+ if (str_starts(start, WPA_EVENT_BSS_REMOVED)) {
+ s = os_strchr(start, ' ');
+ if (s == NULL)
+ return;
+ s = os_strchr(s + 1, ' ');
+ if (s == NULL)
+ return;
+ cli_txt_list_del_addr(&bsses, s + 1);
+ return;
+ }
+
+#ifdef CONFIG_P2P
+ if (str_starts(start, P2P_EVENT_DEVICE_FOUND)) {
+ s = os_strstr(start, " p2p_dev_addr=");
+ if (s == NULL)
+ return;
+ cli_txt_list_add_addr(&p2p_peers, s + 14);
+ return;
+ }
+
+ if (str_starts(start, P2P_EVENT_DEVICE_LOST)) {
+ s = os_strstr(start, " p2p_dev_addr=");
+ if (s == NULL)
+ return;
+ cli_txt_list_del_addr(&p2p_peers, s + 14);
+ return;
+ }
+
+ if (str_starts(start, P2P_EVENT_GROUP_STARTED)) {
+ s = os_strchr(start, ' ');
+ if (s == NULL)
+ return;
+ cli_txt_list_add_word(&p2p_groups, s + 1);
+ return;
+ }
+
+ if (str_starts(start, P2P_EVENT_GROUP_REMOVED)) {
+ s = os_strchr(start, ' ');
+ if (s == NULL)
+ return;
+ cli_txt_list_del_word(&p2p_groups, s + 1);
+ return;
+ }
+#endif /* CONFIG_P2P */
+}
+
+
+static void wpa_cli_recv_pending(struct wpa_ctrl *ctrl, int action_monitor)
{
- int first = 1;
if (ctrl_conn == NULL) {
wpa_cli_reconnect();
return;
if (action_monitor)
wpa_cli_action_process(buf);
else {
- if (in_read && first)
- printf("\r");
- first = 0;
- printf("%s\n", buf);
-#ifdef CONFIG_READLINE
- rl_on_new_line();
- rl_redisplay();
-#endif /* CONFIG_READLINE */
+ cli_event(buf);
+ if (wpa_cli_show_event(buf)) {
+ edit_clear_line();
+ printf("\r%s\n", buf);
+ edit_redraw();
+ }
}
} else {
printf("Could not read pending message.\n");
}
}
+#define max_args 10
-#ifdef CONFIG_READLINE
-static char * wpa_cli_cmd_gen(const char *text, int state)
+static int tokenize_cmd(char *cmd, char *argv[])
{
- static int i, len;
- const char *cmd;
-
- if (state == 0) {
- i = 0;
- len = os_strlen(text);
- }
+ char *pos;
+ int argc = 0;
- while ((cmd = wpa_cli_commands[i].cmd)) {
- i++;
- if (os_strncasecmp(cmd, text, len) == 0)
- return strdup(cmd);
+ pos = cmd;
+ for (;;) {
+ while (*pos == ' ')
+ pos++;
+ if (*pos == '\0')
+ break;
+ argv[argc] = pos;
+ argc++;
+ if (argc == max_args)
+ break;
+ if (*pos == '"') {
+ char *pos2 = os_strrchr(pos, '"');
+ if (pos2)
+ pos = pos2 + 1;
+ }
+ while (*pos != '\0' && *pos != ' ')
+ pos++;
+ if (*pos == ' ')
+ *pos++ = '\0';
}
- return NULL;
+ return argc;
}
-static char * wpa_cli_dummy_gen(const char *text, int state)
+static void wpa_cli_ping(void *eloop_ctx, void *timeout_ctx)
{
- int i;
-
- for (i = 0; wpa_cli_commands[i].cmd; i++) {
- const char *cmd = wpa_cli_commands[i].cmd;
- size_t len = os_strlen(cmd);
- if (os_strncasecmp(rl_line_buffer, cmd, len) == 0 &&
- rl_line_buffer[len] == ' ') {
- printf("\n%s\n", wpa_cli_commands[i].usage);
- rl_on_new_line();
- rl_redisplay();
- break;
- }
+ if (ctrl_conn && _wpa_ctrl_command(ctrl_conn, "PING", 0)) {
+ printf("Connection to wpa_supplicant lost - trying to "
+ "reconnect\n");
+ wpa_cli_close_connection();
}
-
- rl_attempted_completion_over = 1;
- return NULL;
+ if (!ctrl_conn)
+ wpa_cli_reconnect();
+ eloop_register_timeout(ping_interval, 0, wpa_cli_ping, NULL, NULL);
}
-static char * wpa_cli_status_gen(const char *text, int state)
+static void wpa_cli_eloop_terminate(int sig, void *signal_ctx)
{
- static int i, len;
- char *options[] = {
- "verbose", NULL
- };
- char *t;
-
- if (state == 0) {
- i = 0;
- len = os_strlen(text);
- }
+ eloop_terminate();
+}
- while ((t = options[i])) {
- i++;
- if (os_strncasecmp(t, text, len) == 0)
- return strdup(t);
- }
- rl_attempted_completion_over = 1;
- return NULL;
+static void wpa_cli_mon_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ wpa_cli_recv_pending(mon_conn, 0);
}
-static char ** wpa_cli_completion(const char *text, int start, int end)
+static void wpa_cli_edit_cmd_cb(void *ctx, char *cmd)
{
- char * (*func)(const char *text, int state);
+ char *argv[max_args];
+ int argc;
+ argc = tokenize_cmd(cmd, argv);
+ if (argc)
+ wpa_request(ctrl_conn, argc, argv);
+}
- if (start == 0)
- func = wpa_cli_cmd_gen;
- else if (os_strncasecmp(rl_line_buffer, "status ", 7) == 0)
- func = wpa_cli_status_gen;
- else
- func = wpa_cli_dummy_gen;
- return rl_completion_matches(text, func);
+
+static void wpa_cli_edit_eof_cb(void *ctx)
+{
+ eloop_terminate();
}
-#endif /* CONFIG_READLINE */
static void wpa_cli_interactive(void)
{
-#define max_args 10
- char cmdbuf[256], *cmd, *argv[max_args], *pos;
- int argc;
-#ifdef CONFIG_READLINE
char *home, *hfile = NULL;
-#endif /* CONFIG_READLINE */
printf("\nInteractive mode\n\n");
-#ifdef CONFIG_READLINE
- rl_attempted_completion_function = wpa_cli_completion;
home = getenv("HOME");
if (home) {
const char *fname = ".wpa_cli_history";
int hfile_len = os_strlen(home) + 1 + os_strlen(fname) + 1;
hfile = os_malloc(hfile_len);
- if (hfile) {
- int res;
- res = os_snprintf(hfile, hfile_len, "%s/%s", home,
- fname);
- if (res >= 0 && res < hfile_len) {
- hfile[hfile_len - 1] = '\0';
- read_history(hfile);
- stifle_history(100);
- }
- }
+ if (hfile)
+ os_snprintf(hfile, hfile_len, "%s/%s", home, fname);
}
-#endif /* CONFIG_READLINE */
- do {
- wpa_cli_recv_pending(mon_conn, 0, 0);
-#ifndef CONFIG_NATIVE_WINDOWS
- alarm(ping_interval);
-#endif /* CONFIG_NATIVE_WINDOWS */
-#ifdef CONFIG_WPA_CLI_FORK
- if (mon_pid)
- kill(mon_pid, SIGUSR1);
-#endif /* CONFIG_WPA_CLI_FORK */
-#ifdef CONFIG_READLINE
- cmd = readline("> ");
- if (cmd && *cmd) {
- HIST_ENTRY *h;
- while (next_history())
- ;
- h = previous_history();
- if (h == NULL || os_strcmp(cmd, h->line) != 0)
- add_history(cmd);
- next_history();
- }
-#else /* CONFIG_READLINE */
- printf("> ");
- cmd = fgets(cmdbuf, sizeof(cmdbuf), stdin);
-#endif /* CONFIG_READLINE */
-#ifndef CONFIG_NATIVE_WINDOWS
- alarm(0);
-#endif /* CONFIG_NATIVE_WINDOWS */
- if (cmd == NULL)
- break;
- wpa_cli_recv_pending(mon_conn, 0, 0);
- pos = cmd;
- while (*pos != '\0') {
- if (*pos == '\n') {
- *pos = '\0';
- break;
- }
- pos++;
- }
- argc = 0;
- pos = cmd;
- for (;;) {
- while (*pos == ' ')
- pos++;
- if (*pos == '\0')
- break;
- argv[argc] = pos;
- argc++;
- if (argc == max_args)
- break;
- if (*pos == '"') {
- char *pos2 = os_strrchr(pos, '"');
- if (pos2)
- pos = pos2 + 1;
- }
- while (*pos != '\0' && *pos != ' ')
- pos++;
- if (*pos == ' ')
- *pos++ = '\0';
- }
- if (argc)
- wpa_request(ctrl_conn, argc, argv);
-
- if (cmd != cmdbuf)
- free(cmd);
-#ifdef CONFIG_WPA_CLI_FORK
- if (mon_pid)
- kill(mon_pid, SIGUSR2);
-#endif /* CONFIG_WPA_CLI_FORK */
- } while (!wpa_cli_quit);
-
-#ifdef CONFIG_READLINE
- if (hfile) {
- /* Save command history, excluding lines that may contain
- * passwords. */
- HIST_ENTRY *h;
- history_set_pos(0);
- while ((h = current_history())) {
- char *p = h->line;
- while (*p == ' ' || *p == '\t')
- p++;
- if (cmd_has_sensitive_data(p)) {
- h = remove_history(where_history());
- if (h) {
- os_free(h->line);
- os_free(h->data);
- os_free(h);
- } else
- next_history();
- } else
- next_history();
- }
- write_history(hfile);
- os_free(hfile);
- }
-#endif /* CONFIG_READLINE */
+ eloop_register_signal_terminate(wpa_cli_eloop_terminate, NULL);
+ edit_init(wpa_cli_edit_cmd_cb, wpa_cli_edit_eof_cb,
+ wpa_cli_edit_completion_cb, NULL, hfile);
+ eloop_register_timeout(ping_interval, 0, wpa_cli_ping, NULL, NULL);
+
+ eloop_run();
+
+ cli_txt_list_flush(&p2p_peers);
+ cli_txt_list_flush(&p2p_groups);
+ cli_txt_list_flush(&bsses);
+ edit_deinit(hfile, wpa_cli_edit_filter_history_cb);
+ os_free(hfile);
+ eloop_cancel_timeout(wpa_cli_ping, NULL, NULL);
+ wpa_cli_close_connection();
}
}
if (FD_ISSET(fd, &rfds))
- wpa_cli_recv_pending(ctrl, 0, 1);
+ wpa_cli_recv_pending(ctrl, 1);
else {
/* verify that connection is still working */
len = sizeof(buf) - 1;
}
-#ifdef CONFIG_WPA_CLI_FORK
-static void wpa_cli_usr1(int sig)
-{
-#ifdef CONFIG_READLINE
- rl_on_new_line();
- rl_redisplay();
-#endif /* CONFIG_READLINE */
-}
-#endif /* CONFIG_WPA_CLI_FORK */
-
-
-#ifndef CONFIG_NATIVE_WINDOWS
-static void wpa_cli_alarm(int sig)
-{
- if (ctrl_conn && _wpa_ctrl_command(ctrl_conn, "PING", 0)) {
- printf("Connection to wpa_supplicant lost - trying to "
- "reconnect\n");
- wpa_cli_close_connection();
- }
- if (!ctrl_conn)
- wpa_cli_reconnect();
- if (mon_conn)
- wpa_cli_recv_pending(mon_conn, 1, 0);
- alarm(ping_interval);
-}
-#endif /* CONFIG_NATIVE_WINDOWS */
-
-
static char * wpa_cli_get_default_ifname(void)
{
char *ifname = NULL;
#ifdef CONFIG_CTRL_IFACE_UNIX
struct dirent *dent;
DIR *dir = opendir(ctrl_iface_dir);
- if (!dir)
+ if (!dir) {
+#ifdef ANDROID
+ char ifprop[PROPERTY_VALUE_MAX];
+ if (property_get("wifi.interface", ifprop, NULL) != 0) {
+ ifname = os_strdup(ifprop);
+ printf("Using interface '%s'\n", ifname);
+ return ifname;
+ }
+#endif /* ANDROID */
return NULL;
+ }
while ((dent = readdir(dir))) {
#ifdef _DIRENT_HAVE_D_TYPE
/*
if (interactive)
printf("%s\n\n%s\n\n", wpa_cli_version, wpa_cli_license);
+ if (eloop_init())
+ return -1;
+
if (global) {
#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE
ctrl_conn = wpa_ctrl_open(NULL);
signal(SIGINT, wpa_cli_terminate);
signal(SIGTERM, wpa_cli_terminate);
#endif /* _WIN32_WCE */
-#ifndef CONFIG_NATIVE_WINDOWS
- signal(SIGALRM, wpa_cli_alarm);
-#endif /* CONFIG_NATIVE_WINDOWS */
-#ifdef CONFIG_WPA_CLI_FORK
- signal(SIGUSR1, wpa_cli_usr1);
-#endif /* CONFIG_WPA_CLI_FORK */
if (ctrl_ifname == NULL)
ctrl_ifname = wpa_cli_get_default_ifname();
ret = wpa_request(ctrl_conn, argc - optind, &argv[optind]);
os_free(ctrl_ifname);
+ eloop_destroy();
wpa_cli_cleanup();
return ret;
.moc
.obj
.ui
-Makefile
-wpa_gui
qrc_icons.cpp
<file alias="wpa_gui.svg">icons/wpa_gui.svg</file>
<file alias="ap.svg">icons/ap.svg</file>
<file alias="laptop.svg">icons/laptop.svg</file>
+ <file alias="group.svg">icons/group.svg</file>
+ <file alias="invitation.svg">icons/invitation.svg</file>
</qresource>
</RCC>
--- /dev/null
+#!/usr/bin/make -f
+
+NAMES := wpa_gui ap laptop group invitation
+SIZES := 16x16 22x22 32x32 48x48 64x64 128x128
+ICONS := $(addsuffix .png, $(foreach name, $(NAMES), $(foreach size, $(SIZES), $(size)/$(name))))
+ICONS += $(addsuffix .xpm, $(NAMES))
+
+all: $(ICONS)
+
+%.png:
+ mkdir -p hicolor/$(word 1, $(subst /, ,$(@)))/apps/
+ inkscape $(subst .png,.svg, $(word 2, $(subst /, , $(@)))) --without-gui \
+ --export-width=$(word 1, $(subst x, , $(@))) \
+ --export-height=$(word 2, $(subst x, , $(subst /, , $(@)))) \
+ --export-png=hicolor/$(word 1, $(subst /, ,$(@)))/apps/$(word 2, $(subst /, , $@))
+
+%.xpm:
+ mkdir -p pixmaps/
+ convert hicolor/16x16/apps/$(@:.xpm=.png) pixmaps/$(@:.xpm=-16.xpm)
+ convert hicolor/32x32/apps/$(@:.xpm=.png) pixmaps/$@
+
+clean:
+ $(RM) -r pixmaps hicolor
last change: May 18, 2008 07:04 pm (File added)
date: August 27, 2007 04:44 am
license: PD
+
+
+group.svg
+---------
+
+http://www.openclipart.org/detail/25428
+http://www.openclipart.org/people/Anonymous/Anonymous_Network.svg
+Uploader:
+ Anonymous
+Drawn by:
+ Andrew Fitzsimon / Anonymous
+Created:
+ 2009-04-29 04:07:37
+Description:
+ A network icon by Andrew Fitzsimon. Etiquette Icon set.
+ From 0.18 OCAL database.
+
+Public Domain
+
+
+
+invitation.svg
+--------------
+
+http://www.openclipart.org/detail/974
+http://www.openclipart.org/people/jean_victor_balin/jean_victor_balin_unknown_green.svg
+Uploader:
+ jean_victor_balin
+Drawn by:
+ jean_victor_balin
+Created:
+ 2006-10-27 02:12:13
+Description:
+
+Public Domain
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ height="160.00000"
+ id="Andysvg"
+ inkscape:version="0.46"
+ sodipodi:docbase="/home/andy/Desktop/etiquette-icons-0.4/scalable/filesystems"
+ sodipodi:docname="gnome-fs-network.svg"
+ sodipodi:version="0.32"
+ version="1.0"
+ width="160.00000"
+ x="0.00000000"
+ y="0.00000000"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"
+ inkscape:export-filename="C:\Documents and Settings\All Users\Documents\Ubuntu Brig\Andy Fitzsimon\gnome-fs-network.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
+ <metadata
+ id="metadata3">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:title>Etiquette Icons</dc:title>
+ <dc:description />
+ <dc:subject>
+ <rdf:Bag>
+ <rdf:li>hash</rdf:li>
+ <rdf:li />
+ <rdf:li>filesystem</rdf:li>
+ <rdf:li>computer</rdf:li>
+ <rdf:li>icons</rdf:li>
+ </rdf:Bag>
+ </dc:subject>
+ <dc:publisher>
+ <cc:Agent
+ rdf:about="http://www.openclipart.org">
+ <dc:title>Andy Fitzsimon</dc:title>
+ </cc:Agent>
+ </dc:publisher>
+ <dc:creator>
+ <cc:Agent>
+ <dc:title>Andy Fitzsimon</dc:title>
+ </cc:Agent>
+ </dc:creator>
+ <dc:rights>
+ <cc:Agent>
+ <dc:title>Andy Fitzsimon</dc:title>
+ </cc:Agent>
+ </dc:rights>
+ <dc:date />
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <cc:license
+ rdf:resource="http://web.resource.org/cc/PublicDomain" />
+ <dc:language>en</dc:language>
+ </cc:Work>
+ <cc:License
+ rdf:about="http://web.resource.org/cc/PublicDomain">
+ <cc:permits
+ rdf:resource="http://web.resource.org/cc/Reproduction" />
+ <cc:permits
+ rdf:resource="http://web.resource.org/cc/Distribution" />
+ <cc:permits
+ rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
+ </cc:License>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs3">
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 80 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="160 : 80 : 1"
+ inkscape:persp3d-origin="80 : 53.333333 : 1"
+ id="perspective97" />
+ <linearGradient
+ id="linearGradient4894">
+ <stop
+ id="stop4895"
+ offset="0.0000000"
+ style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+ <stop
+ id="stop4896"
+ offset="0.47000000"
+ style="stop-color:#ffffff;stop-opacity:0.85567009;" />
+ <stop
+ id="stop4897"
+ offset="1.0000000"
+ style="stop-color:#ffffff;stop-opacity:0.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient1853">
+ <stop
+ id="stop1854"
+ offset="0.0000000"
+ style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+ <stop
+ id="stop1855"
+ offset="0.47000000"
+ style="stop-color:#ffffff;stop-opacity:0.85567009;" />
+ <stop
+ id="stop1856"
+ offset="1.0000000"
+ style="stop-color:#ffffff;stop-opacity:0.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient1806">
+ <stop
+ id="stop1807"
+ offset="0.0000000"
+ style="stop-color:#000000;stop-opacity:0.35051546;" />
+ <stop
+ id="stop3276"
+ offset="0.64999998"
+ style="stop-color:#000000;stop-opacity:0.13402061;" />
+ <stop
+ id="stop1808"
+ offset="1.0000000"
+ style="stop-color:#000000;stop-opacity:0.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient893">
+ <stop
+ id="stop895"
+ offset="0"
+ style="stop-color:#000;stop-opacity:1;" />
+ <stop
+ id="stop896"
+ offset="1"
+ style="stop-color:#fff;stop-opacity:1;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient1317">
+ <stop
+ id="stop1318"
+ offset="0.00000000"
+ style="stop-color:#000000;stop-opacity:0.52892560;" />
+ <stop
+ id="stop1320"
+ offset="0.50000000"
+ style="stop-color:#000000;stop-opacity:0.17355372;" />
+ <stop
+ id="stop1319"
+ offset="1.0000000"
+ style="stop-color:#000000;stop-opacity:0.00000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient1133">
+ <stop
+ id="stop1134"
+ offset="0.00000000"
+ style="stop-color:#8bb7df;stop-opacity:1.0000000;" />
+ <stop
+ id="stop1136"
+ offset="0.76209301"
+ style="stop-color:#2a6092;stop-opacity:1.0000000;" />
+ <stop
+ id="stop1135"
+ offset="1.0000000"
+ style="stop-color:#375e82;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient1098">
+ <stop
+ id="stop1099"
+ offset="0.00000000"
+ style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+ <stop
+ id="stop1101"
+ offset="0.50000000"
+ style="stop-color:#ffffff;stop-opacity:0.22314049;" />
+ <stop
+ id="stop1102"
+ offset="0.59930235"
+ style="stop-color:#ffffff;stop-opacity:0.00000000;" />
+ <stop
+ id="stop1100"
+ offset="1.0000000"
+ style="stop-color:#ffffff;stop-opacity:0.60330576;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient902">
+ <stop
+ id="stop903"
+ offset="0.00000000"
+ style="stop-color:#000000;stop-opacity:0.00000000;" />
+ <stop
+ id="stop904"
+ offset="1.0000000"
+ style="stop-color:#000000;stop-opacity:0.22000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient892">
+ <stop
+ id="stop893"
+ offset="0.0000000"
+ style="stop-color:#ffffff;stop-opacity:0.0000000;" />
+ <stop
+ id="stop894"
+ offset="1"
+ style="stop-color:#fff;stop-opacity:1;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient888">
+ <stop
+ id="stop889"
+ offset="0.0000000"
+ style="stop-color:#626262;stop-opacity:1.0000000;" />
+ <stop
+ id="stop890"
+ offset="1"
+ style="stop-color:#fff;stop-opacity:1;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient891"
+ x1="1.3485916"
+ x2="0.024647888"
+ xlink:href="#linearGradient888"
+ y1="-0.85185188"
+ y2="1.0899471" />
+ <linearGradient
+ id="linearGradient901"
+ spreadMethod="pad"
+ x1="1.5803921"
+ x2="0.14117648"
+ xlink:href="#linearGradient888"
+ y1="2.4285715"
+ y2="-0.38571429" />
+ <linearGradient
+ id="linearGradient905"
+ x1="-1.5389611"
+ x2="1.0909091"
+ xlink:href="#linearGradient888"
+ y1="2.7890625"
+ y2="-0.19531250" />
+ <radialGradient
+ cx="0.10362694"
+ cy="0.093750000"
+ fx="0.10362694"
+ fy="0.093750000"
+ id="radialGradient1132"
+ r="1.2958785"
+ xlink:href="#linearGradient1133" />
+ <linearGradient
+ id="linearGradient1138"
+ xlink:href="#linearGradient4894" />
+ <linearGradient
+ id="linearGradient1140"
+ x1="0.54117650"
+ x2="0.57647061"
+ xlink:href="#linearGradient888"
+ y1="-2.4210527"
+ y2="4.6315789" />
+ <linearGradient
+ id="linearGradient1141"
+ x1="1.8281938"
+ x2="-0.0088105723"
+ xlink:href="#linearGradient888"
+ y1="3.0546875"
+ y2="-0.44531250" />
+ <linearGradient
+ id="linearGradient1144"
+ x1="0.21960784"
+ x2="0.59607846"
+ xlink:href="#linearGradient1853"
+ y1="-11.111111"
+ y2="5.2777777" />
+ <linearGradient
+ id="linearGradient1146"
+ x1="0.51351351"
+ x2="-0.076576576"
+ xlink:href="#linearGradient892"
+ y1="0.55468750"
+ y2="1.1875000" />
+ <linearGradient
+ id="linearGradient1148"
+ x1="0.23245615"
+ x2="1.0789474"
+ xlink:href="#linearGradient892"
+ y1="0.15625000"
+ y2="-0.64843750" />
+ <linearGradient
+ id="linearGradient1150"
+ x1="0.25221238"
+ x2="-0.57522124"
+ xlink:href="#linearGradient892"
+ y1="0.57812500"
+ y2="1.4765625" />
+ <linearGradient
+ id="linearGradient1156"
+ x1="0.48260871"
+ x2="0.48260871"
+ xlink:href="#linearGradient888"
+ y1="-0.40000001"
+ y2="1.8750000" />
+ <linearGradient
+ id="linearGradient1157"
+ x1="1.5528169"
+ x2="-1.2077465"
+ xlink:href="#linearGradient888"
+ y1="3.3265307"
+ y2="-0.48979592" />
+ <linearGradient
+ id="linearGradient1166"
+ x1="0.52941179"
+ x2="0.57647061"
+ xlink:href="#linearGradient1317"
+ y1="-3.5714285"
+ y2="4.6315789" />
+ <linearGradient
+ id="linearGradient1167"
+ x1="1.6111112"
+ x2="-0.083333336"
+ xlink:href="#linearGradient888"
+ y1="3.0703125"
+ y2="0.046875000" />
+ <linearGradient
+ id="linearGradient1169"
+ x1="1.4780220"
+ x2="-0.13028169"
+ xlink:href="#linearGradient893"
+ y1="2.9218750"
+ y2="-0.26732674" />
+ <linearGradient
+ gradientTransform="scale(0.998371,1.001632)"
+ id="linearGradient1170"
+ x1="0.47284532"
+ x2="0.48655096"
+ xlink:href="#linearGradient902"
+ y1="-0.016295359"
+ y2="1.8378206" />
+ <linearGradient
+ id="linearGradient1171"
+ x1="0.83050847"
+ x2="0.56355929"
+ xlink:href="#linearGradient902"
+ y1="0.57812500"
+ y2="0.36718750" />
+ <radialGradient
+ cx="0.088082902"
+ cy="0.093750000"
+ fx="0.090673581"
+ fy="0.10937500"
+ id="radialGradient1315"
+ r="1.1765809"
+ xlink:href="#linearGradient1133" />
+ <radialGradient
+ cx="0.50000000"
+ cy="0.50000006"
+ fx="0.50352114"
+ fy="0.18269235"
+ id="radialGradient1316"
+ r="0.34964636"
+ xlink:href="#linearGradient1317" />
+ <linearGradient
+ id="linearGradient1404"
+ x1="0.53169012"
+ x2="0.54577464"
+ xlink:href="#linearGradient892"
+ y1="0.28888890"
+ y2="1.1000000" />
+ <linearGradient
+ gradientTransform="scale(0.997825,1.002180)"
+ id="linearGradient1505"
+ x1="0.47157744"
+ x2="0.48548824"
+ xlink:href="#linearGradient902"
+ y1="-0.024853170"
+ y2="1.8570156" />
+ <linearGradient
+ gradientTransform="scale(0.995847,1.004170)"
+ id="linearGradient1506"
+ x1="0.47042510"
+ x2="0.48481107"
+ xlink:href="#linearGradient902"
+ y1="-0.043652620"
+ y2="1.9025002" />
+ <linearGradient
+ gradientTransform="scale(0.997153,1.002855)"
+ id="linearGradient2740"
+ x1="0.47041038"
+ x2="0.48453596"
+ xlink:href="#linearGradient902"
+ y1="-0.033741195"
+ y2="1.8771822" />
+ <linearGradient
+ id="linearGradient4283"
+ x1="-0.77314812"
+ x2="0.99074072"
+ xlink:href="#linearGradient893"
+ y1="2.0837989"
+ y2="-0.033519555" />
+ <linearGradient
+ id="linearGradient4284"
+ x1="-2.3960868e-17"
+ x2="0.92957747"
+ xlink:href="#linearGradient893"
+ y1="3.3012049"
+ y2="-0.45783132" />
+ <radialGradient
+ cx="0.50000000"
+ cy="0.50000000"
+ fx="0.50000000"
+ fy="0.50000000"
+ id="radialGradient1977"
+ r="0.50000000"
+ xlink:href="#linearGradient1853" />
+ </defs>
+ <sodipodi:namedview
+ bordercolor="#666666"
+ borderopacity="1.0"
+ id="base"
+ inkscape:cx="62.122256"
+ inkscape:cy="81.091465"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:window-height="667"
+ inkscape:window-width="573"
+ inkscape:window-x="380"
+ inkscape:window-y="151"
+ inkscape:zoom="2"
+ pagecolor="#ffffff"
+ showborder="true"
+ showgrid="false"
+ inkscape:current-layer="Andysvg" />
+ <path
+ d="M 26.564473,83.749649 L 26.564473,121.41271 L 57.756286,121.41271"
+ id="path3723"
+ sodipodi:nodetypes="ccc"
+ style="fill:none;fill-rule:evenodd;stroke:#9c9c9c;stroke-width:5.7184987;stroke-linecap:round;stroke-linejoin:round;" />
+ <g
+ id="g2843"
+ transform="matrix(0.999379,0.000000,0.000000,0.999379,1.227893e-3,3.986513)">
+ <rect
+ height="8.3153667"
+ id="rect1906"
+ style="fill:url(#linearGradient1156);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient1157);stroke-width:1.4473482pt;"
+ transform="matrix(0.716224,0.000000,0.000000,0.716224,-12.57051,-9.652832)"
+ width="57.567924"
+ x="33.326111"
+ y="78.658051" />
+ <rect
+ height="60.126495"
+ id="rect1907"
+ rx="5.4369707"
+ ry="5.4369707"
+ style="fill:url(#linearGradient905);fill-opacity:1;fill-rule:evenodd;stroke-width:1.6282668;"
+ transform="matrix(0.716224,0.000000,0.000000,0.716224,-12.57051,-9.652832)"
+ width="72.279724"
+ x="26.015469"
+ y="22.413721" />
+ <rect
+ height="38.044163"
+ id="rect1908"
+ style="fill:url(#radialGradient1315);fill-rule:evenodd;stroke:url(#linearGradient891);stroke-width:1.4649456pt;"
+ transform="matrix(0.716224,0.000000,0.000000,0.716224,-12.57051,-9.652832)"
+ width="58.178177"
+ x="33.386066"
+ y="31.695871" />
+ <path
+ d="M 27.690431,52.841444 L 27.370609,74.749236 C 27.319624,78.241665 29.310209,80.477938 32.807578,80.506029 L 72.625393,80.825852 L 76.463254,71.870840 L 32.008024,71.551020 L 31.688202,52.681533 L 27.690431,52.841444 z "
+ id="path1909"
+ sodipodi:nodetypes="czzccccc"
+ style="fill:url(#linearGradient1146);fill-opacity:1;fill-rule:evenodd;stroke-width:1.0000000pt;"
+ transform="matrix(0.716224,0.000000,0.000000,0.716224,-12.57051,-9.652832)" />
+ <rect
+ height="26.147448"
+ id="rect1913"
+ rx="7.4449978"
+ ry="7.4449978"
+ style="fill:url(#linearGradient901);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient1157);stroke-width:2.3625000;"
+ transform="matrix(0.571582,0.000000,0.000000,0.571582,-77.72566,72.35541)"
+ width="104.09673"
+ x="140.62315"
+ y="-34.316952" />
+ <rect
+ height="15.829688"
+ id="rect1914"
+ rx="3.7576280"
+ ry="3.7576280"
+ style="fill:url(#linearGradient901);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient1157);stroke-width:1.3591428;"
+ transform="matrix(0.571582,0.000000,0.000000,0.571582,-77.72566,72.35541)"
+ width="56.908955"
+ x="184.04552"
+ y="-28.539845" />
+ <rect
+ height="15.829688"
+ id="rect1915"
+ rx="2.9970589"
+ ry="2.9970589"
+ style="fill:url(#linearGradient1141);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient1157);stroke-width:0.96249998;"
+ transform="matrix(0.571582,0.000000,0.000000,0.571582,-77.72566,72.35541)"
+ width="28.796961"
+ x="145.28902"
+ y="-28.227346" />
+ <rect
+ height="3.3627598"
+ id="rect1916"
+ rx="1.6813799"
+ ry="1.6813799"
+ style="fill-opacity:0.13836475;fill-rule:evenodd;stroke-width:0.46326005;"
+ transform="matrix(0.571582,0.000000,0.000000,0.571582,-77.72566,72.35541)"
+ width="49.231453"
+ x="187.88426"
+ y="-21.681381" />
+ </g>
+ <path
+ style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:helvetica"
+ d="M 7.0612345,-14.660837 L 7.0612345,-23.250681 L 8.2272501,-23.250681 L 12.738969,-16.50654 L 12.738969,-23.250681 L 13.828813,-23.250681 L 13.828813,-14.660837 L 12.662797,-14.660837 L 8.1510782,-21.410837 L 8.1510782,-14.660837 L 7.0612345,-14.660837 z M 19.869828,-16.664743 L 20.959672,-16.529978 C 20.787791,-15.893258 20.469432,-15.399118 20.004594,-15.047556 C 19.539745,-14.695993 18.945996,-14.520212 18.223344,-14.520212 C 17.313185,-14.520212 16.591506,-14.800485 16.058305,-15.361032 C 15.525101,-15.921578 15.2585,-16.70771 15.2585,-17.719431 C 15.2585,-18.766302 15.528031,-19.578801 16.067094,-20.156931 C 16.606155,-20.73505 17.305373,-21.024112 18.16475,-21.024118 C 18.996777,-21.024112 19.676464,-20.740909 20.203813,-20.174509 C 20.73115,-19.608098 20.994822,-18.811224 20.994828,-17.783884 C 20.994822,-17.721381 20.992869,-17.627631 20.988969,-17.502634 L 16.348344,-17.502634 C 16.387405,-16.819038 16.580764,-16.295601 16.928422,-15.932322 C 17.276076,-15.569039 17.709669,-15.387399 18.229203,-15.3874 C 18.615918,-15.387399 18.945996,-15.488961 19.219438,-15.692087 C 19.49287,-15.895211 19.709667,-16.219429 19.869828,-16.664743 L 19.869828,-16.664743 z M 16.406938,-18.369822 L 19.881547,-18.369822 C 19.834667,-18.893255 19.701855,-19.285833 19.483109,-19.547556 C 19.147168,-19.953801 18.711621,-20.156925 18.176469,-20.156931 C 17.692091,-20.156925 17.284865,-19.994816 16.954789,-19.670603 C 16.624709,-19.346379 16.442092,-18.912786 16.406938,-18.369822 L 16.406938,-18.369822 z M 24.592484,-15.604197 L 24.744828,-14.672556 C 24.44795,-14.610056 24.182326,-14.578806 23.947953,-14.578806 C 23.565139,-14.578806 23.268264,-14.639353 23.057328,-14.760447 C 22.846389,-14.88154 22.697952,-15.04072 22.612016,-15.237986 C 22.526077,-15.43525 22.483108,-15.850289 22.483109,-16.483103 L 22.483109,-20.063181 L 21.709672,-20.063181 L 21.709672,-20.883493 L 22.483109,-20.883493 L 22.483109,-22.424509 L 23.531938,-23.057322 L 23.531938,-20.883493 L 24.592484,-20.883493 L 24.592484,-20.063181 L 23.531938,-20.063181 L 23.531938,-16.424509 C 23.531936,-16.123726 23.55049,-15.930367 23.587602,-15.844431 C 23.624709,-15.758492 23.685256,-15.690133 23.769242,-15.639353 C 23.853224,-15.588571 23.973341,-15.56318 24.129594,-15.563181 C 24.246779,-15.56318 24.401075,-15.576852 24.592484,-15.604197 L 24.592484,-15.604197 z M 26.766313,-14.660837 L 24.862016,-20.883493 L 25.951859,-20.883493 L 26.942094,-17.291697 L 27.311234,-15.955759 C 27.326857,-16.022164 27.434279,-16.449898 27.6335,-17.238962 L 28.623734,-20.883493 L 29.707719,-20.883493 L 30.639359,-17.274118 L 30.949906,-16.084665 L 31.307328,-17.285837 L 32.373734,-20.883493 L 33.399125,-20.883493 L 31.453813,-14.660837 L 30.358109,-14.660837 L 29.367875,-18.3874 L 29.127641,-19.447947 L 27.867875,-14.660837 L 26.766313,-14.660837 z M 33.897172,-17.772165 C 33.897172,-18.924505 34.217484,-19.77802 34.858109,-20.332712 C 35.393264,-20.793644 36.045607,-21.024112 36.815141,-21.024118 C 37.670605,-21.024112 38.369823,-20.743839 38.912797,-20.183298 C 39.45576,-19.622746 39.727244,-18.848333 39.72725,-17.860056 C 39.727244,-17.059272 39.607127,-16.42939 39.366899,-15.970407 C 39.126659,-15.511422 38.77705,-15.154977 38.31807,-14.901072 C 37.859082,-14.647165 37.358106,-14.520212 36.815141,-14.520212 C 35.944045,-14.520212 35.239944,-14.799509 34.702836,-15.358103 C 34.165726,-15.916695 33.897172,-16.721382 33.897172,-17.772165 L 33.897172,-17.772165 z M 34.981156,-17.772165 C 34.981155,-16.975288 35.154983,-16.378609 35.502641,-15.982126 C 35.850295,-15.585641 36.287794,-15.387399 36.815141,-15.3874 C 37.338574,-15.387399 37.774121,-15.586617 38.121781,-15.985056 C 38.469433,-16.383492 38.643261,-16.990913 38.643266,-17.807322 C 38.643261,-18.576849 38.468456,-19.159856 38.118852,-19.556345 C 37.769238,-19.952824 37.334668,-20.151066 36.815141,-20.151072 C 36.287794,-20.151066 35.850295,-19.953801 35.502641,-19.559275 C 35.154983,-19.164739 34.981155,-18.569036 34.981156,-17.772165 L 34.981156,-17.772165 z M 40.957719,-14.660837 L 40.957719,-20.883493 L 41.906938,-20.883493 L 41.906938,-19.940134 C 42.149123,-20.381535 42.372756,-20.67255 42.577836,-20.813181 C 42.782912,-20.9538 43.008497,-21.024112 43.254594,-21.024118 C 43.610059,-21.024112 43.971387,-20.910831 44.338578,-20.684275 L 43.975297,-19.705759 C 43.717481,-19.858098 43.459669,-19.934269 43.201859,-19.934275 C 42.971388,-19.934269 42.764357,-19.864934 42.580766,-19.726267 C 42.39717,-19.58759 42.266311,-19.395207 42.188188,-19.149118 C 42.070998,-18.774114 42.012405,-18.363958 42.012406,-17.91865 L 42.012406,-14.660837 L 40.957719,-14.660837 z M 44.983109,-14.660837 L 44.983109,-23.250681 L 46.037797,-23.250681 L 46.037797,-18.352243 L 48.533891,-20.883493 L 49.899125,-20.883493 L 47.520219,-18.5749 L 50.139359,-14.660837 L 48.838578,-14.660837 L 46.781938,-17.842478 L 46.037797,-17.127634 L 46.037797,-14.660837 L 44.983109,-14.660837 z"
+ id="text1232" />
+ <path
+ transform="scale(0.246729,0.246729)"
+ style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:helvetica"
+ d="M 91.619637,-37.962852 L 92.756355,-37.675743 C 92.518066,-36.742148 92.089356,-36.030234 91.470222,-35.540001 C 90.851076,-35.049766 90.09424,-34.804649 89.199715,-34.804649 C 88.27393,-34.804649 87.521001,-34.993126 86.940926,-35.370079 C 86.360846,-35.747031 85.91944,-36.292929 85.616707,-37.007774 C 85.313972,-37.722615 85.162605,-38.490193 85.162605,-39.310509 C 85.162605,-40.205035 85.333503,-40.985307 85.675301,-41.651329 C 86.017096,-42.317337 86.503424,-42.823196 87.134285,-43.168907 C 87.765141,-43.514602 88.459476,-43.687453 89.217293,-43.687462 C 90.076662,-43.687453 90.799318,-43.468703 91.385262,-43.031212 C 91.971192,-42.593704 92.379394,-41.97847 92.609871,-41.185509 L 91.49073,-40.921837 C 91.291505,-41.54683 91.002443,-42.001908 90.623543,-42.287071 C 90.244631,-42.57222 89.768069,-42.714798 89.193855,-42.714806 C 88.533695,-42.714798 87.981938,-42.556595 87.538582,-42.240196 C 87.09522,-41.923783 86.783697,-41.498979 86.604012,-40.965782 C 86.424322,-40.432574 86.334479,-39.882769 86.33448,-39.316368 C 86.334479,-38.585896 86.440924,-37.948201 86.653816,-37.403282 C 86.866705,-36.858358 87.197759,-36.451132 87.64698,-36.181602 C 88.096196,-35.91207 88.582523,-35.777305 89.105965,-35.777306 C 89.742678,-35.777305 90.28174,-35.960898 90.723152,-36.328087 C 91.164552,-36.695273 91.46338,-37.240194 91.619637,-37.962852 L 91.619637,-37.962852 z M 94.016121,-34.951134 L 94.016121,-41.17379 L 94.96534,-41.17379 L 94.96534,-40.230431 C 95.207525,-40.671831 95.431158,-40.962846 95.636238,-41.103477 C 95.841314,-41.244096 96.066899,-41.314409 96.312996,-41.314415 C 96.668461,-41.314409 97.029789,-41.201127 97.39698,-40.974571 L 97.033699,-39.996056 C 96.775883,-40.148394 96.518071,-40.224566 96.260262,-40.224571 C 96.02979,-40.224566 95.822759,-40.15523 95.639168,-40.016563 C 95.455572,-39.877887 95.324713,-39.685504 95.24659,-39.439415 C 95.1294,-39.064411 95.070807,-38.654255 95.070808,-38.208946 L 95.070808,-34.951134 L 94.016121,-34.951134 z M 102.29542,-36.95504 L 103.38526,-36.820274 C 103.21338,-36.183554 102.89502,-35.689414 102.43018,-35.337852 C 101.96533,-34.98629 101.37159,-34.810509 100.64893,-34.810509 C 99.738775,-34.810509 99.017096,-35.090782 98.483894,-35.651329 C 97.950691,-36.211875 97.684089,-36.998007 97.68409,-38.009727 C 97.684089,-39.056598 97.95362,-39.869098 98.492683,-40.447227 C 99.031744,-41.025346 99.730962,-41.314409 100.59034,-41.314415 C 101.42237,-41.314409 102.10205,-41.031206 102.6294,-40.464806 C 103.15674,-39.898394 103.42041,-39.10152 103.42042,-38.074181 C 103.42041,-38.011678 103.41846,-37.917928 103.41456,-37.792931 L 98.773933,-37.792931 C 98.812994,-37.109335 99.006354,-36.585898 99.354012,-36.222618 C 99.701665,-35.859336 100.13526,-35.677696 100.65479,-35.677696 C 101.04151,-35.677696 101.37159,-35.779258 101.64503,-35.982384 C 101.91846,-36.185507 102.13526,-36.509726 102.29542,-36.95504 L 102.29542,-36.95504 z M 98.832527,-38.660118 L 102.30714,-38.660118 C 102.26026,-39.183551 102.12744,-39.576129 101.9087,-39.837852 C 101.57276,-40.244097 101.13721,-40.447222 100.60206,-40.447227 C 100.11768,-40.447222 99.710454,-40.285113 99.380379,-39.960899 C 99.050299,-39.636676 98.867682,-39.203083 98.832527,-38.660118 L 98.832527,-38.660118 z M 108.77589,-35.718712 C 108.38526,-35.38668 108.00928,-35.152305 107.64796,-35.015587 C 107.28663,-34.878868 106.89893,-34.810509 106.48487,-34.810509 C 105.80128,-34.810509 105.27589,-34.977501 104.9087,-35.311485 C 104.54151,-35.645469 104.35792,-36.072226 104.35792,-36.591759 C 104.35792,-36.896444 104.42725,-37.174764 104.56593,-37.42672 C 104.7046,-37.67867 104.88624,-37.880818 105.11085,-38.033165 C 105.33546,-38.185505 105.58838,-38.30074 105.86964,-38.378868 C 106.07667,-38.433552 106.38917,-38.486286 106.80714,-38.537071 C 107.6587,-38.63863 108.28565,-38.759724 108.688,-38.900352 C 108.6919,-39.04488 108.69385,-39.136676 108.69386,-39.175743 C 108.69385,-39.605426 108.59424,-39.90816 108.39503,-40.083946 C 108.12549,-40.322222 107.7251,-40.441363 107.19386,-40.441368 C 106.69776,-40.441363 106.33155,-40.354449 106.09522,-40.180626 C 105.85889,-40.006793 105.68409,-39.699176 105.57081,-39.257774 L 104.53956,-39.398399 C 104.63331,-39.839801 104.7876,-40.196246 105.00245,-40.467735 C 105.21729,-40.739214 105.52784,-40.948198 105.93409,-41.094688 C 106.34034,-41.241167 106.81104,-41.314409 107.3462,-41.314415 C 107.87745,-41.314409 108.30909,-41.251909 108.64112,-41.126915 C 108.97315,-41.001909 109.21729,-40.844683 109.37354,-40.655235 C 109.52979,-40.465777 109.63916,-40.226519 109.70167,-39.937462 C 109.73682,-39.75777 109.7544,-39.433551 109.7544,-38.964806 L 109.7544,-37.558556 C 109.7544,-36.578085 109.77686,-35.957969 109.82178,-35.698204 C 109.8667,-35.438438 109.95557,-35.189415 110.08839,-34.951134 L 108.98682,-34.951134 C 108.87744,-35.169884 108.80713,-35.425743 108.77589,-35.718712 L 108.77589,-35.718712 z M 108.688,-38.074181 C 108.30518,-37.917928 107.73096,-37.785115 106.96534,-37.675743 C 106.53174,-37.61324 106.2251,-37.542928 106.04542,-37.464806 C 105.86573,-37.386678 105.72706,-37.27242 105.6294,-37.122032 C 105.53174,-36.97164 105.48292,-36.804647 105.48292,-36.621056 C 105.48292,-36.339804 105.58936,-36.105429 105.80225,-35.917931 C 106.01514,-35.73043 106.32667,-35.63668 106.73682,-35.636681 C 107.14307,-35.63668 107.5044,-35.725547 107.82081,-35.903282 C 108.13721,-36.081015 108.36963,-36.324179 108.51807,-36.632774 C 108.63135,-36.871054 108.68799,-37.222616 108.688,-37.687462 L 108.688,-38.074181 z M 113.69776,-35.894493 L 113.85011,-34.962852 C 113.55323,-34.900353 113.2876,-34.869103 113.05323,-34.869102 C 112.67042,-34.869103 112.37354,-34.929649 112.16261,-35.050743 C 111.95167,-35.171837 111.80323,-35.331016 111.71729,-35.528282 C 111.63135,-35.725547 111.58839,-36.140586 111.58839,-36.773399 L 111.58839,-40.353477 L 110.81495,-40.353477 L 110.81495,-41.17379 L 111.58839,-41.17379 L 111.58839,-42.714806 L 112.63721,-43.347618 L 112.63721,-41.17379 L 113.69776,-41.17379 L 113.69776,-40.353477 L 112.63721,-40.353477 L 112.63721,-36.714806 C 112.63721,-36.414023 112.65577,-36.220664 112.69288,-36.134727 C 112.72999,-36.048789 112.79053,-35.98043 112.87452,-35.929649 C 112.9585,-35.878867 113.07862,-35.853477 113.23487,-35.853477 C 113.35206,-35.853477 113.50635,-35.867148 113.69776,-35.894493 L 113.69776,-35.894493 z M 118.98292,-36.95504 L 120.07276,-36.820274 C 119.90088,-36.183554 119.58252,-35.689414 119.11768,-35.337852 C 118.65283,-34.98629 118.05909,-34.810509 117.33643,-34.810509 C 116.42627,-34.810509 115.7046,-35.090782 115.17139,-35.651329 C 114.63819,-36.211875 114.37159,-36.998007 114.37159,-38.009727 C 114.37159,-39.056598 114.64112,-39.869098 115.18018,-40.447227 C 115.71924,-41.025346 116.41846,-41.314409 117.27784,-41.314415 C 118.10987,-41.314409 118.78955,-41.031206 119.3169,-40.464806 C 119.84424,-39.898394 120.10791,-39.10152 120.10792,-38.074181 C 120.10791,-38.011678 120.10596,-37.917928 120.10206,-37.792931 L 115.46143,-37.792931 C 115.50049,-37.109335 115.69385,-36.585898 116.04151,-36.222618 C 116.38917,-35.859336 116.82276,-35.677696 117.34229,-35.677696 C 117.72901,-35.677696 118.05909,-35.779258 118.33253,-35.982384 C 118.60596,-36.185507 118.82276,-36.509726 118.98292,-36.95504 L 118.98292,-36.95504 z M 115.52003,-38.660118 L 118.99464,-38.660118 C 118.94776,-39.183551 118.81494,-39.576129 118.5962,-39.837852 C 118.26026,-40.244097 117.82471,-40.447222 117.28956,-40.447227 C 116.80518,-40.447222 116.39795,-40.285113 116.06788,-39.960899 C 115.7378,-39.636676 115.55518,-39.203083 115.52003,-38.660118 L 115.52003,-38.660118 z M 125.43995,-34.951134 L 125.43995,-35.73629 C 125.04541,-35.119102 124.46534,-34.810509 123.69971,-34.810509 C 123.20362,-34.810509 122.74756,-34.947227 122.33155,-35.220665 C 121.91553,-35.494102 121.59327,-35.875937 121.36475,-36.366173 C 121.13624,-36.856405 121.02198,-37.419881 121.02198,-38.056602 C 121.02198,-38.677693 121.1255,-39.241169 121.33253,-39.747032 C 121.53956,-40.252886 121.8501,-40.640581 122.26417,-40.910118 C 122.67823,-41.179643 123.14112,-41.314409 123.65284,-41.314415 C 124.02784,-41.314409 124.36182,-41.235307 124.65479,-41.07711 C 124.94776,-40.918901 125.18604,-40.712847 125.36964,-40.458946 L 125.36964,-43.540977 L 126.41846,-43.540977 L 126.41846,-34.951134 L 125.43995,-34.951134 z M 122.10596,-38.056602 C 122.10596,-37.259725 122.27393,-36.664023 122.60987,-36.269493 C 122.94581,-35.874961 123.34229,-35.677696 123.79932,-35.677696 C 124.26026,-35.677696 124.65186,-35.866172 124.97413,-36.243126 C 125.29639,-36.620077 125.45752,-37.195272 125.45753,-37.968712 C 125.45752,-38.82027 125.29346,-39.44527 124.96534,-39.843712 C 124.63721,-40.242144 124.23291,-40.441363 123.75245,-40.441368 C 123.2837,-40.441363 122.8921,-40.249957 122.57764,-39.867149 C 122.26319,-39.484332 122.10596,-38.880817 122.10596,-38.056602 L 122.10596,-38.056602 z M 132.38331,-34.951134 L 131.40479,-34.951134 L 131.40479,-43.540977 L 132.45948,-43.540977 L 132.45948,-40.476524 C 132.90479,-41.035112 133.47315,-41.314409 134.16456,-41.314415 C 134.54737,-41.314409 134.90967,-41.23726 135.25147,-41.08297 C 135.59326,-40.928667 135.87451,-40.71187 136.09522,-40.432579 C 136.31592,-40.153277 136.48877,-39.816363 136.61378,-39.421837 C 136.73877,-39.027302 136.80127,-38.605427 136.80128,-38.156212 C 136.80127,-37.089803 136.5376,-36.265586 136.01026,-35.683556 C 135.48291,-35.101524 134.8501,-34.810509 134.11182,-34.810509 C 133.37745,-34.810509 132.80127,-35.117149 132.38331,-35.730431 L 132.38331,-34.951134 z M 132.37159,-38.109337 C 132.37159,-37.363241 132.47315,-36.824179 132.67628,-36.492149 C 133.00831,-35.94918 133.45752,-35.677696 134.02393,-35.677696 C 134.48487,-35.677696 134.8833,-35.877891 135.21925,-36.278282 C 135.55518,-36.678671 135.72315,-37.27535 135.72315,-38.068321 C 135.72315,-38.880817 135.56201,-39.480426 135.23975,-39.867149 C 134.91748,-40.253863 134.52784,-40.447222 134.07081,-40.447227 C 133.60987,-40.447222 133.21143,-40.247027 132.8755,-39.846642 C 132.53956,-39.446246 132.37159,-38.867145 132.37159,-38.109337 L 132.37159,-38.109337 z M 138.04346,-32.554649 L 137.92628,-33.544884 C 138.15675,-33.482385 138.35792,-33.451135 138.52979,-33.451134 C 138.76417,-33.451135 138.95167,-33.490198 139.09229,-33.568321 C 139.23292,-33.646448 139.34815,-33.755822 139.438,-33.896446 C 139.5044,-34.001916 139.61182,-34.263634 139.76026,-34.681602 C 139.77979,-34.740196 139.81104,-34.826134 139.85401,-34.939415 L 137.49268,-41.17379 L 138.6294,-41.17379 L 139.92432,-37.570274 C 140.09229,-37.113241 140.24268,-36.632773 140.3755,-36.128868 C 140.49659,-36.613241 140.64112,-37.085897 140.80909,-37.546837 L 142.13917,-41.17379 L 143.19386,-41.17379 L 140.82667,-34.845665 C 140.57276,-34.162072 140.37549,-33.691369 140.23487,-33.433556 C 140.04737,-33.085901 139.83252,-32.831019 139.59034,-32.668907 C 139.34815,-32.5068 139.05909,-32.425746 138.72315,-32.425743 C 138.52003,-32.425746 138.29346,-32.468714 138.04346,-32.554649 L 138.04346,-32.554649 z M 146.60987,-34.951134 L 149.9087,-43.540977 L 151.13331,-43.540977 L 154.64893,-34.951134 L 153.35401,-34.951134 L 152.35206,-37.552696 L 148.76026,-37.552696 L 147.8169,-34.951134 L 146.60987,-34.951134 z M 149.08839,-38.478477 L 152.0005,-38.478477 L 151.10401,-40.857384 C 150.83057,-41.580033 150.62745,-42.173783 150.49464,-42.638634 C 150.38526,-42.087845 150.23096,-41.540971 150.03175,-40.998009 L 149.08839,-38.478477 z M 155.43409,-34.951134 L 155.43409,-41.17379 L 156.38331,-41.17379 L 156.38331,-40.289024 C 156.84034,-40.972612 157.50049,-41.314409 158.36378,-41.314415 C 158.73877,-41.314409 159.0835,-41.247026 159.39796,-41.112267 C 159.7124,-40.977495 159.94776,-40.800737 160.10401,-40.581993 C 160.26026,-40.363238 160.36963,-40.103472 160.43214,-39.802696 C 160.47119,-39.607379 160.49072,-39.265583 160.49073,-38.777306 L 160.49073,-34.951134 L 159.43604,-34.951134 L 159.43604,-38.73629 C 159.43604,-39.165973 159.39502,-39.487262 159.313,-39.700157 C 159.23096,-39.913043 159.08545,-40.082965 158.87647,-40.209923 C 158.66748,-40.336871 158.42237,-40.400347 158.14112,-40.400352 C 157.6919,-40.400347 157.3042,-40.257769 156.97803,-39.972618 C 156.65186,-39.687457 156.48878,-39.146442 156.48878,-38.349571 L 156.48878,-34.951134 L 155.43409,-34.951134 z M 166.15089,-34.951134 L 166.15089,-35.73629 C 165.75635,-35.119102 165.17627,-34.810509 164.41065,-34.810509 C 163.91456,-34.810509 163.4585,-34.947227 163.04249,-35.220665 C 162.62647,-35.494102 162.30421,-35.875937 162.07569,-36.366173 C 161.84718,-36.856405 161.73292,-37.419881 161.73292,-38.056602 C 161.73292,-38.677693 161.83643,-39.241169 162.04346,-39.747032 C 162.25049,-40.252886 162.56104,-40.640581 162.97511,-40.910118 C 163.38917,-41.179643 163.85206,-41.314409 164.36378,-41.314415 C 164.73877,-41.314409 165.07276,-41.235307 165.36573,-41.07711 C 165.65869,-40.918901 165.89698,-40.712847 166.08057,-40.458946 L 166.08057,-43.540977 L 167.1294,-43.540977 L 167.1294,-34.951134 L 166.15089,-34.951134 z M 162.8169,-38.056602 C 162.8169,-37.259725 162.98487,-36.664023 163.32081,-36.269493 C 163.65674,-35.874961 164.05323,-35.677696 164.51026,-35.677696 C 164.9712,-35.677696 165.3628,-35.866172 165.68507,-36.243126 C 166.00733,-36.620077 166.16846,-37.195272 166.16846,-37.968712 C 166.16846,-38.82027 166.0044,-39.44527 165.67628,-39.843712 C 165.34815,-40.242144 164.94385,-40.441363 164.46339,-40.441368 C 163.99463,-40.441363 163.60303,-40.249957 163.28858,-39.867149 C 162.97413,-39.484332 162.8169,-38.880817 162.8169,-38.056602 L 162.8169,-38.056602 z M 168.78175,-34.951134 L 168.78175,-41.17379 L 169.73096,-41.17379 L 169.73096,-40.230431 C 169.97315,-40.671831 170.19678,-40.962846 170.40186,-41.103477 C 170.60694,-41.244096 170.83252,-41.314409 171.07862,-41.314415 C 171.43409,-41.314409 171.79541,-41.201127 172.16261,-40.974571 L 171.79932,-39.996056 C 171.54151,-40.148394 171.2837,-40.224566 171.02589,-40.224571 C 170.79541,-40.224566 170.58838,-40.15523 170.40479,-40.016563 C 170.2212,-39.877887 170.09034,-39.685504 170.01221,-39.439415 C 169.89503,-39.064411 169.83643,-38.654255 169.83643,-38.208946 L 169.83643,-34.951134 L 168.78175,-34.951134 z M 177.06104,-36.95504 L 178.15089,-36.820274 C 177.97901,-36.183554 177.66065,-35.689414 177.19581,-35.337852 C 176.73096,-34.98629 176.13721,-34.810509 175.41456,-34.810509 C 174.5044,-34.810509 173.78272,-35.090782 173.24952,-35.651329 C 172.71632,-36.211875 172.44971,-36.998007 172.44971,-38.009727 C 172.44971,-39.056598 172.71925,-39.869098 173.25831,-40.447227 C 173.79737,-41.025346 174.49659,-41.314409 175.35596,-41.314415 C 176.18799,-41.314409 176.86768,-41.031206 177.39503,-40.464806 C 177.92236,-39.898394 178.18604,-39.10152 178.18604,-38.074181 C 178.18604,-38.011678 178.18408,-37.917928 178.18018,-37.792931 L 173.53956,-37.792931 C 173.57862,-37.109335 173.77198,-36.585898 174.11964,-36.222618 C 174.46729,-35.859336 174.90088,-35.677696 175.42042,-35.677696 C 175.80713,-35.677696 176.13721,-35.779258 176.41065,-35.982384 C 176.68408,-36.185507 176.90088,-36.509726 177.06104,-36.95504 L 177.06104,-36.95504 z M 173.59815,-38.660118 L 177.07276,-38.660118 C 177.02588,-39.183551 176.89307,-39.576129 176.67432,-39.837852 C 176.33838,-40.244097 175.90284,-40.447222 175.36768,-40.447227 C 174.88331,-40.447222 174.47608,-40.285113 174.146,-39.960899 C 173.81592,-39.636676 173.63331,-39.203083 173.59815,-38.660118 L 173.59815,-38.660118 z M 180.6294,-34.951134 L 178.72511,-41.17379 L 179.81495,-41.17379 L 180.80518,-37.581993 L 181.17432,-36.246056 C 181.18995,-36.31246 181.29737,-36.740194 181.49659,-37.529259 L 182.48682,-41.17379 L 183.57081,-41.17379 L 184.50245,-37.564415 L 184.813,-36.374962 L 185.17042,-37.576134 L 186.23682,-41.17379 L 187.26221,-41.17379 L 185.3169,-34.951134 L 184.2212,-34.951134 L 183.23096,-38.677696 L 182.99073,-39.738243 L 181.73096,-34.951134 L 180.6294,-34.951134 z M 191.67432,-34.951134 L 191.67432,-43.540977 L 197.46925,-43.540977 L 197.46925,-42.527306 L 192.81104,-42.527306 L 192.81104,-39.867149 L 196.84229,-39.867149 L 196.84229,-38.853477 L 192.81104,-38.853477 L 192.81104,-34.951134 L 191.67432,-34.951134 z M 198.82276,-42.328087 L 198.82276,-43.540977 L 199.87745,-43.540977 L 199.87745,-42.328087 L 198.82276,-42.328087 z M 198.82276,-34.951134 L 198.82276,-41.17379 L 199.87745,-41.17379 L 199.87745,-34.951134 L 198.82276,-34.951134 z M 203.79151,-35.894493 L 203.94386,-34.962852 C 203.64698,-34.900353 203.38135,-34.869103 203.14698,-34.869102 C 202.76417,-34.869103 202.46729,-34.929649 202.25636,-35.050743 C 202.04542,-35.171837 201.89698,-35.331016 201.81104,-35.528282 C 201.7251,-35.725547 201.68214,-36.140586 201.68214,-36.773399 L 201.68214,-40.353477 L 200.9087,-40.353477 L 200.9087,-41.17379 L 201.68214,-41.17379 L 201.68214,-42.714806 L 202.73096,-43.347618 L 202.73096,-41.17379 L 203.79151,-41.17379 L 203.79151,-40.353477 L 202.73096,-40.353477 L 202.73096,-36.714806 C 202.73096,-36.414023 202.74952,-36.220664 202.78663,-36.134727 C 202.82374,-36.048789 202.88428,-35.98043 202.96827,-35.929649 C 203.05225,-35.878867 203.17237,-35.853477 203.32862,-35.853477 C 203.44581,-35.853477 203.6001,-35.867148 203.79151,-35.894493 L 203.79151,-35.894493 z M 204.26026,-34.951134 L 204.26026,-35.806602 L 208.2212,-40.353477 C 207.77198,-40.330035 207.37549,-40.318316 207.03175,-40.318321 L 204.49464,-40.318321 L 204.49464,-41.17379 L 209.58057,-41.17379 L 209.58057,-40.476524 L 206.21143,-36.527306 L 205.56104,-35.806602 C 206.0337,-35.841758 206.47706,-35.859336 206.89112,-35.859337 L 209.76807,-35.859337 L 209.76807,-34.951134 L 204.26026,-34.951134 z M 210.39503,-36.808556 L 211.438,-36.972618 C 211.49659,-36.554648 211.65967,-36.234336 211.92725,-36.011681 C 212.19483,-35.789024 212.56885,-35.677696 213.04932,-35.677696 C 213.5337,-35.677696 213.89307,-35.776328 214.12745,-35.973595 C 214.36182,-36.170859 214.47901,-36.402304 214.47901,-36.667931 C 214.47901,-36.90621 214.37549,-37.09371 214.16846,-37.230431 C 214.02393,-37.324178 213.66455,-37.443319 213.09034,-37.587852 C 212.3169,-37.783162 211.78077,-37.952107 211.48194,-38.094688 C 211.18311,-38.237263 210.95655,-38.434529 210.80225,-38.686485 C 210.64796,-38.938434 210.57081,-39.216754 210.57081,-39.521446 C 210.57081,-39.798785 210.63428,-40.055621 210.76124,-40.291954 C 210.88819,-40.528277 211.06104,-40.724565 211.27979,-40.880821 C 211.44385,-41.001909 211.66749,-41.104448 211.95069,-41.188438 C 212.23389,-41.272416 212.5376,-41.314409 212.86182,-41.314415 C 213.3501,-41.314409 213.77881,-41.244096 214.14796,-41.103477 C 214.51709,-40.962846 214.78955,-40.772417 214.96534,-40.532188 C 215.14112,-40.291949 215.26221,-39.97066 215.32862,-39.568321 L 214.29737,-39.427696 C 214.25049,-39.748004 214.11475,-39.998004 213.89014,-40.177696 C 213.66553,-40.357378 213.34815,-40.447222 212.938,-40.447227 C 212.45362,-40.447222 212.10792,-40.367144 211.90089,-40.206993 C 211.69385,-40.046832 211.59034,-39.859332 211.59034,-39.644493 C 211.59034,-39.50777 211.63331,-39.384723 211.71925,-39.275352 C 211.80518,-39.162067 211.93995,-39.068317 212.12354,-38.994102 C 212.22901,-38.955036 212.53956,-38.865192 213.05518,-38.724571 C 213.80127,-38.525349 214.32178,-38.362263 214.61671,-38.235313 C 214.91162,-38.108357 215.14307,-37.923787 215.31104,-37.681602 C 215.47901,-37.439412 215.56299,-37.138632 215.563,-36.779259 C 215.56299,-36.427695 215.46045,-36.09664 215.25538,-35.786095 C 215.0503,-35.475547 214.7544,-35.235313 214.36768,-35.065392 C 213.98096,-34.89547 213.54346,-34.810509 213.05518,-34.810509 C 212.24659,-34.810509 211.63038,-34.978477 211.20655,-35.314415 C 210.78272,-35.650352 210.51221,-36.148398 210.39503,-36.808556 L 210.39503,-36.808556 z M 216.82276,-42.328087 L 216.82276,-43.540977 L 217.87745,-43.540977 L 217.87745,-42.328087 L 216.82276,-42.328087 z M 216.82276,-34.951134 L 216.82276,-41.17379 L 217.87745,-41.17379 L 217.87745,-34.951134 L 216.82276,-34.951134 z M 219.48878,-34.951134 L 219.48878,-41.17379 L 220.43214,-41.17379 L 220.43214,-40.300743 C 220.62745,-40.605425 220.88721,-40.850542 221.21143,-41.036095 C 221.53565,-41.221635 221.90479,-41.314409 222.31886,-41.314415 C 222.77979,-41.314409 223.15772,-41.218706 223.45264,-41.027306 C 223.74756,-40.835893 223.95557,-40.568316 224.07667,-40.224571 C 224.56885,-40.951128 225.20947,-41.314409 225.99854,-41.314415 C 226.61572,-41.314409 227.09033,-41.14351 227.42237,-40.80172 C 227.75439,-40.459917 227.92041,-39.933551 227.92042,-39.222618 L 227.92042,-34.951134 L 226.87159,-34.951134 L 226.87159,-38.871056 C 226.87158,-39.292926 226.8374,-39.596637 226.76905,-39.782188 C 226.70068,-39.96773 226.57666,-40.117144 226.39698,-40.230431 C 226.21729,-40.343706 226.00635,-40.400347 225.76417,-40.400352 C 225.32666,-40.400347 224.96338,-40.254839 224.67432,-39.963829 C 224.38526,-39.672809 224.24072,-39.206989 224.24073,-38.566368 L 224.24073,-34.951134 L 223.18604,-34.951134 L 223.18604,-38.994102 C 223.18604,-39.462848 223.1001,-39.81441 222.92823,-40.04879 C 222.75635,-40.28316 222.4751,-40.400347 222.08448,-40.400352 C 221.7876,-40.400347 221.51319,-40.322222 221.26124,-40.165977 C 221.00928,-40.009722 220.82667,-39.781207 220.71339,-39.480431 C 220.6001,-39.179645 220.54346,-38.746052 220.54346,-38.179649 L 220.54346,-34.951134 L 219.48878,-34.951134 z M 229.10401,-38.062462 C 229.10401,-39.214801 229.42432,-40.068316 230.06495,-40.623009 C 230.6001,-41.08394 231.25245,-41.314409 232.02198,-41.314415 C 232.87744,-41.314409 233.57666,-41.034135 234.11964,-40.473595 C 234.6626,-39.913043 234.93408,-39.13863 234.93409,-38.150352 C 234.93408,-37.349569 234.81397,-36.719687 234.57374,-36.260704 C 234.3335,-35.801719 233.98389,-35.445274 233.52491,-35.191368 C 233.06592,-34.937462 232.56495,-34.810509 232.02198,-34.810509 C 231.15088,-34.810509 230.44678,-35.089805 229.90968,-35.648399 C 229.37257,-36.206992 229.10401,-37.011679 229.10401,-38.062462 L 229.10401,-38.062462 z M 230.188,-38.062462 C 230.18799,-37.265585 230.36182,-36.668905 230.70948,-36.272423 C 231.05713,-35.875937 231.49463,-35.677696 232.02198,-35.677696 C 232.54541,-35.677696 232.98096,-35.876914 233.32862,-36.275352 C 233.67627,-36.673788 233.8501,-37.28121 233.85011,-38.097618 C 233.8501,-38.867145 233.6753,-39.450153 233.32569,-39.846642 C 232.97608,-40.243121 232.54151,-40.441363 232.02198,-40.441368 C 231.49463,-40.441363 231.05713,-40.244097 230.70948,-39.849571 C 230.36182,-39.455035 230.18799,-38.859333 230.188,-38.062462 L 230.188,-38.062462 z M 236.17628,-34.951134 L 236.17628,-41.17379 L 237.1255,-41.17379 L 237.1255,-40.289024 C 237.58252,-40.972612 238.24268,-41.314409 239.10596,-41.314415 C 239.48096,-41.314409 239.82569,-41.247026 240.14014,-41.112267 C 240.45459,-40.977495 240.68994,-40.800737 240.8462,-40.581993 C 241.00244,-40.363238 241.11182,-40.103472 241.17432,-39.802696 C 241.21338,-39.607379 241.23291,-39.265583 241.23292,-38.777306 L 241.23292,-34.951134 L 240.17823,-34.951134 L 240.17823,-38.73629 C 240.17823,-39.165973 240.13721,-39.487262 240.05518,-39.700157 C 239.97315,-39.913043 239.82764,-40.082965 239.61866,-40.209923 C 239.40967,-40.336871 239.16455,-40.400347 238.88331,-40.400352 C 238.43409,-40.400347 238.04639,-40.257769 237.72022,-39.972618 C 237.39405,-39.687457 237.23096,-39.146442 237.23096,-38.349571 L 237.23096,-34.951134 L 236.17628,-34.951134 z"
+ id="text1235" />
+ <g
+ id="g2852"
+ transform="matrix(1.018857,0.000000,0.000000,1.018857,-4.481650,2.131177)">
+ <rect
+ height="8.3153667"
+ id="rect1866"
+ style="fill:url(#linearGradient1156);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient1157);stroke-width:1.4473482pt;"
+ transform="matrix(1.150066,0.000000,0.000000,1.150066,38.98882,26.86863)"
+ width="57.567924"
+ x="33.326111"
+ y="78.658051" />
+ <rect
+ height="60.126495"
+ id="rect1867"
+ rx="5.4369707"
+ ry="5.4369707"
+ style="fill:url(#linearGradient905);fill-opacity:1;fill-rule:evenodd;stroke-width:1.6282668;"
+ transform="matrix(1.150066,0.000000,0.000000,1.150066,38.98882,26.86863)"
+ width="72.279724"
+ x="26.015469"
+ y="22.413721" />
+ <rect
+ height="38.044163"
+ id="rect1868"
+ style="fill:url(#radialGradient1132);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient891);stroke-width:1.4649456pt;"
+ transform="matrix(1.150066,0.000000,0.000000,1.150066,38.98882,26.86863)"
+ width="58.178177"
+ x="33.386066"
+ y="31.695871" />
+ <path
+ d="M 27.690431,52.841444 L 27.370609,74.749236 C 27.319624,78.241665 29.310209,80.477938 32.807578,80.506029 L 72.625393,80.825852 L 76.463254,71.870840 L 32.008024,71.551020 L 31.688202,52.681533 L 27.690431,52.841444 z "
+ id="path1869"
+ sodipodi:nodetypes="czzccccc"
+ style="fill:url(#linearGradient1146);fill-opacity:1;fill-rule:evenodd;stroke-width:1.0000000pt;"
+ transform="matrix(1.150066,0.000000,0.000000,1.150066,38.98882,26.86863)" />
+ <g
+ id="g1870"
+ transform="matrix(1.150066,0.000000,0.000000,1.150066,38.98882,26.86863)">
+ <path
+ d="M 42.062098,33.460351 L 77.341205,33.008055 C 82.787126,32.938235 89.553204,38.416797 89.553204,43.863165 L 89.553204,60.145830 L 41.609801,59.693534 L 42.062098,33.460351 z "
+ id="path1871"
+ sodipodi:nodetypes="czzccc"
+ style="fill:url(#linearGradient1148);fill-opacity:1;fill-rule:evenodd;stroke-width:1.0000000pt;" />
+ <path
+ d="M 78.337784,67.629235 L 46.723745,67.724544 C 41.843589,67.739257 35.829319,62.771024 35.877168,57.891081 L 36.020221,43.301821 L 78.973514,44.128288 L 78.337784,67.629235 z "
+ id="path1872"
+ sodipodi:nodetypes="czzccc"
+ style="fill:url(#linearGradient1150);fill-opacity:1;fill-rule:evenodd;stroke-width:1.0000000pt;" />
+ </g>
+ <rect
+ height="26.147448"
+ id="rect1888"
+ rx="7.4449978"
+ ry="7.4449978"
+ style="fill:url(#linearGradient901);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient1157);stroke-width:2.3625000;"
+ transform="matrix(0.917809,0.000000,0.000000,0.917809,-65.63305,158.5521)"
+ width="104.09673"
+ x="140.62315"
+ y="-34.316952" />
+ <rect
+ height="15.829688"
+ id="rect1889"
+ rx="3.7576280"
+ ry="3.7576280"
+ style="fill:url(#linearGradient901);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient1157);stroke-width:1.3591428;"
+ transform="matrix(0.917809,0.000000,0.000000,0.917809,-65.63305,158.5521)"
+ width="56.908955"
+ x="184.04552"
+ y="-28.539845" />
+ <rect
+ height="15.829688"
+ id="rect1890"
+ rx="2.9970589"
+ ry="2.9970589"
+ style="fill:url(#linearGradient1141);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient1157);stroke-width:0.96249998;"
+ transform="matrix(0.917809,0.000000,0.000000,0.917809,-65.63305,158.5521)"
+ width="28.796961"
+ x="145.28902"
+ y="-28.227346" />
+ <rect
+ height="3.3627598"
+ id="rect1891"
+ rx="1.6813799"
+ ry="1.6813799"
+ style="fill-opacity:0.16981132;fill-rule:evenodd;stroke-width:0.46326005;"
+ transform="matrix(0.917809,0.000000,0.000000,0.917809,-65.63305,158.5521)"
+ width="49.231453"
+ x="187.88426"
+ y="-21.681381" />
+ </g>
+</svg>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://inkscape.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="64.000000px"
+ height="64.000000px"
+ id="svg2"
+ sodipodi:version="0.32"
+ inkscape:version="0.42"
+ sodipodi:docbase="G:\Projs\Cliparts Stocker\released"
+ sodipodi:docname="unknown_green.svg"
+ inkscape:export-filename="/datas/wiki/unknown_green.png"
+ inkscape:export-xdpi="90.000000"
+ inkscape:export-ydpi="90.000000">
+ <defs
+ id="defs4">
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2842"
+ id="linearGradient1363"
+ x1="25.403513"
+ y1="19.175573"
+ x2="35.541985"
+ y2="49.068703"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-2.402975,4.759656e-3)" />
+ <linearGradient
+ id="linearGradient2900">
+ <stop
+ id="stop2902"
+ offset="0.0000000"
+ style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+ <stop
+ id="stop2904"
+ offset="1.0000000"
+ style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2842">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop2844" />
+ <stop
+ style="stop-color:#c8c8c8;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop2846" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2814">
+ <stop
+ id="stop2816"
+ offset="0.0000000"
+ style="stop-color:#e6e6e6;stop-opacity:1.0000000;" />
+ <stop
+ id="stop2818"
+ offset="1.0000000"
+ style="stop-color:#11661d;stop-opacity:0.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2171">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop2173" />
+ <stop
+ style="stop-color:#a3a5ee;stop-opacity:0.0000000;"
+ offset="1.0000000"
+ id="stop2175" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2160">
+ <stop
+ id="stop2162"
+ offset="0.0000000"
+ style="stop-color:#d3cece;stop-opacity:1.0000000;" />
+ <stop
+ id="stop2164"
+ offset="1.0000000"
+ style="stop-color:#474240;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient1367">
+ <stop
+ id="stop1369"
+ offset="0.0000000"
+ style="stop-color:#f67e36;stop-opacity:1.0000000;" />
+ <stop
+ id="stop1371"
+ offset="1.0000000"
+ style="stop-color:#602604;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient1347">
+ <stop
+ style="stop-color:#f0da27;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop1349" />
+ <stop
+ style="stop-color:#bf4d09;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop1351" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient1315">
+ <stop
+ style="stop-color:#97ff82;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop1317" />
+ <stop
+ style="stop-color:#ceff24;stop-opacity:0.0000000;"
+ offset="1.0000000"
+ id="stop1319" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2122">
+ <stop
+ style="stop-color:#2edc32;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop2124" />
+ <stop
+ style="stop-color:#11661d;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop2126" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient1364">
+ <stop
+ style="stop-color:#236b0d;stop-opacity:1.0000000;"
+ offset="0.00000000"
+ id="stop1366" />
+ <stop
+ style="stop-color:#0a2205;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop1368" />
+ </linearGradient>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient1367"
+ id="radialGradient1402"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.211118e-16,1.330643,-1.347411,2.027373e-5,44.09678,-13.39507)"
+ cx="21.959658"
+ cy="14.921703"
+ fx="21.959658"
+ fy="14.921703"
+ r="27.500000" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2122"
+ id="radialGradient1404"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.211118e-16,1.330643,-1.347411,2.027373e-5,44.09678,-13.39507)"
+ cx="21.959658"
+ cy="14.921703"
+ fx="21.959658"
+ fy="14.921703"
+ r="27.500000" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient1364"
+ id="linearGradient1419"
+ gradientUnits="userSpaceOnUse"
+ x1="74.910713"
+ y1="32.362179"
+ x2="84.910713"
+ y2="47.451466" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2122"
+ id="linearGradient1421"
+ gradientUnits="userSpaceOnUse"
+ x1="73.839287"
+ y1="34.428566"
+ x2="76.875000"
+ y2="43.714283" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient1315"
+ id="linearGradient1423"
+ gradientUnits="userSpaceOnUse"
+ x1="72.946426"
+ y1="35.589287"
+ x2="85.000000"
+ y2="47.375000" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2171"
+ id="linearGradient2177"
+ x1="24.916031"
+ y1="28.824427"
+ x2="39.816792"
+ y2="49.099239"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2122"
+ id="radialGradient2184"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(9.909149e-17,1.088708,-1.102427,1.658760e-5,41.48828,-4.732338)"
+ cx="21.959658"
+ cy="14.921703"
+ fx="21.959658"
+ fy="14.921703"
+ r="27.500000" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient1364"
+ id="linearGradient2189"
+ x1="10.018247"
+ y1="8.6306763"
+ x2="63.487556"
+ y2="63.660282"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2171"
+ id="linearGradient1339"
+ gradientUnits="userSpaceOnUse"
+ x1="24.916031"
+ y1="28.824427"
+ x2="39.816792"
+ y2="49.099239" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2122"
+ id="radialGradient1343"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.521415e-2,1.026125,-0.978137,2.404729e-2,38.83024,-3.575704)"
+ cx="24.764277"
+ cy="16.361967"
+ fx="24.764277"
+ fy="16.361967"
+ r="27.500000" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient1364"
+ id="linearGradient1346"
+ gradientUnits="userSpaceOnUse"
+ x1="10.018247"
+ y1="8.6306763"
+ x2="63.487556"
+ y2="63.660282" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2814"
+ id="radialGradient2812"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.142398e-2,1.098850,-1.843995,1.878760e-2,52.15051,-5.667446)"
+ cx="18.387238"
+ cy="14.046815"
+ fx="18.387238"
+ fy="14.046815"
+ r="27.500000" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient1364"
+ id="linearGradient2832"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-2.841000e-3,-2.841000e-3)"
+ x1="10.018247"
+ y1="8.6306763"
+ x2="63.487556"
+ y2="63.660282" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2842"
+ id="linearGradient2848"
+ x1="-0.56685609"
+ y1="22.651009"
+ x2="-0.33713850"
+ y2="23.858734"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2842"
+ id="linearGradient2864"
+ gradientUnits="userSpaceOnUse"
+ x1="-0.82287467"
+ y1="22.444542"
+ x2="-0.33713850"
+ y2="23.858734" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="8.2031250"
+ inkscape:cx="32.000000"
+ inkscape:cy="32.000000"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:grid-bbox="true"
+ inkscape:grid-points="true"
+ inkscape:window-width="1156"
+ inkscape:window-height="693"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ showguides="false" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title>Green Unknown</dc:title>
+ <dc:date>2005-11-01</dc:date>
+ <dc:creator>
+ <cc:Agent>
+ <dc:title>Jean-Victor Balin</dc:title>
+ </cc:Agent>
+ </dc:creator>
+ <dc:description>jean.victor.balin@gmail.com</dc:description>
+ <cc:license
+ rdf:resource="http://web.resource.org/cc/PublicDomain" />
+ <dc:subject>
+ <rdf:Bag>
+ <rdf:li>icon</rdf:li>
+ </rdf:Bag>
+ </dc:subject>
+ </cc:Work>
+ <cc:License
+ rdf:about="http://web.resource.org/cc/PublicDomain">
+ <cc:permits
+ rdf:resource="http://web.resource.org/cc/Reproduction" />
+ <cc:permits
+ rdf:resource="http://web.resource.org/cc/Distribution" />
+ <cc:permits
+ rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
+ </cc:License>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Calque 1"
+ inkscape:groupmode="layer"
+ id="layer1">
+ <g
+ id="g1354">
+ <path
+ id="path1373"
+ d="M 32.000000,8.6306766 C 19.113097,8.6306766 8.6306766,19.113097 8.6306766,32.000000 C 8.6306766,44.886903 19.113097,55.369323 32.000000,55.369323 C 44.886903,55.369323 55.369323,44.886903 55.369323,32.000000 C 55.369323,19.113097 44.886903,8.6306766 32.000000,8.6306766 z "
+ style="fill:url(#linearGradient1346);fill-opacity:1.0000000;stroke:none;stroke-width:2.0000000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000" />
+ <path
+ id="path1339"
+ d="M 54.500005,32.000000 C 54.500005,44.420003 44.420003,54.500005 32.000000,54.500005 C 19.579997,54.500005 9.4999950,44.420003 9.4999950,32.000000 C 9.4999950,19.579997 19.579997,9.4999950 32.000000,9.4999950 C 44.420003,9.4999950 54.500005,19.579997 54.500005,32.000000 z "
+ style="fill:url(#radialGradient1343);fill-opacity:1.0000000;stroke:none;stroke-width:2.0000000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000" />
+ <path
+ id="path1341"
+ d="M 32.016991,9.1562500 C 22.574792,9.1562500 14.505423,14.865048 11.062500,22.968750 C 16.006322,25.801817 21.393258,27.855853 27.181339,27.593750 C 32.755311,27.279922 37.553510,23.530916 43.236968,23.812500 C 47.451058,23.716455 52.244330,25.294372 54.488550,29.000000 C 53.142630,17.846718 43.657640,9.1562500 32.016991,9.1562500 z "
+ style="fill:url(#radialGradient2812);fill-opacity:1.0000000;stroke:none;stroke-width:2.0000000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000" />
+ <path
+ id="path2827"
+ d="M 32.000000,8.6250000 C 19.113098,8.6250000 8.6250000,19.113097 8.6250000,32.000000 C 8.6250000,44.886904 19.113097,55.375000 32.000000,55.375000 C 44.886904,55.375000 55.375000,44.886903 55.375000,32.000000 C 55.375000,19.113098 44.886903,8.6250000 32.000000,8.6250000 z M 32.000000,9.5000000 C 44.420004,9.4999998 54.500000,19.579997 54.500000,32.000000 C 54.499998,44.420004 44.420003,54.500000 32.000000,54.500000 C 19.579998,54.499998 9.5000000,44.420003 9.5000000,32.000000 C 9.5000000,19.579998 19.579997,9.5000000 32.000000,9.5000000 z "
+ style="fill:url(#linearGradient2832);fill-opacity:1.0000000;stroke:none;stroke-width:2.0000000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000" />
+ <path
+ id="text1353"
+ d="M 32.556888,39.006317 C 32.692760,35.835967 33.100380,35.066018 35.908404,32.892064 C 39.395790,30.219911 39.803410,29.902873 40.120445,29.631129 C 41.705621,28.272407 42.611437,26.189029 42.611437,24.015074 C 42.611437,19.078386 38.625844,15.953318 32.285143,15.953318 C 26.306768,15.953318 22.094721,18.851931 22.094721,23.018677 C 22.094721,25.464376 23.906354,27.230718 26.397344,27.230718 C 28.707171,27.230718 30.292350,25.736121 30.292350,23.607457 C 30.292350,22.384608 29.794150,21.388209 28.843045,20.663558 C 28.027812,20.029488 27.982521,19.984196 27.982521,19.667161 C 27.982521,19.033091 28.978919,18.534892 30.382931,18.534892 C 33.100374,18.534892 34.640263,20.346525 34.640263,23.516876 C 34.640263,25.373795 33.960900,27.683628 32.828632,29.721710 C 30.337643,34.160201 29.975314,35.066023 29.975314,37.104105 C 29.975314,37.557012 30.020605,38.281665 30.111187,39.006317 L 32.556888,39.006317 M 31.424619,41.497309 C 29.069501,41.497309 27.167287,43.399523 27.167287,45.754641 C 27.167287,48.064467 29.069501,50.011973 31.379328,50.011973 C 33.779736,50.011973 35.681951,48.109758 35.681951,45.754641 C 35.681951,43.399523 33.779736,41.497309 31.424619,41.497309"
+ style="font-size:45.290764px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125.00000%;writing-mode:lr-tb;text-anchor:start;fill:url(#linearGradient1363);fill-opacity:1.0000000;stroke:none;stroke-width:1.0000000px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;font-family:Century Schoolbook L" />
+ </g>
+ </g>
+</svg>
<file alias="wpa_gui.png">icons/hicolor/16x16/apps/wpa_gui.png</file>
<file alias="ap.png">icons/hicolor/32x32/apps/ap.png</file>
<file alias="laptop.png">icons/hicolor/32x32/apps/laptop.png</file>
+ <file alias="group.png">icons/hicolor/32x32/apps/group.png</file>
+ <file alias="invitation.png">icons/hicolor/32x32/apps/invitation.png</file>
</qresource>
</RCC>
/*
* wpa_gui - Peers class
- * Copyright (c) 2009, Atheros Communications
+ * Copyright (c) 2009-2010, Atheros Communications
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
peer_role_type,
peer_role_uuid,
peer_role_details,
+ peer_role_ifname,
peer_role_pri_dev_type,
peer_role_ssid,
peer_role_config_methods,
peer_role_dev_passwd_id,
- peer_role_bss_id
+ peer_role_bss_id,
+ peer_role_selected_method,
+ peer_role_selected_pin,
+ peer_role_requested_method,
+ peer_role_network_id
+};
+
+enum selected_method {
+ SEL_METHOD_NONE,
+ SEL_METHOD_PIN_PEER_DISPLAY,
+ SEL_METHOD_PIN_LOCAL_DISPLAY
};
/*
PEER_TYPE_AP,
PEER_TYPE_AP_WPS,
PEER_TYPE_WPS_PIN_NEEDED,
+ PEER_TYPE_P2P,
+ PEER_TYPE_P2P_CLIENT,
+ PEER_TYPE_P2P_GROUP,
+ PEER_TYPE_P2P_PERSISTENT_GROUP_GO,
+ PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT,
+ PEER_TYPE_P2P_INVITATION,
PEER_TYPE_WPS_ER_AP,
PEER_TYPE_WPS_ER_AP_UNCONFIGURED,
PEER_TYPE_WPS_ER_ENROLLEE,
default_icon = new QIcon(":/icons/wpa_gui.svg");
ap_icon = new QIcon(":/icons/ap.svg");
laptop_icon = new QIcon(":/icons/laptop.svg");
+ group_icon = new QIcon(":/icons/group.svg");
+ invitation_icon = new QIcon(":/icons/invitation.svg");
} else {
default_icon = new QIcon(":/icons/wpa_gui.png");
ap_icon = new QIcon(":/icons/ap.png");
laptop_icon = new QIcon(":/icons/laptop.png");
+ group_icon = new QIcon(":/icons/group.png");
+ invitation_icon = new QIcon(":/icons/invitation.png");
}
peers->setModel(&model);
peers->setResizeMode(QListView::Adjust);
+ peers->setDragEnabled(false);
+ peers->setSelectionMode(QAbstractItemView::NoSelection);
peers->setContextMenuPolicy(Qt::CustomContextMenu);
connect(peers, SIGNAL(customContextMenuRequested(const QPoint &)),
this, SLOT(context_menu(const QPoint &)));
wpagui = NULL;
+ hide_ap = false;
}
delete default_icon;
delete ap_icon;
delete laptop_icon;
+ delete group_icon;
+ delete invitation_icon;
}
case PEER_TYPE_WPS_PIN_NEEDED:
title = tr("WPS PIN needed");
break;
+ case PEER_TYPE_P2P:
+ title = tr("P2P Device");
+ break;
+ case PEER_TYPE_P2P_CLIENT:
+ title = tr("P2P Device (group client)");
+ break;
+ case PEER_TYPE_P2P_GROUP:
+ title = tr("P2P Group");
+ break;
+ case PEER_TYPE_P2P_PERSISTENT_GROUP_GO:
+ title = tr("P2P Persistent Group (GO)");
+ break;
+ case PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT:
+ title = tr("P2P Persistent Group (client)");
+ break;
+ case PEER_TYPE_P2P_INVITATION:
+ title = tr("P2P Invitation");
+ break;
case PEER_TYPE_WPS_ER_AP:
title = tr("ER: WPS AP");
break;
if (var.isValid())
config_methods = var.toInt();
+ enum selected_method method = SEL_METHOD_NONE;
+ var = ctx_item->data(peer_role_selected_method);
+ if (var.isValid())
+ method = (enum selected_method) var.toInt();
+
if ((type == PEER_TYPE_ASSOCIATED_STATION ||
type == PEER_TYPE_AP_WPS ||
type == PEER_TYPE_WPS_PIN_NEEDED ||
SLOT(enter_pin()));
}
+ if (type == PEER_TYPE_P2P || type == PEER_TYPE_P2P_CLIENT) {
+ menu->addAction(tr("P2P Connect"), this,
+ SLOT(ctx_p2p_connect()));
+ if (method == SEL_METHOD_NONE &&
+ config_methods > -1 &&
+ config_methods & 0x0080 /* PBC */ &&
+ config_methods != 0x0080)
+ menu->addAction(tr("P2P Connect (PBC)"), this,
+ SLOT(connect_pbc()));
+ if (method == SEL_METHOD_NONE) {
+ menu->addAction(tr("P2P Request PIN"), this,
+ SLOT(ctx_p2p_req_pin()));
+ menu->addAction(tr("P2P Show PIN"), this,
+ SLOT(ctx_p2p_show_pin()));
+ }
+
+ if (config_methods > -1 && (config_methods & 0x0100)) {
+ /* Peer has Keypad */
+ menu->addAction(tr("P2P Display PIN"), this,
+ SLOT(ctx_p2p_display_pin()));
+ }
+
+ if (config_methods > -1 && (config_methods & 0x000c)) {
+ /* Peer has Label or Display */
+ menu->addAction(tr("P2P Enter PIN"), this,
+ SLOT(ctx_p2p_enter_pin()));
+ }
+ }
+
+ if (type == PEER_TYPE_P2P_GROUP) {
+ menu->addAction(tr("Show passphrase"), this,
+ SLOT(ctx_p2p_show_passphrase()));
+ menu->addAction(tr("Remove P2P Group"), this,
+ SLOT(ctx_p2p_remove_group()));
+ }
+
+ if (type == PEER_TYPE_P2P_PERSISTENT_GROUP_GO ||
+ type == PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT ||
+ type == PEER_TYPE_P2P_INVITATION) {
+ menu->addAction(tr("Start group"), this,
+ SLOT(ctx_p2p_start_persistent()));
+ }
+
+ if (type == PEER_TYPE_P2P_PERSISTENT_GROUP_GO ||
+ type == PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT) {
+ menu->addAction(tr("Invite"), this,
+ SLOT(ctx_p2p_invite()));
+ }
+
+ if (type == PEER_TYPE_P2P_INVITATION) {
+ menu->addAction(tr("Ignore"), this,
+ SLOT(ctx_p2p_delete()));
+ }
+
if (type == PEER_TYPE_AP_WPS) {
menu->addAction(tr("Connect (PBC)"), this,
SLOT(connect_pbc()));
ctx_item = NULL;
menu->addAction(QString(tr("Refresh")), this,
SLOT(ctx_refresh()));
+ menu->addAction(tr("Start P2P discovery"), this,
+ SLOT(ctx_p2p_start()));
+ menu->addAction(tr("Stop P2P discovery"), this,
+ SLOT(ctx_p2p_stop()));
+ menu->addAction(tr("P2P listen only"), this,
+ SLOT(ctx_p2p_listen()));
+ menu->addAction(tr("Start P2P group"), this,
+ SLOT(ctx_p2p_start_group()));
+ if (hide_ap)
+ menu->addAction(tr("Show AP entries"), this,
+ SLOT(ctx_show_ap()));
+ else
+ menu->addAction(tr("Hide AP entries"), this,
+ SLOT(ctx_hide_ap()));
}
menu->exec(peers->mapToGlobal(pos));
int peer_type = ctx_item->data(peer_role_type).toInt();
QString uuid;
QString addr;
+ addr = ctx_item->data(peer_role_address).toString();
if (peer_type == PEER_TYPE_WPS_ER_ENROLLEE)
uuid = ctx_item->data(peer_role_uuid).toString();
- else
- addr = ctx_item->data(peer_role_address).toString();
StringQuery input(tr("PIN:"));
input.setWindowTitle(tr("PIN for ") + ctx_item->text());
size_t reply_len;
if (peer_type == PEER_TYPE_WPS_ER_ENROLLEE) {
- snprintf(cmd, sizeof(cmd), "WPS_ER_PIN %s %s",
+ snprintf(cmd, sizeof(cmd), "WPS_ER_PIN %s %s %s",
uuid.toAscii().constData(),
- input.get_string().toAscii().constData());
+ input.get_string().toAscii().constData(),
+ addr.toAscii().constData());
} else {
snprintf(cmd, sizeof(cmd), "WPS_PIN %s %s",
addr.toAscii().constData(),
}
+void Peers::ctx_p2p_start()
+{
+ char reply[20];
+ size_t reply_len;
+ reply_len = sizeof(reply) - 1;
+ if (wpagui->ctrlRequest("P2P_FIND", reply, &reply_len) < 0 ||
+ memcmp(reply, "FAIL", 4) == 0) {
+ QMessageBox msg;
+ msg.setIcon(QMessageBox::Warning);
+ msg.setText("Failed to start P2P discovery.");
+ msg.exec();
+ }
+}
+
+
+void Peers::ctx_p2p_stop()
+{
+ char reply[20];
+ size_t reply_len;
+ reply_len = sizeof(reply) - 1;
+ wpagui->ctrlRequest("P2P_STOP_FIND", reply, &reply_len);
+}
+
+
+void Peers::ctx_p2p_listen()
+{
+ char reply[20];
+ size_t reply_len;
+ reply_len = sizeof(reply) - 1;
+ if (wpagui->ctrlRequest("P2P_LISTEN 3600", reply, &reply_len) < 0 ||
+ memcmp(reply, "FAIL", 4) == 0) {
+ QMessageBox msg;
+ msg.setIcon(QMessageBox::Warning);
+ msg.setText("Failed to start P2P listen.");
+ msg.exec();
+ }
+}
+
+
+void Peers::ctx_p2p_start_group()
+{
+ char reply[20];
+ size_t reply_len;
+ reply_len = sizeof(reply) - 1;
+ if (wpagui->ctrlRequest("P2P_GROUP_ADD", reply, &reply_len) < 0 ||
+ memcmp(reply, "FAIL", 4) == 0) {
+ QMessageBox msg;
+ msg.setIcon(QMessageBox::Warning);
+ msg.setText("Failed to start P2P group.");
+ msg.exec();
+ }
+}
+
+
void Peers::add_station(QString info)
{
QStringList lines = info.split(QRegExp("\\n"));
if ((*it).startsWith("wpsDeviceName="))
name = (*it).mid(pos);
+ else if ((*it).startsWith("p2p_device_name="))
+ name = (*it).mid(pos);
}
if (name.isEmpty())
QStandardItem *item = new QStandardItem(*laptop_icon, name);
if (item) {
+ /* Remove WPS enrollee entry if one is still pending */
+ if (model.rowCount() > 0) {
+ QModelIndexList lst = model.match(model.index(0, 0),
+ peer_role_address,
+ lines[0]);
+ for (int i = 0; i < lst.size(); i++) {
+ QStandardItem *item;
+ item = model.itemFromIndex(lst[i]);
+ if (item == NULL)
+ continue;
+ int type = item->data(peer_role_type).toInt();
+ if (type == PEER_TYPE_WPS_ENROLLEE) {
+ model.removeRow(lst[i].row());
+ break;
+ }
+ }
+ }
+
item->setData(lines[0], peer_role_address);
item->setData(PEER_TYPE_ASSOCIATED_STATION,
peer_role_type);
}
+void Peers::add_p2p_group_client(QStandardItem * /*parent*/, QString params)
+{
+ /*
+ * dev=02:b5:64:63:30:63 iface=02:b5:64:63:30:63 dev_capab=0x0
+ * dev_type=1-0050f204-1 dev_name='Wireless Client'
+ * config_methods=0x8c
+ */
+
+ QStringList items =
+ params.split(QRegExp(" (?=[^']*('[^']*'[^']*)*$)"));
+ QString addr = "";
+ QString name = "";
+ int config_methods = 0;
+ QString dev_type;
+
+ for (int i = 0; i < items.size(); i++) {
+ QString str = items.at(i);
+ int pos = str.indexOf('=') + 1;
+ if (str.startsWith("dev_name='"))
+ name = str.section('\'', 1, -2);
+ else if (str.startsWith("config_methods="))
+ config_methods =
+ str.section('=', 1).toInt(0, 0);
+ else if (str.startsWith("dev="))
+ addr = str.mid(pos);
+ else if (str.startsWith("dev_type=") && dev_type.isEmpty())
+ dev_type = str.mid(pos);
+ }
+
+ QStandardItem *item = find_addr(addr);
+ if (item)
+ return;
+
+ item = new QStandardItem(*default_icon, name);
+ if (item) {
+ /* TODO: indicate somehow the relationship to the group owner
+ * (parent) */
+ item->setData(addr, peer_role_address);
+ item->setData(config_methods, peer_role_config_methods);
+ item->setData(PEER_TYPE_P2P_CLIENT, peer_role_type);
+ if (!dev_type.isEmpty())
+ item->setData(dev_type, peer_role_pri_dev_type);
+ item->setData(items.join(QString("\n")), peer_role_details);
+ item->setToolTip(ItemType(PEER_TYPE_P2P_CLIENT));
+ model.appendRow(item);
+ }
+}
+
+
void Peers::remove_bss(int id)
{
if (model.rowCount() == 0)
char reply[2048];
size_t reply_len;
+ if (hide_ap)
+ return false;
+
reply_len = sizeof(reply) - 1;
if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0)
return false;
if (!ssid.isEmpty())
item->setData(ssid, peer_role_ssid);
model.appendRow(item);
+
+ lines = bss.split(QRegExp("\\n"));
+ for (QStringList::Iterator it = lines.begin();
+ it != lines.end(); it++) {
+ if ((*it).startsWith("p2p_group_client:"))
+ add_p2p_group_client(item,
+ (*it).mid(18));
+ }
}
return true;
}
+void Peers::add_persistent(int id, const char *ssid, const char *bssid)
+{
+ char cmd[100];
+ char reply[100];
+ size_t reply_len;
+ int mode;
+
+ snprintf(cmd, sizeof(cmd), "GET_NETWORK %d mode", id);
+ if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0)
+ return;
+ reply[reply_len] = '\0';
+ mode = atoi(reply);
+
+ QString name = ssid;
+ name = '[' + name + ']';
+
+ QStandardItem *item = new QStandardItem(*group_icon, name);
+ if (!item)
+ return;
+
+ int type;
+ if (mode == 3)
+ type = PEER_TYPE_P2P_PERSISTENT_GROUP_GO;
+ else
+ type = PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT;
+ item->setData(type, peer_role_type);
+ item->setToolTip(ItemType(type));
+ item->setData(ssid, peer_role_ssid);
+ if (bssid && strcmp(bssid, "any") == 0)
+ bssid = NULL;
+ if (bssid)
+ item->setData(bssid, peer_role_address);
+ item->setData(id, peer_role_network_id);
+ item->setBackground(Qt::BDiagPattern);
+
+ model.appendRow(item);
+}
+
+
+void Peers::add_persistent_groups()
+{
+ char buf[2048], *start, *end, *id, *ssid, *bssid, *flags;
+ size_t len;
+
+ len = sizeof(buf) - 1;
+ if (wpagui->ctrlRequest("LIST_NETWORKS", buf, &len) < 0)
+ return;
+
+ buf[len] = '\0';
+ start = strchr(buf, '\n');
+ if (start == NULL)
+ return;
+ start++;
+
+ while (*start) {
+ bool last = false;
+ end = strchr(start, '\n');
+ if (end == NULL) {
+ last = true;
+ end = start;
+ while (end[0] && end[1])
+ end++;
+ }
+ *end = '\0';
+
+ id = start;
+ ssid = strchr(id, '\t');
+ if (ssid == NULL)
+ break;
+ *ssid++ = '\0';
+ bssid = strchr(ssid, '\t');
+ if (bssid == NULL)
+ break;
+ *bssid++ = '\0';
+ flags = strchr(bssid, '\t');
+ if (flags == NULL)
+ break;
+ *flags++ = '\0';
+
+ if (strstr(flags, "[DISABLED][P2P-PERSISTENT]"))
+ add_persistent(atoi(id), ssid, bssid);
+
+ if (last)
+ break;
+ start = end + 1;
+ }
+}
+
+
void Peers::update_peers()
{
model.clear();
add_stations();
add_scan_results();
+ add_persistent_groups();
}
}
+QStandardItem * Peers::find_addr_type(QString addr, int type)
+{
+ if (model.rowCount() == 0)
+ return NULL;
+
+ QModelIndexList lst = model.match(model.index(0, 0), peer_role_address,
+ addr);
+ for (int i = 0; i < lst.size(); i++) {
+ QStandardItem *item = model.itemFromIndex(lst[i]);
+ if (item->data(peer_role_type).toInt() == type)
+ return item;
+ }
+ return NULL;
+}
+
+
QStandardItem * Peers::find_uuid(QString uuid)
{
if (model.rowCount() == 0)
return;
QModelIndexList lst = model.match(model.index(0, 0),
- peer_role_address, addr);
+ peer_role_address, addr, -1);
for (int i = 0; i < lst.size(); i++) {
QStandardItem *item = model.itemFromIndex(lst[i]);
if (item && item->data(peer_role_type).toInt() ==
- PEER_TYPE_ASSOCIATED_STATION)
+ PEER_TYPE_ASSOCIATED_STATION) {
model.removeRow(lst[i].row());
+ break;
+ }
+ }
+ return;
+ }
+
+ if (text.startsWith(P2P_EVENT_DEVICE_FOUND)) {
+ /*
+ * P2P-DEVICE-FOUND 02:b5:64:63:30:63
+ * p2p_dev_addr=02:b5:64:63:30:63 pri_dev_type=1-0050f204-1
+ * name='Wireless Client' config_methods=0x84 dev_capab=0x21
+ * group_capab=0x0
+ */
+ QStringList items =
+ text.split(QRegExp(" (?=[^']*('[^']*'[^']*)*$)"));
+ QString addr = items[1];
+ QString name = "";
+ QString pri_dev_type;
+ int config_methods = 0;
+ for (int i = 0; i < items.size(); i++) {
+ QString str = items.at(i);
+ if (str.startsWith("name='"))
+ name = str.section('\'', 1, -2);
+ else if (str.startsWith("config_methods="))
+ config_methods =
+ str.section('=', 1).toInt(0, 0);
+ else if (str.startsWith("pri_dev_type="))
+ pri_dev_type = str.section('=', 1);
+ }
+
+ QStandardItem *item = find_addr(addr);
+ if (item) {
+ int type = item->data(peer_role_type).toInt();
+ if (type == PEER_TYPE_P2P)
+ return;
+ }
+
+ item = new QStandardItem(*default_icon, name);
+ if (item) {
+ item->setData(addr, peer_role_address);
+ item->setData(config_methods,
+ peer_role_config_methods);
+ item->setData(PEER_TYPE_P2P, peer_role_type);
+ if (!pri_dev_type.isEmpty())
+ item->setData(pri_dev_type,
+ peer_role_pri_dev_type);
+ item->setData(items.join(QString("\n")),
+ peer_role_details);
+ item->setToolTip(ItemType(PEER_TYPE_P2P));
+ model.appendRow(item);
+ }
+
+ item = find_addr_type(addr,
+ PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT);
+ if (item)
+ item->setBackground(Qt::NoBrush);
+ }
+
+ if (text.startsWith(P2P_EVENT_GROUP_STARTED)) {
+ /* P2P-GROUP-STARTED wlan0-p2p-0 GO ssid="DIRECT-3F"
+ * passphrase="YOyTkxID" go_dev_addr=02:40:61:c2:f3:b7
+ * [PERSISTENT] */
+ QStringList items = text.split(' ');
+ if (items.size() < 4)
+ return;
+
+ int pos = text.indexOf(" ssid=\"");
+ if (pos < 0)
+ return;
+ QString ssid = text.mid(pos + 7);
+ pos = ssid.indexOf(" passphrase=\"");
+ if (pos < 0)
+ pos = ssid.indexOf(" psk=");
+ if (pos >= 0)
+ ssid.truncate(pos);
+ pos = ssid.lastIndexOf('"');
+ if (pos >= 0)
+ ssid.truncate(pos);
+
+ QStandardItem *item = new QStandardItem(*group_icon, ssid);
+ if (item) {
+ item->setData(PEER_TYPE_P2P_GROUP, peer_role_type);
+ item->setData(items[1], peer_role_ifname);
+ QString details;
+ if (items[2] == "GO") {
+ details = tr("P2P GO for interface ") +
+ items[1];
+ } else {
+ details = tr("P2P client for interface ") +
+ items[1];
+ }
+ if (text.contains(" [PERSISTENT]"))
+ details += "\nPersistent group";
+ item->setData(details, peer_role_details);
+ item->setToolTip(ItemType(PEER_TYPE_P2P_GROUP));
+ model.appendRow(item);
+ }
+ }
+
+ if (text.startsWith(P2P_EVENT_GROUP_REMOVED)) {
+ /* P2P-GROUP-REMOVED wlan0-p2p-0 GO */
+ QStringList items = text.split(' ');
+ if (items.size() < 2)
+ return;
+
+ if (model.rowCount() == 0)
+ return;
+
+ QModelIndexList lst = model.match(model.index(0, 0),
+ peer_role_ifname, items[1]);
+ for (int i = 0; i < lst.size(); i++)
+ model.removeRow(lst[i].row());
+ return;
+ }
+
+ if (text.startsWith(P2P_EVENT_PROV_DISC_SHOW_PIN)) {
+ /* P2P-PROV-DISC-SHOW-PIN 02:40:61:c2:f3:b7 12345670 */
+ QStringList items = text.split(' ');
+ if (items.size() < 3)
+ return;
+ QString addr = items[1];
+ QString pin = items[2];
+
+ QStandardItem *item = find_addr_type(addr, PEER_TYPE_P2P);
+ if (item == NULL)
+ return;
+ item->setData(SEL_METHOD_PIN_LOCAL_DISPLAY,
+ peer_role_selected_method);
+ item->setData(pin, peer_role_selected_pin);
+ QVariant var = item->data(peer_role_requested_method);
+ if (var.isValid() &&
+ var.toInt() == SEL_METHOD_PIN_LOCAL_DISPLAY) {
+ ctx_item = item;
+ ctx_p2p_display_pin_pd();
+ }
+ return;
+ }
+
+ if (text.startsWith(P2P_EVENT_PROV_DISC_ENTER_PIN)) {
+ /* P2P-PROV-DISC-ENTER-PIN 02:40:61:c2:f3:b7 */
+ QStringList items = text.split(' ');
+ if (items.size() < 2)
+ return;
+ QString addr = items[1];
+
+ QStandardItem *item = find_addr_type(addr, PEER_TYPE_P2P);
+ if (item == NULL)
+ return;
+ item->setData(SEL_METHOD_PIN_PEER_DISPLAY,
+ peer_role_selected_method);
+ QVariant var = item->data(peer_role_requested_method);
+ if (var.isValid() &&
+ var.toInt() == SEL_METHOD_PIN_PEER_DISPLAY) {
+ ctx_item = item;
+ ctx_p2p_connect();
}
return;
}
+ if (text.startsWith(P2P_EVENT_INVITATION_RECEIVED)) {
+ /* P2P-INVITATION-RECEIVED sa=02:f0:bc:44:87:62 persistent=4 */
+ QStringList items = text.split(' ');
+ if (items.size() < 3)
+ return;
+ if (!items[1].startsWith("sa=") ||
+ !items[2].startsWith("persistent="))
+ return;
+ QString addr = items[1].mid(3);
+ int id = items[2].mid(11).toInt();
+
+ char cmd[100];
+ char reply[100];
+ size_t reply_len;
+
+ snprintf(cmd, sizeof(cmd), "GET_NETWORK %d ssid", id);
+ reply_len = sizeof(reply) - 1;
+ if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0)
+ return;
+ reply[reply_len] = '\0';
+ QString name;
+ char *pos = strrchr(reply, '"');
+ if (pos && reply[0] == '"') {
+ *pos = '\0';
+ name = reply + 1;
+ } else
+ name = reply;
+
+ QStandardItem *item;
+ item = find_addr_type(addr, PEER_TYPE_P2P_INVITATION);
+ if (item)
+ model.removeRow(item->row());
+
+ item = new QStandardItem(*invitation_icon, name);
+ if (!item)
+ return;
+ item->setData(PEER_TYPE_P2P_INVITATION, peer_role_type);
+ item->setToolTip(ItemType(PEER_TYPE_P2P_INVITATION));
+ item->setData(addr, peer_role_address);
+ item->setData(id, peer_role_network_id);
+
+ model.appendRow(item);
+
+ enable_persistent(id);
+
+ return;
+ }
+
+ if (text.startsWith(P2P_EVENT_INVITATION_RESULT)) {
+ /* P2P-INVITATION-RESULT status=1 */
+ /* TODO */
+ return;
+ }
+
if (text.startsWith(WPS_EVENT_ER_AP_ADD)) {
/*
* WPS-ER-AP-ADD 87654321-9abc-def0-1234-56789abc0002
int dev_passwd_id = items[5].toInt();
QString name;
+ QStandardItem *item = find_addr(addr);
+ if (item) {
+ int type = item->data(peer_role_type).toInt();
+ if (type == PEER_TYPE_ASSOCIATED_STATION)
+ return; /* already associated */
+ }
+
int pos = text.indexOf('[');
if (pos >= 0) {
int pos2 = text.lastIndexOf(']');
if (name.isEmpty())
name = addr;
- QStandardItem *item;
-
item = find_uuid(uuid);
if (item) {
QVariant var = item->data(peer_role_config_methods);
}
+void Peers::ctx_p2p_connect()
+{
+ if (ctx_item == NULL)
+ return;
+ QString addr = ctx_item->data(peer_role_address).toString();
+ QString arg;
+ int config_methods =
+ ctx_item->data(peer_role_config_methods).toInt();
+ enum selected_method method = SEL_METHOD_NONE;
+ QVariant var = ctx_item->data(peer_role_selected_method);
+ if (var.isValid())
+ method = (enum selected_method) var.toInt();
+ if (method == SEL_METHOD_PIN_LOCAL_DISPLAY) {
+ arg = ctx_item->data(peer_role_selected_pin).toString();
+ char cmd[100];
+ char reply[100];
+ size_t reply_len;
+ snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s display",
+ addr.toAscii().constData(),
+ arg.toAscii().constData());
+ reply_len = sizeof(reply) - 1;
+ if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
+ QMessageBox msg;
+ msg.setIcon(QMessageBox::Warning);
+ msg.setText("Failed to initiate P2P connect.");
+ msg.exec();
+ return;
+ }
+ QMessageBox::information(this,
+ tr("PIN for ") + ctx_item->text(),
+ tr("Enter the following PIN on the\n"
+ "peer device: ") + arg);
+ } else if (method == SEL_METHOD_PIN_PEER_DISPLAY) {
+ StringQuery input(tr("PIN from peer display:"));
+ input.setWindowTitle(tr("PIN for ") + ctx_item->text());
+ if (input.exec() != QDialog::Accepted)
+ return;
+ arg = input.get_string();
+ } else if (config_methods == 0x0080 /* PBC */) {
+ arg = "pbc";
+ } else {
+ StringQuery input(tr("PIN:"));
+ input.setWindowTitle(tr("PIN for ") + ctx_item->text());
+ if (input.exec() != QDialog::Accepted)
+ return;
+ arg = input.get_string();
+ }
+
+ char cmd[100];
+ char reply[100];
+ size_t reply_len;
+ snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s",
+ addr.toAscii().constData(),
+ arg.toAscii().constData());
+ reply_len = sizeof(reply) - 1;
+ if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
+ QMessageBox msg;
+ msg.setIcon(QMessageBox::Warning);
+ msg.setText("Failed to initiate P2P connect.");
+ msg.exec();
+ }
+}
+
+
+void Peers::ctx_p2p_req_pin()
+{
+ if (ctx_item == NULL)
+ return;
+ QString addr = ctx_item->data(peer_role_address).toString();
+ ctx_item->setData(SEL_METHOD_PIN_PEER_DISPLAY,
+ peer_role_requested_method);
+
+ char cmd[100];
+ char reply[100];
+ size_t reply_len;
+ snprintf(cmd, sizeof(cmd), "P2P_PROV_DISC %s display",
+ addr.toAscii().constData());
+ reply_len = sizeof(reply) - 1;
+ if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
+ QMessageBox msg;
+ msg.setIcon(QMessageBox::Warning);
+ msg.setText(tr("Failed to request PIN from peer."));
+ msg.exec();
+ }
+}
+
+
+void Peers::ctx_p2p_show_pin()
+{
+ if (ctx_item == NULL)
+ return;
+ QString addr = ctx_item->data(peer_role_address).toString();
+ ctx_item->setData(SEL_METHOD_PIN_LOCAL_DISPLAY,
+ peer_role_requested_method);
+
+ char cmd[100];
+ char reply[100];
+ size_t reply_len;
+ snprintf(cmd, sizeof(cmd), "P2P_PROV_DISC %s keypad",
+ addr.toAscii().constData());
+ reply_len = sizeof(reply) - 1;
+ if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
+ QMessageBox msg;
+ msg.setIcon(QMessageBox::Warning);
+ msg.setText(tr("Failed to request peer to enter PIN."));
+ msg.exec();
+ }
+}
+
+
+void Peers::ctx_p2p_display_pin()
+{
+ if (ctx_item == NULL)
+ return;
+ QString addr = ctx_item->data(peer_role_address).toString();
+
+ char cmd[100];
+ char reply[100];
+ size_t reply_len;
+ snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s pin",
+ addr.toAscii().constData());
+ reply_len = sizeof(reply) - 1;
+ if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
+ QMessageBox msg;
+ msg.setIcon(QMessageBox::Warning);
+ msg.setText("Failed to initiate P2P connect.");
+ msg.exec();
+ return;
+ }
+ reply[reply_len] = '\0';
+ QMessageBox::information(this,
+ tr("PIN for ") + ctx_item->text(),
+ tr("Enter the following PIN on the\n"
+ "peer device: ") + reply);
+}
+
+
+void Peers::ctx_p2p_display_pin_pd()
+{
+ if (ctx_item == NULL)
+ return;
+ QString addr = ctx_item->data(peer_role_address).toString();
+ QString arg = ctx_item->data(peer_role_selected_pin).toString();
+
+ char cmd[100];
+ char reply[100];
+ size_t reply_len;
+ snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s display",
+ addr.toAscii().constData(),
+ arg.toAscii().constData());
+ reply_len = sizeof(reply) - 1;
+ if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
+ QMessageBox msg;
+ msg.setIcon(QMessageBox::Warning);
+ msg.setText("Failed to initiate P2P connect.");
+ msg.exec();
+ return;
+ }
+ reply[reply_len] = '\0';
+ QMessageBox::information(this,
+ tr("PIN for ") + ctx_item->text(),
+ tr("Enter the following PIN on the\n"
+ "peer device: ") + arg);
+}
+
+
+void Peers::ctx_p2p_enter_pin()
+{
+ if (ctx_item == NULL)
+ return;
+ QString addr = ctx_item->data(peer_role_address).toString();
+ QString arg;
+
+ StringQuery input(tr("PIN from peer:"));
+ input.setWindowTitle(tr("PIN for ") + ctx_item->text());
+ if (input.exec() != QDialog::Accepted)
+ return;
+ arg = input.get_string();
+
+ char cmd[100];
+ char reply[100];
+ size_t reply_len;
+ snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s keypad",
+ addr.toAscii().constData(),
+ arg.toAscii().constData());
+ reply_len = sizeof(reply) - 1;
+ if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
+ QMessageBox msg;
+ msg.setIcon(QMessageBox::Warning);
+ msg.setText("Failed to initiate P2P connect.");
+ msg.exec();
+ }
+}
+
+
+void Peers::ctx_p2p_remove_group()
+{
+ if (ctx_item == NULL)
+ return;
+ char cmd[100];
+ char reply[100];
+ size_t reply_len;
+ snprintf(cmd, sizeof(cmd), "P2P_GROUP_REMOVE %s",
+ ctx_item->data(peer_role_ifname).toString().toAscii().
+ constData());
+ reply_len = sizeof(reply) - 1;
+ if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
+ QMessageBox msg;
+ msg.setIcon(QMessageBox::Warning);
+ msg.setText("Failed to remove P2P Group.");
+ msg.exec();
+ }
+}
+
+
void Peers::closeEvent(QCloseEvent *)
{
if (wpagui) {
info += "\n";
}
+ var = ctx_item->data(peer_role_selected_method);
+ if (var.isValid()) {
+ enum selected_method method =
+ (enum selected_method) var.toInt();
+ switch (method) {
+ case SEL_METHOD_NONE:
+ break;
+ case SEL_METHOD_PIN_PEER_DISPLAY:
+ info += tr("Selected Method: PIN on peer display\n");
+ break;
+ case SEL_METHOD_PIN_LOCAL_DISPLAY:
+ info += tr("Selected Method: PIN on local display\n");
+ break;
+ }
+ }
+
+ var = ctx_item->data(peer_role_selected_pin);
+ if (var.isValid()) {
+ info += tr("PIN to enter on peer: ") + var.toString() + "\n";
+ }
+
var = ctx_item->data(peer_role_dev_passwd_id);
if (var.isValid()) {
info += tr("Device Password ID: ") + var.toString();
snprintf(cmd, sizeof(cmd), "WPS_ER_PBC %s",
ctx_item->data(peer_role_uuid).toString().toAscii().
constData());
+ } else if (peer_type == PEER_TYPE_P2P ||
+ peer_type == PEER_TYPE_P2P_CLIENT) {
+ snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s pbc",
+ ctx_item->data(peer_role_address).toString().
+ toAscii().constData());
} else {
snprintf(cmd, sizeof(cmd), "WPS_PBC");
}
msg.exec();
}
}
+
+
+void Peers::ctx_hide_ap()
+{
+ hide_ap = true;
+
+ if (model.rowCount() == 0)
+ return;
+
+ do {
+ QModelIndexList lst;
+ lst = model.match(model.index(0, 0),
+ peer_role_type, PEER_TYPE_AP);
+ if (lst.size() == 0) {
+ lst = model.match(model.index(0, 0),
+ peer_role_type, PEER_TYPE_AP_WPS);
+ if (lst.size() == 0)
+ break;
+ }
+
+ model.removeRow(lst[0].row());
+ } while (1);
+}
+
+
+void Peers::ctx_show_ap()
+{
+ hide_ap = false;
+ add_scan_results();
+}
+
+
+void Peers::ctx_p2p_show_passphrase()
+{
+ char reply[64];
+ size_t reply_len;
+
+ reply_len = sizeof(reply) - 1;
+ if (wpagui->ctrlRequest("P2P_GET_PASSPHRASE", reply, &reply_len) < 0 ||
+ memcmp(reply, "FAIL", 4) == 0) {
+ QMessageBox msg;
+ msg.setIcon(QMessageBox::Warning);
+ msg.setText("Failed to get P2P group passphrase.");
+ msg.exec();
+ } else {
+ reply[reply_len] = '\0';
+ QMessageBox::information(this, tr("Passphrase"),
+ tr("P2P group passphrase:\n") +
+ reply);
+ }
+}
+
+
+void Peers::ctx_p2p_start_persistent()
+{
+ if (ctx_item == NULL)
+ return;
+
+ char cmd[100];
+ char reply[100];
+ size_t reply_len;
+
+ snprintf(cmd, sizeof(cmd), "P2P_GROUP_ADD persistent=%d",
+ ctx_item->data(peer_role_network_id).toInt());
+ if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0 ||
+ memcmp(reply, "FAIL", 4) == 0) {
+ QMessageBox msg;
+ msg.setIcon(QMessageBox::Warning);
+ msg.setText(tr("Failed to start persistent P2P Group."));
+ msg.exec();
+ } else if (ctx_item->data(peer_role_type).toInt() ==
+ PEER_TYPE_P2P_INVITATION)
+ model.removeRow(ctx_item->row());
+}
+
+
+void Peers::ctx_p2p_invite()
+{
+ if (ctx_item == NULL)
+ return;
+
+ char cmd[100];
+ char reply[100];
+ size_t reply_len;
+
+ snprintf(cmd, sizeof(cmd), "P2P_INVITE persistent=%d",
+ ctx_item->data(peer_role_network_id).toInt());
+ if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0 ||
+ memcmp(reply, "FAIL", 4) == 0) {
+ QMessageBox msg;
+ msg.setIcon(QMessageBox::Warning);
+ msg.setText(tr("Failed to invite peer to start persistent "
+ "P2P Group."));
+ msg.exec();
+ }
+}
+
+
+void Peers::ctx_p2p_delete()
+{
+ if (ctx_item == NULL)
+ return;
+ model.removeRow(ctx_item->row());
+}
+
+
+void Peers::enable_persistent(int id)
+{
+ if (model.rowCount() == 0)
+ return;
+
+ QModelIndexList lst = model.match(model.index(0, 0),
+ peer_role_network_id, id);
+ for (int i = 0; i < lst.size(); i++) {
+ QStandardItem *item = model.itemFromIndex(lst[i]);
+ int type = item->data(peer_role_type).toInt();
+ if (type == PEER_TYPE_P2P_PERSISTENT_GROUP_GO ||
+ type == PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT)
+ item->setBackground(Qt::NoBrush);
+ }
+}
/*
* wpa_gui - Peers class
- * Copyright (c) 2009, Atheros Communications
+ * Copyright (c) 2009-2010, Atheros Communications
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
virtual void connect_pbc();
virtual void learn_ap_config();
virtual void ctx_refresh();
+ virtual void ctx_p2p_start();
+ virtual void ctx_p2p_stop();
+ virtual void ctx_p2p_listen();
+ virtual void ctx_p2p_start_group();
+ virtual void ctx_p2p_remove_group();
+ virtual void ctx_p2p_connect();
+ virtual void ctx_p2p_req_pin();
+ virtual void ctx_p2p_show_pin();
+ virtual void ctx_p2p_display_pin();
+ virtual void ctx_p2p_display_pin_pd();
+ virtual void ctx_p2p_enter_pin();
virtual void properties();
+ virtual void ctx_hide_ap();
+ virtual void ctx_show_ap();
+ virtual void ctx_p2p_show_passphrase();
+ virtual void ctx_p2p_start_persistent();
+ virtual void ctx_p2p_invite();
+ virtual void ctx_p2p_delete();
protected slots:
virtual void languageChange();
bool add_bss(const char *cmd);
void remove_bss(int id);
void add_scan_results();
+ void add_persistent(int id, const char *ssid, const char *bssid);
+ void add_persistent_groups();
void update_peers();
QStandardItem * find_addr(QString addr);
+ QStandardItem * find_addr_type(QString addr, int type);
+ void add_p2p_group_client(QStandardItem *parent, QString params);
QStandardItem * find_uuid(QString uuid);
void done(int r);
void remove_enrollee_uuid(QString uuid);
QString ItemType(int type);
+ void enable_persistent(int id);
WpaGui *wpagui;
QStandardItemModel model;
QIcon *default_icon;
QIcon *ap_icon;
QIcon *laptop_icon;
+ QIcon *group_icon;
+ QIcon *invitation_icon;
QStandardItem *ctx_item;
+
+ bool hide_ap;
};
#endif /* PEERS_H */
/*
* wpa_gui - WpaGui class
- * Copyright (c) 2005-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2005-2011, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
break;
*flags++ = '\0';
+ if (strstr(flags, "[DISABLED][P2P-PERSISTENT]")) {
+ if (last)
+ break;
+ start = end + 1;
+ continue;
+ }
+
QString network(id);
network.append(": ");
network.append(ssid);
void WpaGui::helpAbout()
{
QMessageBox::about(this, "wpa_gui for wpa_supplicant",
- "Copyright (c) 2003-2010,\n"
+ "Copyright (c) 2003-2011,\n"
"Jouni Malinen <j@w1.fi>\n"
"and contributors.\n"
"\n"
if (textStatus->text() == "INACTIVE" ||
textStatus->text() == "DISCONNECTED")
wpaguiTab->setCurrentWidget(wpsTab);
+ } else if (str_match(pos, WPS_EVENT_AP_AVAILABLE_AUTH)) {
+ showTrayMessage(QSystemTrayIcon::Information, 3,
+ "Wi-Fi Protected Setup (WPS) AP\n"
+ "indicating this client is authorized.");
+ wpsStatusText->setText("WPS AP indicating this client is "
+ "authorized");
+ if (textStatus->text() == "INACTIVE" ||
+ textStatus->text() == "DISCONNECTED")
+ wpaguiTab->setCurrentWidget(wpsTab);
} else if (str_match(pos, WPS_EVENT_AP_AVAILABLE)) {
wpsStatusText->setText(tr("WPS AP detected"));
} else if (str_match(pos, WPS_EVENT_OVERLAP)) {
.moc
.obj
.ui
-Makefile
-wpa_gui
}
-void wpa_supplicant_event(void *ctx, wpa_event_type event,
+void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
union wpa_event_data *data)
{
struct wpa_priv_interface *iface = ctx;
}
-static void wpa_priv_terminate(int sig, void *eloop_ctx, void *signal_ctx)
+static void wpa_priv_terminate(int sig, void *signal_ctx)
{
wpa_printf(MSG_DEBUG, "wpa_priv termination requested");
eloop_terminate();
/*
* WPA Supplicant
- * Copyright (c) 2003-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
#include "includes.h"
#include "common.h"
+#include "crypto/random.h"
+#include "crypto/sha1.h"
#include "eapol_supp/eapol_supp_sm.h"
#include "eap_peer/eap.h"
#include "eap_server/eap_methods.h"
#include "rsn_supp/preauth.h"
#include "rsn_supp/pmksa_cache.h"
#include "common/wpa_ctrl.h"
-#include "mlme.h"
#include "common/ieee802_11_defs.h"
+#include "p2p/p2p.h"
#include "blacklist.h"
#include "wpas_glue.h"
#include "wps_supplicant.h"
#include "ibss_rsn.h"
#include "sme.h"
+#include "gas_query.h"
#include "ap.h"
+#include "p2p_supplicant.h"
#include "notify.h"
#include "bgscan.h"
#include "bss.h"
#include "scan.h"
+#include "offchannel.h"
const char *wpa_supplicant_version =
"wpa_supplicant v" VERSION_STR "\n"
-"Copyright (c) 2003-2010, Jouni Malinen <j@w1.fi> and contributors";
+"Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi> and contributors";
const char *wpa_supplicant_license =
"This program is free software. You can distribute it and/or modify it\n"
continue;
set = 1;
- wpa_drv_set_key(wpa_s, WPA_ALG_WEP,
- (u8 *) "\xff\xff\xff\xff\xff\xff",
- i, i == ssid->wep_tx_keyidx, (u8 *) "", 0,
+ wpa_drv_set_key(wpa_s, WPA_ALG_WEP, NULL,
+ i, i == ssid->wep_tx_keyidx, NULL, 0,
ssid->wep_key[i], ssid->wep_key_len[i]);
}
* sending unicast and multicast packets. */
if (ssid->mode != WPAS_MODE_IBSS) {
- wpa_printf(MSG_INFO, "WPA: Invalid mode %d (not IBSS/ad-hoc) "
- "for WPA-None", ssid->mode);
+ wpa_msg(wpa_s, MSG_INFO, "WPA: Invalid mode %d (not "
+ "IBSS/ad-hoc) for WPA-None", ssid->mode);
return -1;
}
if (!ssid->psk_set) {
- wpa_printf(MSG_INFO, "WPA: No PSK configured for WPA-None");
+ wpa_msg(wpa_s, MSG_INFO, "WPA: No PSK configured for "
+ "WPA-None");
return -1;
}
alg = WPA_ALG_TKIP;
break;
default:
- wpa_printf(MSG_INFO, "WPA: Invalid group cipher %d for "
- "WPA-None", wpa_s->group_cipher);
+ wpa_msg(wpa_s, MSG_INFO, "WPA: Invalid group cipher %d for "
+ "WPA-None", wpa_s->group_cipher);
return -1;
}
/* TODO: should actually remember the previously used seq#, both for TX
* and RX from each STA.. */
- return wpa_drv_set_key(wpa_s, alg, (u8 *) "\xff\xff\xff\xff\xff\xff",
- 0, 1, seq, 6, key, keylen);
+ return wpa_drv_set_key(wpa_s, alg, NULL, 0, 1, seq, 6, key, keylen);
}
wpa_sm_notify_disassoc(wpa_s->wpa);
wpa_supplicant_disassociate(wpa_s, WLAN_REASON_DEAUTH_LEAVING);
wpa_s->reassociate = 1;
- wpa_supplicant_req_scan(wpa_s, 0, 0);
+
+ /*
+ * If we timed out, the AP or the local radio may be busy.
+ * So, wait a second until scanning again.
+ */
+ wpa_supplicant_req_scan(wpa_s, 1, 0);
}
(wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED))
return;
- wpa_msg(wpa_s, MSG_DEBUG, "Setting authentication timeout: %d sec "
+ wpa_dbg(wpa_s, MSG_DEBUG, "Setting authentication timeout: %d sec "
"%d usec", sec, usec);
eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL);
eloop_register_timeout(sec, usec, wpa_supplicant_timeout, wpa_s, NULL);
*/
void wpa_supplicant_cancel_auth_timeout(struct wpa_supplicant *wpa_s)
{
- wpa_msg(wpa_s, MSG_DEBUG, "Cancelling authentication timeout");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling authentication timeout");
eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL);
wpa_blacklist_del(wpa_s, wpa_s->bssid);
}
}
+void free_hw_features(struct wpa_supplicant *wpa_s)
+{
+ int i;
+ if (wpa_s->hw.modes == NULL)
+ return;
+
+ for (i = 0; i < wpa_s->hw.num_modes; i++) {
+ os_free(wpa_s->hw.modes[i].channels);
+ os_free(wpa_s->hw.modes[i].rates);
+ }
+
+ os_free(wpa_s->hw.modes);
+ wpa_s->hw.modes = NULL;
+}
+
+
static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
{
bgscan_deinit(wpa_s);
rsn_preauth_deinit(wpa_s->wpa);
+#ifdef CONFIG_TDLS
+ wpa_tdls_deinit(wpa_s->wpa);
+#endif /* CONFIG_TDLS */
+
pmksa_candidate_free(wpa_s->wpa);
wpa_sm_deinit(wpa_s->wpa);
wpa_s->wpa = NULL;
wpa_supplicant_cancel_scan(wpa_s);
wpa_supplicant_cancel_auth_timeout(wpa_s);
-
- ieee80211_sta_deinit(wpa_s);
+ eloop_cancel_timeout(wpa_supplicant_stop_countermeasures, wpa_s, NULL);
+#ifdef CONFIG_DELAYED_MIC_ERROR_REPORT
+ eloop_cancel_timeout(wpa_supplicant_delayed_mic_error_report,
+ wpa_s, NULL);
+#endif /* CONFIG_DELAYED_MIC_ERROR_REPORT */
wpas_wps_deinit(wpa_s);
wpa_s->ibss_rsn = NULL;
#endif /* CONFIG_IBSS_RSN */
-#ifdef CONFIG_SME
- os_free(wpa_s->sme.ft_ies);
- wpa_s->sme.ft_ies = NULL;
- wpa_s->sme.ft_ies_len = 0;
-#endif /* CONFIG_SME */
+ sme_deinit(wpa_s);
#ifdef CONFIG_AP
wpa_supplicant_ap_deinit(wpa_s);
#endif /* CONFIG_AP */
+
+#ifdef CONFIG_P2P
+ wpas_p2p_deinit(wpa_s);
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_OFFCHANNEL
+ offchannel_deinit(wpa_s);
+#endif /* CONFIG_OFFCHANNEL */
+
+ wpa_supplicant_cancel_sched_scan(wpa_s);
+
+ os_free(wpa_s->next_scan_freqs);
+ wpa_s->next_scan_freqs = NULL;
+
+ gas_query_deinit(wpa_s->gas);
+ wpa_s->gas = NULL;
+
+ free_hw_features(wpa_s);
}
*/
void wpa_clear_keys(struct wpa_supplicant *wpa_s, const u8 *addr)
{
- u8 *bcast = (u8 *) "\xff\xff\xff\xff\xff\xff";
-
if (wpa_s->keys_cleared) {
/* Some drivers (e.g., ndiswrapper & NDIS drivers) seem to have
* timing issues with keys being cleared just before new keys
* client not receiving the first encrypted packets correctly.
* Skipping some of the extra key clearing steps seems to help
* in completing group key handshake more reliably. */
- wpa_printf(MSG_DEBUG, "No keys have been configured - "
- "skip key clearing");
+ wpa_dbg(wpa_s, MSG_DEBUG, "No keys have been configured - "
+ "skip key clearing");
return;
}
/* MLME-DELETEKEYS.request */
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 0, 0, NULL, 0, NULL, 0);
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 1, 0, NULL, 0, NULL, 0);
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 2, 0, NULL, 0, NULL, 0);
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 3, 0, NULL, 0, NULL, 0);
+ wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 0, 0, NULL, 0, NULL, 0);
+ wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 1, 0, NULL, 0, NULL, 0);
+ wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 2, 0, NULL, 0, NULL, 0);
+ wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 3, 0, NULL, 0, NULL, 0);
#ifdef CONFIG_IEEE80211W
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 4, 0, NULL, 0, NULL, 0);
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 5, 0, NULL, 0, NULL, 0);
+ wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 4, 0, NULL, 0, NULL, 0);
+ wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 5, 0, NULL, 0, NULL, 0);
#endif /* CONFIG_IEEE80211W */
if (addr) {
wpa_drv_set_key(wpa_s, WPA_ALG_NONE, addr, 0, 0, NULL, 0, NULL,
return "DISCONNECTED";
case WPA_INACTIVE:
return "INACTIVE";
+ case WPA_INTERFACE_DISABLED:
+ return "INTERFACE_DISABLED";
case WPA_SCANNING:
return "SCANNING";
case WPA_AUTHENTICATING:
}
+#ifdef CONFIG_BGSCAN
+
+static void wpa_supplicant_start_bgscan(struct wpa_supplicant *wpa_s)
+{
+ if (wpas_driver_bss_selection(wpa_s))
+ return;
+ if (wpa_s->current_ssid == wpa_s->bgscan_ssid)
+ return;
+
+ bgscan_deinit(wpa_s);
+ if (wpa_s->current_ssid && wpa_s->current_ssid->bgscan) {
+ if (bgscan_init(wpa_s, wpa_s->current_ssid)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Failed to initialize "
+ "bgscan");
+ /*
+ * Live without bgscan; it is only used as a roaming
+ * optimization, so the initial connection is not
+ * affected.
+ */
+ } else
+ wpa_s->bgscan_ssid = wpa_s->current_ssid;
+ } else
+ wpa_s->bgscan_ssid = NULL;
+}
+
+
+static void wpa_supplicant_stop_bgscan(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->bgscan_ssid != NULL) {
+ bgscan_deinit(wpa_s);
+ wpa_s->bgscan_ssid = NULL;
+ }
+}
+
+#endif /* CONFIG_BGSCAN */
+
+
/**
* wpa_supplicant_set_state - Set current connection state
* @wpa_s: Pointer to wpa_supplicant data
{
enum wpa_states old_state = wpa_s->wpa_state;
- wpa_printf(MSG_DEBUG, "State: %s -> %s",
- wpa_supplicant_state_txt(wpa_s->wpa_state),
- wpa_supplicant_state_txt(state));
+ wpa_dbg(wpa_s, MSG_DEBUG, "State: %s -> %s",
+ wpa_supplicant_state_txt(wpa_s->wpa_state),
+ wpa_supplicant_state_txt(state));
if (state != WPA_SCANNING)
wpa_supplicant_notify_scanning(wpa_s, 0);
wpa_s->new_connection = 0;
wpa_s->reassociated_connection = 1;
wpa_drv_set_operstate(wpa_s, 1);
+#ifndef IEEE8021X_EAPOL
+ wpa_drv_set_supp_port(wpa_s, 1);
+#endif /* IEEE8021X_EAPOL */
wpa_s->after_wps = 0;
+#ifdef CONFIG_P2P
+ wpas_p2p_completed(wpa_s);
+#endif /* CONFIG_P2P */
} else if (state == WPA_DISCONNECTED || state == WPA_ASSOCIATING ||
state == WPA_ASSOCIATED) {
wpa_s->new_connection = 1;
wpa_drv_set_operstate(wpa_s, 0);
+#ifndef IEEE8021X_EAPOL
+ wpa_drv_set_supp_port(wpa_s, 0);
+#endif /* IEEE8021X_EAPOL */
}
wpa_s->wpa_state = state;
- if (wpa_s->wpa_state != old_state)
+#ifdef CONFIG_BGSCAN
+ if (state == WPA_COMPLETED)
+ wpa_supplicant_start_bgscan(wpa_s);
+ else
+ wpa_supplicant_stop_bgscan(wpa_s);
+#endif /* CONFIG_BGSCAN */
+
+ if (wpa_s->wpa_state != old_state) {
wpas_notify_state_changed(wpa_s, wpa_s->wpa_state, old_state);
+
+ if (wpa_s->wpa_state == WPA_COMPLETED ||
+ old_state == WPA_COMPLETED)
+ wpas_notify_auth_changed(wpa_s);
+ }
}
}
-static void wpa_supplicant_clear_status(struct wpa_supplicant *wpa_s)
+void wpa_supplicant_clear_status(struct wpa_supplicant *wpa_s)
{
enum wpa_states old_state = wpa_s->wpa_state;
wpa_s->group_cipher = 0;
wpa_s->mgmt_group_cipher = 0;
wpa_s->key_mgmt = 0;
- wpa_s->wpa_state = WPA_DISCONNECTED;
+ if (wpa_s->wpa_state != WPA_INTERFACE_DISABLED)
+ wpa_s->wpa_state = WPA_DISCONNECTED;
if (wpa_s->wpa_state != old_state)
wpas_notify_state_changed(wpa_s, wpa_s->wpa_state, old_state);
int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s)
{
struct wpa_config *conf;
- struct wpa_ssid *old_ssid;
int reconf_ctrl;
int old_ap_scan;
"file '%s' - exiting", wpa_s->confname);
return -1;
}
+ conf->changed_parameters = (unsigned int) -1;
reconf_ctrl = !!conf->ctrl_interface != !!wpa_s->conf->ctrl_interface
|| (conf->ctrl_interface && wpa_s->conf->ctrl_interface &&
}
eapol_sm_invalidate_cached_session(wpa_s->eapol);
- old_ssid = wpa_s->current_ssid;
- wpa_s->current_ssid = NULL;
- if (old_ssid != wpa_s->current_ssid)
- wpas_notify_network_changed(wpa_s);
+ if (wpa_s->current_ssid) {
+ wpa_supplicant_deauthenticate(wpa_s,
+ WLAN_REASON_DEAUTH_LEAVING);
+ }
/*
* TODO: should notify EAPOL SM about changes in opensc_engine_path,
}
eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
wpa_sm_set_config(wpa_s->wpa, NULL);
+ wpa_sm_pmksa_cache_flush(wpa_s->wpa, NULL);
wpa_sm_set_fast_reauth(wpa_s->wpa, wpa_s->conf->fast_reauth);
rsn_preauth_deinit(wpa_s->wpa);
if (reconf_ctrl)
wpa_s->ctrl_iface = wpa_supplicant_ctrl_iface_init(wpa_s);
+ wpa_supplicant_update_config(wpa_s);
+
wpa_supplicant_clear_status(wpa_s);
- wpa_s->reassociate = 1;
- wpa_supplicant_req_scan(wpa_s, 0, 0);
- wpa_msg(wpa_s, MSG_DEBUG, "Reconfiguration completed");
+ if (wpa_supplicant_enabled_networks(wpa_s->conf)) {
+ wpa_s->reassociate = 1;
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+ }
+ wpa_dbg(wpa_s, MSG_DEBUG, "Reconfiguration completed");
return 0;
}
{
struct wpa_global *global = signal_ctx;
struct wpa_supplicant *wpa_s;
- wpa_printf(MSG_DEBUG, "Signal %d received - reconfiguring", sig);
for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Signal %d received - reconfiguring",
+ sig);
if (wpa_supplicant_reload_configuration(wpa_s) < 0) {
wpa_supplicant_terminate_proc(global);
}
return -1;
}
- wpa_printf(MSG_DEBUG, "WPA: Using WPA IE from AssocReq to set cipher "
- "suites");
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Using WPA IE from AssocReq to set "
+ "cipher suites");
if (!(ie->group_cipher & ssid->group_cipher)) {
wpa_msg(wpa_s, MSG_INFO, "WPA: Driver used disabled group "
"cipher 0x%x (mask 0x%x) - reject",
(ie.group_cipher & ssid->group_cipher) &&
(ie.pairwise_cipher & ssid->pairwise_cipher) &&
(ie.key_mgmt & ssid->key_mgmt)) {
- wpa_msg(wpa_s, MSG_DEBUG, "RSN: using IEEE 802.11i/D9.0");
+ wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using IEEE 802.11i/D9.0");
proto = WPA_PROTO_RSN;
} else if (bss_wpa && (ssid->proto & WPA_PROTO_WPA) &&
wpa_parse_wpa_ie(bss_wpa, 2 +bss_wpa[1], &ie) == 0 &&
(ie.group_cipher & ssid->group_cipher) &&
(ie.pairwise_cipher & ssid->pairwise_cipher) &&
(ie.key_mgmt & ssid->key_mgmt)) {
- wpa_msg(wpa_s, MSG_DEBUG, "WPA: using IEEE 802.11i/D3.0");
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using IEEE 802.11i/D3.0");
proto = WPA_PROTO_WPA;
} else if (bss) {
wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select WPA/RSN");
ssid->ieee80211w != NO_MGMT_FRAME_PROTECTION ?
WPA_CIPHER_AES_128_CMAC : 0;
#endif /* CONFIG_IEEE80211W */
- wpa_printf(MSG_DEBUG, "WPA: Set cipher suites based "
- "on configuration");
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Set cipher suites "
+ "based on configuration");
} else
proto = ie.proto;
}
- wpa_printf(MSG_DEBUG, "WPA: Selected cipher suites: group %d "
- "pairwise %d key_mgmt %d proto %d",
- ie.group_cipher, ie.pairwise_cipher, ie.key_mgmt, proto);
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Selected cipher suites: group %d "
+ "pairwise %d key_mgmt %d proto %d",
+ ie.group_cipher, ie.pairwise_cipher, ie.key_mgmt, proto);
#ifdef CONFIG_IEEE80211W
if (ssid->ieee80211w) {
- wpa_printf(MSG_DEBUG, "WPA: Selected mgmt group cipher %d",
- ie.mgmt_group_cipher);
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Selected mgmt group cipher %d",
+ ie.mgmt_group_cipher);
}
#endif /* CONFIG_IEEE80211W */
+ wpa_s->wpa_proto = proto;
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_PROTO, proto);
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_RSN_ENABLED,
!!(ssid->proto & WPA_PROTO_RSN));
sel = ie.group_cipher & ssid->group_cipher;
if (sel & WPA_CIPHER_CCMP) {
wpa_s->group_cipher = WPA_CIPHER_CCMP;
- wpa_msg(wpa_s, MSG_DEBUG, "WPA: using GTK CCMP");
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using GTK CCMP");
} else if (sel & WPA_CIPHER_TKIP) {
wpa_s->group_cipher = WPA_CIPHER_TKIP;
- wpa_msg(wpa_s, MSG_DEBUG, "WPA: using GTK TKIP");
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using GTK TKIP");
} else if (sel & WPA_CIPHER_WEP104) {
wpa_s->group_cipher = WPA_CIPHER_WEP104;
- wpa_msg(wpa_s, MSG_DEBUG, "WPA: using GTK WEP104");
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using GTK WEP104");
} else if (sel & WPA_CIPHER_WEP40) {
wpa_s->group_cipher = WPA_CIPHER_WEP40;
- wpa_msg(wpa_s, MSG_DEBUG, "WPA: using GTK WEP40");
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using GTK WEP40");
} else {
- wpa_printf(MSG_WARNING, "WPA: Failed to select group cipher.");
+ wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select group "
+ "cipher");
return -1;
}
sel = ie.pairwise_cipher & ssid->pairwise_cipher;
if (sel & WPA_CIPHER_CCMP) {
wpa_s->pairwise_cipher = WPA_CIPHER_CCMP;
- wpa_msg(wpa_s, MSG_DEBUG, "WPA: using PTK CCMP");
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using PTK CCMP");
} else if (sel & WPA_CIPHER_TKIP) {
wpa_s->pairwise_cipher = WPA_CIPHER_TKIP;
- wpa_msg(wpa_s, MSG_DEBUG, "WPA: using PTK TKIP");
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using PTK TKIP");
} else if (sel & WPA_CIPHER_NONE) {
wpa_s->pairwise_cipher = WPA_CIPHER_NONE;
- wpa_msg(wpa_s, MSG_DEBUG, "WPA: using PTK NONE");
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using PTK NONE");
} else {
- wpa_printf(MSG_WARNING, "WPA: Failed to select pairwise "
- "cipher.");
+ wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select pairwise "
+ "cipher");
return -1;
}
#ifdef CONFIG_IEEE80211R
} else if (sel & WPA_KEY_MGMT_FT_IEEE8021X) {
wpa_s->key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X;
- wpa_msg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT/802.1X");
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT/802.1X");
} else if (sel & WPA_KEY_MGMT_FT_PSK) {
wpa_s->key_mgmt = WPA_KEY_MGMT_FT_PSK;
- wpa_msg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT/PSK");
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT/PSK");
#endif /* CONFIG_IEEE80211R */
#ifdef CONFIG_IEEE80211W
} else if (sel & WPA_KEY_MGMT_IEEE8021X_SHA256) {
wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_SHA256;
- wpa_msg(wpa_s, MSG_DEBUG,
+ wpa_dbg(wpa_s, MSG_DEBUG,
"WPA: using KEY_MGMT 802.1X with SHA256");
} else if (sel & WPA_KEY_MGMT_PSK_SHA256) {
wpa_s->key_mgmt = WPA_KEY_MGMT_PSK_SHA256;
- wpa_msg(wpa_s, MSG_DEBUG,
+ wpa_dbg(wpa_s, MSG_DEBUG,
"WPA: using KEY_MGMT PSK with SHA256");
#endif /* CONFIG_IEEE80211W */
} else if (sel & WPA_KEY_MGMT_IEEE8021X) {
wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
- wpa_msg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT 802.1X");
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT 802.1X");
} else if (sel & WPA_KEY_MGMT_PSK) {
wpa_s->key_mgmt = WPA_KEY_MGMT_PSK;
- wpa_msg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT WPA-PSK");
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT WPA-PSK");
} else if (sel & WPA_KEY_MGMT_WPA_NONE) {
wpa_s->key_mgmt = WPA_KEY_MGMT_WPA_NONE;
- wpa_msg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT WPA-NONE");
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT WPA-NONE");
} else {
- wpa_printf(MSG_WARNING, "WPA: Failed to select authenticated "
- "key management type.");
+ wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select "
+ "authenticated key management type");
return -1;
}
sel = 0;
if (sel & WPA_CIPHER_AES_128_CMAC) {
wpa_s->mgmt_group_cipher = WPA_CIPHER_AES_128_CMAC;
- wpa_msg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher "
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher "
"AES-128-CMAC");
} else {
wpa_s->mgmt_group_cipher = 0;
- wpa_msg(wpa_s, MSG_DEBUG, "WPA: not using MGMT group cipher");
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: not using MGMT group cipher");
}
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MGMT_GROUP,
wpa_s->mgmt_group_cipher);
#endif /* CONFIG_IEEE80211W */
if (wpa_sm_set_assoc_wpa_ie_default(wpa_s->wpa, wpa_ie, wpa_ie_len)) {
- wpa_printf(MSG_WARNING, "WPA: Failed to generate WPA IE.");
+ wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to generate WPA IE");
return -1;
}
if (ssid->key_mgmt &
(WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_FT_PSK | WPA_KEY_MGMT_PSK_SHA256))
+ {
wpa_sm_set_pmk(wpa_s->wpa, ssid->psk, PMK_LEN);
- else
+#ifndef CONFIG_NO_PBKDF2
+ if (bss && ssid->bssid_set && ssid->ssid_len == 0 &&
+ ssid->passphrase) {
+ u8 psk[PMK_LEN];
+ pbkdf2_sha1(ssid->passphrase, (char *) bss->ssid,
+ bss->ssid_len, 4096, psk, PMK_LEN);
+ wpa_hexdump_key(MSG_MSGDUMP, "PSK (from passphrase)",
+ psk, PMK_LEN);
+ wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN);
+ }
+#endif /* CONFIG_NO_PBKDF2 */
+ } else
wpa_sm_set_pmk_from_pmksa(wpa_s->wpa);
return 0;
void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss, struct wpa_ssid *ssid)
{
- u8 wpa_ie[80];
+ u8 wpa_ie[200];
size_t wpa_ie_len;
int use_crypt, ret, i, bssid_changed;
int algs = WPA_AUTH_ALG_OPEN;
int assoc_failed = 0;
struct wpa_ssid *old_ssid;
- if (ssid->mode == WPAS_MODE_AP) {
+#ifdef CONFIG_IBSS_RSN
+ ibss_rsn_deinit(wpa_s->ibss_rsn);
+ wpa_s->ibss_rsn = NULL;
+#endif /* CONFIG_IBSS_RSN */
+
+ if (ssid->mode == WPAS_MODE_AP || ssid->mode == WPAS_MODE_P2P_GO ||
+ ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) {
#ifdef CONFIG_AP
if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_AP)) {
- wpa_printf(MSG_INFO, "Driver does not support AP "
- "mode");
+ wpa_msg(wpa_s, MSG_INFO, "Driver does not support AP "
+ "mode");
return;
}
wpa_supplicant_create_ap(wpa_s, ssid);
wpa_s->current_bss = bss;
#else /* CONFIG_AP */
- wpa_printf(MSG_ERROR, "AP mode support not included in the "
- "build");
+ wpa_msg(wpa_s, MSG_ERROR, "AP mode support not included in "
+ "the build");
#endif /* CONFIG_AP */
return;
}
+#ifdef CONFIG_TDLS
+ if (bss)
+ wpa_tdls_ap_ies(wpa_s->wpa, (const u8 *) (bss + 1),
+ bss->ie_len);
+#endif /* CONFIG_TDLS */
+
if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) &&
ssid->mode == IEEE80211_MODE_INFRA) {
sme_authenticate(wpa_s, bss, ssid);
return;
}
+ os_memset(¶ms, 0, sizeof(params));
wpa_s->reassociate = 0;
- if (bss) {
+ if (bss && !wpas_driver_bss_selection(wpa_s)) {
#ifdef CONFIG_IEEE80211R
const u8 *ie, *md = NULL;
#endif /* CONFIG_IEEE80211R */
wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
}
+ wpa_supplicant_cancel_sched_scan(wpa_s);
wpa_supplicant_cancel_scan(wpa_s);
/* Starting new association, so clear the possibly used WPA IE from the
}
}
#endif /* IEEE8021X_EAPOL */
- wpa_printf(MSG_DEBUG, "Automatic auth_alg selection: 0x%x", algs);
+ wpa_dbg(wpa_s, MSG_DEBUG, "Automatic auth_alg selection: 0x%x", algs);
if (ssid->auth_alg) {
algs = ssid->auth_alg;
- wpa_printf(MSG_DEBUG, "Overriding auth_alg selection: 0x%x",
- algs);
+ wpa_dbg(wpa_s, MSG_DEBUG, "Overriding auth_alg selection: "
+ "0x%x", algs);
}
if (bss && (wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE) ||
wpa_ie_len = sizeof(wpa_ie);
if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
wpa_ie, &wpa_ie_len)) {
- wpa_printf(MSG_WARNING, "WPA: Failed to set WPA key "
- "management and encryption suites");
+ wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to set WPA "
+ "key management and encryption suites");
return;
}
} else if (ssid->key_mgmt &
wpa_ie_len = sizeof(wpa_ie);
if (wpa_supplicant_set_suites(wpa_s, NULL, ssid,
wpa_ie, &wpa_ie_len)) {
- wpa_printf(MSG_WARNING, "WPA: Failed to set WPA key "
- "management and encryption suites (no scan "
- "results)");
+ wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to set WPA "
+ "key management and encryption suites (no "
+ "scan results)");
return;
}
#ifdef CONFIG_WPS
wpa_ie_len = 0;
wpabuf_free(wps_ie);
wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
+ if (!bss || (bss->caps & IEEE80211_CAP_PRIVACY))
+ params.wps = WPS_MODE_PRIVACY;
+ else
+ params.wps = WPS_MODE_OPEN;
+ wpa_s->wpa_proto = 0;
#endif /* CONFIG_WPS */
} else {
wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
wpa_ie_len = 0;
+ wpa_s->wpa_proto = 0;
}
+#ifdef CONFIG_P2P
+ if (wpa_s->global->p2p) {
+ u8 *pos;
+ size_t len;
+ int res;
+ pos = wpa_ie + wpa_ie_len;
+ len = sizeof(wpa_ie) - wpa_ie_len;
+ res = wpas_p2p_assoc_req_ie(wpa_s, bss, pos, len,
+ ssid->p2p_group);
+ if (res >= 0)
+ wpa_ie_len += res;
+ }
+
+ wpa_s->cross_connect_disallowed = 0;
+ if (bss) {
+ struct wpabuf *p2p;
+ p2p = wpa_bss_get_vendor_ie_multi(bss, P2P_IE_VENDOR_TYPE);
+ if (p2p) {
+ wpa_s->cross_connect_disallowed =
+ p2p_get_cross_connect_disallowed(p2p);
+ wpabuf_free(p2p);
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: WLAN AP %s cross "
+ "connection",
+ wpa_s->cross_connect_disallowed ?
+ "disallows" : "allows");
+ }
+ }
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_INTERWORKING
+ if (wpa_s->conf->interworking) {
+ u8 *pos = wpa_ie;
+ if (wpa_ie_len > 0 && pos[0] == WLAN_EID_RSN)
+ pos += 2 + pos[1];
+ os_memmove(pos + 6, pos, wpa_ie_len - (pos - wpa_ie));
+ wpa_ie_len += 6;
+ *pos++ = WLAN_EID_EXT_CAPAB;
+ *pos++ = 4;
+ *pos++ = 0x00;
+ *pos++ = 0x00;
+ *pos++ = 0x00;
+ *pos++ = 0x80; /* Bit 31 - Interworking */
+ }
+#endif /* CONFIG_INTERWORKING */
+
wpa_clear_keys(wpa_s, bss ? bss->bssid : NULL);
use_crypt = 1;
cipher_pairwise = cipher_suite2driver(wpa_s->pairwise_cipher);
}
wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATING);
- os_memset(¶ms, 0, sizeof(params));
if (bss) {
- params.bssid = bss->bssid;
params.ssid = bss->ssid;
params.ssid_len = bss->ssid_len;
- params.freq = bss->freq;
+ if (!wpas_driver_bss_selection(wpa_s)) {
+ params.bssid = bss->bssid;
+ params.freq = bss->freq;
+ }
} else {
params.ssid = ssid->ssid;
params.ssid_len = ssid->ssid_len;
params.pairwise_suite = cipher_pairwise;
params.group_suite = cipher_group;
params.key_mgmt_suite = key_mgmt2driver(wpa_s->key_mgmt);
+ params.wpa_proto = wpa_s->wpa_proto;
params.auth_alg = algs;
params.mode = ssid->mode;
for (i = 0; i < NUM_WEP_KEYS; i++) {
if (rsn && wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ie) == 0 &&
ie.capabilities &
(WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR)) {
- wpa_printf(MSG_DEBUG, "WPA: Selected AP supports MFP: "
- "require MFP");
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Selected AP supports "
+ "MFP: require MFP");
params.mgmt_frame_protection =
MGMT_FRAME_PROTECTION_REQUIRED;
}
}
#endif /* CONFIG_IEEE80211W */
- if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)
- ret = ieee80211_sta_associate(wpa_s, ¶ms);
+ params.p2p = ssid->p2p_group;
+
+ if (wpa_s->parent->set_sta_uapsd)
+ params.uapsd = wpa_s->parent->sta_uapsd;
else
- ret = wpa_drv_associate(wpa_s, ¶ms);
+ params.uapsd = -1;
+
+ ret = wpa_drv_associate(wpa_s, ¶ms);
if (ret < 0) {
wpa_msg(wpa_s, MSG_INFO, "Association request to the driver "
"failed");
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SANE_ERROR_CODES) {
+ /*
+ * The driver is known to mean what is saying, so we
+ * can stop right here; the association will not
+ * succeed.
+ */
+ wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
+ os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
+ return;
+ }
/* try to continue anyway; new association will be tried again
* after timeout */
assoc_failed = 1;
} else if (ssid->mode == WPAS_MODE_IBSS &&
wpa_s->key_mgmt != WPA_KEY_MGMT_NONE &&
wpa_s->key_mgmt != WPA_KEY_MGMT_WPA_NONE) {
- ibss_rsn_set_psk(wpa_s->ibss_rsn, ssid->psk);
/*
* RSN IBSS authentication is per-STA and we can disable the
* per-BSSID authentication.
}
+static void wpa_supplicant_clear_connection(struct wpa_supplicant *wpa_s,
+ const u8 *addr)
+{
+ struct wpa_ssid *old_ssid;
+
+ wpa_clear_keys(wpa_s, addr);
+ wpa_supplicant_mark_disassoc(wpa_s);
+ old_ssid = wpa_s->current_ssid;
+ wpa_s->current_ssid = NULL;
+ wpa_s->current_bss = NULL;
+ wpa_sm_set_config(wpa_s->wpa, NULL);
+ eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
+ if (old_ssid != wpa_s->current_ssid)
+ wpas_notify_network_changed(wpa_s);
+ eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL);
+}
+
+
/**
* wpa_supplicant_disassociate - Disassociate the current connection
* @wpa_s: Pointer to wpa_supplicant data
void wpa_supplicant_disassociate(struct wpa_supplicant *wpa_s,
int reason_code)
{
- struct wpa_ssid *old_ssid;
u8 *addr = NULL;
if (!is_zero_ether_addr(wpa_s->bssid)) {
- if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)
- ieee80211_sta_disassociate(wpa_s, reason_code);
- else
- wpa_drv_disassociate(wpa_s, wpa_s->bssid, reason_code);
+ wpa_drv_disassociate(wpa_s, wpa_s->bssid, reason_code);
addr = wpa_s->bssid;
}
- wpa_clear_keys(wpa_s, addr);
- wpa_supplicant_mark_disassoc(wpa_s);
- old_ssid = wpa_s->current_ssid;
- wpa_s->current_ssid = NULL;
- wpa_s->current_bss = NULL;
- wpa_sm_set_config(wpa_s->wpa, NULL);
- eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
- if (old_ssid != wpa_s->current_ssid)
- wpas_notify_network_changed(wpa_s);
- eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL);
+
+ wpa_supplicant_clear_connection(wpa_s, addr);
}
void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s,
int reason_code)
{
- struct wpa_ssid *old_ssid;
u8 *addr = NULL;
if (!is_zero_ether_addr(wpa_s->bssid)) {
- if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)
- ieee80211_sta_deauthenticate(wpa_s, reason_code);
- else
- wpa_drv_deauthenticate(wpa_s, wpa_s->bssid,
- reason_code);
+ wpa_drv_deauthenticate(wpa_s, wpa_s->bssid, reason_code);
addr = wpa_s->bssid;
}
- wpa_clear_keys(wpa_s, addr);
- wpa_supplicant_mark_disassoc(wpa_s);
- old_ssid = wpa_s->current_ssid;
- wpa_s->current_ssid = NULL;
- wpa_s->current_bss = NULL;
- wpa_sm_set_config(wpa_s->wpa, NULL);
- eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
- if (old_ssid != wpa_s->current_ssid)
- wpas_notify_network_changed(wpa_s);
- eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL);
+
+ wpa_supplicant_clear_connection(wpa_s, addr);
}
int was_disabled;
if (ssid == NULL) {
- other_ssid = wpa_s->conf->ssid;
- while (other_ssid) {
+ for (other_ssid = wpa_s->conf->ssid; other_ssid;
+ other_ssid = other_ssid->next) {
+ if (other_ssid->disabled == 2)
+ continue; /* do not change persistent P2P group
+ * data */
if (other_ssid == wpa_s->current_ssid &&
other_ssid->disabled)
wpa_s->reassociate = 1;
if (was_disabled != other_ssid->disabled)
wpas_notify_network_enabled_changed(
wpa_s, other_ssid);
-
- other_ssid = other_ssid->next;
}
if (wpa_s->reassociate)
wpa_supplicant_req_scan(wpa_s, 0, 0);
- } else if (ssid->disabled) {
+ } else if (ssid->disabled && ssid->disabled != 2) {
if (wpa_s->current_ssid == NULL) {
/*
* Try to reassociate since there is no current
int was_disabled;
if (ssid == NULL) {
- other_ssid = wpa_s->conf->ssid;
- while (other_ssid) {
+ for (other_ssid = wpa_s->conf->ssid; other_ssid;
+ other_ssid = other_ssid->next) {
was_disabled = other_ssid->disabled;
+ if (was_disabled == 2)
+ continue; /* do not change persistent P2P group
+ * data */
other_ssid->disabled = 1;
if (was_disabled != other_ssid->disabled)
wpas_notify_network_enabled_changed(
wpa_s, other_ssid);
-
- other_ssid = other_ssid->next;
}
if (wpa_s->current_ssid)
wpa_supplicant_disassociate(
wpa_s, WLAN_REASON_DEAUTH_LEAVING);
- } else {
+ } else if (ssid->disabled != 2) {
if (ssid == wpa_s->current_ssid)
wpa_supplicant_disassociate(
wpa_s, WLAN_REASON_DEAUTH_LEAVING);
{
struct wpa_ssid *other_ssid;
+ int disconnected = 0;
- if (ssid && ssid != wpa_s->current_ssid && wpa_s->current_ssid)
+ if (ssid && ssid != wpa_s->current_ssid && wpa_s->current_ssid) {
wpa_supplicant_disassociate(
wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+ disconnected = 1;
+ }
/*
* Mark all other networks disabled or mark all networks enabled if no
* network specified.
*/
- other_ssid = wpa_s->conf->ssid;
- while (other_ssid) {
+ for (other_ssid = wpa_s->conf->ssid; other_ssid;
+ other_ssid = other_ssid->next) {
int was_disabled = other_ssid->disabled;
+ if (was_disabled == 2)
+ continue; /* do not change persistent P2P group data */
other_ssid->disabled = ssid ? (ssid->id != other_ssid->id) : 0;
if (was_disabled != other_ssid->disabled)
wpas_notify_network_enabled_changed(wpa_s, other_ssid);
+ }
- other_ssid = other_ssid->next;
+ if (ssid && ssid == wpa_s->current_ssid && wpa_s->current_ssid) {
+ /* We are already associated with the selected network */
+ wpa_printf(MSG_DEBUG, "Already associated with the "
+ "selected network - do nothing");
+ return;
}
+
+ wpa_s->connect_without_scan = NULL;
wpa_s->disconnected = 0;
wpa_s->reassociate = 1;
- wpa_supplicant_req_scan(wpa_s, 0, 0);
+ wpa_supplicant_req_scan(wpa_s, 0, disconnected ? 100000 : 0);
if (ssid)
wpas_notify_network_selected(wpa_s, ssid);
if (ap_scan < 0 || ap_scan > 2)
return -1;
+#ifdef ANDROID
+ if (ap_scan == 2 && ap_scan != wpa_s->conf->ap_scan &&
+ wpa_s->wpa_state >= WPA_ASSOCIATING &&
+ wpa_s->wpa_state < WPA_COMPLETED) {
+ wpa_printf(MSG_ERROR, "ap_scan = %d (%d) rejected while "
+ "associating", wpa_s->conf->ap_scan, ap_scan);
+ return 0;
+ }
+#endif /* ANDROID */
+
old_ap_scan = wpa_s->conf->ap_scan;
wpa_s->conf->ap_scan = ap_scan;
/**
+ * wpa_supplicant_set_bss_expiration_age - Set BSS entry expiration age
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * @expire_age: Expiration age in seconds
+ * Returns: 0 if succeed or -1 if expire_age has an invalid value
+ *
+ */
+int wpa_supplicant_set_bss_expiration_age(struct wpa_supplicant *wpa_s,
+ unsigned int bss_expire_age)
+{
+ if (bss_expire_age < 10) {
+ wpa_msg(wpa_s, MSG_ERROR, "Invalid bss expiration age %u",
+ bss_expire_age);
+ return -1;
+ }
+ wpa_msg(wpa_s, MSG_DEBUG, "Setting bss expiration age: %d sec",
+ bss_expire_age);
+ wpa_s->conf->bss_expiration_age = bss_expire_age;
+
+ return 0;
+}
+
+
+/**
+ * wpa_supplicant_set_bss_expiration_count - Set BSS entry expiration scan count
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * @expire_count: number of scans after which an unseen BSS is reclaimed
+ * Returns: 0 if succeed or -1 if expire_count has an invalid value
+ *
+ */
+int wpa_supplicant_set_bss_expiration_count(struct wpa_supplicant *wpa_s,
+ unsigned int bss_expire_count)
+{
+ if (bss_expire_count < 1) {
+ wpa_msg(wpa_s, MSG_ERROR, "Invalid bss expiration count %u",
+ bss_expire_count);
+ return -1;
+ }
+ wpa_msg(wpa_s, MSG_DEBUG, "Setting bss expiration scan count: %u",
+ bss_expire_count);
+ wpa_s->conf->bss_expiration_scan_count = bss_expire_count;
+
+ return 0;
+}
+
+
+/**
* wpa_supplicant_set_debug_params - Set global debug params
* @global: wpa_global structure
* @debug_level: debug level
int old_level, old_timestamp, old_show_keys;
/* check for allowed debuglevels */
- if (debug_level != MSG_MSGDUMP &&
+ if (debug_level != MSG_EXCESSIVE &&
+ debug_level != MSG_MSGDUMP &&
debug_level != MSG_DEBUG &&
debug_level != MSG_INFO &&
debug_level != MSG_WARNING &&
u8 bssid[ETH_ALEN];
int wired;
- if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) {
- if (ieee80211_sta_get_ssid(wpa_s, ssid, &ssid_len)) {
- wpa_printf(MSG_WARNING, "Could not read SSID from "
- "MLME.");
- return NULL;
- }
- } else {
- res = wpa_drv_get_ssid(wpa_s, ssid);
- if (res < 0) {
- wpa_printf(MSG_WARNING, "Could not read SSID from "
- "driver.");
- return NULL;
- }
- ssid_len = res;
+ res = wpa_drv_get_ssid(wpa_s, ssid);
+ if (res < 0) {
+ wpa_msg(wpa_s, MSG_WARNING, "Could not read SSID from "
+ "driver");
+ return NULL;
}
+ ssid_len = res;
- if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)
- os_memcpy(bssid, wpa_s->bssid, ETH_ALEN);
- else if (wpa_drv_get_bssid(wpa_s, bssid) < 0) {
- wpa_printf(MSG_WARNING, "Could not read BSSID from driver.");
+ if (wpa_drv_get_bssid(wpa_s, bssid) < 0) {
+ wpa_msg(wpa_s, MSG_WARNING, "Could not read BSSID from "
+ "driver");
return NULL;
}
os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0))
return entry;
#endif /* CONFIG_WPS */
+
+ if (!entry->disabled && entry->bssid_set &&
+ entry->ssid_len == 0 &&
+ os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0)
+ return entry;
+
entry = entry->next;
}
}
+static int select_driver(struct wpa_supplicant *wpa_s, int i)
+{
+ struct wpa_global *global = wpa_s->global;
+
+ if (wpa_drivers[i]->global_init && global->drv_priv[i] == NULL) {
+ global->drv_priv[i] = wpa_drivers[i]->global_init();
+ if (global->drv_priv[i] == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to initialize driver "
+ "'%s'", wpa_drivers[i]->name);
+ return -1;
+ }
+ }
+
+ wpa_s->driver = wpa_drivers[i];
+ wpa_s->global_drv_priv = global->drv_priv[i];
+
+ return 0;
+}
+
+
static int wpa_supplicant_set_driver(struct wpa_supplicant *wpa_s,
const char *name)
{
int i;
size_t len;
- const char *pos;
+ const char *pos, *driver = name;
if (wpa_s == NULL)
return -1;
if (wpa_drivers[0] == NULL) {
- wpa_printf(MSG_ERROR, "No driver interfaces build into "
- "wpa_supplicant.");
+ wpa_msg(wpa_s, MSG_ERROR, "No driver interfaces build into "
+ "wpa_supplicant");
return -1;
}
if (name == NULL) {
/* default to first driver in the list */
- wpa_s->driver = wpa_drivers[0];
- wpa_s->global_drv_priv = wpa_s->global->drv_priv[0];
- return 0;
+ return select_driver(wpa_s, 0);
}
- pos = os_strchr(name, ',');
- if (pos)
- len = pos - name;
- else
- len = os_strlen(name);
- for (i = 0; wpa_drivers[i]; i++) {
- if (os_strlen(wpa_drivers[i]->name) == len &&
- os_strncmp(name, wpa_drivers[i]->name, len) ==
- 0) {
- wpa_s->driver = wpa_drivers[i];
- wpa_s->global_drv_priv = wpa_s->global->drv_priv[i];
- return 0;
+ do {
+ pos = os_strchr(driver, ',');
+ if (pos)
+ len = pos - driver;
+ else
+ len = os_strlen(driver);
+
+ for (i = 0; wpa_drivers[i]; i++) {
+ if (os_strlen(wpa_drivers[i]->name) == len &&
+ os_strncmp(driver, wpa_drivers[i]->name, len) ==
+ 0) {
+ /* First driver that succeeds wins */
+ if (select_driver(wpa_s, i) == 0)
+ return 0;
+ }
}
- }
- wpa_printf(MSG_ERROR, "Unsupported driver '%s'.", name);
+ driver = pos + 1;
+ } while (pos);
+
+ wpa_msg(wpa_s, MSG_ERROR, "Unsupported driver '%s'", name);
return -1;
}
{
struct wpa_supplicant *wpa_s = ctx;
- wpa_printf(MSG_DEBUG, "RX EAPOL from " MACSTR, MAC2STR(src_addr));
+ wpa_dbg(wpa_s, MSG_DEBUG, "RX EAPOL from " MACSTR, MAC2STR(src_addr));
wpa_hexdump(MSG_MSGDUMP, "RX EAPOL", buf, len);
if (wpa_s->wpa_state < WPA_ASSOCIATED) {
* association information, lets queue it for processing until
* the association event is received.
*/
- wpa_printf(MSG_DEBUG, "Not associated - Delay processing of "
- "received EAPOL frame");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Not associated - Delay processing "
+ "of received EAPOL frame");
wpabuf_free(wpa_s->pending_eapol_rx);
wpa_s->pending_eapol_rx = wpabuf_alloc_copy(buf, len);
if (wpa_s->pending_eapol_rx) {
#endif /* CONFIG_AP */
if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE) {
- wpa_printf(MSG_DEBUG, "Ignored received EAPOL frame since "
- "no key management is configured");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Ignored received EAPOL frame since "
+ "no key management is configured");
return;
}
wpa_s->eapol_received++;
if (wpa_s->countermeasures) {
- wpa_printf(MSG_INFO, "WPA: Countermeasures - dropped EAPOL "
- "packet");
+ wpa_msg(wpa_s, MSG_INFO, "WPA: Countermeasures - dropped "
+ "EAPOL packet");
return;
}
}
-/**
- * wpa_supplicant_driver_init - Initialize driver interface parameters
- * @wpa_s: Pointer to wpa_supplicant data
- * Returns: 0 on success, -1 on failure
- *
- * This function is called to initialize driver interface parameters.
- * wpa_drv_init() must have been called before this function to initialize the
- * driver interface.
- */
-int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s)
+int wpa_supplicant_update_mac_addr(struct wpa_supplicant *wpa_s)
{
- static int interface_count = 0;
-
if (wpa_s->driver->send_eapol) {
const u8 *addr = wpa_drv_get_mac_addr(wpa_s);
if (addr)
os_memcpy(wpa_s->own_addr, addr, ETH_ALEN);
- } else {
+ } else if (!(wpa_s->drv_flags &
+ WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE)) {
+ l2_packet_deinit(wpa_s->l2);
wpa_s->l2 = l2_packet_init(wpa_s->ifname,
wpa_drv_get_mac_addr(wpa_s),
ETH_P_EAPOL,
wpa_supplicant_rx_eapol, wpa_s, 0);
if (wpa_s->l2 == NULL)
return -1;
+ } else {
+ const u8 *addr = wpa_drv_get_mac_addr(wpa_s);
+ if (addr)
+ os_memcpy(wpa_s->own_addr, addr, ETH_ALEN);
}
if (wpa_s->l2 && l2_packet_get_own_addr(wpa_s->l2, wpa_s->own_addr)) {
- wpa_printf(MSG_ERROR, "Failed to get own L2 address");
+ wpa_msg(wpa_s, MSG_ERROR, "Failed to get own L2 address");
return -1;
}
- wpa_printf(MSG_DEBUG, "Own MAC address: " MACSTR,
- MAC2STR(wpa_s->own_addr));
+ wpa_dbg(wpa_s, MSG_DEBUG, "Own MAC address: " MACSTR,
+ MAC2STR(wpa_s->own_addr));
+ wpa_sm_set_own_addr(wpa_s->wpa, wpa_s->own_addr);
+
+ return 0;
+}
+
+
+/**
+ * wpa_supplicant_driver_init - Initialize driver interface parameters
+ * @wpa_s: Pointer to wpa_supplicant data
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is called to initialize driver interface parameters.
+ * wpa_drv_init() must have been called before this function to initialize the
+ * driver interface.
+ */
+int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s)
+{
+ static int interface_count = 0;
+
+ if (wpa_supplicant_update_mac_addr(wpa_s) < 0)
+ return -1;
if (wpa_s->bridge_ifname[0]) {
- wpa_printf(MSG_DEBUG, "Receiving packets from bridge interface"
- " '%s'", wpa_s->bridge_ifname);
+ wpa_dbg(wpa_s, MSG_DEBUG, "Receiving packets from bridge "
+ "interface '%s'", wpa_s->bridge_ifname);
wpa_s->l2_br = l2_packet_init(wpa_s->bridge_ifname,
wpa_s->own_addr,
ETH_P_EAPOL,
wpa_supplicant_rx_eapol, wpa_s,
0);
if (wpa_s->l2_br == NULL) {
- wpa_printf(MSG_ERROR, "Failed to open l2_packet "
- "connection for the bridge interface '%s'",
- wpa_s->bridge_ifname);
+ wpa_msg(wpa_s, MSG_ERROR, "Failed to open l2_packet "
+ "connection for the bridge interface '%s'",
+ wpa_s->bridge_ifname);
return -1;
}
}
* happen if wpa_supplicant is killed during countermeasures. */
wpa_drv_set_countermeasures(wpa_s, 0);
- wpa_printf(MSG_DEBUG, "RSN: flushing PMKID list in the driver");
+ wpa_dbg(wpa_s, MSG_DEBUG, "RSN: flushing PMKID list in the driver");
wpa_drv_flush_pmkid(wpa_s);
wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN;
if (wpa_supplicant_enabled_networks(wpa_s->conf)) {
- wpa_supplicant_req_scan(wpa_s, interface_count, 100000);
+ if (wpa_supplicant_delayed_sched_scan(wpa_s, interface_count,
+ 100000))
+ wpa_supplicant_req_scan(wpa_s, interface_count,
+ 100000);
interface_count++;
} else
wpa_supplicant_set_state(wpa_s, WPA_INACTIVE);
if (wpa_s == NULL)
return NULL;
wpa_s->scan_req = 1;
+ wpa_s->scan_interval = 5;
wpa_s->new_connection = 1;
+ wpa_s->parent = wpa_s;
+ wpa_s->sched_scanning = 0;
return wpa_s;
}
const char *pos;
pos = driver ? os_strchr(driver, ',') : NULL;
if (pos) {
- wpa_printf(MSG_DEBUG, "Failed to initialize driver "
- "interface - try next driver wrapper");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Failed to initialize "
+ "driver interface - try next driver wrapper");
driver = pos + 1;
goto next_driver;
}
- wpa_printf(MSG_ERROR, "Failed to initialize driver interface");
+ wpa_msg(wpa_s, MSG_ERROR, "Failed to initialize driver "
+ "interface");
return -1;
}
if (wpa_drv_set_param(wpa_s, wpa_s->conf->driver_param) < 0) {
- wpa_printf(MSG_ERROR, "Driver interface rejected "
- "driver_param '%s'", wpa_s->conf->driver_param);
+ wpa_msg(wpa_s, MSG_ERROR, "Driver interface rejected "
+ "driver_param '%s'", wpa_s->conf->driver_param);
return -1;
}
ifname = wpa_drv_get_ifname(wpa_s);
if (ifname && os_strcmp(ifname, wpa_s->ifname) != 0) {
- wpa_printf(MSG_DEBUG, "Driver interface replaced interface "
- "name with '%s'", ifname);
+ wpa_dbg(wpa_s, MSG_DEBUG, "Driver interface replaced "
+ "interface name with '%s'", ifname);
os_strlcpy(wpa_s->ifname, ifname, sizeof(wpa_s->ifname));
}
if (wpa_s->conf->dot11RSNAConfigPMKLifetime &&
wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_LIFETIME,
wpa_s->conf->dot11RSNAConfigPMKLifetime)) {
- wpa_printf(MSG_ERROR, "Invalid WPA parameter value for "
- "dot11RSNAConfigPMKLifetime");
+ wpa_msg(wpa_s, MSG_ERROR, "Invalid WPA parameter value for "
+ "dot11RSNAConfigPMKLifetime");
return -1;
}
if (wpa_s->conf->dot11RSNAConfigPMKReauthThreshold &&
wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_REAUTH_THRESHOLD,
wpa_s->conf->dot11RSNAConfigPMKReauthThreshold)) {
- wpa_printf(MSG_ERROR, "Invalid WPA parameter value for "
+ wpa_msg(wpa_s, MSG_ERROR, "Invalid WPA parameter value for "
"dot11RSNAConfigPMKReauthThreshold");
return -1;
}
if (wpa_s->conf->dot11RSNAConfigSATimeout &&
wpa_sm_set_param(wpa_s->wpa, RSNA_SA_TIMEOUT,
wpa_s->conf->dot11RSNAConfigSATimeout)) {
- wpa_printf(MSG_ERROR, "Invalid WPA parameter value for "
- "dot11RSNAConfigSATimeout");
+ wpa_msg(wpa_s, MSG_ERROR, "Invalid WPA parameter value for "
+ "dot11RSNAConfigSATimeout");
return -1;
}
+ wpa_s->hw.modes = wpa_drv_get_hw_feature_data(wpa_s,
+ &wpa_s->hw.num_modes,
+ &wpa_s->hw.flags);
+
if (wpa_drv_get_capa(wpa_s, &capa) == 0) {
+ wpa_s->drv_capa_known = 1;
wpa_s->drv_flags = capa.flags;
- if (capa.flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) {
- if (ieee80211_sta_init(wpa_s))
- return -1;
- }
wpa_s->max_scan_ssids = capa.max_scan_ssids;
+ wpa_s->max_sched_scan_ssids = capa.max_sched_scan_ssids;
+ wpa_s->sched_scan_supported = capa.sched_scan_supported;
+ wpa_s->max_match_sets = capa.max_match_sets;
wpa_s->max_remain_on_chan = capa.max_remain_on_chan;
+ wpa_s->max_stations = capa.max_stations;
}
if (wpa_s->max_remain_on_chan == 0)
wpa_s->max_remain_on_chan = 1000;
if (wpa_supplicant_driver_init(wpa_s) < 0)
return -1;
+#ifdef CONFIG_TDLS
+ if (wpa_tdls_init(wpa_s->wpa))
+ return -1;
+#endif /* CONFIG_TDLS */
+
if (wpa_s->conf->country[0] && wpa_s->conf->country[1] &&
wpa_drv_set_country(wpa_s, wpa_s->conf->country)) {
- wpa_printf(MSG_DEBUG, "Failed to set country");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Failed to set country");
return -1;
}
- wpa_sm_set_own_addr(wpa_s->wpa, wpa_s->own_addr);
-
if (wpas_wps_init(wpa_s))
return -1;
return -1;
}
-#ifdef CONFIG_IBSS_RSN
- wpa_s->ibss_rsn = ibss_rsn_init(wpa_s);
- if (!wpa_s->ibss_rsn) {
- wpa_printf(MSG_DEBUG, "Failed to init IBSS RSN");
+ wpa_s->gas = gas_query_init(wpa_s);
+ if (wpa_s->gas == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to initialize GAS query");
return -1;
}
-#endif /* CONFIG_IBSS_RSN */
+
+#ifdef CONFIG_P2P
+ if (wpas_p2p_init(wpa_s->global, wpa_s) < 0) {
+ wpa_msg(wpa_s, MSG_ERROR, "Failed to init P2P");
+ return -1;
+ }
+#endif /* CONFIG_P2P */
if (wpa_bss_init(wpa_s) < 0)
return -1;
wpa_s->next = global->ifaces;
global->ifaces = wpa_s;
- wpa_printf(MSG_DEBUG, "Added interface %s", wpa_s->ifname);
+ wpa_dbg(wpa_s, MSG_DEBUG, "Added interface %s", wpa_s->ifname);
return wpa_s;
}
prev->next = wpa_s->next;
}
- wpa_printf(MSG_DEBUG, "Removing interface %s", wpa_s->ifname);
+ wpa_dbg(wpa_s, MSG_DEBUG, "Removing interface %s", wpa_s->ifname);
+ if (global->p2p_group_formation == wpa_s)
+ global->p2p_group_formation = NULL;
wpa_supplicant_deinit_iface(wpa_s, 1);
os_free(wpa_s);
/**
+ * wpa_supplicant_get_eap_mode - Get the current EAP mode
+ * @wpa_s: Pointer to the network interface
+ * Returns: Pointer to the eap mode or the string "UNKNOWN" if not found
+ */
+const char * wpa_supplicant_get_eap_mode(struct wpa_supplicant *wpa_s)
+{
+ const char *eapol_method;
+
+ if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) == 0 &&
+ wpa_s->key_mgmt != WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
+ return "NO-EAP";
+ }
+
+ eapol_method = eapol_sm_get_method_name(wpa_s->eapol);
+ if (eapol_method == NULL)
+ return "UNKNOWN-EAP";
+
+ return eapol_method;
+}
+
+
+/**
* wpa_supplicant_get_iface - Get a new network interface
* @global: Pointer to global data from wpa_supplicant_init()
* @ifname: Interface name
}
+#ifndef CONFIG_NO_WPA_MSG
+static const char * wpa_supplicant_msg_ifname_cb(void *ctx)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ if (wpa_s == NULL)
+ return NULL;
+ return wpa_s->ifname;
+}
+#endif /* CONFIG_NO_WPA_MSG */
+
+
/**
* wpa_supplicant_init - Initialize %wpa_supplicant
* @params: Parameters for %wpa_supplicant
if (params == NULL)
return NULL;
+#ifdef CONFIG_DRIVER_NDIS
+ {
+ void driver_ndis_init_ops(void);
+ driver_ndis_init_ops();
+ }
+#endif /* CONFIG_DRIVER_NDIS */
+
+#ifndef CONFIG_NO_WPA_MSG
+ wpa_msg_register_ifname_cb(wpa_supplicant_msg_ifname_cb);
+#endif /* CONFIG_NO_WPA_MSG */
+
wpa_debug_open_file(params->wpa_debug_file_path);
if (params->wpa_debug_syslog)
wpa_debug_open_syslog();
global = os_zalloc(sizeof(*global));
if (global == NULL)
return NULL;
+ dl_list_init(&global->p2p_srv_bonjour);
+ dl_list_init(&global->p2p_srv_upnp);
global->params.daemonize = params->daemonize;
global->params.wait_for_monitor = params->wait_for_monitor;
global->params.dbus_ctrl_interface = params->dbus_ctrl_interface;
wpa_debug_timestamp = global->params.wpa_debug_timestamp =
params->wpa_debug_timestamp;
+ wpa_printf(MSG_DEBUG, "wpa_supplicant v" VERSION_STR);
+
if (eloop_init()) {
wpa_printf(MSG_ERROR, "Failed to initialize event loop");
wpa_supplicant_deinit(global);
return NULL;
}
+ random_init(params->entropy_file);
+
global->ctrl_iface = wpa_supplicant_global_ctrl_iface_init(global);
if (global->ctrl_iface == NULL) {
wpa_supplicant_deinit(global);
wpa_supplicant_deinit(global);
return NULL;
}
- for (i = 0; wpa_drivers[i]; i++) {
- if (!wpa_drivers[i]->global_init)
- continue;
- global->drv_priv[i] = wpa_drivers[i]->global_init();
- if (global->drv_priv[i] == NULL) {
- wpa_printf(MSG_ERROR, "Failed to initialize driver "
- "'%s'", wpa_drivers[i]->name);
- wpa_supplicant_deinit(global);
- return NULL;
- }
- }
return global;
}
if (global == NULL)
return;
+#ifdef CONFIG_P2P
+ wpas_p2p_deinit_global(global);
+#endif /* CONFIG_P2P */
+
while (global->ifaces)
wpa_supplicant_remove_iface(global, global->ifaces);
}
os_free(global->drv_priv);
+ random_deinit();
+
eloop_destroy();
if (global->params.pid_file) {
wpa_debug_close_syslog();
wpa_debug_close_file();
}
+
+
+void wpa_supplicant_update_config(struct wpa_supplicant *wpa_s)
+{
+ if ((wpa_s->conf->changed_parameters & CFG_CHANGED_COUNTRY) &&
+ wpa_s->conf->country[0] && wpa_s->conf->country[1]) {
+ char country[3];
+ country[0] = wpa_s->conf->country[0];
+ country[1] = wpa_s->conf->country[1];
+ country[2] = '\0';
+ if (wpa_drv_set_country(wpa_s, country) < 0) {
+ wpa_printf(MSG_ERROR, "Failed to set country code "
+ "'%s'", country);
+ }
+ }
+
+#ifdef CONFIG_WPS
+ wpas_wps_update_config(wpa_s);
+#endif /* CONFIG_WPS */
+
+#ifdef CONFIG_P2P
+ wpas_p2p_update_config(wpa_s);
+#endif /* CONFIG_P2P */
+
+ wpa_s->conf->changed_parameters = 0;
+}
+
+
+static void add_freq(int *freqs, int *num_freqs, int freq)
+{
+ int i;
+
+ for (i = 0; i < *num_freqs; i++) {
+ if (freqs[i] == freq)
+ return;
+ }
+
+ freqs[*num_freqs] = freq;
+ (*num_freqs)++;
+}
+
+
+static int * get_bss_freqs_in_ess(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_bss *bss, *cbss;
+ const int max_freqs = 10;
+ int *freqs;
+ int num_freqs = 0;
+
+ freqs = os_zalloc(sizeof(int) * (max_freqs + 1));
+ if (freqs == NULL)
+ return NULL;
+
+ cbss = wpa_s->current_bss;
+
+ dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+ if (bss == cbss)
+ continue;
+ if (bss->ssid_len == cbss->ssid_len &&
+ os_memcmp(bss->ssid, cbss->ssid, bss->ssid_len) == 0 &&
+ wpa_blacklist_get(wpa_s, bss->bssid) == NULL) {
+ add_freq(freqs, &num_freqs, bss->freq);
+ if (num_freqs == max_freqs)
+ break;
+ }
+ }
+
+ if (num_freqs == 0) {
+ os_free(freqs);
+ freqs = NULL;
+ }
+
+ return freqs;
+}
+
+
+void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid)
+{
+ int timeout;
+ int count;
+ int *freqs = NULL;
+
+ /*
+ * Remove possible authentication timeout since the connection failed.
+ */
+ eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL);
+
+ /*
+ * Add the failed BSSID into the blacklist and speed up next scan
+ * attempt if there could be other APs that could accept association.
+ * The current blacklist count indicates how many times we have tried
+ * connecting to this AP and multiple attempts mean that other APs are
+ * either not available or has already been tried, so that we can start
+ * increasing the delay here to avoid constant scanning.
+ */
+ count = wpa_blacklist_add(wpa_s, bssid);
+ if (count == 1 && wpa_s->current_bss) {
+ /*
+ * This BSS was not in the blacklist before. If there is
+ * another BSS available for the same ESS, we should try that
+ * next. Otherwise, we may as well try this one once more
+ * before allowing other, likely worse, ESSes to be considered.
+ */
+ freqs = get_bss_freqs_in_ess(wpa_s);
+ if (freqs) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Another BSS in this ESS "
+ "has been seen; try it next");
+ wpa_blacklist_add(wpa_s, bssid);
+ /*
+ * On the next scan, go through only the known channels
+ * used in this ESS based on previous scans to speed up
+ * common load balancing use case.
+ */
+ os_free(wpa_s->next_scan_freqs);
+ wpa_s->next_scan_freqs = freqs;
+ }
+ }
+
+ switch (count) {
+ case 1:
+ timeout = 100;
+ break;
+ case 2:
+ timeout = 500;
+ break;
+ case 3:
+ timeout = 1000;
+ break;
+ default:
+ timeout = 5000;
+ }
+
+ /*
+ * TODO: if more than one possible AP is available in scan results,
+ * could try the other ones before requesting a new scan.
+ */
+ wpa_supplicant_req_scan(wpa_s, timeout / 1000,
+ 1000 * (timeout % 1000));
+}
+
+
+int wpas_driver_bss_selection(struct wpa_supplicant *wpa_s)
+{
+ return wpa_s->conf->ap_scan == 2 ||
+ (wpa_s->drv_flags & WPA_DRIVER_FLAGS_BSS_SELECTION);
+}
# Parameters for the control interface. If this is specified, wpa_supplicant
# will open a control interface that is available for external programs to
# manage wpa_supplicant. The meaning of this string depends on which control
-# interface mechanism is used. For all cases, the existance of this parameter
+# interface mechanism is used. For all cases, the existence of this parameter
# in configuration is used to determine whether the control interface is
# enabled.
#
# Config Methods
# List of the supported configuration methods
# Available methods: usba ethernet label display ext_nfc_token int_nfc_token
-# nfc_interface push_button keypad
+# nfc_interface push_button keypad virtual_display physical_display
+# virtual_push_button physical_push_button
+# For WSC 1.0:
#config_methods=label display push_button keypad
+# For WSC 2.0:
+#config_methods=label virtual_display virtual_push_button keypad
# Credential processing
# 0 = process received credentials internally (default)
#filter_ssids=0
+# Interworking (IEEE 802.11u)
+
+# Enable Interworking
+# interworking=1
+
+# Homogenous ESS identifier
+# If this is set, scans will be used to request response only from BSSes
+# belonging to the specified Homogeneous ESS. This is used only if interworking
+# is enabled.
+# hessid=00:11:22:33:44:55
+
+# Home Realm for Interworking
+#home_realm=example.com
+
+# Username for Interworking network selection
+#home_username=user
+
+# Password for Interworking network selection
+#home_password=secret
+
+# CA certificate for Interworking network selection
+#home_ca_cert=/etc/cert/ca.pem
+
+# IMSI in <MCC> | <MNC> | '-' | <MSIN> format
+#home_imsi=232010000000000
+
+# Milenage parameters for SIM/USIM simulator in <Ki>:<OPc>:<SQN> format
+#home_milenage=90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123
+
# network block
#
# Each network (usually AP's sharing the same SSID) is configured as a separate
#
# mixed_cell: This option can be used to configure whether so called mixed
# cells, i.e., networks that use both plaintext and encryption in the same
-# SSID, are allowed when selecting a BSS form scan results.
+# SSID, are allowed when selecting a BSS from scan results.
# 0 = disabled (default)
# 1 = enabled
#
#include "utils/list.h"
#include "common/defs.h"
+#include "config_ssid.h"
extern const char *wpa_supplicant_version;
extern const char *wpa_supplicant_license;
struct scan_info;
struct wpa_bss;
struct wpa_scan_results;
+struct hostapd_hw_modes;
/*
* Forward declarations of private structures used within the ctrl_iface
* created.
*/
char *override_ctrl_interface;
+
+ /**
+ * entropy_file - Optional entropy file
+ *
+ * This parameter can be used to configure wpa_supplicant to maintain
+ * its internal entropy store over restarts.
+ */
+ char *entropy_file;
+};
+
+struct p2p_srv_bonjour {
+ struct dl_list list;
+ struct wpabuf *query;
+ struct wpabuf *resp;
+};
+
+struct p2p_srv_upnp {
+ struct dl_list list;
+ u8 version;
+ char *service;
};
/**
void **drv_priv;
size_t drv_count;
struct os_time suspend_time;
+ struct p2p_data *p2p;
+ struct wpa_supplicant *p2p_group_formation;
+ u8 p2p_dev_addr[ETH_ALEN];
+ struct dl_list p2p_srv_bonjour; /* struct p2p_srv_bonjour */
+ struct dl_list p2p_srv_upnp; /* struct p2p_srv_upnp */
+ int p2p_disabled;
+ int cross_connection;
};
-struct wpa_client_mlme {
-#ifdef CONFIG_CLIENT_MLME
- enum {
- IEEE80211_DISABLED, IEEE80211_AUTHENTICATE,
- IEEE80211_ASSOCIATE, IEEE80211_ASSOCIATED,
- IEEE80211_IBSS_SEARCH, IEEE80211_IBSS_JOINED
- } state;
- u8 prev_bssid[ETH_ALEN];
- u8 ssid[32];
- size_t ssid_len;
- u16 aid;
- u16 ap_capab, capab;
- u8 *extra_ie; /* to be added to the end of AssocReq */
- size_t extra_ie_len;
- u8 *extra_probe_ie; /* to be added to the end of ProbeReq */
- size_t extra_probe_ie_len;
- enum wpa_key_mgmt key_mgmt;
-
- /* The last AssocReq/Resp IEs */
- u8 *assocreq_ies, *assocresp_ies;
- size_t assocreq_ies_len, assocresp_ies_len;
-
- int auth_tries, assoc_tries;
-
- unsigned int ssid_set:1;
- unsigned int bssid_set:1;
- unsigned int prev_bssid_set:1;
- unsigned int authenticated:1;
- unsigned int associated:1;
- unsigned int probereq_poll:1;
- unsigned int use_protection:1;
- unsigned int create_ibss:1;
- unsigned int mixed_cell:1;
- unsigned int wmm_enabled:1;
-
- struct os_time last_probe;
-
- unsigned int auth_algs; /* bitfield of allowed auth algs
- * (WPA_AUTH_ALG_*) */
- int auth_alg; /* currently used IEEE 802.11 authentication algorithm */
- int auth_transaction;
-
- struct os_time ibss_join_req;
- u8 *probe_resp; /* ProbeResp template for IBSS */
- size_t probe_resp_len;
- u32 supp_rates_bits;
-
- int wmm_last_param_set;
-
- int sta_scanning;
- int scan_hw_mode_idx;
- int scan_channel_idx;
- enum { SCAN_SET_CHANNEL, SCAN_SEND_PROBE } scan_state;
- struct os_time last_scan_completed;
- int scan_oper_channel;
- int scan_oper_freq;
- int scan_oper_phymode;
- u8 scan_ssid[32];
- size_t scan_ssid_len;
- int scan_skip_11b;
- int *scan_freqs;
-
- struct ieee80211_sta_bss *sta_bss_list;
-#define STA_HASH_SIZE 256
-#define STA_HASH(sta) (sta[5])
- struct ieee80211_sta_bss *sta_bss_hash[STA_HASH_SIZE];
-
- int cts_protect_erp_frames;
-
- enum hostapd_hw_mode phymode; /* current mode */
- struct hostapd_hw_modes *modes;
- size_t num_modes;
- unsigned int hw_modes; /* bitfield of allowed hardware modes;
- * (1 << HOSTAPD_MODE_*) */
- int num_curr_rates;
- int *curr_rates;
- int freq; /* The current frequency in MHz */
- int channel; /* The current IEEE 802.11 channel number */
-
-#ifdef CONFIG_IEEE80211R
- u8 current_md[6];
- u8 *ft_ies;
- size_t ft_ies_len;
-#endif /* CONFIG_IEEE80211R */
-
- void (*public_action_cb)(void *ctx, const u8 *buf, size_t len,
- int freq);
- void *public_action_cb_ctx;
-
-#else /* CONFIG_CLIENT_MLME */
- int dummy; /* to keep MSVC happy */
-#endif /* CONFIG_CLIENT_MLME */
+enum offchannel_send_action_result {
+ OFFCHANNEL_SEND_ACTION_SUCCESS /* Frame was send and acknowledged */,
+ OFFCHANNEL_SEND_ACTION_NO_ACK /* Frame was sent, but not acknowledged
+ */,
+ OFFCHANNEL_SEND_ACTION_FAILED /* Frame was not sent due to a failure */
};
/**
*/
struct wpa_supplicant {
struct wpa_global *global;
+ struct wpa_supplicant *parent;
struct wpa_supplicant *next;
struct l2_packet_data *l2;
struct l2_packet_data *l2_br;
#endif /* CONFIG_CTRL_IFACE_DBUS */
#ifdef CONFIG_CTRL_IFACE_DBUS_NEW
char *dbus_new_path;
+ char *dbus_groupobj_path;
#endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
char bridge_ifname[16];
os_time_t last_michael_mic_error;
u8 bssid[ETH_ALEN];
u8 pending_bssid[ETH_ALEN]; /* If wpa_state == WPA_ASSOCIATING, this
- * field contains the targer BSSID. */
+ * field contains the target BSSID. */
int reassociate; /* reassociation requested */
int disconnected; /* all connections disabled; i.e., do no reassociate
* before this has been cleared */
int pairwise_cipher;
int group_cipher;
int key_mgmt;
+ int wpa_proto;
int mgmt_group_cipher;
void *drv_priv; /* private data used by driver_ops */
*/
#define WILDCARD_SSID_SCAN ((struct wpa_ssid *) 1)
+ struct wpa_ssid *prev_sched_ssid; /* last SSID used in sched scan */
+ int sched_scan_timeout;
+ int sched_scan_interval;
+ int first_sched_scan;
+ int sched_scan_timed_out;
+
void (*scan_res_handler)(struct wpa_supplicant *wpa_s,
struct wpa_scan_results *scan_res);
struct dl_list bss; /* struct wpa_bss::list */
enum wpa_states wpa_state;
int scanning;
+ int sched_scanning;
int new_connection;
int reassociated_connection;
int scan_req; /* manual scan request; this forces a scan even if there
* are no enabled networks in the configuration */
int scan_runs; /* number of scan runs since WPS was started */
+ int *next_scan_freqs;
+ int scan_interval; /* time in sec between scans to find suitable AP */
- struct wpa_client_mlme mlme;
unsigned int drv_flags;
int max_scan_ssids;
+ int max_sched_scan_ssids;
+ int sched_scan_supported;
+ unsigned int max_match_sets;
unsigned int max_remain_on_chan;
+ unsigned int max_stations;
int pending_mic_error_report;
int pending_mic_error_pairwise;
struct ibss_rsn *ibss_rsn;
+ int set_sta_uapsd;
+ int sta_uapsd;
+ int set_ap_uapsd;
+ int ap_uapsd;
+
#ifdef CONFIG_SME
struct {
u8 ssid[32];
size_t ssid_len;
int freq;
- u8 assoc_req_ie[80];
+ u8 assoc_req_ie[200];
size_t assoc_req_ie_len;
int mfp;
int ft_used;
u8 prev_bssid[ETH_ALEN];
int prev_bssid_set;
int auth_alg;
+ int proto;
+
+ int sa_query_count; /* number of pending SA Query requests;
+ * 0 = no SA Query in progress */
+ int sa_query_timed_out;
+ u8 *sa_query_trans_id; /* buffer of WLAN_SA_QUERY_TR_ID_LEN *
+ * sa_query_count octets of pending
+ * SA Query transaction identifiers */
+ struct os_time sa_query_start;
} sme;
#endif /* CONFIG_SME */
void *ap_configured_cb_data;
#endif /* CONFIG_AP */
+ unsigned int off_channel_freq;
+ struct wpabuf *pending_action_tx;
+ u8 pending_action_src[ETH_ALEN];
+ u8 pending_action_dst[ETH_ALEN];
+ u8 pending_action_bssid[ETH_ALEN];
+ unsigned int pending_action_freq;
+ int pending_action_no_cck;
+ int pending_action_without_roc;
+ void (*pending_action_tx_status_cb)(struct wpa_supplicant *wpa_s,
+ unsigned int freq, const u8 *dst,
+ const u8 *src, const u8 *bssid,
+ const u8 *data, size_t data_len,
+ enum offchannel_send_action_result
+ result);
+ unsigned int roc_waiting_drv_freq;
+ int action_tx_wait_time;
+
+#ifdef CONFIG_P2P
+ struct p2p_go_neg_results *go_params;
+ int create_p2p_iface;
+ u8 pending_interface_addr[ETH_ALEN];
+ char pending_interface_name[100];
+ int pending_interface_type;
+ int p2p_group_idx;
+ unsigned int pending_listen_freq;
+ unsigned int pending_listen_duration;
+ enum {
+ NOT_P2P_GROUP_INTERFACE,
+ P2P_GROUP_INTERFACE_PENDING,
+ P2P_GROUP_INTERFACE_GO,
+ P2P_GROUP_INTERFACE_CLIENT
+ } p2p_group_interface;
+ struct p2p_group *p2p_group;
+ int p2p_long_listen; /* remaining time in long Listen state in ms */
+ char p2p_pin[10];
+ int p2p_wps_method;
+ u8 p2p_auth_invite[ETH_ALEN];
+ int p2p_sd_over_ctrl_iface;
+ int p2p_in_provisioning;
+ int pending_invite_ssid_id;
+ int show_group_started;
+ u8 go_dev_addr[ETH_ALEN];
+ int pending_pd_before_join;
+ u8 pending_join_iface_addr[ETH_ALEN];
+ u8 pending_join_dev_addr[ETH_ALEN];
+ int pending_join_wps_method;
+ int p2p_join_scan_count;
+ int force_long_sd;
+
+ /*
+ * Whether cross connection is disallowed by the AP to which this
+ * interface is associated (only valid if there is an association).
+ */
+ int cross_connect_disallowed;
+
+ /*
+ * Whether this P2P group is configured to use cross connection (only
+ * valid if this is P2P GO interface). The actual cross connect packet
+ * forwarding may not be configured depending on the uplink status.
+ */
+ int cross_connect_enabled;
+
+ /* Whether cross connection forwarding is in use at the moment. */
+ int cross_connect_in_use;
+
+ /*
+ * Uplink interface name for cross connection
+ */
+ char cross_connect_uplink[100];
+
+ enum {
+ P2P_GROUP_REMOVAL_UNKNOWN,
+ P2P_GROUP_REMOVAL_REQUESTED,
+ P2P_GROUP_REMOVAL_IDLE_TIMEOUT,
+ P2P_GROUP_REMOVAL_UNAVAILABLE
+ } removal_reason;
+
+ unsigned int p2p_cb_on_scan_complete:1;
+#endif /* CONFIG_P2P */
+
struct wpa_ssid *bgscan_ssid;
const struct bgscan_ops *bgscan;
void *bgscan_priv;
- int connect_without_scan;
+ struct wpa_ssid *connect_without_scan;
int after_wps;
unsigned int wps_freq;
+ int wps_fragment_size;
+ int auto_reconnect_disabled;
+
+ /* Channel preferences for AP/P2P GO use */
+ int best_24_freq;
+ int best_5_freq;
+ int best_overall_freq;
+
+ struct gas_query *gas;
+
+#ifdef CONFIG_INTERWORKING
+ unsigned int fetch_anqp_in_progress:1;
+ unsigned int network_select:1;
+ unsigned int auto_select:1;
+#endif /* CONFIG_INTERWORKING */
+ unsigned int drv_capa_known;
+
+ struct {
+ struct hostapd_hw_modes *modes;
+ u16 num_modes;
+ u16 flags;
+ } hw;
};
int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s);
const char * wpa_supplicant_state_txt(enum wpa_states state);
+int wpa_supplicant_update_mac_addr(struct wpa_supplicant *wpa_s);
int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s);
int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss, struct wpa_ssid *ssid,
void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s,
enum wpa_states state);
struct wpa_ssid * wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s);
+const char * wpa_supplicant_get_eap_mode(struct wpa_supplicant *wpa_s);
void wpa_supplicant_cancel_auth_timeout(struct wpa_supplicant *wpa_s);
void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s,
int reason_code);
struct wpa_ssid *ssid);
int wpa_supplicant_set_ap_scan(struct wpa_supplicant *wpa_s,
int ap_scan);
+int wpa_supplicant_set_bss_expiration_age(struct wpa_supplicant *wpa_s,
+ unsigned int expire_age);
+int wpa_supplicant_set_bss_expiration_count(struct wpa_supplicant *wpa_s,
+ unsigned int expire_count);
int wpa_supplicant_set_debug_params(struct wpa_global *global,
int debug_level, int debug_timestamp,
int debug_show_keys);
+void free_hw_features(struct wpa_supplicant *wpa_s);
void wpa_show_license(void);
const u8 *buf, size_t len);
enum wpa_key_mgmt key_mgmt2driver(int key_mgmt);
enum wpa_cipher cipher_suite2driver(int cipher);
+void wpa_supplicant_update_config(struct wpa_supplicant *wpa_s);
+void wpa_supplicant_clear_status(struct wpa_supplicant *wpa_s);
+void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid);
+int wpas_driver_bss_selection(struct wpa_supplicant *wpa_s);
/* events.c */
void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s);
-void wpa_supplicant_connect(struct wpa_supplicant *wpa_s,
- struct wpa_bss *selected,
- struct wpa_ssid *ssid);
+int wpa_supplicant_connect(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *selected,
+ struct wpa_ssid *ssid);
+void wpa_supplicant_stop_countermeasures(void *eloop_ctx, void *sock_ctx);
+void wpa_supplicant_delayed_mic_error_report(void *eloop_ctx, void *sock_ctx);
/* eap_register.c */
int eap_register_methods(void);
+/**
+ * Utility method to tell if a given network is a persistent group
+ * @ssid: Network object
+ * Returns: 1 if network is a persistent group, 0 otherwise
+ */
+static inline int network_is_persistent_group(struct wpa_ssid *ssid)
+{
+ return ((ssid->disabled == 2) || ssid->p2p_persistent_group);
+}
+
#endif /* WPA_SUPPLICANT_I_H */
#include "wpa_supplicant_i.h"
#include "driver_i.h"
#include "rsn_supp/pmksa_cache.h"
-#include "mlme.h"
#include "sme.h"
#include "common/ieee802_11_defs.h"
#include "common/wpa_ctrl.h"
#include "wps_supplicant.h"
#include "bss.h"
#include "scan.h"
+#include "notify.h"
#ifndef CONFIG_NO_CONFIG_BLOBS
wpa_s->group_cipher = cipher;
}
return wpa_drv_set_key(wpa_s, WPA_ALG_WEP,
- unicast ? wpa_s->bssid :
- (u8 *) "\xff\xff\xff\xff\xff\xff",
- keyidx, unicast, (u8 *) "", 0, key, keylen);
+ unicast ? wpa_s->bssid : NULL,
+ keyidx, unicast, NULL, 0, key, keylen);
}
"handshake");
pmk_len = PMK_LEN;
- res = eapol_sm_get_key(eapol, pmk, PMK_LEN);
- if (res) {
- /*
- * EAP-LEAP is an exception from other EAP methods: it
- * uses only 16-byte PMK.
- */
- res = eapol_sm_get_key(eapol, pmk, 16);
- pmk_len = 16;
+ if (wpa_key_mgmt_ft(wpa_s->key_mgmt)) {
+#ifdef CONFIG_IEEE80211R
+ u8 buf[2 * PMK_LEN];
+ wpa_printf(MSG_DEBUG, "RSN: Use FT XXKey as PMK for "
+ "driver-based 4-way hs and FT");
+ res = eapol_sm_get_key(eapol, buf, 2 * PMK_LEN);
+ if (res == 0) {
+ os_memcpy(pmk, buf + PMK_LEN, PMK_LEN);
+ os_memset(buf, 0, sizeof(buf));
+ }
+#else /* CONFIG_IEEE80211R */
+ res = -1;
+#endif /* CONFIG_IEEE80211R */
+ } else {
+ res = eapol_sm_get_key(eapol, pmk, PMK_LEN);
+ if (res) {
+ /*
+ * EAP-LEAP is an exception from other EAP methods: it
+ * uses only 16-byte PMK.
+ */
+ res = eapol_sm_get_key(eapol, pmk, 16);
+ pmk_len = 16;
+ }
}
if (res) {
return;
}
+ wpa_hexdump_key(MSG_DEBUG, "RSN: Configure PMK for driver-based 4-way "
+ "handshake", pmk, pmk_len);
+
if (wpa_drv_set_key(wpa_s, WPA_ALG_PMK, NULL, 0, 0, NULL, 0, pmk,
pmk_len)) {
wpa_printf(MSG_DEBUG, "Failed to set PMK to the driver");
static int wpa_supplicant_get_bssid(void *ctx, u8 *bssid)
{
struct wpa_supplicant *wpa_s = ctx;
- if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) {
- os_memcpy(bssid, wpa_s->bssid, ETH_ALEN);
- return 0;
- }
return wpa_drv_get_bssid(wpa_s, bssid);
}
const u8 *ies, size_t ies_len)
{
struct wpa_supplicant *wpa_s = ctx;
- if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)
- return ieee80211_sta_update_ft_ies(wpa_s, md, ies, ies_len);
if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)
return sme_update_ft_ies(wpa_s, md, ies, ies_len);
return wpa_drv_update_ft_ies(wpa_s, md, ies, ies_len);
const u8 *ies, size_t ies_len)
{
struct wpa_supplicant *wpa_s = ctx;
- if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)
- return ieee80211_sta_send_ft_action(wpa_s, action, target_ap,
- ies, ies_len);
return wpa_drv_send_ft_action(wpa_s, action, target_ap, ies, ies_len);
}
struct wpa_driver_auth_params params;
struct wpa_bss *bss;
- if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)
- return -1;
-
bss = wpa_bss_get_bssid(wpa_s, target_ap);
if (bss == NULL)
return -1;
#endif /* CONFIG_NO_WPA */
+#ifdef CONFIG_TDLS
+
+static int wpa_supplicant_tdls_get_capa(void *ctx, int *tdls_supported,
+ int *tdls_ext_setup)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ *tdls_supported = 0;
+ *tdls_ext_setup = 0;
+
+ if (!wpa_s->drv_capa_known)
+ return -1;
+
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)
+ *tdls_supported = 1;
+
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP)
+ *tdls_ext_setup = 1;
+
+ return 0;
+}
+
+
+static int wpa_supplicant_send_tdls_mgmt(void *ctx, const u8 *dst,
+ u8 action_code, u8 dialog_token,
+ u16 status_code, const u8 *buf,
+ size_t len)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ return wpa_drv_send_tdls_mgmt(wpa_s, dst, action_code, dialog_token,
+ status_code, buf, len);
+}
+
+
+static int wpa_supplicant_tdls_oper(void *ctx, int oper, const u8 *peer)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ return wpa_drv_tdls_oper(wpa_s, oper, peer);
+}
+
+
+static int wpa_supplicant_tdls_peer_addset(
+ void *ctx, const u8 *peer, int add, u16 capability,
+ const u8 *supp_rates, size_t supp_rates_len)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ struct hostapd_sta_add_params params;
+
+ params.addr = peer;
+ params.aid = 1;
+ params.capability = capability;
+ params.flags = WPA_STA_TDLS_PEER | WPA_STA_AUTHORIZED;
+ params.ht_capabilities = NULL;
+ params.listen_interval = 0;
+ params.supp_rates = supp_rates;
+ params.supp_rates_len = supp_rates_len;
+ params.set = !add;
+
+ return wpa_drv_sta_add(wpa_s, ¶ms);
+}
+
+#endif /* CONFIG_TDLS */
+
+
+enum wpa_ctrl_req_type wpa_supplicant_ctrl_req_from_string(const char *field)
+{
+ if (os_strcmp(field, "IDENTITY") == 0)
+ return WPA_CTRL_REQ_EAP_IDENTITY;
+ else if (os_strcmp(field, "PASSWORD") == 0)
+ return WPA_CTRL_REQ_EAP_PASSWORD;
+ else if (os_strcmp(field, "NEW_PASSWORD") == 0)
+ return WPA_CTRL_REQ_EAP_NEW_PASSWORD;
+ else if (os_strcmp(field, "PIN") == 0)
+ return WPA_CTRL_REQ_EAP_PIN;
+ else if (os_strcmp(field, "OTP") == 0)
+ return WPA_CTRL_REQ_EAP_OTP;
+ else if (os_strcmp(field, "PASSPHRASE") == 0)
+ return WPA_CTRL_REQ_EAP_PASSPHRASE;
+ return WPA_CTRL_REQ_UNKNOWN;
+}
+
+
+const char * wpa_supplicant_ctrl_req_to_string(enum wpa_ctrl_req_type field,
+ const char *default_txt,
+ const char **txt)
+{
+ const char *ret = NULL;
+
+ *txt = default_txt;
+
+ switch (field) {
+ case WPA_CTRL_REQ_EAP_IDENTITY:
+ *txt = "Identity";
+ ret = "IDENTITY";
+ break;
+ case WPA_CTRL_REQ_EAP_PASSWORD:
+ *txt = "Password";
+ ret = "PASSWORD";
+ break;
+ case WPA_CTRL_REQ_EAP_NEW_PASSWORD:
+ *txt = "New Password";
+ ret = "NEW_PASSWORD";
+ break;
+ case WPA_CTRL_REQ_EAP_PIN:
+ *txt = "PIN";
+ ret = "PIN";
+ break;
+ case WPA_CTRL_REQ_EAP_OTP:
+ ret = "OTP";
+ break;
+ case WPA_CTRL_REQ_EAP_PASSPHRASE:
+ *txt = "Private key passphrase";
+ ret = "PASSPHRASE";
+ break;
+ default:
+ break;
+ }
+
+ /* txt needs to be something */
+ if (*txt == NULL) {
+ wpa_printf(MSG_WARNING, "No message for request %d", field);
+ ret = NULL;
+ }
+
+ return ret;
+}
+
#ifdef IEEE8021X_EAPOL
#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
-static void wpa_supplicant_eap_param_needed(void *ctx, const char *field,
- const char *txt)
+static void wpa_supplicant_eap_param_needed(void *ctx,
+ enum wpa_ctrl_req_type field,
+ const char *default_txt)
{
struct wpa_supplicant *wpa_s = ctx;
struct wpa_ssid *ssid = wpa_s->current_ssid;
+ const char *field_name, *txt = NULL;
char *buf;
size_t buflen;
int len;
if (ssid == NULL)
return;
+ wpas_notify_network_request(wpa_s, ssid, field, default_txt);
+
+ field_name = wpa_supplicant_ctrl_req_to_string(field, default_txt,
+ &txt);
+ if (field_name == NULL) {
+ wpa_printf(MSG_WARNING, "Unhandled EAP param %d needed",
+ field);
+ return;
+ }
+
buflen = 100 + os_strlen(txt) + ssid->ssid_len;
buf = os_malloc(buflen);
if (buf == NULL)
return;
len = os_snprintf(buf, buflen,
WPA_CTRL_REQ "%s-%d:%s needed for SSID ",
- field, ssid->id, txt);
+ field_name, ssid->id, txt);
if (len < 0 || (size_t) len >= buflen) {
os_free(buf);
return;
authorized ? "Authorized" : "Unauthorized");
wpa_drv_set_supp_port(wpa_s, authorized);
}
+
+
+static void wpa_supplicant_cert_cb(void *ctx, int depth, const char *subject,
+ const char *cert_hash,
+ const struct wpabuf *cert)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ wpas_notify_certification(wpa_s, depth, subject, cert_hash, cert);
+}
#endif /* IEEE8021X_EAPOL */
ctx->eap_param_needed = wpa_supplicant_eap_param_needed;
ctx->port_cb = wpa_supplicant_port_cb;
ctx->cb = wpa_supplicant_eapol_cb;
+ ctx->cert_cb = wpa_supplicant_cert_cb;
ctx->cb_ctx = wpa_s;
wpa_s->eapol = eapol_sm_init(ctx);
if (wpa_s->eapol == NULL) {
}
+static void wpa_supplicant_set_rekey_offload(void *ctx, const u8 *kek,
+ const u8 *kck,
+ const u8 *replay_ctr)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ wpa_drv_set_rekey_info(wpa_s, kek, kck, replay_ctr);
+}
+
+
int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s)
{
#ifndef CONFIG_NO_WPA
ctx->send_ft_action = wpa_supplicant_send_ft_action;
ctx->mark_authenticated = wpa_supplicant_mark_authenticated;
#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_TDLS
+ ctx->tdls_get_capa = wpa_supplicant_tdls_get_capa;
+ ctx->send_tdls_mgmt = wpa_supplicant_send_tdls_mgmt;
+ ctx->tdls_oper = wpa_supplicant_tdls_oper;
+ ctx->tdls_peer_addset = wpa_supplicant_tdls_peer_addset;
+#endif /* CONFIG_TDLS */
+ ctx->set_rekey_offload = wpa_supplicant_set_rekey_offload;
wpa_s->wpa = wpa_sm_init(ctx);
if (wpa_s->wpa == NULL) {
conf.peerkey_enabled = ssid->peerkey;
conf.allowed_pairwise_cipher = ssid->pairwise_cipher;
#ifdef IEEE8021X_EAPOL
+ conf.proactive_key_caching = ssid->proactive_key_caching;
conf.eap_workaround = ssid->eap_workaround;
conf.eap_conf_ctx = &ssid->eap;
#endif /* IEEE8021X_EAPOL */
#ifndef WPAS_GLUE_H
#define WPAS_GLUE_H
+enum wpa_ctrl_req_type;
+
int wpa_supplicant_init_eapol(struct wpa_supplicant *wpa_s);
int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s);
void wpa_supplicant_rsn_supp_set_config(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid);
+const char * wpa_supplicant_ctrl_req_to_string(enum wpa_ctrl_req_type field,
+ const char *default_txt,
+ const char **txt);
+
+enum wpa_ctrl_req_type wpa_supplicant_ctrl_req_from_string(const char *field);
+
#endif /* WPAS_GLUE_H */
#include "common/wpa_ctrl.h"
#include "eap_common/eap_wsc_common.h"
#include "eap_peer/eap.h"
+#include "eapol_supp/eapol_supp_sm.h"
#include "rsn_supp/wpa.h"
#include "config.h"
#include "wpa_supplicant_i.h"
#include "blacklist.h"
#include "bss.h"
#include "scan.h"
+#include "ap.h"
+#include "p2p/p2p.h"
+#include "p2p_supplicant.h"
#include "wps_supplicant.h"
+#ifndef WPS_PIN_SCAN_IGNORE_SEL_REG
#define WPS_PIN_SCAN_IGNORE_SEL_REG 3
+#endif /* WPS_PIN_SCAN_IGNORE_SEL_REG */
static void wpas_wps_timeout(void *eloop_ctx, void *timeout_ctx);
static void wpas_clear_wps(struct wpa_supplicant *wpa_s);
}
eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL);
+ if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS && !wpa_s->wps_success)
+ wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_FAIL);
if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS && wpa_s->current_ssid &&
!(wpa_s->current_ssid->key_mgmt & WPA_KEY_MGMT_WPS)) {
+ int disabled = wpa_s->current_ssid->disabled;
+ unsigned int freq = wpa_s->assoc_freq;
wpa_printf(MSG_DEBUG, "WPS: Network configuration replaced - "
- "try to associate with the received credential");
+ "try to associate with the received credential "
+ "(freq=%u)", freq);
wpa_supplicant_deauthenticate(wpa_s,
WLAN_REASON_DEAUTH_LEAVING);
+ if (disabled) {
+ wpa_printf(MSG_DEBUG, "WPS: Current network is "
+ "disabled - wait for user to enable");
+ return 1;
+ }
wpa_s->after_wps = 5;
- wpa_s->wps_freq = wpa_s->assoc_freq;
+ wpa_s->wps_freq = freq;
wpa_s->reassociate = 1;
wpa_supplicant_req_scan(wpa_s, 0, 0);
return 1;
if (wpa_drv_get_capa(wpa_s, &capa))
return; /* Unknown what driver supports */
+ if (ssid->ssid == NULL)
+ return;
bss = wpa_bss_get(wpa_s, cred->mac_addr, ssid->ssid, ssid->ssid_len);
if (bss == NULL) {
wpa_printf(MSG_DEBUG, "WPS: The AP was not found from BSS "
struct wpa_ssid *ssid = wpa_s->current_ssid;
u8 key_idx = 0;
u16 auth_type;
+#ifdef CONFIG_WPS_REG_DISABLE_OPEN
+ int registrar = 0;
+#endif /* CONFIG_WPS_REG_DISABLE_OPEN */
if ((wpa_s->conf->wps_cred_processing == 1 ||
wpa_s->conf->wps_cred_processing == 2) && cred->cred_attr) {
if (ssid && (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) {
wpa_printf(MSG_DEBUG, "WPS: Replace WPS network block based "
"on the received credential");
+#ifdef CONFIG_WPS_REG_DISABLE_OPEN
+ if (ssid->eap.identity &&
+ ssid->eap.identity_len == WSC_ID_REGISTRAR_LEN &&
+ os_memcmp(ssid->eap.identity, WSC_ID_REGISTRAR,
+ WSC_ID_REGISTRAR_LEN) == 0)
+ registrar = 1;
+#endif /* CONFIG_WPS_REG_DISABLE_OPEN */
os_free(ssid->eap.identity);
ssid->eap.identity = NULL;
ssid->eap.identity_len = 0;
ssid->eap.phase1 = NULL;
os_free(ssid->eap.eap_methods);
ssid->eap.eap_methods = NULL;
+ if (!ssid->p2p_group)
+ ssid->temporary = 0;
} else {
wpa_printf(MSG_DEBUG, "WPS: Create a new network based on the "
"received credential");
ssid->auth_alg = WPA_AUTH_ALG_OPEN;
ssid->key_mgmt = WPA_KEY_MGMT_NONE;
ssid->proto = 0;
+#ifdef CONFIG_WPS_REG_DISABLE_OPEN
+ if (registrar) {
+ wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_OPEN_NETWORK
+ "id=%d - Credentials for an open "
+ "network disabled by default - use "
+ "'select_network %d' to enable",
+ ssid->id, ssid->id);
+ ssid->disabled = 1;
+ }
+#endif /* CONFIG_WPS_REG_DISABLE_OPEN */
break;
case WPS_AUTH_SHARED:
ssid->auth_alg = WPA_AUTH_ALG_SHARED;
return -1;
}
ssid->psk_set = 1;
+ ssid->export_keys = 1;
} else if (cred->key_len >= 8 && cred->key_len < 2 * PMK_LEN) {
os_free(ssid->passphrase);
ssid->passphrase = os_malloc(cred->key_len + 1);
os_memcpy(ssid->passphrase, cred->key, cred->key_len);
ssid->passphrase[cred->key_len] = '\0';
wpa_config_update_psk(ssid);
+ ssid->export_keys = 1;
} else {
wpa_printf(MSG_ERROR, "WPS: Invalid Network Key "
"length %lu",
}
+#ifdef CONFIG_P2P
+static void wpas_wps_pbc_overlap_cb(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ wpas_p2p_notif_pbc_overlap(wpa_s);
+}
+#endif /* CONFIG_P2P */
+
+
static void wpa_supplicant_wps_event_m2d(struct wpa_supplicant *wpa_s,
struct wps_event_m2d *m2d)
{
"dev_password_id=%d config_error=%d",
m2d->dev_password_id, m2d->config_error);
wpas_notify_wps_event_m2d(wpa_s, m2d);
+#ifdef CONFIG_P2P
+ if (wpa_s->parent && wpa_s->parent != wpa_s) {
+ wpa_msg(wpa_s->parent, MSG_INFO, WPS_EVENT_M2D
+ "dev_password_id=%d config_error=%d",
+ m2d->dev_password_id, m2d->config_error);
+ }
+ if (m2d->config_error == WPS_CFG_MULTIPLE_PBC_DETECTED) {
+ /*
+ * Notify P2P from eloop timeout to avoid issues with the
+ * interface getting removed while processing a message.
+ */
+ eloop_register_timeout(0, 0, wpas_wps_pbc_overlap_cb, wpa_s,
+ NULL);
+ }
+#endif /* CONFIG_P2P */
}
+static const char * wps_event_fail_reason[NUM_WPS_EI_VALUES] = {
+ "No Error", /* WPS_EI_NO_ERROR */
+ "TKIP Only Prohibited", /* WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED */
+ "WEP Prohibited" /* WPS_EI_SECURITY_WEP_PROHIBITED */
+};
+
static void wpa_supplicant_wps_event_fail(struct wpa_supplicant *wpa_s,
struct wps_event_fail *fail)
{
- wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_FAIL "msg=%d", fail->msg);
+ if (fail->error_indication > 0 &&
+ fail->error_indication < NUM_WPS_EI_VALUES) {
+ wpa_msg(wpa_s, MSG_INFO,
+ WPS_EVENT_FAIL "msg=%d config_error=%d reason=%d (%s)",
+ fail->msg, fail->config_error, fail->error_indication,
+ wps_event_fail_reason[fail->error_indication]);
+ if (wpa_s->parent && wpa_s->parent != wpa_s)
+ wpa_msg(wpa_s->parent, MSG_INFO, WPS_EVENT_FAIL
+ "msg=%d config_error=%d reason=%d (%s)",
+ fail->msg, fail->config_error,
+ fail->error_indication,
+ wps_event_fail_reason[fail->error_indication]);
+ } else {
+ wpa_msg(wpa_s, MSG_INFO,
+ WPS_EVENT_FAIL "msg=%d config_error=%d",
+ fail->msg, fail->config_error);
+ if (wpa_s->parent && wpa_s->parent != wpa_s)
+ wpa_msg(wpa_s->parent, MSG_INFO, WPS_EVENT_FAIL
+ "msg=%d config_error=%d",
+ fail->msg, fail->config_error);
+ }
wpas_clear_wps(wpa_s);
wpas_notify_wps_event_fail(wpa_s, fail);
+#ifdef CONFIG_P2P
+ wpas_p2p_wps_failed(wpa_s, fail);
+#endif /* CONFIG_P2P */
}
wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_SUCCESS);
wpa_s->wps_success = 1;
wpas_notify_wps_event_success(wpa_s);
+#ifdef CONFIG_P2P
+ wpas_p2p_wps_success(wpa_s, wpa_s->bssid, 0);
+#endif /* CONFIG_P2P */
}
}
+static void wpa_supplicant_wps_event_er_ap_settings(
+ struct wpa_supplicant *wpa_s,
+ struct wps_event_er_ap_settings *ap_settings)
+{
+ char uuid_str[100];
+ char key_str[65];
+ const struct wps_credential *cred = ap_settings->cred;
+
+ key_str[0] = '\0';
+ if (cred->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) {
+ if (cred->key_len >= 8 && cred->key_len <= 64) {
+ os_memcpy(key_str, cred->key, cred->key_len);
+ key_str[cred->key_len] = '\0';
+ }
+ }
+
+ uuid_bin2str(ap_settings->uuid, uuid_str, sizeof(uuid_str));
+ /* Use wpa_msg_ctrl to avoid showing the key in debug log */
+ wpa_msg_ctrl(wpa_s, MSG_INFO, WPS_EVENT_ER_AP_SETTINGS
+ "uuid=%s ssid=%s auth_type=0x%04x encr_type=0x%04x "
+ "key=%s",
+ uuid_str, wpa_ssid_txt(cred->ssid, cred->ssid_len),
+ cred->auth_type, cred->encr_type, key_str);
+}
+
+
+static void wpa_supplicant_wps_event_er_set_sel_reg(
+ struct wpa_supplicant *wpa_s,
+ struct wps_event_er_set_selected_registrar *ev)
+{
+ char uuid_str[100];
+
+ uuid_bin2str(ev->uuid, uuid_str, sizeof(uuid_str));
+ switch (ev->state) {
+ case WPS_ER_SET_SEL_REG_START:
+ wpa_msg(wpa_s, MSG_DEBUG, WPS_EVENT_ER_SET_SEL_REG
+ "uuid=%s state=START sel_reg=%d dev_passwd_id=%u "
+ "sel_reg_config_methods=0x%x",
+ uuid_str, ev->sel_reg, ev->dev_passwd_id,
+ ev->sel_reg_config_methods);
+ break;
+ case WPS_ER_SET_SEL_REG_DONE:
+ wpa_msg(wpa_s, MSG_DEBUG, WPS_EVENT_ER_SET_SEL_REG
+ "uuid=%s state=DONE", uuid_str);
+ break;
+ case WPS_ER_SET_SEL_REG_FAILED:
+ wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_ER_SET_SEL_REG
+ "uuid=%s state=FAILED", uuid_str);
+ break;
+ }
+}
+
+
static void wpa_supplicant_wps_event(void *ctx, enum wps_event event,
union wps_event_data *data)
{
wpa_supplicant_wps_event_success(wpa_s);
break;
case WPS_EV_PWD_AUTH_FAIL:
+#ifdef CONFIG_AP
+ if (wpa_s->ap_iface && data->pwd_auth_fail.enrollee)
+ wpa_supplicant_ap_pwd_auth_fail(wpa_s);
+#endif /* CONFIG_AP */
break;
case WPS_EV_PBC_OVERLAP:
break;
wpa_supplicant_wps_event_er_enrollee_remove(wpa_s,
&data->enrollee);
break;
+ case WPS_EV_ER_AP_SETTINGS:
+ wpa_supplicant_wps_event_er_ap_settings(wpa_s,
+ &data->ap_settings);
+ break;
+ case WPS_EV_ER_SET_SELECTED_REGISTRAR:
+ wpa_supplicant_wps_event_er_set_sel_reg(wpa_s,
+ &data->set_sel_reg);
+ break;
+ case WPS_EV_AP_PIN_SUCCESS:
+ break;
}
}
static void wpas_clear_wps(struct wpa_supplicant *wpa_s)
{
int id;
- struct wpa_ssid *ssid, *remove_ssid = NULL;
+ struct wpa_ssid *ssid, *remove_ssid = NULL, *prev_current;
+
+ prev_current = wpa_s->current_ssid;
eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL);
id = -1;
ssid = ssid->next;
if (id >= 0) {
+ if (prev_current == remove_ssid) {
+ wpa_sm_set_config(wpa_s->wpa, NULL);
+ eapol_sm_notify_config(wpa_s->eapol, NULL,
+ NULL);
+ }
wpas_notify_network_removed(wpa_s, remove_ssid);
wpa_config_remove_network(wpa_s->conf, id);
}
static void wpas_wps_timeout(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
- wpa_printf(MSG_INFO, WPS_EVENT_TIMEOUT "Requested operation timed "
- "out");
+ wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_TIMEOUT "Requested operation timed "
+ "out");
wpas_clear_wps(wpa_s);
}
return NULL;
wpas_notify_network_added(wpa_s, ssid);
wpa_config_set_network_defaults(ssid);
+ ssid->temporary = 1;
if (wpa_config_set(ssid, "key_mgmt", "WPS", 0) < 0 ||
wpa_config_set(ssid, "eap", "WSC", 0) < 0 ||
wpa_config_set(ssid, "identity", registrar ?
}
if (bssid) {
+#ifndef CONFIG_P2P
struct wpa_bss *bss;
int count = 0;
+#endif /* CONFIG_P2P */
os_memcpy(ssid->bssid, bssid, ETH_ALEN);
ssid->bssid_set = 1;
+ /*
+ * Note: With P2P, the SSID may change at the time the WPS
+ * provisioning is started, so better not filter the AP based
+ * on the current SSID in the scan results.
+ */
+#ifndef CONFIG_P2P
dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
if (os_memcmp(bssid, bss->bssid, ETH_ALEN) != 0)
continue;
ssid->ssid = NULL;
ssid->ssid_len = 0;
}
+#endif /* CONFIG_P2P */
}
return ssid;
{
struct wpa_ssid *ssid;
+ if (wpa_s->current_ssid)
+ wpa_supplicant_deauthenticate(
+ wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+
/* Mark all other networks disabled and trigger reassociation */
ssid = wpa_s->conf->ssid;
while (ssid) {
int was_disabled = ssid->disabled;
- ssid->disabled = ssid != selected;
- if (was_disabled != ssid->disabled)
- wpas_notify_network_enabled_changed(wpa_s, ssid);
+ /*
+ * In case the network object corresponds to a persistent group
+ * then do not send out network disabled signal. In addition,
+ * do not change disabled status of persistent network objects
+ * from 2 to 1 should we connect to another network.
+ */
+ if (was_disabled != 2) {
+ ssid->disabled = ssid != selected;
+ if (was_disabled != ssid->disabled)
+ wpas_notify_network_enabled_changed(wpa_s,
+ ssid);
+ }
ssid = ssid->next;
}
wpa_s->disconnected = 0;
}
-int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid)
+int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid,
+ int p2p_group)
{
struct wpa_ssid *ssid;
wpas_clear_wps(wpa_s);
ssid = wpas_wps_add_network(wpa_s, 0, bssid);
if (ssid == NULL)
return -1;
+ ssid->temporary = 1;
+ ssid->p2p_group = p2p_group;
+#ifdef CONFIG_P2P
+ if (p2p_group && wpa_s->go_params && wpa_s->go_params->ssid_len) {
+ ssid->ssid = os_zalloc(wpa_s->go_params->ssid_len + 1);
+ if (ssid->ssid) {
+ ssid->ssid_len = wpa_s->go_params->ssid_len;
+ os_memcpy(ssid->ssid, wpa_s->go_params->ssid,
+ ssid->ssid_len);
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: Use specific AP "
+ "SSID", ssid->ssid, ssid->ssid_len);
+ }
+ }
+#endif /* CONFIG_P2P */
wpa_config_set(ssid, "phase1", "\"pbc=1\"", 0);
+ if (wpa_s->wps_fragment_size)
+ ssid->eap.fragment_size = wpa_s->wps_fragment_size;
eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout,
wpa_s, NULL);
wpas_wps_reassoc(wpa_s, ssid);
int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
- const char *pin)
+ const char *pin, int p2p_group, u16 dev_pw_id)
{
struct wpa_ssid *ssid;
char val[128];
ssid = wpas_wps_add_network(wpa_s, 0, bssid);
if (ssid == NULL)
return -1;
+ ssid->temporary = 1;
+ ssid->p2p_group = p2p_group;
+#ifdef CONFIG_P2P
+ if (p2p_group && wpa_s->go_params && wpa_s->go_params->ssid_len) {
+ ssid->ssid = os_zalloc(wpa_s->go_params->ssid_len + 1);
+ if (ssid->ssid) {
+ ssid->ssid_len = wpa_s->go_params->ssid_len;
+ os_memcpy(ssid->ssid, wpa_s->go_params->ssid,
+ ssid->ssid_len);
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: Use specific AP "
+ "SSID", ssid->ssid, ssid->ssid_len);
+ }
+ }
+#endif /* CONFIG_P2P */
if (pin)
- os_snprintf(val, sizeof(val), "\"pin=%s\"", pin);
+ os_snprintf(val, sizeof(val), "\"pin=%s dev_pw_id=%u\"",
+ pin, dev_pw_id);
else {
rpin = wps_generate_pin();
- os_snprintf(val, sizeof(val), "\"pin=%08d\"", rpin);
+ os_snprintf(val, sizeof(val), "\"pin=%08d dev_pw_id=%u\"",
+ rpin, dev_pw_id);
}
wpa_config_set(ssid, "phase1", val, 0);
+ if (wpa_s->wps_fragment_size)
+ ssid->eap.fragment_size = wpa_s->wps_fragment_size;
eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout,
wpa_s, NULL);
wpas_wps_reassoc(wpa_s, ssid);
}
+/* Cancel the wps pbc/pin requests */
+int wpas_wps_cancel(struct wpa_supplicant *wpa_s)
+{
+#ifdef CONFIG_AP
+ if (wpa_s->ap_iface) {
+ wpa_printf(MSG_DEBUG, "WPS: Cancelling in AP mode");
+ return wpa_supplicant_ap_wps_cancel(wpa_s);
+ }
+#endif /* CONFIG_AP */
+
+ if (wpa_s->wpa_state == WPA_SCANNING) {
+ wpa_printf(MSG_DEBUG, "WPS: Cancel operation - cancel scan");
+ wpa_supplicant_cancel_scan(wpa_s);
+ wpas_clear_wps(wpa_s);
+ } else if (wpa_s->wpa_state >= WPA_ASSOCIATED) {
+ wpa_printf(MSG_DEBUG, "WPS: Cancel operation - "
+ "deauthenticate");
+ wpa_supplicant_deauthenticate(wpa_s,
+ WLAN_REASON_DEAUTH_LEAVING);
+ wpas_clear_wps(wpa_s);
+ }
+
+ return 0;
+}
+
+
#ifdef CONFIG_WPS_OOB
int wpas_wps_start_oob(struct wpa_supplicant *wpa_s, char *device_type,
char *path, char *method, char *name)
if ((wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_E ||
wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_R) &&
wpas_wps_start_pin(wpa_s, NULL,
- wpabuf_head(wps->oob_conf.dev_password)) < 0)
+ wpabuf_head(wps->oob_conf.dev_password), 0,
+ DEV_PW_DEFAULT) < 0)
return -1;
return 0;
ssid = wpas_wps_add_network(wpa_s, 1, bssid);
if (ssid == NULL)
return -1;
+ ssid->temporary = 1;
pos = val;
end = pos + sizeof(val);
res = os_snprintf(pos, end - pos, "\"pin=%s", pin);
if (res < 0 || res >= end - pos)
return -1;
wpa_config_set(ssid, "phase1", val, 0);
+ if (wpa_s->wps_fragment_size)
+ ssid->eap.fragment_size = wpa_s->wps_fragment_size;
eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout,
wpa_s, NULL);
wpas_wps_reassoc(wpa_s, ssid);
if (wpa_s->wps_er == NULL)
return;
+ wpa_printf(MSG_DEBUG, "WPS ER: SetSelectedRegistrar - sel_reg=%d "
+ "dev_password_id=%u sel_reg_config_methods=0x%x",
+ sel_reg, dev_passwd_id, sel_reg_config_methods);
wps_er_set_sel_reg(wpa_s->wps_er, sel_reg, dev_passwd_id,
sel_reg_config_methods);
#endif /* CONFIG_WPS_ER */
}
+static u16 wps_fix_config_methods(u16 config_methods)
+{
+#ifdef CONFIG_WPS2
+ if ((config_methods &
+ (WPS_CONFIG_DISPLAY | WPS_CONFIG_VIRT_DISPLAY |
+ WPS_CONFIG_PHY_DISPLAY)) == WPS_CONFIG_DISPLAY) {
+ wpa_printf(MSG_INFO, "WPS: Converting display to "
+ "virtual_display for WPS 2.0 compliance");
+ config_methods |= WPS_CONFIG_VIRT_DISPLAY;
+ }
+ if ((config_methods &
+ (WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_VIRT_PUSHBUTTON |
+ WPS_CONFIG_PHY_PUSHBUTTON)) == WPS_CONFIG_PUSHBUTTON) {
+ wpa_printf(MSG_INFO, "WPS: Converting push_button to "
+ "virtual_push_button for WPS 2.0 compliance");
+ config_methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
+ }
+#endif /* CONFIG_WPS2 */
+
+ return config_methods;
+}
+
+
+static void wpas_wps_set_uuid(struct wpa_supplicant *wpa_s,
+ struct wps_context *wps)
+{
+ wpa_printf(MSG_DEBUG, "WPS: Set UUID for interface %s", wpa_s->ifname);
+ if (is_nil_uuid(wpa_s->conf->uuid)) {
+ struct wpa_supplicant *first;
+ first = wpa_s->global->ifaces;
+ while (first && first->next)
+ first = first->next;
+ if (first && first != wpa_s) {
+ os_memcpy(wps->uuid, wpa_s->global->ifaces->wps->uuid,
+ WPS_UUID_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: UUID from the first "
+ "interface", wps->uuid, WPS_UUID_LEN);
+ } else {
+ uuid_gen_mac_addr(wpa_s->own_addr, wps->uuid);
+ wpa_hexdump(MSG_DEBUG, "WPS: UUID based on MAC "
+ "address", wps->uuid, WPS_UUID_LEN);
+ }
+ } else {
+ os_memcpy(wps->uuid, wpa_s->conf->uuid, WPS_UUID_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: UUID based on configuration",
+ wps->uuid, WPS_UUID_LEN);
+ }
+}
+
+
int wpas_wps_init(struct wpa_supplicant *wpa_s)
{
struct wps_context *wps;
struct wps_registrar_config rcfg;
+ struct hostapd_hw_modes *modes;
+ u16 m;
wps = os_zalloc(sizeof(*wps));
if (wps == NULL)
wps->dev.serial_number = wpa_s->conf->serial_number;
wps->config_methods =
wps_config_methods_str2bin(wpa_s->conf->config_methods);
- if (wpa_s->conf->device_type &&
- wps_dev_type_str2bin(wpa_s->conf->device_type,
- wps->dev.pri_dev_type) < 0) {
- wpa_printf(MSG_ERROR, "WPS: Invalid device_type");
+ if ((wps->config_methods & (WPS_CONFIG_DISPLAY | WPS_CONFIG_LABEL)) ==
+ (WPS_CONFIG_DISPLAY | WPS_CONFIG_LABEL)) {
+ wpa_printf(MSG_ERROR, "WPS: Both Label and Display config "
+ "methods are not allowed at the same time");
os_free(wps);
return -1;
}
+ wps->config_methods = wps_fix_config_methods(wps->config_methods);
+ wps->dev.config_methods = wps->config_methods;
+ os_memcpy(wps->dev.pri_dev_type, wpa_s->conf->device_type,
+ WPS_DEV_TYPE_LEN);
+
+ wps->dev.num_sec_dev_types = wpa_s->conf->num_sec_device_types;
+ os_memcpy(wps->dev.sec_dev_type, wpa_s->conf->sec_device_type,
+ WPS_DEV_TYPE_LEN * wps->dev.num_sec_dev_types);
+
wps->dev.os_version = WPA_GET_BE32(wpa_s->conf->os_version);
- wps->dev.rf_bands = WPS_RF_24GHZ | WPS_RF_50GHZ; /* TODO: config */
+ modes = wpa_s->hw.modes;
+ if (modes) {
+ for (m = 0; m < wpa_s->hw.num_modes; m++) {
+ if (modes[m].mode == HOSTAPD_MODE_IEEE80211B ||
+ modes[m].mode == HOSTAPD_MODE_IEEE80211G)
+ wps->dev.rf_bands |= WPS_RF_24GHZ;
+ else if (modes[m].mode == HOSTAPD_MODE_IEEE80211A)
+ wps->dev.rf_bands |= WPS_RF_50GHZ;
+ }
+ }
+ if (wps->dev.rf_bands == 0) {
+ /*
+ * Default to claiming support for both bands if the driver
+ * does not provide support for fetching supported bands.
+ */
+ wps->dev.rf_bands = WPS_RF_24GHZ | WPS_RF_50GHZ;
+ }
os_memcpy(wps->dev.mac_addr, wpa_s->own_addr, ETH_ALEN);
- if (is_nil_uuid(wpa_s->conf->uuid)) {
- uuid_gen_mac_addr(wpa_s->own_addr, wps->uuid);
- wpa_hexdump(MSG_DEBUG, "WPS: UUID based on MAC address",
- wps->uuid, WPS_UUID_LEN);
- } else
- os_memcpy(wps->uuid, wpa_s->conf->uuid, WPS_UUID_LEN);
+ wpas_wps_set_uuid(wpa_s, wps);
wps->auth_types = WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK;
wps->encr_types = WPS_ENCR_AES | WPS_ENCR_TKIP;
}
/*
- * Start with WPS APs that advertise active PIN Registrar and
- * allow any WPS AP after third scan since some APs do not set
- * Selected Registrar attribute properly when using external
- * Registrar.
+ * Start with WPS APs that advertise our address as an
+ * authorized MAC (v2.0) or active PIN Registrar (v1.0) and
+ * allow any WPS AP after couple of scans since some APs do not
+ * set Selected Registrar attribute properly when using
+ * external Registrar.
*/
- if (!wps_is_selected_pin_registrar(wps_ie)) {
+ if (!wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 1)) {
if (wpa_s->scan_runs < WPS_PIN_SCAN_IGNORE_SEL_REG) {
wpa_printf(MSG_DEBUG, " skip - WPS AP "
"without active PIN Registrar");
wpa_printf(MSG_DEBUG, " selected based on WPS IE");
} else {
wpa_printf(MSG_DEBUG, " selected based on WPS IE "
- "(Active PIN)");
+ "(Authorized MAC or Active PIN)");
}
wpabuf_free(wps_ie);
return 1;
} else if (eap_is_wps_pin_enrollee(&ssid->eap)) {
wps_ie = wpa_scan_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
if (wps_ie &&
- (wps_is_selected_pin_registrar(wps_ie) ||
+ (wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 1) ||
wpa_s->scan_runs >= WPS_PIN_SCAN_IGNORE_SEL_REG)) {
/* allow wildcard SSID for WPS PIN */
ret = 1;
ret = 1;
}
+#ifdef CONFIG_WPS_STRICT
+ if (wps_ie) {
+ if (wps_validate_beacon_probe_resp(wps_ie, bss->beacon_ie_len >
+ 0, bss->bssid) < 0)
+ ret = 0;
+ if (bss->beacon_ie_len) {
+ struct wpabuf *bcn_wps;
+ bcn_wps = wpa_scan_get_vendor_ie_multi_beacon(
+ bss, WPS_IE_VENDOR_TYPE);
+ if (bcn_wps == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Mandatory WPS IE "
+ "missing from AP Beacon");
+ ret = 0;
+ } else {
+ if (wps_validate_beacon(wps_ie) < 0)
+ ret = 0;
+ wpabuf_free(bcn_wps);
+ }
+ }
+ }
+#endif /* CONFIG_WPS_STRICT */
+
wpabuf_free(wps_ie);
return ret;
if (!eap_is_wps_pbc_enrollee(&ssid->eap))
return 0;
+ wpa_printf(MSG_DEBUG, "WPS: Check whether PBC session overlap is "
+ "present in scan results; selected BSSID " MACSTR,
+ MAC2STR(selected->bssid));
+
/* Make sure that only one AP is in active PBC mode */
wps_ie = wpa_bss_get_vendor_ie_multi(selected, WPS_IE_VENDOR_TYPE);
- if (wps_ie)
+ if (wps_ie) {
sel_uuid = wps_get_uuid_e(wps_ie);
- else
+ wpa_hexdump(MSG_DEBUG, "WPS: UUID of the selected BSS",
+ sel_uuid, UUID_LEN);
+ } else {
+ wpa_printf(MSG_DEBUG, "WPS: Selected BSS does not include "
+ "WPS IE?!");
sel_uuid = NULL;
+ }
dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
struct wpabuf *ie;
wpabuf_free(ie);
continue;
}
+ wpa_printf(MSG_DEBUG, "WPS: Another BSS in active PBC mode: "
+ MACSTR, MAC2STR(bss->bssid));
uuid = wps_get_uuid_e(ie);
+ wpa_hexdump(MSG_DEBUG, "WPS: UUID of the other BSS",
+ uuid, UUID_LEN);
if (sel_uuid == NULL || uuid == NULL ||
- os_memcmp(sel_uuid, uuid, 16) != 0) {
+ os_memcmp(sel_uuid, uuid, UUID_LEN) != 0) {
ret = 1; /* PBC overlap */
+ wpa_msg(wpa_s, MSG_INFO, "WPS: PBC overlap detected: "
+ MACSTR " and " MACSTR,
+ MAC2STR(selected->bssid),
+ MAC2STR(bss->bssid));
wpabuf_free(ie);
break;
}
void wpas_wps_notify_scan_results(struct wpa_supplicant *wpa_s)
{
struct wpa_bss *bss;
+ unsigned int pbc = 0, auth = 0, pin = 0, wps = 0;
if (wpa_s->disconnected || wpa_s->wpa_state >= WPA_ASSOCIATED)
return;
if (!ie)
continue;
if (wps_is_selected_pbc_registrar(ie))
- wpa_msg_ctrl(wpa_s, MSG_INFO,
- WPS_EVENT_AP_AVAILABLE_PBC);
+ pbc++;
+ else if (wps_is_addr_authorized(ie, wpa_s->own_addr, 0))
+ auth++;
else if (wps_is_selected_pin_registrar(ie))
- wpa_msg_ctrl(wpa_s, MSG_INFO,
- WPS_EVENT_AP_AVAILABLE_PIN);
+ pin++;
else
- wpa_msg_ctrl(wpa_s, MSG_INFO,
- WPS_EVENT_AP_AVAILABLE);
+ wps++;
wpabuf_free(ie);
- break;
}
+
+ if (pbc)
+ wpa_msg_ctrl(wpa_s, MSG_INFO, WPS_EVENT_AP_AVAILABLE_PBC);
+ else if (auth)
+ wpa_msg_ctrl(wpa_s, MSG_INFO, WPS_EVENT_AP_AVAILABLE_AUTH);
+ else if (pin)
+ wpa_msg_ctrl(wpa_s, MSG_INFO, WPS_EVENT_AP_AVAILABLE_PIN);
+ else if (wps)
+ wpa_msg_ctrl(wpa_s, MSG_INFO, WPS_EVENT_AP_AVAILABLE);
}
}
-int wpas_wps_er_start(struct wpa_supplicant *wpa_s)
+int wpas_wps_er_start(struct wpa_supplicant *wpa_s, const char *filter)
{
#ifdef CONFIG_WPS_ER
if (wpa_s->wps_er) {
wps_er_refresh(wpa_s->wps_er);
return 0;
}
- wpa_s->wps_er = wps_er_init(wpa_s->wps, wpa_s->ifname);
+ wpa_s->wps_er = wps_er_init(wpa_s->wps, wpa_s->ifname, filter);
if (wpa_s->wps_er == NULL)
return -1;
return 0;
#ifdef CONFIG_WPS_ER
-int wpas_wps_er_add_pin(struct wpa_supplicant *wpa_s, const char *uuid,
- const char *pin)
+int wpas_wps_er_add_pin(struct wpa_supplicant *wpa_s, const u8 *addr,
+ const char *uuid, const char *pin)
{
u8 u[UUID_LEN];
int any = 0;
any = 1;
else if (uuid_str2bin(uuid, u))
return -1;
- return wps_registrar_add_pin(wpa_s->wps->registrar, any ? NULL : u,
+ return wps_registrar_add_pin(wpa_s->wps->registrar, addr,
+ any ? NULL : u,
(const u8 *) pin, os_strlen(pin), 300);
}
}
+int wpas_wps_er_set_config(struct wpa_supplicant *wpa_s, const char *uuid,
+ int id)
+{
+ u8 u[UUID_LEN];
+ struct wpa_ssid *ssid;
+ struct wps_credential cred;
+
+ if (uuid_str2bin(uuid, u))
+ return -1;
+ ssid = wpa_config_get_network(wpa_s->conf, id);
+ if (ssid == NULL || ssid->ssid == NULL)
+ return -1;
+
+ os_memset(&cred, 0, sizeof(cred));
+ if (ssid->ssid_len > 32)
+ return -1;
+ os_memcpy(cred.ssid, ssid->ssid, ssid->ssid_len);
+ cred.ssid_len = ssid->ssid_len;
+ if (ssid->key_mgmt & WPA_KEY_MGMT_PSK) {
+ cred.auth_type = (ssid->proto & WPA_PROTO_RSN) ?
+ WPS_AUTH_WPA2PSK : WPS_AUTH_WPAPSK;
+ if (ssid->pairwise_cipher & WPA_CIPHER_CCMP)
+ cred.encr_type = WPS_ENCR_AES;
+ else
+ cred.encr_type = WPS_ENCR_TKIP;
+ if (ssid->passphrase) {
+ cred.key_len = os_strlen(ssid->passphrase);
+ if (cred.key_len >= 64)
+ return -1;
+ os_memcpy(cred.key, ssid->passphrase, cred.key_len);
+ } else if (ssid->psk_set) {
+ cred.key_len = 32;
+ os_memcpy(cred.key, ssid->psk, 32);
+ } else
+ return -1;
+ } else {
+ cred.auth_type = WPS_AUTH_OPEN;
+ cred.encr_type = WPS_ENCR_NONE;
+ }
+ return wps_er_set_config(wpa_s->wps_er, u, &cred);
+}
+
+
+int wpas_wps_er_config(struct wpa_supplicant *wpa_s, const char *uuid,
+ const char *pin, struct wps_new_ap_settings *settings)
+{
+ u8 u[UUID_LEN];
+ struct wps_credential cred;
+ size_t len;
+
+ if (uuid_str2bin(uuid, u))
+ return -1;
+ if (settings->ssid_hex == NULL || settings->auth == NULL ||
+ settings->encr == NULL || settings->key_hex == NULL)
+ return -1;
+
+ os_memset(&cred, 0, sizeof(cred));
+ len = os_strlen(settings->ssid_hex);
+ if ((len & 1) || len > 2 * sizeof(cred.ssid) ||
+ hexstr2bin(settings->ssid_hex, cred.ssid, len / 2))
+ return -1;
+ cred.ssid_len = len / 2;
+
+ len = os_strlen(settings->key_hex);
+ if ((len & 1) || len > 2 * sizeof(cred.key) ||
+ hexstr2bin(settings->key_hex, cred.key, len / 2))
+ return -1;
+ cred.key_len = len / 2;
+
+ if (os_strcmp(settings->auth, "OPEN") == 0)
+ cred.auth_type = WPS_AUTH_OPEN;
+ else if (os_strcmp(settings->auth, "WPAPSK") == 0)
+ cred.auth_type = WPS_AUTH_WPAPSK;
+ else if (os_strcmp(settings->auth, "WPA2PSK") == 0)
+ cred.auth_type = WPS_AUTH_WPA2PSK;
+ else
+ return -1;
+
+ if (os_strcmp(settings->encr, "NONE") == 0)
+ cred.encr_type = WPS_ENCR_NONE;
+ else if (os_strcmp(settings->encr, "WEP") == 0)
+ cred.encr_type = WPS_ENCR_WEP;
+ else if (os_strcmp(settings->encr, "TKIP") == 0)
+ cred.encr_type = WPS_ENCR_TKIP;
+ else if (os_strcmp(settings->encr, "CCMP") == 0)
+ cred.encr_type = WPS_ENCR_AES;
+ else
+ return -1;
+
+ return wps_er_config(wpa_s->wps_er, u, (const u8 *) pin,
+ os_strlen(pin), &cred);
+}
+
+
+static int callbacks_pending = 0;
+
static void wpas_wps_terminate_cb(void *ctx)
{
wpa_printf(MSG_DEBUG, "WPS ER: Terminated");
- eloop_terminate();
+ if (--callbacks_pending <= 0)
+ eloop_terminate();
}
#endif /* CONFIG_WPS_ER */
{
#ifdef CONFIG_WPS_ER
if (wpa_s->wps_er) {
+ callbacks_pending++;
wps_er_deinit(wpa_s->wps_er, wpas_wps_terminate_cb, wpa_s);
wpa_s->wps_er = NULL;
return 1;
#endif /* CONFIG_WPS_ER */
return 0;
}
+
+
+int wpas_wps_in_progress(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_ssid *ssid;
+
+ for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+ if (!ssid->disabled && ssid->key_mgmt == WPA_KEY_MGMT_WPS)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+void wpas_wps_update_config(struct wpa_supplicant *wpa_s)
+{
+ struct wps_context *wps = wpa_s->wps;
+
+ if (wps == NULL)
+ return;
+
+ if (wpa_s->conf->changed_parameters & CFG_CHANGED_CONFIG_METHODS) {
+ wps->config_methods = wps_config_methods_str2bin(
+ wpa_s->conf->config_methods);
+ if ((wps->config_methods &
+ (WPS_CONFIG_DISPLAY | WPS_CONFIG_LABEL)) ==
+ (WPS_CONFIG_DISPLAY | WPS_CONFIG_LABEL)) {
+ wpa_printf(MSG_ERROR, "WPS: Both Label and Display "
+ "config methods are not allowed at the "
+ "same time");
+ wps->config_methods &= ~WPS_CONFIG_LABEL;
+ }
+ }
+ wps->config_methods = wps_fix_config_methods(wps->config_methods);
+
+ if (wpa_s->conf->changed_parameters & CFG_CHANGED_DEVICE_TYPE)
+ os_memcpy(wps->dev.pri_dev_type, wpa_s->conf->device_type,
+ WPS_DEV_TYPE_LEN);
+
+ if (wpa_s->conf->changed_parameters & CFG_CHANGED_SEC_DEVICE_TYPE) {
+ wps->dev.num_sec_dev_types = wpa_s->conf->num_sec_device_types;
+ os_memcpy(wps->dev.sec_dev_type, wpa_s->conf->sec_device_type,
+ wps->dev.num_sec_dev_types * WPS_DEV_TYPE_LEN);
+ }
+
+ if (wpa_s->conf->changed_parameters & CFG_CHANGED_OS_VERSION)
+ wps->dev.os_version = WPA_GET_BE32(wpa_s->conf->os_version);
+
+ if (wpa_s->conf->changed_parameters & CFG_CHANGED_UUID)
+ wpas_wps_set_uuid(wpa_s, wps);
+
+ if (wpa_s->conf->changed_parameters &
+ (CFG_CHANGED_DEVICE_NAME | CFG_CHANGED_WPS_STRING)) {
+ /* Update pointers to make sure they refer current values */
+ wps->dev.device_name = wpa_s->conf->device_name;
+ wps->dev.manufacturer = wpa_s->conf->manufacturer;
+ wps->dev.model_name = wpa_s->conf->model_name;
+ wps->dev.model_number = wpa_s->conf->model_number;
+ wps->dev.serial_number = wpa_s->conf->serial_number;
+ }
+}
void wpas_wps_deinit(struct wpa_supplicant *wpa_s);
int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s);
enum wps_request_type wpas_wps_get_req_type(struct wpa_ssid *ssid);
-int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid);
+int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid,
+ int p2p_group);
int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
- const char *pin);
+ const char *pin, int p2p_group, u16 dev_pw_id);
+int wpas_wps_cancel(struct wpa_supplicant *wpa_s);
int wpas_wps_start_oob(struct wpa_supplicant *wpa_s, char *device_type,
char *path, char *method, char *name);
int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid,
int wpas_wps_searching(struct wpa_supplicant *wpa_s);
int wpas_wps_scan_result_text(const u8 *ies, size_t ies_len, char *pos,
char *end);
-int wpas_wps_er_start(struct wpa_supplicant *wpa_s);
+int wpas_wps_er_start(struct wpa_supplicant *wpa_s, const char *filter);
int wpas_wps_er_stop(struct wpa_supplicant *wpa_s);
-int wpas_wps_er_add_pin(struct wpa_supplicant *wpa_s, const char *uuid,
- const char *pin);
+int wpas_wps_er_add_pin(struct wpa_supplicant *wpa_s, const u8 *addr,
+ const char *uuid, const char *pin);
int wpas_wps_er_pbc(struct wpa_supplicant *wpa_s, const char *uuid);
int wpas_wps_er_learn(struct wpa_supplicant *wpa_s, const char *uuid,
const char *pin);
+int wpas_wps_er_set_config(struct wpa_supplicant *wpa_s, const char *uuid,
+ int id);
+int wpas_wps_er_config(struct wpa_supplicant *wpa_s, const char *uuid,
+ const char *pin, struct wps_new_ap_settings *settings);
int wpas_wps_terminate_pending(struct wpa_supplicant *wpa_s);
+int wpas_wps_in_progress(struct wpa_supplicant *wpa_s);
+void wpas_wps_update_config(struct wpa_supplicant *wpa_s);
#else /* CONFIG_WPS */