diff options
43 files changed, 1569 insertions, 428 deletions
diff --git a/hostapd/android.config b/hostapd/android.config index f51a5bfe..51d6656f 100644 --- a/hostapd/android.config +++ b/hostapd/android.config @@ -52,7 +52,7 @@ CONFIG_LIBNL20=y # This version is an experimental implementation based on IEEE 802.11w/D1.0 # draft and is subject to change since the standard has not yet been finalized. # Driver support is also needed for IEEE 802.11w. -#CONFIG_IEEE80211W=y +CONFIG_IEEE80211W=y # Integrated EAP server #CONFIG_EAP=y diff --git a/hostapd/main.c b/hostapd/main.c index d2ec1a50..61229d77 100644 --- a/hostapd/main.c +++ b/hostapd/main.c @@ -277,6 +277,7 @@ static int hostapd_driver_init(struct hostapd_iface *iface) iface->extended_capa = capa.extended_capa; iface->extended_capa_mask = capa.extended_capa_mask; iface->extended_capa_len = capa.extended_capa_len; + iface->drv_max_acl_mac_addrs = capa.max_acl_mac_addrs; } return 0; diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h index ceb7e68e..70fab553 100644 --- a/src/ap/ap_drv_ops.h +++ b/src/ap/ap_drv_ops.h @@ -173,6 +173,14 @@ static inline int hostapd_drv_sta_clear_stats(struct hostapd_data *hapd, return hapd->driver->sta_clear_stats(hapd->drv_priv, addr); } +static inline int hostapd_drv_set_acl(struct hostapd_data *hapd, + struct hostapd_acl_params *params) +{ + if (hapd->driver == NULL || hapd->driver->set_acl == NULL) + return 0; + return hapd->driver->set_acl(hapd->drv_priv, params); +} + static inline int hostapd_drv_set_ap(struct hostapd_data *hapd, struct wpa_driver_ap_params *params) { diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c index a0ac38c4..780b2e2e 100644 --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c @@ -837,6 +837,74 @@ static void hostapd_tx_queue_params(struct hostapd_iface *iface) } +static int hostapd_set_acl_list(struct hostapd_data *hapd, + struct mac_acl_entry *mac_acl, + int n_entries, u8 accept_acl) +{ + struct hostapd_acl_params *acl_params; + int i, err; + + acl_params = os_zalloc(sizeof(*acl_params) + + (n_entries * sizeof(acl_params->mac_acl[0]))); + if (!acl_params) + return -ENOMEM; + + for (i = 0; i < n_entries; i++) + os_memcpy(acl_params->mac_acl[i].addr, mac_acl[i].addr, + ETH_ALEN); + + acl_params->acl_policy = accept_acl; + acl_params->num_mac_acl = n_entries; + + err = hostapd_drv_set_acl(hapd, acl_params); + + os_free(acl_params); + + return err; +} + + +static void hostapd_set_acl(struct hostapd_data *hapd) +{ + struct hostapd_config *conf = hapd->iconf; + int err; + u8 accept_acl; + + if (hapd->iface->drv_max_acl_mac_addrs == 0) + return; + if (!(conf->bss->num_accept_mac || conf->bss->num_deny_mac)) + return; + + if (conf->bss->macaddr_acl == DENY_UNLESS_ACCEPTED) { + if (conf->bss->num_accept_mac) { + accept_acl = 1; + err = hostapd_set_acl_list(hapd, conf->bss->accept_mac, + conf->bss->num_accept_mac, + accept_acl); + if (err) { + wpa_printf(MSG_DEBUG, "Failed to set accept acl"); + return; + } + } else { + wpa_printf(MSG_DEBUG, "Mismatch between ACL Policy & Accept/deny lists file"); + } + } else if (conf->bss->macaddr_acl == ACCEPT_UNLESS_DENIED) { + if (conf->bss->num_deny_mac) { + accept_acl = 0; + err = hostapd_set_acl_list(hapd, conf->bss->deny_mac, + conf->bss->num_deny_mac, + accept_acl); + if (err) { + wpa_printf(MSG_DEBUG, "Failed to set deny acl"); + return; + } + } else { + wpa_printf(MSG_DEBUG, "Mismatch between ACL Policy & Accept/deny lists file"); + } + } +} + + static int setup_interface(struct hostapd_iface *iface) { struct hostapd_data *hapd = iface->bss[0]; @@ -962,6 +1030,8 @@ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err) ap_list_init(iface); + hostapd_set_acl(hapd); + if (hostapd_driver_commit(hapd) < 0) { wpa_printf(MSG_ERROR, "%s: Failed to commit driver " "configuration", __func__); diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index 9a3bb686..55f6dd84 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -233,6 +233,8 @@ struct hostapd_iface { const u8 *extended_capa, *extended_capa_mask; unsigned int extended_capa_len; + unsigned int drv_max_acl_mac_addrs; + struct hostapd_hw_modes *hw_features; int num_hw_features; struct hostapd_hw_modes *current_mode; diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h index f782c865..08485902 100644 --- a/src/common/ieee802_11_defs.h +++ b/src/common/ieee802_11_defs.h @@ -549,6 +549,14 @@ struct ieee80211_mgmt { * Entries (optional) */ u8 variable[0]; } STRUCT_PACKED bss_tm_resp; + struct { + u8 action; /* 6 */ + u8 dialog_token; + u8 query_reason; + /* BSS Transition Candidate List + * Entries (optional) */ + u8 variable[0]; + } STRUCT_PACKED bss_tm_query; } u; } STRUCT_PACKED action; } u; @@ -1049,6 +1057,15 @@ enum wnm_action { #define WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED BIT(3) #define WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT BIT(4) +#define WNM_NEIGHBOR_TSF 1 +#define WNM_NEIGHBOR_CONDENSED_COUNTRY_STRING 2 +#define WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE 3 +#define WNM_NEIGHBOR_BSS_TERMINATION_DURATION 4 +#define WNM_NEIGHBOR_BEARING 5 +#define WNM_NEIGHBOR_MEASUREMENT_PILOT 66 +#define WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES 70 +#define WNM_NEIGHBOR_MULTIPLE_BSSID 71 + /* IEEE Std 802.11-2012, 8.4.2.62 20/40 BSS Coexistence element */ #define WLAN_20_40_BSS_COEX_INFO_REQ BIT(0) #define WLAN_20_40_BSS_COEX_40MHZ_INTOL BIT(1) diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c index 2bf47c7c..fec8e385 100644 --- a/src/crypto/tls_openssl.c +++ b/src/crypto/tls_openssl.c @@ -34,6 +34,10 @@ #define OPENSSL_d2i_TYPE unsigned char ** #endif +#if defined(SSL_CTX_get_app_data) && defined(SSL_CTX_set_app_data) +#define OPENSSL_SUPPORTS_CTX_APP_DATA +#endif + #ifdef SSL_F_SSL_SET_SESSION_TICKET_EXT #ifdef SSL_OP_NO_TICKET /* @@ -63,17 +67,18 @@ static BIO * BIO_from_keystore(const char *key) static int tls_openssl_ref_count = 0; -struct tls_global { +struct tls_context { 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; +static struct tls_context *tls_global = NULL; struct tls_connection { + struct tls_context *context; SSL *ssl; BIO *ssl_in, *ssl_out; #ifndef OPENSSL_NO_ENGINE @@ -100,6 +105,20 @@ struct tls_connection { }; +static struct tls_context * tls_context_new(const struct tls_config *conf) +{ + struct tls_context *context = os_zalloc(sizeof(*context)); + if (context == NULL) + return NULL; + if (conf) { + context->event_cb = conf->event_cb; + context->cb_ctx = conf->cb_ctx; + context->cert_in_cb = conf->cert_in_cb; + } + return context; +} + + #ifdef CONFIG_NO_STDOUT_DEBUG static void _tls_show_errors(void) @@ -525,6 +544,7 @@ static void ssl_info_cb(const SSL *ssl, int where, int ret) wpa_printf(MSG_DEBUG, "SSL: %s:%s", str, SSL_state_string_long(ssl)); } else if (where & SSL_CB_ALERT) { + struct tls_connection *conn = SSL_get_app_data((SSL *) ssl); wpa_printf(MSG_INFO, "SSL: SSL3 alert: %s:%s:%s", where & SSL_CB_READ ? "read (remote end reported an error)" : @@ -532,21 +552,19 @@ static void ssl_info_cb(const SSL *ssl, int where, int ret) SSL_alert_type_string_long(ret), SSL_alert_desc_string_long(ret)); if ((ret >> 8) == SSL3_AL_FATAL) { - struct tls_connection *conn = - SSL_get_app_data((SSL *) ssl); if (where & SSL_CB_READ) conn->read_alerts++; else conn->write_alerts++; } - if (tls_global->event_cb != NULL) { + if (conn->context->event_cb != NULL) { union tls_event_data ev; + struct tls_context *context = conn->context; os_memset(&ev, 0, sizeof(ev)); ev.alert.is_local = !(where & SSL_CB_READ); ev.alert.type = SSL_alert_type_string_long(ret); ev.alert.description = SSL_alert_desc_string_long(ret); - tls_global->event_cb(tls_global->cb_ctx, TLS_ALERT, - &ev); + context->event_cb(context->cb_ctx, TLS_ALERT, &ev); } } else if (where & SSL_CB_EXIT && ret <= 0) { wpa_printf(MSG_DEBUG, "SSL: %s:%s in %s", @@ -704,17 +722,12 @@ static int tls_engine_load_dynamic_opensc(const char *opensc_so_path) void * tls_init(const struct tls_config *conf) { SSL_CTX *ssl; + struct tls_context *context; if (tls_openssl_ref_count == 0) { - tls_global = os_zalloc(sizeof(*tls_global)); - if (tls_global == NULL) + tls_global = context = tls_context_new(conf); + if (context == NULL) return NULL; - 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 #ifdef OPENSSL_FIPS if (conf && conf->fips_mode) { @@ -760,14 +773,33 @@ void * tls_init(const struct tls_config *conf) #endif /* OPENSSL_NO_RC2 */ PKCS12_PBE_add(); #endif /* PKCS12_FUNCS */ + } else { + context = tls_global; +#ifdef OPENSSL_SUPPORTS_CTX_APP_DATA + /* Newer OpenSSL can store app-data per-SSL */ + context = tls_context_new(conf); + if (context == NULL) + return NULL; +#endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */ } tls_openssl_ref_count++; ssl = SSL_CTX_new(TLSv1_method()); - if (ssl == NULL) + if (ssl == NULL) { + tls_openssl_ref_count--; + if (tls_openssl_ref_count == 0) { + os_free(tls_global); + tls_global = NULL; + } else if (context != tls_global) { + os_free(context); + } return NULL; + } SSL_CTX_set_info_callback(ssl, ssl_info_cb); +#ifdef OPENSSL_SUPPORTS_CTX_APP_DATA + SSL_CTX_set_app_data(ssl, context); +#endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */ #ifndef OPENSSL_NO_ENGINE if (conf && @@ -793,6 +825,11 @@ void * tls_init(const struct tls_config *conf) void tls_deinit(void *ssl_ctx) { SSL_CTX *ssl = ssl_ctx; +#ifdef OPENSSL_SUPPORTS_CTX_APP_DATA + struct tls_context *context = SSL_CTX_get_app_data(ssl); + if (context != tls_global) + os_free(context); +#endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */ SSL_CTX_free(ssl); tls_openssl_ref_count--; @@ -936,6 +973,10 @@ struct tls_connection * tls_connection_init(void *ssl_ctx) SSL_CTX *ssl = ssl_ctx; struct tls_connection *conn; long options; + struct tls_context *context = tls_global; +#ifdef OPENSSL_SUPPORTS_CTX_APP_DATA + context = SSL_CTX_get_app_data(ssl); +#endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */ conn = os_zalloc(sizeof(*conn)); if (conn == NULL) @@ -948,6 +989,7 @@ struct tls_connection * tls_connection_init(void *ssl_ctx) return NULL; } + conn->context = context; SSL_set_app_data(conn->ssl, conn); options = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_SINGLE_DH_USE; @@ -1148,8 +1190,9 @@ static void openssl_tls_fail_event(struct tls_connection *conn, { union tls_event_data ev; struct wpabuf *cert = NULL; + struct tls_context *context = conn->context; - if (tls_global->event_cb == NULL) + if (context->event_cb == NULL) return; cert = get_x509_cert(err_cert); @@ -1160,7 +1203,7 @@ static void openssl_tls_fail_event(struct tls_connection *conn, ev.cert_fail.subject = subject; ev.cert_fail.reason_txt = err_str; ev.cert_fail.cert = cert; - tls_global->event_cb(tls_global->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev); + context->event_cb(context->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev); wpabuf_free(cert); } @@ -1171,15 +1214,16 @@ static void openssl_tls_cert_event(struct tls_connection *conn, { struct wpabuf *cert = NULL; union tls_event_data ev; + struct tls_context *context = conn->context; #ifdef CONFIG_SHA256 u8 hash[32]; #endif /* CONFIG_SHA256 */ - if (tls_global->event_cb == NULL) + if (context->event_cb == NULL) return; os_memset(&ev, 0, sizeof(ev)); - if (conn->cert_probe || tls_global->cert_in_cb) { + if (conn->cert_probe || context->cert_in_cb) { cert = get_x509_cert(err_cert); ev.peer_cert.cert = cert; } @@ -1197,7 +1241,7 @@ static void openssl_tls_cert_event(struct tls_connection *conn, #endif /* CONFIG_SHA256 */ ev.peer_cert.depth = depth; ev.peer_cert.subject = subject; - tls_global->event_cb(tls_global->cb_ctx, TLS_PEER_CERTIFICATE, &ev); + context->event_cb(context->cb_ctx, TLS_PEER_CERTIFICATE, &ev); wpabuf_free(cert); } @@ -1209,6 +1253,7 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) int err, depth; SSL *ssl; struct tls_connection *conn; + struct tls_context *context; char *match, *altmatch; const char *err_str; @@ -1222,6 +1267,7 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) conn = SSL_get_app_data(ssl); if (conn == NULL) return 0; + context = conn->context; match = conn->subject_match; altmatch = conn->altsubject_match; @@ -1304,9 +1350,9 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) TLS_FAIL_SERVER_CHAIN_PROBE); } - if (preverify_ok && tls_global->event_cb != NULL) - tls_global->event_cb(tls_global->cb_ctx, - TLS_CERT_CHAIN_SUCCESS, NULL); + if (preverify_ok && context->event_cb != NULL) + context->event_cb(context->cb_ctx, + TLS_CERT_CHAIN_SUCCESS, NULL); return preverify_ok; } diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 14b64a6c..0091db88 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -898,6 +898,8 @@ struct wpa_driver_capa { #define WPA_DRIVER_PROBE_RESP_OFFLOAD_INTERWORKING 0x00000008 unsigned int probe_resp_offloads; + unsigned int max_acl_mac_addrs; + /** * extended_capa - extended capabilities in driver/device * @@ -958,6 +960,16 @@ struct hostapd_freq_params { int bandwidth; }; +struct mac_address { + u8 addr[ETH_ALEN]; +}; + +struct hostapd_acl_params { + u8 acl_policy; + unsigned int num_mac_acl; + struct mac_address mac_acl[0]; +}; + enum wpa_driver_if_type { /** * WPA_IF_STATION - Station mode interface @@ -1588,6 +1600,16 @@ struct wpa_driver_ops { int (*set_ap)(void *priv, struct wpa_driver_ap_params *params); /** + * set_acl - Set ACL in AP mode + * @priv: Private driver interface data + * @params: Parameters to configure ACL + * Returns: 0 on success, -1 on failure + * + * This is used only for the drivers which support MAC address ACL. + */ + int (*set_acl)(void *priv, struct hostapd_acl_params *params); + + /** * hapd_init - Initialize driver interface (hostapd only) * @hapd: Pointer to hostapd context * @params: Configuration for the driver wrapper diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index c812d959..07cc8308 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -1094,7 +1094,8 @@ static unsigned int nl80211_get_assoc_freq(struct wpa_driver_nl80211_data *drv) 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; + return (drv->assoc_freq = (arg.assoc_freq ? + arg.assoc_freq : drv->assoc_freq )); } wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d " "(%s)", ret, strerror(-ret)); @@ -2237,6 +2238,13 @@ static void nl80211_tdls_oper_event(struct wpa_driver_nl80211_data *drv, } +static void nl80211_stop_ap(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_UNAVAILABLE, NULL); +} + + static void nl80211_connect_failed_event(struct wpa_driver_nl80211_data *drv, struct nlattr **tb) { @@ -2418,6 +2426,9 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd, case NL80211_CMD_FT_EVENT: mlme_event_ft_event(drv, tb); break; + case NL80211_CMD_STOP_AP: + nl80211_stop_ap(drv, tb); + break; default: wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Ignored unknown event " "(cmd=%d)", cmd); @@ -2860,6 +2871,10 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg) capa->max_match_sets = nla_get_u8(tb[NL80211_ATTR_MAX_MATCH_SETS]); + if (tb[NL80211_ATTR_MAC_ACL_MAX]) + capa->max_acl_mac_addrs = + nla_get_u8(tb[NL80211_ATTR_MAC_ACL_MAX]); + wiphy_info_supported_iftypes(info, tb[NL80211_ATTR_SUPPORTED_IFTYPES]); wiphy_info_iface_comb(info, tb[NL80211_ATTR_INTERFACE_COMBINATIONS]); wiphy_info_supp_cmds(info, tb[NL80211_ATTR_SUPPORTED_COMMANDS]); @@ -4537,14 +4552,17 @@ static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss, int ifindex = if_nametoindex(ifname); struct nl_msg *msg; int ret; + int tdls = 0; wpa_printf(MSG_DEBUG, "%s: ifindex=%d alg=%d addr=%p key_idx=%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) + if (key_idx == -1) { key_idx = 0; + tdls = 1; + } #endif /* CONFIG_TDLS */ msg = nlmsg_alloc(); @@ -4637,7 +4655,7 @@ static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss, * If we failed or don't need to set the default TX key (below), * we're done here. */ - if (ret || !set_tx || alg == WPA_ALG_NONE) + if (ret || !set_tx || alg == WPA_ALG_NONE || tdls) return ret; if (is_ap_interface(drv->nlmode) && addr && !is_broadcast_ether_addr(addr)) @@ -4850,12 +4868,20 @@ nla_put_failure: static int wpa_driver_nl80211_disconnect(struct wpa_driver_nl80211_data *drv, int reason_code) { + int ret; + wpa_printf(MSG_DEBUG, "%s(reason_code=%d)", __func__, reason_code); drv->associated = 0; - drv->ignore_next_local_disconnect = 0; /* Disconnect command doesn't need BSSID - it uses cached value */ - return wpa_driver_nl80211_mlme(drv, NULL, NL80211_CMD_DISCONNECT, - reason_code, 0); + ret = wpa_driver_nl80211_mlme(drv, NULL, NL80211_CMD_DISCONNECT, + reason_code, 0); + /* + * For locally generated disconnect, supplicant already generates a + * DEAUTH event, so ignore the event from NL80211. + */ + drv->ignore_next_local_disconnect = ret == 0; + + return ret; } @@ -5862,6 +5888,60 @@ static int nl80211_set_bss(struct i802_bss *bss, int cts, int preamble, } +static int wpa_driver_nl80211_set_acl(void *priv, + struct hostapd_acl_params *params) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + struct nlattr *acl; + unsigned int i; + int ret = 0; + + if (!(drv->capa.max_acl_mac_addrs)) + return -ENOTSUP; + + if (params->num_mac_acl > drv->capa.max_acl_mac_addrs) + return -ENOTSUP; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + wpa_printf(MSG_DEBUG, "nl80211: Set %s ACL (num_mac_acl=%u)", + params->acl_policy ? "Accept" : "Deny", params->num_mac_acl); + + nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_MAC_ACL); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + + NLA_PUT_U32(msg, NL80211_ATTR_ACL_POLICY, params->acl_policy ? + NL80211_ACL_POLICY_DENY_UNLESS_LISTED : + NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED); + + acl = nla_nest_start(msg, NL80211_ATTR_MAC_ADDRS); + if (acl == NULL) + goto nla_put_failure; + + for (i = 0; i < params->num_mac_acl; i++) + NLA_PUT(msg, i + 1, ETH_ALEN, params->mac_acl[i].addr); + + nla_nest_end(msg, acl); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Failed to set MAC ACL: %d (%s)", + ret, strerror(-ret)); + } + +nla_put_failure: + nlmsg_free(msg); + + return ret; +} + + static int wpa_driver_nl80211_set_ap(void *priv, struct wpa_driver_ap_params *params) { @@ -6190,12 +6270,25 @@ static int wpa_driver_nl80211_sta_add(void *priv, wpa_hexdump(MSG_DEBUG, " * supported rates", params->supp_rates, params->supp_rates_len); if (!params->set) { - wpa_printf(MSG_DEBUG, " * aid=%u", params->aid); - NLA_PUT_U16(msg, NL80211_ATTR_STA_AID, params->aid); + if (params->aid) { + wpa_printf(MSG_DEBUG, " * aid=%u", params->aid); + NLA_PUT_U16(msg, NL80211_ATTR_STA_AID, params->aid); + } else { + /* + * cfg80211 validates that AID is non-zero, so we have + * to make this a non-zero value for the TDLS case where + * a dummy STA entry is used for now. + */ + wpa_printf(MSG_DEBUG, " * aid=1 (TDLS workaround)"); + NLA_PUT_U16(msg, NL80211_ATTR_STA_AID, 1); + } wpa_printf(MSG_DEBUG, " * listen_interval=%u", params->listen_interval); NLA_PUT_U16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL, params->listen_interval); + } else if (params->aid && (params->flags & WPA_STA_TDLS_PEER)) { + wpa_printf(MSG_DEBUG, " * peer_aid=%u", params->aid); + NLA_PUT_U16(msg, NL80211_ATTR_PEER_AID, params->aid); } if (params->ht_capabilities) { wpa_hexdump(MSG_DEBUG, " * ht_capabilities", @@ -7241,7 +7334,9 @@ static int wpa_driver_nl80211_try_connect( if (params->freq) { wpa_printf(MSG_DEBUG, " * freq=%d", params->freq); NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq); - } + drv->assoc_freq = params->freq; + } else + drv->assoc_freq = 0; if (params->bg_scan_period >= 0) { wpa_printf(MSG_DEBUG, " * bg scan period=%d", params->bg_scan_period); @@ -7452,8 +7547,6 @@ static int wpa_driver_nl80211_connect( if (wpa_driver_nl80211_disconnect( drv, WLAN_REASON_PREV_AUTH_NOT_VALID)) return -1; - /* Ignore the next local disconnect message. */ - drv->ignore_next_local_disconnect = 1; ret = wpa_driver_nl80211_try_connect(drv, params); } return ret; @@ -9974,6 +10067,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .set_supp_port = wpa_driver_nl80211_set_supp_port, .set_country = wpa_driver_nl80211_set_country, .set_ap = wpa_driver_nl80211_set_ap, + .set_acl = wpa_driver_nl80211_set_acl, .if_add = wpa_driver_nl80211_if_add, .if_remove = driver_nl80211_if_remove, .send_mlme = driver_nl80211_send_mlme, diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h index 79da8710..32b060ea 100644 --- a/src/drivers/nl80211_copy.h +++ b/src/drivers/nl80211_copy.h @@ -27,6 +27,8 @@ #include <linux/types.h> +#define NL80211_GENL_NAME "nl80211" + /** * DOC: Station handling * @@ -639,6 +641,13 @@ * with the relevant Information Elements. This event is used to report * received FT IEs (MDIE, FTIE, RSN IE, TIE, RICIE). * + * @NL80211_CMD_CRIT_PROTOCOL_START: Indicates user-space will start running + * a critical protocol that needs more reliability in the connection to + * complete. + * + * @NL80211_CMD_CRIT_PROTOCOL_STOP: Indicates the connection reliability can + * return back to normal. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -798,6 +807,9 @@ enum nl80211_commands { NL80211_CMD_UPDATE_FT_IES, NL80211_CMD_FT_EVENT, + NL80211_CMD_CRIT_PROTOCOL_START, + NL80211_CMD_CRIT_PROTOCOL_STOP, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -1414,6 +1426,16 @@ enum nl80211_commands { * @NL80211_ATTR_IE_RIC: Resource Information Container Information * Element * + * @NL80211_ATTR_CRIT_PROT_ID: critical protocol identifier requiring increased + * reliability, see &enum nl80211_crit_proto_id (u16). + * @NL80211_ATTR_MAX_CRIT_PROT_DURATION: duration in milliseconds in which + * the connection should have increased reliability (u16). + * + * @NL80211_ATTR_PEER_AID: Association ID for the peer TDLS station (u16). + * This is similar to @NL80211_ATTR_STA_AID but with a difference of being + * allowed to be used with the first @NL80211_CMD_SET_STATION command to + * update a TDLS peer STA entry. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -1709,6 +1731,11 @@ enum nl80211_attrs { NL80211_ATTR_MDID, NL80211_ATTR_IE_RIC, + NL80211_ATTR_CRIT_PROT_ID, + NL80211_ATTR_MAX_CRIT_PROT_DURATION, + + NL80211_ATTR_PEER_AID, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -1973,6 +2000,10 @@ enum nl80211_sta_bss_param { * @NL80211_STA_INFO_PEER_PM: peer mesh STA link-specific power mode * @NL80211_STA_INFO_NONPEER_PM: neighbor mesh STA power save mode towards * non-peer STA + * @NL80211_STA_INFO_CHAIN_SIGNAL: per-chain signal strength of last PPDU + * Contains a nested array of signal strength attributes (u8, dBm) + * @NL80211_STA_INFO_CHAIN_SIGNAL_AVG: per-chain signal strength average + * Same format as NL80211_STA_INFO_CHAIN_SIGNAL. * @__NL80211_STA_INFO_AFTER_LAST: internal * @NL80211_STA_INFO_MAX: highest possible station info attribute */ @@ -2002,6 +2033,8 @@ enum nl80211_sta_info { NL80211_STA_INFO_NONPEER_PM, NL80211_STA_INFO_RX_BYTES64, NL80211_STA_INFO_TX_BYTES64, + NL80211_STA_INFO_CHAIN_SIGNAL, + NL80211_STA_INFO_CHAIN_SIGNAL_AVG, /* keep last */ __NL80211_STA_INFO_AFTER_LAST, @@ -2619,6 +2652,10 @@ enum nl80211_meshconf_params { * @NL80211_MESH_SETUP_USERSPACE_MPM: Enable this option if userspace will * implement an MPM which handles peer allocation and state. * + * @NL80211_MESH_SETUP_AUTH_PROTOCOL: Inform the kernel of the authentication + * method (u8, as defined in IEEE 8.4.2.100.6, e.g. 0x1 for SAE). + * Default is no authentication method required. + * * @NL80211_MESH_SETUP_ATTR_MAX: highest possible mesh setup attribute number * * @__NL80211_MESH_SETUP_ATTR_AFTER_LAST: Internal use @@ -2632,6 +2669,7 @@ enum nl80211_mesh_setup_params { NL80211_MESH_SETUP_USERSPACE_AMPE, NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC, NL80211_MESH_SETUP_USERSPACE_MPM, + NL80211_MESH_SETUP_AUTH_PROTOCOL, /* keep last */ __NL80211_MESH_SETUP_ATTR_AFTER_LAST, @@ -3682,4 +3720,25 @@ enum nl80211_protocol_features { NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP = 1 << 0, }; +/** + * enum nl80211_crit_proto_id - nl80211 critical protocol identifiers + * + * @NL80211_CRIT_PROTO_UNSPEC: protocol unspecified. + * @NL80211_CRIT_PROTO_DHCP: BOOTP or DHCPv6 protocol. + * @NL80211_CRIT_PROTO_EAPOL: EAPOL protocol. + * @NL80211_CRIT_PROTO_APIPA: APIPA protocol. + * @NUM_NL80211_CRIT_PROTO: must be kept last. + */ +enum nl80211_crit_proto_id { + NL80211_CRIT_PROTO_UNSPEC, + NL80211_CRIT_PROTO_DHCP, + NL80211_CRIT_PROTO_EAPOL, + NL80211_CRIT_PROTO_APIPA, + /* add other protocols before this one */ + NUM_NL80211_CRIT_PROTO +}; + +/* maximum duration for critical protocol measures */ +#define NL80211_CRIT_PROTO_MAX_DURATION 5000 /* msec */ + #endif /* __LINUX_NL80211_H */ diff --git a/src/eapol_supp/eapol_supp_sm.c b/src/eapol_supp/eapol_supp_sm.c index 2e560865..898285e1 100644 --- a/src/eapol_supp/eapol_supp_sm.c +++ b/src/eapol_supp/eapol_supp_sm.c @@ -1257,6 +1257,24 @@ int eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src, const u8 *buf, switch (hdr->type) { case IEEE802_1X_TYPE_EAP_PACKET: + if (sm->conf.workaround) { + /* + * An AP has been reported to send out EAP message with + * undocumented code 10 at some point near the + * completion of EAP authentication. This can result in + * issues with the unexpected EAP message triggering + * restart of EAPOL authentication. Avoid this by + * skipping the message without advancing the state + * machine. + */ + const struct eap_hdr *ehdr = + (const struct eap_hdr *) (hdr + 1); + if (plen >= sizeof(*ehdr) && ehdr->code == 10) { + wpa_printf(MSG_DEBUG, "EAPOL: Ignore EAP packet with unknown code 10"); + break; + } + } + if (sm->cached_pmk) { /* Trying to use PMKSA caching, but Authenticator did * not seem to have a matching entry. Need to restart diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c index 2b5e5bd1..4424c92f 100644 --- a/src/p2p/p2p.c +++ b/src/p2p/p2p.c @@ -71,6 +71,8 @@ int p2p_connection_in_progress(struct p2p_data *p2p) wpa_printf(MSG_DEBUG, "p2p_connection_in_progress state %d", p2p->state); ret = 0; } + if (p2p->pending_action_state == P2P_PENDING_PD) + ret = 1; return ret; } @@ -268,8 +270,7 @@ static void p2p_listen_in_find(struct p2p_data *p2p, int dev_disc) "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); + freq = p2p_channel_to_freq(p2p->cfg->reg_class, p2p->cfg->channel); if (freq < 0) { wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Unknown regulatory class/channel"); @@ -319,8 +320,7 @@ int p2p_listen(struct p2p_data *p2p, unsigned int timeout) 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); + freq = p2p_channel_to_freq(p2p->cfg->reg_class, p2p->cfg->channel); if (freq < 0) { wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Unknown regulatory class/channel"); @@ -914,7 +914,7 @@ static int p2p_get_next_prog_freq(struct p2p_data *p2p) channel = c->reg_class[cl].channel[ch]; } - freq = p2p_channel_to_freq(p2p->cfg->country, reg_class, channel); + freq = p2p_channel_to_freq(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); @@ -1231,6 +1231,19 @@ void p2p_stop_listen_for_freq(struct p2p_data *p2p, int freq) } +void p2p_stop_listen(struct p2p_data *p2p) +{ + if (p2p->state != P2P_LISTEN_ONLY) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "Skip stop_listen " + "since not in listen_only state."); + return; + } + + p2p_stop_listen_for_freq(p2p, 0); + p2p_set_state(p2p, P2P_IDLE); +} + + void p2p_stop_find(struct p2p_data *p2p) { p2p_stop_find_for_freq(p2p, 0); @@ -1244,8 +1257,7 @@ static int p2p_prepare_channel_pref(struct p2p_data *p2p, u8 op_class, op_channel; unsigned int freq = force_freq ? force_freq : pref_freq; - if (p2p_freq_to_channel(p2p->cfg->country, freq, - &op_class, &op_channel) < 0) { + if (p2p_freq_to_channel(freq, &op_class, &op_channel) < 0) { wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Unsupported frequency %u MHz", freq); return -1; @@ -1281,24 +1293,24 @@ static void p2p_prepare_channel_best(struct p2p_data *p2p) 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_class, &op_channel) == 0) { + p2p_freq_to_channel(p2p->best_freq_overall, &op_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_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_class, &op_channel) == 0) { + p2p_freq_to_channel(p2p->best_freq_5, &op_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_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_class, &op_channel) == 0) { + p2p_freq_to_channel(p2p->best_freq_24, &op_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_class; @@ -1547,8 +1559,7 @@ void p2p_add_dev_info(struct p2p_data *p2p, const u8 *addr, if (msg->listen_channel) { int freq; - freq = p2p_channel_to_freq((char *) msg->listen_channel, - msg->listen_channel[3], + freq = p2p_channel_to_freq(msg->listen_channel[3], msg->listen_channel[4]); if (freq < 0) { wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, @@ -1647,8 +1658,7 @@ void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer) if (go) { /* Setup AP mode for WPS provisioning */ - res.freq = p2p_channel_to_freq(p2p->cfg->country, - p2p->op_reg_class, + res.freq = p2p_channel_to_freq(p2p->op_reg_class, p2p->op_channel); os_memcpy(res.ssid, p2p->ssid, p2p->ssid_len); res.ssid_len = p2p->ssid_len; @@ -1672,8 +1682,7 @@ void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer) int freq; if (freqs + 1 == P2P_MAX_CHANNELS) break; - freq = p2p_channel_to_freq(peer->country, c->reg_class, - c->channel[j]); + freq = p2p_channel_to_freq(c->reg_class, c->channel[j]); if (freq < 0) continue; res.freq_list[freqs++] = freq; @@ -1903,8 +1912,7 @@ static void p2p_add_dev_from_probe_req(struct p2p_data *p2p, const u8 *addr, 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], + dev->listen_freq = p2p_channel_to_freq(msg.listen_channel[3], msg.listen_channel[4]); } @@ -4142,7 +4150,7 @@ 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) { - if (p2p_channel_to_freq(p2p->cfg->country, reg_class, channel) < 0) + if (p2p_channel_to_freq(reg_class, channel) < 0) return -1; wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Set Listen channel: " @@ -4172,8 +4180,7 @@ int p2p_set_ssid_postfix(struct p2p_data *p2p, const u8 *postfix, size_t len) 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) + if (p2p_channel_to_freq(op_reg_class, op_channel) < 0) return -1; wpa_msg(p2p->cfg->msg_ctx, MSG_INFO, "P2P: Set Operating channel: " diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h index 28a0a1d1..f76f401d 100644 --- a/src/p2p/p2p.h +++ b/src/p2p/p2p.h @@ -693,6 +693,7 @@ struct p2p_config { * @persistent_group: Whether this is an invitation to reinvoke a * persistent group (instead of invitation to join an active * group) + * @channels: Available operating channels for the group * Returns: Status code (P2P_SC_*) * * This optional callback can be used to implement persistent reconnect @@ -713,7 +714,8 @@ struct p2p_config { 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); + int *force_freq, int persistent_group, + const struct p2p_channels *channels); /** * invitation_received - Callback on Invitation Request RX @@ -906,6 +908,12 @@ void p2p_stop_find_for_freq(struct p2p_data *p2p, int freq); int p2p_listen(struct p2p_data *p2p, unsigned int timeout); /** + * p2p_stop_listen - Stop P2P Listen + * @p2p: P2P module context from p2p_init() + */ +void p2p_stop_listen(struct p2p_data *p2p); + +/** * p2p_connect - Start P2P group formation (GO negotiation) * @p2p: P2P module context from p2p_init() * @peer_addr: MAC address of the peer P2P client @@ -1648,6 +1656,15 @@ int p2p_channels_includes_freq(const struct p2p_channels *channels, */ int p2p_supported_freq(struct p2p_data *p2p, unsigned int freq); +/** + * p2p_get_pref_freq - Get channel from preferred channel list + * @p2p: P2P module context from p2p_init() + * @channels: List of channels + * Returns: Preferred channel + */ +unsigned int p2p_get_pref_freq(struct p2p_data *p2p, + const struct p2p_channels *channels); + void p2p_update_channel_list(struct p2p_data *p2p, struct p2p_channels *chan); /** diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c index c143ef4a..5d0b5cae 100644 --- a/src/p2p/p2p_go_neg.c +++ b/src/p2p/p2p_go_neg.c @@ -351,7 +351,7 @@ void p2p_reselect_channel(struct p2p_data *p2p, unsigned int i; if (p2p->own_freq_preference > 0 && - p2p_freq_to_channel(p2p->cfg->country, p2p->own_freq_preference, + p2p_freq_to_channel(p2p->own_freq_preference, &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 own channel " @@ -363,7 +363,7 @@ void p2p_reselect_channel(struct p2p_data *p2p, } if (p2p->best_freq_overall > 0 && - p2p_freq_to_channel(p2p->cfg->country, p2p->best_freq_overall, + p2p_freq_to_channel(p2p->best_freq_overall, &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 overall " @@ -375,12 +375,11 @@ void p2p_reselect_channel(struct p2p_data *p2p, } /* 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); + freq = p2p_channel_to_freq(p2p->op_reg_class, p2p->op_channel); if (freq >= 2400 && freq < 2500 && p2p->best_freq_5 > 0 && !p2p_channels_includes(intersection, p2p->op_reg_class, p2p->op_channel) && - p2p_freq_to_channel(p2p->cfg->country, p2p->best_freq_5, + p2p_freq_to_channel(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 " @@ -394,7 +393,7 @@ void p2p_reselect_channel(struct p2p_data *p2p, if (freq >= 4900 && freq < 6000 && p2p->best_freq_24 > 0 && !p2p_channels_includes(intersection, p2p->op_reg_class, p2p->op_channel) && - p2p_freq_to_channel(p2p->cfg->country, p2p->best_freq_24, + p2p_freq_to_channel(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 " @@ -435,6 +434,29 @@ void p2p_reselect_channel(struct p2p_data *p2p, } } + /* Prefer a 5 GHz channel */ + for (i = 0; i < intersection->reg_classes; i++) { + struct p2p_reg_class *c = &intersection->reg_class[i]; + if ((c->reg_class == 115 || c->reg_class == 124) && + c->channels) { + unsigned int r; + + /* + * Pick one of the available channels in the operating + * class at random. + */ + os_get_random((u8 *) &r, sizeof(r)); + r %= c->channels; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Pick possible 5 GHz channel (reg_class " + "%u channel %u) from intersection", + c->reg_class, c->channel[r]); + p2p->op_reg_class = c->reg_class; + p2p->op_channel = c->channel[r]; + return; + } + } + /* * Try to see if the original channel is in the intersection. If * so, no need to change anything, as it already contains some @@ -736,9 +758,7 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, goto fail; dev->go_state = go ? LOCAL_GO : REMOTE_GO; - dev->oper_freq = p2p_channel_to_freq((const char *) - msg.operating_channel, - msg.operating_channel[3], + dev->oper_freq = p2p_channel_to_freq(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); @@ -773,8 +793,7 @@ fail: if (rx_freq > 0) freq = rx_freq; else - freq = p2p_channel_to_freq(p2p->cfg->country, - p2p->cfg->reg_class, + freq = p2p_channel_to_freq(p2p->cfg->reg_class, p2p->cfg->channel); if (freq < 0) { wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, @@ -1050,9 +1069,7 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, } if (msg.operating_channel) { - dev->oper_freq = p2p_channel_to_freq((const char *) - msg.operating_channel, - msg.operating_channel[3], + dev->oper_freq = p2p_channel_to_freq(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); @@ -1231,10 +1248,8 @@ void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa, #ifdef ANDROID_P2P if (msg.operating_channel) { - dev->oper_freq = p2p_channel_to_freq((const char *) - msg.operating_channel, - msg.operating_channel[3], - msg.operating_channel[4]); + dev->oper_freq = p2p_channel_to_freq(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 diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h index d5ce52f8..7261ea80 100644 --- a/src/p2p/p2p_i.h +++ b/src/p2p/p2p_i.h @@ -569,9 +569,8 @@ struct p2p_group_info { /* 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); +int p2p_channel_to_freq(int op_class, int channel); +int p2p_freq_to_channel(unsigned int freq, u8 *op_class, u8 *channel); void p2p_channels_intersect(const struct p2p_channels *a, const struct p2p_channels *b, struct p2p_channels *res); diff --git a/src/p2p/p2p_invitation.c b/src/p2p/p2p_invitation.c index 3beefd24..11a7727c 100644 --- a/src/p2p/p2p_invitation.c +++ b/src/p2p/p2p_invitation.c @@ -228,18 +228,20 @@ void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, goto fail; } + p2p_channels_intersect(&p2p->cfg->channels, &dev->channels, + &intersection); + 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); + &go, group_bssid, &op_freq, persistent, &intersection); } if (op_freq) { wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Invitation " "processing forced frequency %d MHz", op_freq); - if (p2p_freq_to_channel(p2p->cfg->country, op_freq, - ®_class, &channel) < 0) { + if (p2p_freq_to_channel(op_freq, ®_class, &channel) < 0) { wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Unknown forced freq %d MHz from " "invitation_process()", op_freq); @@ -247,8 +249,6 @@ void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, 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, @@ -265,8 +265,6 @@ void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, "P2P: No forced channel from invitation processing - " "figure out best one to use"); - p2p_channels_intersect(&p2p->cfg->channels, &dev->channels, - &intersection); /* Default to own configuration as a starting point */ p2p->op_reg_class = p2p->cfg->op_reg_class; p2p->op_channel = p2p->cfg->op_channel; @@ -278,7 +276,6 @@ void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, if (msg.operating_channel) { int req_freq; req_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 " @@ -324,10 +321,17 @@ void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, status = P2P_SC_FAIL_NO_COMMON_CHANNELS; goto fail; } + } else if (!(dev->flags & P2P_DEV_FORCE_FREQ) && + !p2p->cfg->cfg_op_channel) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Try to reselect channel selection with " + "peer information received; " + "previously selected op_class %u channel %u", + p2p->op_reg_class, p2p->op_channel); + p2p_reselect_channel(p2p, &intersection); } - op_freq = p2p_channel_to_freq(p2p->cfg->country, - p2p->op_reg_class, + op_freq = p2p_channel_to_freq(p2p->op_reg_class, p2p->op_channel); if (op_freq < 0) { wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, @@ -362,8 +366,7 @@ fail: if (rx_freq > 0) freq = rx_freq; else - freq = p2p_channel_to_freq(p2p->cfg->country, - p2p->cfg->reg_class, + freq = p2p_channel_to_freq(p2p->cfg->reg_class, p2p->cfg->channel); if (freq < 0) { wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, @@ -533,11 +536,8 @@ void p2p_invitation_req_cb(struct p2p_data *p2p, int success) * channel. */ p2p_set_state(p2p, P2P_INVITE); -#ifdef ANDROID_P2P - p2p_set_timeout(p2p, 0, 350000); -#else - p2p_set_timeout(p2p, 0, 100000); -#endif + + p2p_set_timeout(p2p, 0, success ? 350000 : 100000); } diff --git a/src/p2p/p2p_pd.c b/src/p2p/p2p_pd.c index a1268e44..3bbce164 100644 --- a/src/p2p/p2p_pd.c +++ b/src/p2p/p2p_pd.c @@ -217,8 +217,7 @@ out: if (rx_freq > 0) freq = rx_freq; else - freq = p2p_channel_to_freq(p2p->cfg->country, - p2p->cfg->reg_class, + freq = p2p_channel_to_freq(p2p->cfg->reg_class, p2p->cfg->channel); if (freq < 0) { wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, diff --git a/src/p2p/p2p_sd.c b/src/p2p/p2p_sd.c index bf756050..d8daa59d 100644 --- a/src/p2p/p2p_sd.c +++ b/src/p2p/p2p_sd.c @@ -290,8 +290,7 @@ void p2p_rx_gas_initial_req(struct p2p_data *p2p, const u8 *sa, if (rx_freq > 0) freq = rx_freq; else - freq = p2p_channel_to_freq(p2p->cfg->country, - p2p->cfg->reg_class, + freq = p2p_channel_to_freq(p2p->cfg->reg_class, p2p->cfg->channel); if (freq < 0) return; diff --git a/src/p2p/p2p_utils.c b/src/p2p/p2p_utils.c index 37b9361a..0da26827 100644 --- a/src/p2p/p2p_utils.c +++ b/src/p2p/p2p_utils.c @@ -46,11 +46,17 @@ int p2p_random(char *buf, size_t len) } -static int p2p_channel_to_freq_j4(int reg_class, int channel) +/** + * p2p_channel_to_freq - Convert channel info to frequency + * @op_class: Operating class + * @channel: Channel number + * Returns: Frequency in MHz or -1 if the specified channel is unknown + */ +int p2p_channel_to_freq(int op_class, int channel) { - /* Table J-4 in P802.11REVmb/D4.0 - Global operating classes */ - /* TODO: more regulatory classes */ - switch (reg_class) { + /* Table E-4 in IEEE Std 802.11-2012 - Global operating classes */ + /* TODO: more operating classes */ + switch (op_class) { case 81: /* channels 1..13 */ if (channel < 1 || channel > 13) @@ -94,75 +100,34 @@ static int p2p_channel_to_freq_j4(int reg_class, int channel) /** - * 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 + * @op_class: Buffer for returning operating 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) +int p2p_freq_to_channel(unsigned int freq, u8 *op_class, u8 *channel) { /* TODO: more operating classes */ if (freq >= 2412 && freq <= 2472) { - *reg_class = 81; /* 2.407 GHz, channels 1..13 */ + *op_class = 81; /* 2.407 GHz, channels 1..13 */ *channel = (freq - 2407) / 5; return 0; } if (freq == 2484) { - *reg_class = 82; /* channel 14 */ + *op_class = 82; /* channel 14 */ *channel = 14; return 0; } if (freq >= 5180 && freq <= 5240) { - *reg_class = 115; /* 5 GHz, channels 36..48 */ + *op_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 */ + *op_class = 124; /* 5 GHz, channels 149..161 */ *channel = (freq - 5000) / 5; return 0; } @@ -261,9 +226,8 @@ int p2p_channels_includes_freq(const struct p2p_channels *channels, for (i = 0; i < channels->reg_classes; i++) { const struct p2p_reg_class *reg = &channels->reg_class[i]; for (j = 0; j < reg->channels; j++) { - if (p2p_channel_to_freq_j4(reg->reg_class, - reg->channel[j]) == - (int) freq) + if (p2p_channel_to_freq(reg->reg_class, + reg->channel[j]) == (int) freq) return 1; } } @@ -274,9 +238,36 @@ int p2p_channels_includes_freq(const struct p2p_channels *channels, 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) + if (p2p_freq_to_channel(freq, &op_reg_class, &op_channel) < 0) return 0; return p2p_channels_includes(&p2p->cfg->channels, op_reg_class, op_channel); } + + +unsigned int p2p_get_pref_freq(struct p2p_data *p2p, + const struct p2p_channels *channels) +{ + unsigned int i; + int freq = 0; + + if (channels == NULL) { + if (p2p->cfg->num_pref_chan) { + freq = p2p_channel_to_freq( + p2p->cfg->pref_chan[0].op_class, + p2p->cfg->pref_chan[0].chan); + if (freq < 0) + freq = 0; + } + return freq; + } + + for (i = 0; p2p->cfg->pref_chan && i < p2p->cfg->num_pref_chan; i++) { + freq = p2p_channel_to_freq(p2p->cfg->pref_chan[i].op_class, + p2p->cfg->pref_chan[i].chan); + if (p2p_channels_includes_freq(channels, freq)) + return freq; + } + + return 0; +} diff --git a/src/rsn_supp/tdls.c b/src/rsn_supp/tdls.c index 8ceaf6c3..53206142 100644 --- a/src/rsn_supp/tdls.c +++ b/src/rsn_supp/tdls.c @@ -37,8 +37,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 TPK_M1_RETRY_COUNT 3 +#define TPK_M1_TIMEOUT 5000 /* in milliseconds */ +#define TPK_M2_RETRY_COUNT 10 +#define TPK_M2_TIMEOUT 500 /* in milliseconds */ #define TDLS_MIC_LEN 16 @@ -79,6 +81,8 @@ struct wpa_tdls_frame { 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); +static void wpa_tdls_disable_peer_link(struct wpa_sm *sm, + struct wpa_tdls_peer *peer); #define TDLS_MAX_IE_LEN 80 @@ -104,6 +108,7 @@ struct wpa_tdls_peer { } tpk; int tpk_set; int tpk_success; + int tpk_in_progress; struct tpk_timer { u8 dest[ETH_ALEN]; @@ -126,6 +131,8 @@ struct wpa_tdls_peer { u8 qos_info; + u16 aid; + u8 *ext_capab; size_t ext_capab_len; }; @@ -241,8 +248,13 @@ static int wpa_tdls_tpk_send(struct wpa_sm *sm, const u8 *dest, u8 action_code, eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer); - peer->sm_tmr.count = TPK_RETRY_COUNT; - peer->sm_tmr.timer = TPK_TIMEOUT; + if (action_code == WLAN_TDLS_SETUP_RESPONSE) { + peer->sm_tmr.count = TPK_M2_RETRY_COUNT; + peer->sm_tmr.timer = TPK_M2_TIMEOUT; + } else { + peer->sm_tmr.count = TPK_M1_RETRY_COUNT; + peer->sm_tmr.timer = TPK_M1_TIMEOUT; + } /* Copy message to resend on timeout */ os_memcpy(peer->sm_tmr.dest, dest, ETH_ALEN); @@ -258,28 +270,21 @@ static int wpa_tdls_tpk_send(struct wpa_sm *sm, const u8 *dest, u8 action_code, wpa_printf(MSG_DEBUG, "TDLS: Retry timeout registered " "(action_code=%u)", action_code); - eloop_register_timeout(peer->sm_tmr.timer / 1000, 0, + eloop_register_timeout(peer->sm_tmr.timer / 1000, + (peer->sm_tmr.timer % 1000) * 1000, 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) + u16 reason_code) { 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); + ret = wpa_tdls_send_teardown(sm, peer->addr, reason_code); + /* disable the link after teardown was sent */ + wpa_tdls_disable_peer_link(sm, peer); return ret; } @@ -293,7 +298,6 @@ static void wpa_tdls_tpk_retry_timeout(void *eloop_ctx, void *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)", @@ -320,14 +324,15 @@ static void wpa_tdls_tpk_retry_timeout(void *eloop_ctx, void *timeout_ctx) } eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer); - eloop_register_timeout(peer->sm_tmr.timer / 1000, 0, + eloop_register_timeout(peer->sm_tmr.timer / 1000, + (peer->sm_tmr.timer % 1000) * 1000, 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); + WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED); } } @@ -605,7 +610,7 @@ static void wpa_tdls_tpk_timeout(void *eloop_ctx, void *timeout_ctx) 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); + WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED); } } @@ -617,6 +622,7 @@ static void wpa_tdls_peer_free(struct wpa_sm *sm, struct wpa_tdls_peer *peer) eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer); eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer); peer->initiator = 0; + peer->tpk_in_progress = 0; os_free(peer->sm_tmr.buf); peer->sm_tmr.buf = NULL; os_free(peer->ht_capabilities); @@ -768,7 +774,15 @@ int wpa_tdls_teardown_link(struct wpa_sm *sm, const u8 *addr, u16 reason_code) return -1; } - return wpa_tdls_do_teardown(sm, peer, reason_code, 0); + return wpa_tdls_do_teardown(sm, peer, reason_code); +} + + +static void wpa_tdls_disable_peer_link(struct wpa_sm *sm, + struct wpa_tdls_peer *peer) +{ + wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr); + wpa_tdls_peer_free(sm, peer); } @@ -781,10 +795,8 @@ void wpa_tdls_disable_link(struct wpa_sm *sm, const u8 *addr) break; } - if (peer) { - wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, addr); - wpa_tdls_peer_free(sm, peer); - } + if (peer) + wpa_tdls_disable_peer_link(sm, peer); } @@ -857,11 +869,7 @@ 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); - + wpa_tdls_disable_peer_link(sm, peer); return 0; } @@ -925,6 +933,7 @@ static int wpa_tdls_send_tpk_m1(struct wpa_sm *sm, u8 *rbuf, *pos, *count_pos; u16 count; struct rsn_ie_hdr *hdr; + int status; if (!wpa_tdls_get_privacy(sm)) { wpa_printf(MSG_DEBUG, "TDLS: No security used on the link"); @@ -1085,11 +1094,11 @@ skip_ies: "Handshake Message 1 (peer " MACSTR ")", MAC2STR(peer->addr)); - wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_SETUP_REQUEST, 1, 0, - rbuf, pos - rbuf); + status = wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_SETUP_REQUEST, + 1, 0, rbuf, pos - rbuf); os_free(rbuf); - return 0; + return status; } @@ -1103,6 +1112,7 @@ static int wpa_tdls_send_tpk_m2(struct wpa_sm *sm, u32 lifetime; struct wpa_tdls_timeoutie timeoutie; struct wpa_tdls_ftie *ftie; + int status; buf_len = 0; if (wpa_tdls_get_privacy(sm)) { @@ -1168,11 +1178,11 @@ static int wpa_tdls_send_tpk_m2(struct wpa_sm *sm, (u8 *) &timeoutie, (u8 *) ftie, ftie->mic); skip_ies: - wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE, dtoken, 0, - rbuf, pos - rbuf); + status = wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE, + dtoken, 0, rbuf, pos - rbuf); os_free(rbuf); - return 0; + return status; } @@ -1186,6 +1196,7 @@ static int wpa_tdls_send_tpk_m3(struct wpa_sm *sm, struct wpa_tdls_ftie *ftie; struct wpa_tdls_timeoutie timeoutie; u32 lifetime; + int status; buf_len = 0; if (wpa_tdls_get_privacy(sm)) { @@ -1249,11 +1260,11 @@ static int wpa_tdls_send_tpk_m3(struct wpa_sm *sm, (u8 *) &timeoutie, (u8 *) ftie, ftie->mic); skip_ies: - wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM, dtoken, 0, - rbuf, pos - rbuf); + status = wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM, + dtoken, 0, rbuf, pos - rbuf); os_free(rbuf); - return 0; + return status; } @@ -1340,7 +1351,8 @@ static int copy_supp_rates(const struct wpa_eapol_ie_parse *kde, peer->supp_rates_len = merge_byte_arrays( peer->supp_rates, sizeof(peer->supp_rates), kde->supp_rates + 2, kde->supp_rates_len - 2, - kde->ext_supp_rates + 2, kde->ext_supp_rates_len - 2); + kde->ext_supp_rates ? kde->ext_supp_rates + 2 : NULL, + kde->ext_supp_rates_len - 2); return 0; } @@ -1472,19 +1484,7 @@ static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr, 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); + wpa_tdls_disable_peer_link(sm, peer); } /* @@ -1505,12 +1505,7 @@ static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr, MACSTR " (terminate previously " "initiated negotiation", MAC2STR(src_addr)); - if (sm->tdls_external_setup) - wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, - src_addr); - else - wpa_tdls_del_key(sm, peer); - wpa_tdls_peer_free(sm, peer); + wpa_tdls_disable_peer_link(sm, peer); } } } @@ -1556,6 +1551,8 @@ static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr, peer->qos_info = kde.qosinfo; + peer->aid = kde.aid; + #ifdef CONFIG_TDLS_TESTING if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT) { peer = wpa_tdls_add_peer(sm, src_addr, NULL); @@ -1670,16 +1667,27 @@ skip_rsn: } 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 (os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) != 0) { + /* + * There is no point in updating the RNonce for every obtained + * TPK M1 frame (e.g., retransmission due to timeout) with the + * same INonce (SNonce in FTIE). However, if the TPK M1 is + * retransmitted with a different INonce, update the RNonce + * since this is for a new TDLS session. + */ + wpa_printf(MSG_DEBUG, + "TDLS: New TPK M1 INonce - generate new RNonce"); + os_memcpy(peer->inonce, ftie->Snonce, WPA_NONCE_LEN); + 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 @@ -1733,12 +1741,13 @@ skip_rsn: 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, NULL, NULL, 0, + wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, 0, NULL, 0, NULL, NULL, 0, NULL, 0); + peer->tpk_in_progress = 1; 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); + wpa_tdls_disable_peer_link(sm, peer); goto error; } @@ -1754,6 +1763,7 @@ error: static void wpa_tdls_enable_link(struct wpa_sm *sm, struct wpa_tdls_peer *peer) { peer->tpk_success = 1; + peer->tpk_in_progress = 0; eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer); if (wpa_tdls_get_privacy(sm)) { u32 lifetime = peer->lifetime; @@ -1775,9 +1785,11 @@ static void wpa_tdls_enable_link(struct wpa_sm *sm, struct wpa_tdls_peer *peer) } /* add supported rates, capabilities, and qos_info to the TDLS peer */ - wpa_sm_tdls_peer_addset(sm, peer->addr, 0, peer->capability, + wpa_sm_tdls_peer_addset(sm, peer->addr, 0, peer->aid, + peer->capability, peer->supp_rates, peer->supp_rates_len, - peer->ht_capabilities, peer->vht_capabilities, + peer->ht_capabilities, + peer->vht_capabilities, peer->qos_info, peer->ext_capab, peer->ext_capab_len); @@ -1824,8 +1836,11 @@ static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr, } wpa_tdls_tpk_retry_timeout_cancel(sm, peer, WLAN_TDLS_SETUP_REQUEST); - if (len < 3 + 2 + 1) + if (len < 3 + 2 + 1) { + wpa_tdls_disable_peer_link(sm, peer); return -1; + } + pos = buf; pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */; status = WPA_GET_LE16(pos); @@ -1834,8 +1849,7 @@ static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr, 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); + wpa_tdls_disable_peer_link(sm, peer); return -1; } @@ -1846,8 +1860,10 @@ static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr, wpa_printf(MSG_DEBUG, "TDLS: Dialog Token in TPK M2 %d", dtoken); - if (len < 3 + 2 + 1 + 2) + if (len < 3 + 2 + 1 + 2) { + wpa_tdls_disable_peer_link(sm, peer); return -1; + } /* capability information */ peer->capability = WPA_GET_LE16(pos); @@ -1896,6 +1912,8 @@ static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr, peer->qos_info = kde.qosinfo; + peer->aid = kde.aid; + if (!wpa_tdls_get_privacy(sm)) { peer->rsnie_p_len = 0; peer->cipher = WPA_CIPHER_NONE; @@ -1986,9 +2004,7 @@ static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr, (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); + wpa_tdls_disable_peer_link(sm, peer); return -1; } @@ -1999,8 +2015,10 @@ skip_rsn: wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Confirm / " "TPK Handshake Message 3"); - wpa_tdls_send_tpk_m3(sm, src_addr, dtoken, lnkid, peer); - + if (wpa_tdls_send_tpk_m3(sm, src_addr, dtoken, lnkid, peer) < 0) { + wpa_tdls_disable_peer_link(sm, peer); + return -1; + } wpa_tdls_enable_link(sm, peer); return 0; @@ -2008,8 +2026,7 @@ skip_rsn: 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); + wpa_tdls_disable_peer_link(sm, peer); return -1; } @@ -2041,7 +2058,7 @@ static int wpa_tdls_process_tpk_m3(struct wpa_sm *sm, const u8 *src_addr, wpa_tdls_tpk_retry_timeout_cancel(sm, peer, WLAN_TDLS_SETUP_RESPONSE); if (len < 3 + 3) - return -1; + goto error; pos = buf; pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */; @@ -2050,21 +2067,19 @@ static int wpa_tdls_process_tpk_m3(struct wpa_sm *sm, const u8 *src_addr, 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; + goto error; } 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; + goto error; } if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) { wpa_printf(MSG_INFO, "TDLS: No Link Identifier IE in TPK M3"); - return -1; + goto error; } wpa_hexdump(MSG_DEBUG, "TDLS: Link ID Received from TPK M3", (u8 *) kde.lnkid, kde.lnkid_len); @@ -2072,7 +2087,7 @@ static int wpa_tdls_process_tpk_m3(struct wpa_sm *sm, const u8 *src_addr, if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) { wpa_printf(MSG_INFO, "TDLS: TPK M3 from diff BSS"); - return -1; + goto error; } if (!wpa_tdls_get_privacy(sm)) @@ -2080,7 +2095,7 @@ static int wpa_tdls_process_tpk_m3(struct wpa_sm *sm, const u8 *src_addr, if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie)) { wpa_printf(MSG_INFO, "TDLS: No FTIE in TPK M3"); - return -1; + goto error; } wpa_hexdump(MSG_DEBUG, "TDLS: FTIE Received from TPK M3", kde.ftie, sizeof(*ftie)); @@ -2088,7 +2103,7 @@ static int wpa_tdls_process_tpk_m3(struct wpa_sm *sm, const u8 *src_addr, if (kde.rsn_ie == NULL) { wpa_printf(MSG_INFO, "TDLS: No RSN IE in TPK M3"); - return -1; + goto error; } wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M3", kde.rsn_ie, kde.rsn_ie_len); @@ -2096,24 +2111,24 @@ static int wpa_tdls_process_tpk_m3(struct wpa_sm *sm, const u8 *src_addr, 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; + goto error; } 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; + goto error; } 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; + goto error; } if (kde.key_lifetime == NULL) { wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M3"); - return -1; + goto error; } timeoutie = (struct wpa_tdls_timeoutie *) kde.key_lifetime; wpa_hexdump(MSG_DEBUG, "TDLS: Timeout IE Received from TPK M3", @@ -2124,16 +2139,13 @@ static int wpa_tdls_process_tpk_m3(struct wpa_sm *sm, const u8 *src_addr, 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; + goto error; } 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; + goto error; } if (wpa_tdls_set_key(sm, peer) < 0) @@ -2143,6 +2155,9 @@ skip_rsn: wpa_tdls_enable_link(sm, peer); return 0; +error: + wpa_tdls_disable_peer_link(sm, peer); + return -1; } @@ -2196,14 +2211,21 @@ int wpa_tdls_start(struct wpa_sm *sm, const u8 *addr) if (peer == NULL) return -1; + if (peer->tpk_in_progress) { + wpa_printf(MSG_DEBUG, "TDLS: Setup is already in progress with the peer"); + return 0; + } + 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, NULL, NULL, 0, + wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, 0, NULL, 0, NULL, NULL, 0, NULL, 0); + peer->tpk_in_progress = 1; + if (wpa_tdls_send_tpk_m1(sm, peer) < 0) { - wpa_tdls_disable_link(sm, peer->addr); + wpa_tdls_disable_peer_link(sm, peer); return -1; } diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h index dbb493e4..78dfb523 100644 --- a/src/rsn_supp/wpa.h +++ b/src/rsn_supp/wpa.h @@ -56,7 +56,7 @@ struct wpa_sm_ctx { 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, + int (*tdls_peer_addset)(void *ctx, const u8 *addr, int add, u16 aid, u16 capability, const u8 *supp_rates, size_t supp_rates_len, const struct ieee80211_ht_capabilities *ht_capab, @@ -315,6 +315,7 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, int ft_action, const u8 *target_ap, const u8 *ric_ies, size_t ric_ies_len); int wpa_ft_is_completed(struct wpa_sm *sm); +void wpa_reset_ft_completed(struct wpa_sm *sm); int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, size_t ies_len, const u8 *src_addr); int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap, @@ -346,6 +347,10 @@ static inline int wpa_ft_is_completed(struct wpa_sm *sm) return 0; } +static inline void wpa_reset_ft_completed(struct wpa_sm *sm) +{ +} + static inline int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, size_t ies_len, const u8 *src_addr) diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c index 4b08a624..3a40c96f 100644 --- a/src/rsn_supp/wpa_ft.c +++ b/src/rsn_supp/wpa_ft.c @@ -534,6 +534,13 @@ int wpa_ft_is_completed(struct wpa_sm *sm) } +void wpa_reset_ft_completed(struct wpa_sm *sm) +{ + if (sm != NULL) + sm->ft_completed = 0; +} + + static int wpa_ft_process_gtk_subelem(struct wpa_sm *sm, const u8 *gtk_elem, size_t gtk_elem_len) { @@ -589,6 +596,13 @@ static int wpa_ft_process_gtk_subelem(struct wpa_sm *sm, const u8 *gtk_elem, } wpa_hexdump_key(MSG_DEBUG, "FT: GTK from Reassoc Resp", gtk, keylen); + if (sm->group_cipher == WPA_CIPHER_TKIP) { + /* Swap Tx/Rx keys for Michael MIC */ + u8 tmp[8]; + os_memcpy(tmp, gtk + 16, 8); + os_memcpy(gtk + 16, gtk + 24, 8); + os_memcpy(gtk + 24, tmp, 8); + } 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 " diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h index 877e6de1..0e0d373f 100644 --- a/src/rsn_supp/wpa_i.h +++ b/src/rsn_supp/wpa_i.h @@ -282,7 +282,7 @@ static inline int wpa_sm_tdls_oper(struct wpa_sm *sm, int oper, static inline int wpa_sm_tdls_peer_addset(struct wpa_sm *sm, const u8 *addr, int add, - u16 capability, const u8 *supp_rates, + u16 aid, u16 capability, const u8 *supp_rates, size_t supp_rates_len, const struct ieee80211_ht_capabilities *ht_capab, const struct ieee80211_vht_capabilities *vht_capab, @@ -290,7 +290,7 @@ wpa_sm_tdls_peer_addset(struct wpa_sm *sm, const u8 *addr, int add, { if (sm->ctx->tdls_peer_addset) return sm->ctx->tdls_peer_addset(sm->ctx->ctx, addr, add, - capability, supp_rates, + aid, capability, supp_rates, supp_rates_len, ht_capab, vht_capab, qosinfo, ext_capab, ext_capab_len); diff --git a/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c index 652197ff..ba203e62 100644 --- a/src/rsn_supp/wpa_ie.c +++ b/src/rsn_supp/wpa_ie.c @@ -430,6 +430,9 @@ int wpa_supplicant_parse_ies(const u8 *buf, size_t len, } else if (*pos == WLAN_EID_HT_CAP) { ie->ht_capabilities = pos + 2; ie->ht_capabilities_len = pos[1]; + } else if (*pos == WLAN_EID_VHT_AID) { + if (pos[1] >= 2) + ie->aid = WPA_GET_LE16(pos + 2); } else if (*pos == WLAN_EID_VHT_CAP) { ie->vht_capabilities = pos + 2; ie->vht_capabilities_len = pos[1]; diff --git a/src/rsn_supp/wpa_ie.h b/src/rsn_supp/wpa_ie.h index 82a5c08c..2c788012 100644 --- a/src/rsn_supp/wpa_ie.h +++ b/src/rsn_supp/wpa_ie.h @@ -54,6 +54,7 @@ struct wpa_eapol_ie_parse { const u8 *vht_capabilities; size_t vht_capabilities_len; u8 qosinfo; + u16 aid; }; int wpa_supplicant_parse_ies(const u8 *buf, size_t len, diff --git a/src/wps/wps_enrollee.c b/src/wps/wps_enrollee.c index 9c0cebb7..9a62fce8 100644 --- a/src/wps/wps_enrollee.c +++ b/src/wps/wps_enrollee.c @@ -855,6 +855,12 @@ static int wps_process_dev_pw_id(struct wps_data *wps, const u8 *dev_pw_id) wpa_printf(MSG_DEBUG, "WPS: Registrar trying to change Device Password " "ID from %u to %u", wps->dev_pw_id, id); + if (wps->dev_pw_id == DEV_PW_PUSHBUTTON && id == DEV_PW_DEFAULT) { + wpa_printf(MSG_DEBUG, + "WPS: Workaround - ignore PBC-to-PIN change"); + return 0; + } + if (wps->alt_dev_password && wps->alt_dev_pw_id == id) { wpa_printf(MSG_DEBUG, "WPS: Found a matching Device Password"); os_free(wps->dev_password); diff --git a/wpa_supplicant/android.config b/wpa_supplicant/android.config index b86c4c7a..c22bb5cb 100644 --- a/wpa_supplicant/android.config +++ b/wpa_supplicant/android.config @@ -329,7 +329,7 @@ CONFIG_PEERKEY=y # This version is an experimental implementation based on IEEE 802.11w/D1.0 # draft and is subject to change since the standard has not yet been finalized. # Driver support is also needed for IEEE 802.11w. -#CONFIG_IEEE80211W=y +CONFIG_IEEE80211W=y # Select TLS implementation # openssl = OpenSSL (default) @@ -407,7 +407,7 @@ CONFIG_PEERKEY=y #CONFIG_DYNAMIC_EAP_METHODS=y # IEEE Std 802.11r-2008 (Fast BSS Transition) -#CONFIG_IEEE80211R=y +CONFIG_IEEE80211R=y # Add support for writing debug log to a file (/tmp/wpa_supplicant-log-#.txt) #CONFIG_DEBUG_FILE=y @@ -486,14 +486,18 @@ CONFIG_ANDROID_LOG=y # IEEE 802.11n (High Throughput) support (mainly for AP mode) CONFIG_IEEE80211N=y +# Wireless Network Management (IEEE Std 802.11v-2011) +# Note: This is experimental and not complete implementation. +CONFIG_WNM=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 +CONFIG_INTERWORKING=y # Hotspot 2.0 -#CONFIG_HS20=y +CONFIG_HS20=y # Disable roaming in wpa_supplicant CONFIG_NO_ROAMING=y diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c index 3ba44964..4033492a 100644 --- a/wpa_supplicant/ap.c +++ b/wpa_supplicant/ap.c @@ -251,6 +251,16 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, bss->rsn_pairwise = WPA_CIPHER_NONE; } + if (bss->wpa_group_rekey < 86400 && (bss->wpa & 2) && + (bss->wpa_group == WPA_CIPHER_CCMP || + bss->wpa_group == WPA_CIPHER_GCMP)) { + /* + * Strong ciphers do not need frequent rekeying, so increase + * the default GTK rekeying period to 24 hours. + */ + bss->wpa_group_rekey = 86400; + } + #ifdef CONFIG_WPS /* * Enable WPS by default for open and WPA/WPA2-Personal network, but diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c index 0d988840..64964db2 100644 --- a/wpa_supplicant/config.c +++ b/wpa_supplicant/config.c @@ -3090,6 +3090,7 @@ static const struct global_parse_data global_fields[] = { { INT(beacon_int), 0 }, { FUNC(ap_vendor_elements), 0 }, { INT_RANGE(ignore_old_scan_res, 0, 1), 0 }, + { INT(sched_scan_interval), 0 }, }; #undef FUNC diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h index 4a175ce9..9b69a4b0 100644 --- a/wpa_supplicant/config.h +++ b/wpa_supplicant/config.h @@ -854,6 +854,11 @@ struct wpa_config { * allowing it to update the internal BSS table. */ int ignore_old_scan_res; + + /** + * sched_scan_interval - schedule scan interval + */ + unsigned int sched_scan_interval; }; diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c index 8604ae8a..d13af1eb 100644 --- a/wpa_supplicant/config_file.c +++ b/wpa_supplicant/config_file.c @@ -1015,6 +1015,9 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) if (config->ignore_old_scan_res) fprintf(f, "ignore_old_scan_res=%d\n", config->ignore_old_scan_res); + if (config->sched_scan_interval) + fprintf(f, "sched_scan_interval=%u\n", + config->sched_scan_interval); } #endif /* CONFIG_NO_CONFIG_WRITE */ diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index 22921154..b06b90cd 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -50,7 +50,7 @@ static int wpa_supplicant_global_iface_interfaces(struct wpa_global *global, static int pno_start(struct wpa_supplicant *wpa_s) { - int ret; + int ret, interval; size_t i, num_ssid; struct wpa_ssid *ssid; struct wpa_driver_scan_params params; @@ -108,7 +108,10 @@ static int pno_start(struct wpa_supplicant *wpa_s) if (wpa_s->conf->filter_rssi) params.filter_rssi = wpa_s->conf->filter_rssi; - ret = wpa_drv_sched_scan(wpa_s, ¶ms, 10 * 1000); + interval = wpa_s->conf->sched_scan_interval ? + wpa_s->conf->sched_scan_interval : 10; + + ret = wpa_drv_sched_scan(wpa_s, ¶ms, interval * 1000); os_free(params.filter_ssids); if (ret == 0) wpa_s->pno = 1; @@ -4989,6 +4992,19 @@ static int wpas_ctrl_iface_wnm_sleep(struct wpa_supplicant *wpa_s, char *cmd) return ret; } + +static int wpas_ctrl_iface_wnm_bss_query(struct wpa_supplicant *wpa_s, char *cmd) +{ + int query_reason; + + query_reason = atoi(cmd); + + wpa_printf(MSG_DEBUG, "CTRL_IFACE: WNM_BSS_QUERY query_reason=%d", + query_reason); + + return wnm_send_bss_transition_mgmt_query(wpa_s, query_reason); +} + #endif /* CONFIG_WNM */ @@ -5036,7 +5052,27 @@ static int wpa_supplicant_driver_cmd(struct wpa_supplicant *wpa_s, char *cmd, { int ret; - ret = wpa_drv_driver_cmd(wpa_s, cmd, buf, buflen); + if (os_strncasecmp(cmd, "SETBAND ", 8) == 0) { + int val = atoi(cmd + 8); + /* + * Use driver_cmd for drivers that support it, but ignore the + * return value since scan requests from wpa_supplicant will + * provide a list of channels to scan for based on the SETBAND + * setting. + */ + wpa_printf(MSG_DEBUG, "SETBAND: %d", val); + wpa_drv_driver_cmd(wpa_s, cmd, buf, buflen); + ret = 0; + if (val == 0) + wpa_s->setband = WPA_SETBAND_AUTO; + else if (val == 1) + wpa_s->setband = WPA_SETBAND_5G; + else if (val == 2) + wpa_s->setband = WPA_SETBAND_2G; + else + ret = -1; + } else + ret = wpa_drv_driver_cmd(wpa_s, cmd, buf, buflen); if (ret == 0) ret = sprintf(buf, "%s\n", "OK"); return ret; @@ -5625,6 +5661,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strncmp(buf, "WNM_SLEEP ", 10) == 0) { if (wpas_ctrl_iface_wnm_sleep(wpa_s, buf + 10)) reply_len = -1; + } else if (os_strncmp(buf, "WNM_BSS_QUERY ", 10) == 0) { + if (wpas_ctrl_iface_wnm_bss_query(wpa_s, buf + 10)) + reply_len = -1; #endif /* CONFIG_WNM */ } else if (os_strcmp(buf, "FLUSH") == 0) { wpa_supplicant_ctrl_iface_flush(wpa_s); diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index a85bc181..184b14ef 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -333,11 +333,24 @@ int wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s, } +static int has_wep_key(struct wpa_ssid *ssid) +{ + int i; + + for (i = 0; i < NUM_WEP_KEYS; i++) { + if (ssid->wep_key_len[i]) + return 1; + } + + return 0; +} + + #ifndef CONFIG_NO_SCAN_PROCESSING static int wpa_supplicant_match_privacy(struct wpa_bss *bss, struct wpa_ssid *ssid) { - int i, privacy = 0; + int privacy = 0; if (ssid->mixed_cell) return 1; @@ -347,12 +360,9 @@ static int wpa_supplicant_match_privacy(struct wpa_bss *bss, return 1; #endif /* CONFIG_WPS */ - for (i = 0; i < NUM_WEP_KEYS; i++) { - if (ssid->wep_key_len[i]) { - privacy = 1; - break; - } - } + if (has_wep_key(ssid)) + privacy = 1; + #ifdef IEEE8021X_EAPOL if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) && ssid->eapol_flags & (EAPOL_FLAG_REQUIRE_KEY_UNICAST | @@ -788,6 +798,13 @@ static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, continue; } + if (wpa && !wpa_key_mgmt_wpa(ssid->key_mgmt) && + has_wep_key(ssid)) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - ignore WPA/WPA2 " + "AP for WEP network block"); + continue; + } + if (!wpa_supplicant_match_privacy(bss, ssid)) { wpa_dbg(wpa_s, MSG_DEBUG, " skip - privacy " "mismatch"); @@ -1780,6 +1797,16 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE || (wpa_s->current_ssid && wpa_s->current_ssid->mode == IEEE80211_MODE_IBSS)) { + if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE && + (wpa_s->drv_flags & + WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE)) { + /* + * Set the key after having received joined-IBSS event + * from the driver. + */ + wpa_supplicant_set_wpa_none_key(wpa_s, + wpa_s->current_ssid); + } wpa_supplicant_cancel_auth_timeout(wpa_s); wpa_supplicant_set_state(wpa_s, WPA_COMPLETED); } else if (!ft_completed) { @@ -2219,8 +2246,12 @@ static void wpa_supplicant_event_tdls(struct wpa_supplicant *wpa_s, wpa_drv_tdls_oper(wpa_s, TDLS_SETUP, data->tdls.peer); break; case TDLS_REQUEST_TEARDOWN: - wpa_tdls_teardown_link(wpa_s->wpa, data->tdls.peer, - data->tdls.reason_code); + if (wpa_tdls_is_external_setup(wpa_s->wpa)) + wpa_tdls_teardown_link(wpa_s->wpa, data->tdls.peer, + data->tdls.reason_code); + else + wpa_drv_tdls_oper(wpa_s, TDLS_TEARDOWN, + data->tdls.peer); break; } } @@ -2473,6 +2504,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, data->deauth_info.ie, data->deauth_info.ie_len); } + wpa_reset_ft_completed(wpa_s->wpa); } #ifdef CONFIG_AP if (wpa_s->ap_iface && data && data->deauth_info.addr) { @@ -2571,72 +2603,11 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) sme_event_assoc_reject(wpa_s, data); else { -#ifdef ANDROID_P2P -#if defined(LEGACY_STA_EVENTS) - /* If assoc reject is reported by the driver, then avoid - * waiting for the authentication timeout. Cancel the - * authentication timeout and retry the assoc. - */ - if(wpa_s->assoc_retries++ < 5) { - wpa_printf(MSG_ERROR, "Retrying assoc " - "Iteration:%d", wpa_s->assoc_retries); - wpa_supplicant_cancel_auth_timeout(wpa_s); - - /* Clear the states */ - wpa_sm_notify_disassoc(wpa_s->wpa); - wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); - - wpa_s->reassociate = 1; - wpa_supplicant_req_scan(wpa_s, 1, 0); - } else - wpa_s->assoc_retries = 0; -#else - if(!wpa_s->current_ssid) { - wpa_printf(MSG_ERROR, "current_ssid == NULL"); - break; - } - /* If assoc reject is reported by the driver, then avoid - * waiting for the authentication timeout. Cancel the - * authentication timeout and retry the assoc. - */ - if(wpa_s->current_ssid->assoc_retry++ < 10) { - wpa_printf(MSG_ERROR, "Retrying assoc: %d ", - wpa_s->current_ssid->assoc_retry); - - wpa_supplicant_cancel_auth_timeout(wpa_s); - - /* Clear the states */ - wpa_sm_notify_disassoc(wpa_s->wpa); - wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); - - wpa_s->reassociate = 1; - if (wpa_s->p2p_group_interface == NOT_P2P_GROUP_INTERFACE) { - const u8 *bl_bssid = data->assoc_reject.bssid; - if (!bl_bssid || is_zero_ether_addr(bl_bssid)) - bl_bssid = wpa_s->pending_bssid; - wpa_blacklist_add(wpa_s, bl_bssid); - wpa_supplicant_req_scan(wpa_s, 0, 0); - } else { - wpa_supplicant_req_scan(wpa_s, 1, 0); - } - } else if (wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) { - /* If we ASSOC_REJECT's hits threshold, disable the - * network - */ - wpa_printf(MSG_ERROR, "Assoc retry threshold reached. " - "Disabling the network"); - wpa_s->current_ssid->assoc_retry = 0; - wpa_supplicant_disable_network(wpa_s, wpa_s->current_ssid); - wpas_p2p_group_remove(wpa_s, wpa_s->ifname); - } -#endif -#else const u8 *bssid = data->assoc_reject.bssid; if (bssid == NULL || is_zero_ether_addr(bssid)) bssid = wpa_s->pending_bssid; wpas_connection_failed(wpa_s, bssid); wpa_supplicant_mark_disassoc(wpa_s); -#endif /* ANDROID_P2P */ } break; case EVENT_AUTH_TIMED_OUT: diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c index fc311e97..7216c3aa 100644 --- a/wpa_supplicant/p2p_supplicant.c +++ b/wpa_supplicant/p2p_supplicant.c @@ -61,6 +61,16 @@ #define P2P_MAX_INITIAL_CONN_WAIT 10 #endif /* P2P_MAX_INITIAL_CONN_WAIT */ +#ifndef P2P_MAX_INITIAL_CONN_WAIT_GO +/* + * How many seconds to wait for initial 4-way handshake to get completed after + * WPS provisioning step on the GO. This controls the extra time the P2P + * operation is considered to be in progress (e.g., to delay other scans) after + * WPS provisioning has been completed on the GO during group formation. + */ +#define P2P_MAX_INITIAL_CONN_WAIT_GO 10 +#endif /* P2P_MAX_INITIAL_CONN_WAIT_GO */ + #ifndef P2P_CONCURRENT_SEARCH_DELAY #define P2P_CONCURRENT_SEARCH_DELAY 500 #endif /* P2P_CONCURRENT_SEARCH_DELAY */ @@ -736,8 +746,10 @@ static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s, ssid, go_dev_addr); if (network_id < 0 && ssid) network_id = ssid->id; - if (!client) + if (!client) { wpas_notify_p2p_group_started(wpa_s, ssid, network_id, 0); + os_get_time(&wpa_s->global->p2p_go_wait_client); + } } @@ -2406,10 +2418,20 @@ static void wpas_prov_disc_fail(void *ctx, const u8 *peer, } +static int freq_included(const struct p2p_channels *channels, unsigned int freq) +{ + if (channels == NULL) + return 1; /* Assume no restrictions */ + return p2p_channels_includes_freq(channels, freq); + +} + + 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) + int *force_freq, int persistent_group, + const struct p2p_channels *channels) { struct wpa_supplicant *wpa_s = ctx; struct wpa_ssid *s; @@ -2507,6 +2529,25 @@ accept_inv: wpas_p2p_set_own_freq_preference(wpa_s, res); } + if (*force_freq > 0 && + (wpa_s->drv_flags & WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT)) { + if (*go == 0) { + /* We are the client */ + wpa_printf(MSG_DEBUG, "P2P: Peer was found to be " + "running a GO but we are capable of MCC, " + "figure out the best channel to use"); + *force_freq = 0; + } else if (!freq_included(channels, *force_freq)) { + /* We are the GO, and *force_freq is not in the + * intersection */ + wpa_printf(MSG_DEBUG, "P2P: Forced GO freq %d MHz not " + "in intersection but we are capable of MCC, " + "figure out the best channel to use", + *force_freq); + *force_freq = 0; + } + } + return P2P_SC_SUCCESS; } @@ -3096,6 +3137,12 @@ int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s) wpa_printf(MSG_DEBUG, "P2P: Random operating channel: " "%d:%d", p2p.op_reg_class, p2p.op_channel); } + + if (wpa_s->conf->p2p_pref_chan && wpa_s->conf->num_p2p_pref_chan) { + p2p.pref_chan = wpa_s->conf->p2p_pref_chan; + p2p.num_pref_chan = wpa_s->conf->num_p2p_pref_chan; + } + if (wpa_s->conf->country[0] && wpa_s->conf->country[1]) { os_memcpy(p2p.country, wpa_s->conf->country, 2); p2p.country[2] = 0x04; @@ -4018,6 +4065,12 @@ void wpas_p2p_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s, 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); + } else { + /* + * When listen duration is over, stop listen & update p2p_state + * to IDLE. + */ + p2p_stop_listen(wpa_s->global->p2p); } } @@ -4059,11 +4112,57 @@ int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname) } -static int freq_included(const struct p2p_channels *channels, unsigned int freq) +static int wpas_p2p_select_go_freq(struct wpa_supplicant *wpa_s, int freq) { - if (channels == NULL) - return 1; /* Assume no restrictions */ - return p2p_channels_includes_freq(channels, freq); + unsigned int r; + + 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; + } + + return freq; } @@ -4074,6 +4173,7 @@ static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s, { u8 bssid[ETH_ALEN]; int res; + unsigned int pref_freq; os_memset(params, 0, sizeof(*params)); params->role_go = 1; @@ -4130,6 +4230,11 @@ static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s, 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 if ((pref_freq = p2p_get_pref_freq(wpa_s->global->p2p, + channels))) { + params->freq = pref_freq; + wpa_printf(MSG_DEBUG, "P2P: Set GO freq %d MHz from preferred " + "channels", params->freq); } else { int chan; for (chan = 0; chan < 11; chan++) { @@ -4255,7 +4360,6 @@ int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group, int freq, int ht40) { struct p2p_go_neg_results params; - unsigned int r; if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return -1; @@ -4264,51 +4368,9 @@ int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group, wpa_printf(MSG_DEBUG, "P2P: Stop any on-going P2P FIND"); wpas_p2p_stop_find_oper(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); + freq = wpas_p2p_select_go_freq(wpa_s, freq); + if (freq < 0) return -1; - } if (wpas_p2p_init_go_params(wpa_s, ¶ms, freq, ht40, NULL)) return -1; @@ -4404,6 +4466,10 @@ int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s, if (ssid->mode != WPAS_MODE_P2P_GO) return -1; + freq = wpas_p2p_select_go_freq(wpa_s, freq); + if (freq < 0) + return -1; + if (wpas_p2p_init_go_params(wpa_s, ¶ms, freq, ht40, channels)) return -1; @@ -5160,7 +5226,7 @@ static void wpas_p2p_set_group_idle_timeout(struct wpa_supplicant *wpa_s) "during provisioning"); return; } -#ifndef ANDROID_P2P + if (wpa_s->show_group_started) { /* * Use the normal group formation timeout between the end of @@ -5173,7 +5239,6 @@ static void wpas_p2p_set_group_idle_timeout(struct wpa_supplicant *wpa_s) "complete"); return; } -#endif wpa_printf(MSG_DEBUG, "P2P: Set P2P group idle timeout to %u seconds", timeout); @@ -5695,6 +5760,19 @@ int wpas_p2p_in_progress(struct wpa_supplicant *wpa_s) } } + if (!ret && wpa_s->global->p2p_go_wait_client.sec) { + struct os_time now; + os_get_time(&now); + if (now.sec > wpa_s->global->p2p_go_wait_client.sec + + P2P_MAX_INITIAL_CONN_WAIT_GO) { + /* Wait for the first client has expired */ + wpa_s->global->p2p_go_wait_client.sec = 0; + } else { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Waiting for initial client connection during group formation"); + ret = 1; + } + } + return ret; } @@ -5754,6 +5832,7 @@ struct wpa_ssid * wpas_p2p_get_persistent(struct wpa_supplicant *wpa_s, void wpas_p2p_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s, const u8 *addr) { + wpa_s->global->p2p_go_wait_client.sec = 0; if (addr == NULL) return; wpas_p2p_add_persistent_group_client(wpa_s, addr); diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c index db73a186..07e3e06c 100644 --- a/wpa_supplicant/scan.c +++ b/wpa_supplicant/scan.c @@ -522,6 +522,59 @@ static int shared_vif_oper_freq(struct wpa_supplicant *wpa_s) #endif /* CONFIG_P2P */ +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 void wpa_setband_scan_freqs_list(struct wpa_supplicant *wpa_s, + enum hostapd_hw_mode band, + struct wpa_driver_scan_params *params) +{ + /* Include only supported channels for the specified band */ + struct hostapd_hw_modes *mode; + int count, i; + + mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, band); + if (mode == NULL) { + /* No channels supported in this band - use empty list */ + params->freqs = os_zalloc(sizeof(int)); + return; + } + + params->freqs = os_zalloc((mode->num_channels + 1) * sizeof(int)); + if (params->freqs == NULL) + return; + for (count = 0, i = 0; i < mode->num_channels; i++) { + if (mode->channels[i].flag & HOSTAPD_CHAN_DISABLED) + continue; + params->freqs[count++] = mode->channels[i].freq; + } +} + +static void wpa_setband_scan_freqs(struct wpa_supplicant *wpa_s, + struct wpa_driver_scan_params *params) +{ + if (wpa_s->hw.modes == NULL) + return; /* unknown what channels the driver supports */ + if (params->freqs) + return; /* already using a limited channel set */ + if (wpa_s->setband == WPA_SETBAND_5G) + wpa_setband_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211A, + params); + else if (wpa_s->setband == WPA_SETBAND_2G) + wpa_setband_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211G, + params); +} static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) { @@ -576,7 +629,7 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) } #ifdef CONFIG_P2P - if (wpas_p2p_in_progress(wpa_s)) { + if (wpas_p2p_in_progress(wpa_s) || wpas_wpa_is_in_progress(wpa_s)) { if (wpa_s->sta_scan_pending && wpas_p2p_in_progress(wpa_s) == 2 && wpa_s->global->p2p_cb_on_scan_complete) { @@ -761,7 +814,7 @@ ssid_list_set: } else os_free(wpa_s->next_scan_freqs); wpa_s->next_scan_freqs = NULL; - + wpa_setband_scan_freqs(wpa_s, ¶ms); params.filter_ssids = wpa_supplicant_build_filter_ssids( wpa_s->conf, ¶ms.num_filter_ssids); if (extra_ie) { @@ -1028,7 +1081,9 @@ int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s) if (!ssid || !wpa_s->prev_sched_ssid) { wpa_dbg(wpa_s, MSG_DEBUG, "Beginning of SSID list"); - + if (wpa_s->conf->sched_scan_interval) + wpa_s->sched_scan_interval = + wpa_s->conf->sched_scan_interval; if (wpa_s->sched_scan_interval == 0) wpa_s->sched_scan_interval = 10; wpa_s->sched_scan_timeout = max_sched_scan_ssids * 2; @@ -1114,7 +1169,7 @@ scan: "Starting sched scan: interval %d (no timeout)", wpa_s->sched_scan_interval); } - + wpa_setband_scan_freqs(wpa_s, scan_params); ret = wpa_supplicant_start_sched_scan(wpa_s, scan_params, wpa_s->sched_scan_interval); wpabuf_free(extra_ie); diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c index 4d9e4533..7de96c54 100644 --- a/wpa_supplicant/wnm_sta.c +++ b/wpa_supplicant/wnm_sta.c @@ -14,8 +14,12 @@ #include "wpa_supplicant_i.h" #include "driver_i.h" #include "scan.h" +#include "ctrl_iface.h" +#include "bss.h" +#include "wnm_sta.h" #define MAX_TFS_IE_LEN 1024 +#define WNM_MAX_NEIGHBOR_REPORT 10 /* get the TFS IE from driver */ @@ -294,6 +298,199 @@ static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s, } +void wnm_deallocate_memory(struct wpa_supplicant *wpa_s) +{ + int i; + + for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { + os_free(wpa_s->wnm_neighbor_report_elements[i].tsf_info); + os_free(wpa_s->wnm_neighbor_report_elements[i].con_coun_str); + os_free(wpa_s->wnm_neighbor_report_elements[i].bss_tran_can); + os_free(wpa_s->wnm_neighbor_report_elements[i].bss_term_dur); + os_free(wpa_s->wnm_neighbor_report_elements[i].bearing); + os_free(wpa_s->wnm_neighbor_report_elements[i].meas_pilot); + os_free(wpa_s->wnm_neighbor_report_elements[i].rrm_cap); + os_free(wpa_s->wnm_neighbor_report_elements[i].mul_bssid); + } + + os_free(wpa_s->wnm_neighbor_report_elements); + wpa_s->wnm_neighbor_report_elements = NULL; +} + + +static void wnm_parse_neighbor_report_elem(struct neighbor_report *rep, + u8 id, u8 elen, const u8 *pos) +{ + switch (id) { + case WNM_NEIGHBOR_TSF: + if (elen < 2 + 2) { + wpa_printf(MSG_DEBUG, "WNM: Too short TSF"); + break; + } + rep->tsf_info = os_zalloc(sizeof(struct tsf_info)); + if (rep->tsf_info == NULL) + break; + rep->tsf_info->present = 1; + os_memcpy(rep->tsf_info->tsf_offset, pos, 2); + os_memcpy(rep->tsf_info->beacon_interval, pos + 2, 2); + break; + case WNM_NEIGHBOR_CONDENSED_COUNTRY_STRING: + if (elen < 2) { + wpa_printf(MSG_DEBUG, "WNM: Too short condensed " + "country string"); + break; + } + rep->con_coun_str = + os_zalloc(sizeof(struct condensed_country_string)); + if (rep->con_coun_str == NULL) + break; + rep->con_coun_str->present = 1; + os_memcpy(rep->con_coun_str->country_string, pos, 2); + break; + case WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE: + if (elen < 1) { + wpa_printf(MSG_DEBUG, "WNM: Too short BSS transition " + "candidate"); + break; + } + rep->bss_tran_can = + os_zalloc(sizeof(struct bss_transition_candidate)); + if (rep->bss_tran_can == NULL) + break; + rep->bss_tran_can->present = 1; + rep->bss_tran_can->preference = pos[0]; + break; + case WNM_NEIGHBOR_BSS_TERMINATION_DURATION: + if (elen < 12) { + wpa_printf(MSG_DEBUG, "WNM: Too short BSS termination " + "duration"); + break; + } + rep->bss_term_dur = + os_zalloc(sizeof(struct bss_termination_duration)); + if (rep->bss_term_dur == NULL) + break; + rep->bss_term_dur->present = 1; + os_memcpy(rep->bss_term_dur->duration, pos, 12); + break; + case WNM_NEIGHBOR_BEARING: + if (elen < 8) { + wpa_printf(MSG_DEBUG, "WNM: Too short neighbor " + "bearing"); + break; + } + rep->bearing = os_zalloc(sizeof(struct bearing)); + if (rep->bearing == NULL) + break; + rep->bearing->present = 1; + os_memcpy(rep->bearing->bearing, pos, 8); + break; + case WNM_NEIGHBOR_MEASUREMENT_PILOT: + if (elen < 2) { + wpa_printf(MSG_DEBUG, "WNM: Too short measurement " + "pilot"); + break; + } + rep->meas_pilot = os_zalloc(sizeof(struct measurement_pilot)); + if (rep->meas_pilot == NULL) + break; + rep->meas_pilot->present = 1; + rep->meas_pilot->measurement_pilot = pos[0]; + rep->meas_pilot->num_vendor_specific = pos[1]; + os_memcpy(rep->meas_pilot->vendor_specific, pos + 2, elen - 2); + break; + case WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES: + if (elen < 4) { + wpa_printf(MSG_DEBUG, "WNM: Too short RRM enabled " + "capabilities"); + break; + } + rep->rrm_cap = + os_zalloc(sizeof(struct rrm_enabled_capabilities)); + if (rep->rrm_cap == NULL) + break; + rep->rrm_cap->present = 1; + os_memcpy(rep->rrm_cap->capabilities, pos, 4); + break; + case WNM_NEIGHBOR_MULTIPLE_BSSID: + if (elen < 2) { + wpa_printf(MSG_DEBUG, "WNM: Too short multiple BSSID"); + break; + } + rep->mul_bssid = os_zalloc(sizeof(struct multiple_bssid)); + if (rep->mul_bssid == NULL) + break; + rep->mul_bssid->present = 1; + rep->mul_bssid->max_bssid_indicator = pos[0]; + rep->mul_bssid->num_vendor_specific = pos[1]; + os_memcpy(rep->mul_bssid->vendor_specific, pos + 2, elen - 2); + break; + } +} + + +static void wnm_parse_neighbor_report(struct wpa_supplicant *wpa_s, + const u8 *pos, u8 len, + struct neighbor_report *rep) +{ + u8 left = len; + + if (left < 13) { + wpa_printf(MSG_DEBUG, "WNM: Too short neighbor report"); + return; + } + + os_memcpy(rep->bssid, pos, ETH_ALEN); + os_memcpy(rep->bssid_information, pos + ETH_ALEN, 4); + rep->regulatory_class = *(pos + 10); + rep->channel_number = *(pos + 11); + rep->phy_type = *(pos + 12); + + pos += 13; + left -= 13; + + while (left >= 2) { + u8 id, elen; + + id = *pos++; + elen = *pos++; + wnm_parse_neighbor_report_elem(rep, id, elen, pos); + left -= 2 + elen; + pos += elen; + } +} + + +static int compare_scan_neighbor_results(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res, + struct neighbor_report *neigh_rep, + u8 num_neigh_rep, u8 *bssid_to_connect) +{ + + u8 i, j; + + if (scan_res == NULL || num_neigh_rep == 0) + return 0; + + for (i = 0; i < num_neigh_rep; i++) { + for (j = 0; j < scan_res->num; j++) { + /* Check for a better RSSI AP */ + if (os_memcmp(scan_res->res[j]->bssid, + neigh_rep[i].bssid, ETH_ALEN) == 0 && + scan_res->res[j]->level > + wpa_s->current_bss->level) { + /* Got a BSSID with better RSSI value */ + os_memcpy(bssid_to_connect, neigh_rep[i].bssid, + ETH_ALEN); + return 1; + } + } + } + + return 0; +} + + static void wnm_send_bss_transition_mgmt_resp(struct wpa_supplicant *wpa_s, u8 dialog_token, u8 status, u8 delay, const u8 *target_bssid) @@ -332,29 +529,90 @@ static void wnm_send_bss_transition_mgmt_resp(struct wpa_supplicant *wpa_s, } +void wnm_scan_response(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res) +{ + u8 bssid[ETH_ALEN]; + + if (scan_res == NULL) { + wpa_printf(MSG_ERROR, "Scan result is NULL"); + goto send_bss_resp_fail; + } + + /* Compare the Neighbor Report and scan results */ + if (compare_scan_neighbor_results(wpa_s, scan_res, + wpa_s->wnm_neighbor_report_elements, + wpa_s->wnm_num_neighbor_report, + bssid) == 1) { + /* Associate to the network */ + struct wpa_bss *bss; + struct wpa_ssid *ssid = wpa_s->current_ssid; + + bss = wpa_bss_get_bssid(wpa_s, bssid); + if (!bss) { + wpa_printf(MSG_DEBUG, "WNM: Target AP not found from " + "BSS table"); + goto send_bss_resp_fail; + } + + /* Send the BSS Management Response - Accept */ + if (wpa_s->wnm_reply) { + wnm_send_bss_transition_mgmt_resp(wpa_s, + wpa_s->wnm_dialog_token, + 0, /* Accept */ + 0, NULL); + } + + wpa_s->reassociate = 1; + wpa_supplicant_connect(wpa_s, bss, ssid); + wnm_deallocate_memory(wpa_s); + return; + } + + /* Send reject response for all the failures */ +send_bss_resp_fail: + wnm_deallocate_memory(wpa_s); + if (wpa_s->wnm_reply) { + wnm_send_bss_transition_mgmt_resp(wpa_s, + wpa_s->wnm_dialog_token, + 1 /* Reject - unspecified */, + 0, NULL); + } + return; +} + + static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, const u8 *pos, const u8 *end, int reply) { - u8 dialog_token; - u8 mode; - u16 disassoc_timer; - if (pos + 5 > end) return; - dialog_token = pos[0]; - mode = pos[1]; - disassoc_timer = WPA_GET_LE16(pos + 2); + wpa_s->wnm_dialog_token = pos[0]; + wpa_s->wnm_mode = pos[1]; + wpa_s->wnm_dissoc_timer = WPA_GET_LE16(pos + 2); + wpa_s->wnm_validity_interval = pos[4]; + wpa_s->wnm_reply = reply; wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Request: " "dialog_token=%u request_mode=0x%x " "disassoc_timer=%u validity_interval=%u", - dialog_token, mode, disassoc_timer, pos[4]); + wpa_s->wnm_dialog_token, wpa_s->wnm_mode, + wpa_s->wnm_dissoc_timer, wpa_s->wnm_validity_interval); + pos += 5; - if (mode & 0x08) + + if (wpa_s->wnm_mode & 0x08) { + if (pos + 12 > end) { + wpa_printf(MSG_DEBUG, "WNM: Too short BSS TM Request"); + return; + } + os_memcpy(wpa_s->wnm_bss_termination_duration, pos, 12); pos += 12; /* BSS Termination Duration */ - if (mode & 0x10) { + } + + if (wpa_s->wnm_mode & 0x10) { char url[256]; if (pos + 1 > end || pos + 1 + pos[0] > end) { wpa_printf(MSG_DEBUG, "WNM: Invalid BSS Transition " @@ -363,14 +621,15 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, } os_memcpy(url, pos + 1, pos[0]); url[pos[0]] = '\0'; + pos += 1 + pos[0]; wpa_msg(wpa_s, MSG_INFO, "WNM: ESS Disassociation Imminent - " "session_info_url=%s", url); } - if (mode & 0x04) { + if (wpa_s->wnm_mode & 0x04) { wpa_msg(wpa_s, MSG_INFO, "WNM: Disassociation Imminent - " - "Disassociation Timer %u", disassoc_timer); - if (disassoc_timer && !wpa_s->scanning) { + "Disassociation Timer %u", wpa_s->wnm_dissoc_timer); + if (wpa_s->wnm_dissoc_timer && !wpa_s->scanning) { /* TODO: mark current BSS less preferred for * selection */ wpa_printf(MSG_DEBUG, "Trying to find another BSS"); @@ -378,15 +637,85 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, } } - if (reply) { - /* TODO: add support for reporting Accept */ - wnm_send_bss_transition_mgmt_resp(wpa_s, dialog_token, + if (wpa_s->wnm_mode & 0x01) { + wpa_msg(wpa_s, MSG_INFO, "WNM: Preferred List Available"); + wpa_s->wnm_num_neighbor_report = 0; + os_free(wpa_s->wnm_neighbor_report_elements); + wpa_s->wnm_neighbor_report_elements = os_zalloc( + WNM_MAX_NEIGHBOR_REPORT * + sizeof(struct neighbor_report)); + if (wpa_s->wnm_neighbor_report_elements == NULL) + return; + + while (pos + 2 <= end && + wpa_s->wnm_num_neighbor_report < WNM_MAX_NEIGHBOR_REPORT) + { + u8 tag = *pos++; + u8 len = *pos++; + + wpa_printf(MSG_DEBUG, "WNM: Neighbor report tag %u", + tag); + if (pos + len > end) { + wpa_printf(MSG_DEBUG, "WNM: Truncated request"); + return; + } + wnm_parse_neighbor_report( + wpa_s, pos, len, + &wpa_s->wnm_neighbor_report_elements[ + wpa_s->wnm_num_neighbor_report]); + + pos += len; + wpa_s->wnm_num_neighbor_report++; + } + + wpa_s->scan_res_handler = wnm_scan_response; + wpa_supplicant_req_scan(wpa_s, 0, 0); + } else if (reply) { + wpa_msg(wpa_s, MSG_INFO, "WNM: BSS Transition Management " + "Request Mode is zero"); + wnm_send_bss_transition_mgmt_resp(wpa_s, + wpa_s->wnm_dialog_token, 1 /* Reject - unspecified */, 0, NULL); } } +int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s, + u8 query_reason) +{ + u8 buf[1000], *pos; + struct ieee80211_mgmt *mgmt; + size_t len; + int ret; + + wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Query to " + MACSTR " query_reason=%u", + MAC2STR(wpa_s->bssid), query_reason); + + mgmt = (struct ieee80211_mgmt *) buf; + os_memset(&buf, 0, sizeof(buf)); + 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_WNM; + mgmt->u.action.u.bss_tm_query.action = WNM_BSS_TRANS_MGMT_QUERY; + mgmt->u.action.u.bss_tm_query.dialog_token = 0; + mgmt->u.action.u.bss_tm_query.query_reason = query_reason; + pos = mgmt->u.action.u.bss_tm_query.variable; + + len = pos - (u8 *) &mgmt->u.action.category; + + ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, + wpa_s->own_addr, wpa_s->bssid, + &mgmt->u.action.category, len, 0); + + return ret; +} + + void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s, struct rx_action *action) { @@ -418,6 +747,7 @@ void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s, ieee802_11_rx_wnmsleep_resp(wpa_s, action->data, action->len); break; default: + wpa_printf(MSG_ERROR, "WNM: Unknown request"); break; } } diff --git a/wpa_supplicant/wnm_sta.h b/wpa_supplicant/wnm_sta.h index 3f9d88b7..2933926c 100644 --- a/wpa_supplicant/wnm_sta.h +++ b/wpa_supplicant/wnm_sta.h @@ -12,10 +12,79 @@ struct rx_action; struct wpa_supplicant; +struct tsf_info { + u8 present; + u8 tsf_offset[2]; + u8 beacon_interval[2]; +}; + +struct condensed_country_string { + u8 present; + u8 country_string[2]; +}; + +struct bss_transition_candidate { + u8 present; + u8 preference; +}; + +struct bss_termination_duration { + u8 present; + u8 duration[12]; +}; + +struct bearing { + u8 present; + u8 bearing[8]; +}; + +struct measurement_pilot { + u8 present; + u8 measurement_pilot; + u8 num_vendor_specific; + u8 vendor_specific[255]; +}; + +struct rrm_enabled_capabilities { + u8 present; + u8 capabilities[4]; +}; + +struct multiple_bssid { + u8 present; + u8 max_bssid_indicator; + u8 num_vendor_specific; + u8 vendor_specific[255]; +}; + +struct neighbor_report { + u8 bssid[ETH_ALEN]; + u8 bssid_information[4]; + u8 regulatory_class; + u8 channel_number; + u8 phy_type; + struct tsf_info *tsf_info; + struct condensed_country_string *con_coun_str; + struct bss_transition_candidate *bss_tran_can; + struct bss_termination_duration *bss_term_dur; + struct bearing *bearing; + struct measurement_pilot *meas_pilot; + struct rrm_enabled_capabilities *rrm_cap; + struct multiple_bssid *mul_bssid; +}; + + int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s, u8 action, u16 intval, struct wpabuf *tfs_req); void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s, struct rx_action *action); +void wnm_scan_response(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res); + +int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s, + u8 query_reason); +void wnm_deallocate_memory(struct wpa_supplicant *wpa_s); + #endif /* WNM_STA_H */ diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c index 6238c110..6bba4fa0 100644 --- a/wpa_supplicant/wpa_cli.c +++ b/wpa_supplicant/wpa_cli.c @@ -2290,6 +2290,12 @@ static int wpa_cli_cmd_wnm_sleep(struct wpa_ctrl *ctrl, int argc, char *argv[]) return wpa_cli_cmd(ctrl, "WNM_SLEEP", 0, argc, argv); } + +static int wpa_cli_cmd_wnm_bss_query(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_cli_cmd(ctrl, "WNM_BSS_QUERY", 1, argc, argv); +} + #endif /* CONFIG_WNM */ @@ -2773,6 +2779,8 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { #ifdef CONFIG_WNM { "wnm_sleep", wpa_cli_cmd_wnm_sleep, NULL, cli_cmd_flag_none, "<enter/exit> [interval=#] = enter/exit WNM-Sleep mode" }, + { "wnm_bss_query", wpa_cli_cmd_wnm_bss_query, NULL, cli_cmd_flag_none, + "<query reason> = Send BSS Transition Management Query" }, #endif /* CONFIG_WNM */ { "raw", wpa_cli_cmd_raw, NULL, cli_cmd_flag_sensitive, "<params..> = Sent unprocessed command" }, diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index d1e86566..15e35f86 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -49,6 +49,7 @@ #include "scan.h" #include "offchannel.h" #include "hs20_supplicant.h" +#include "wnm_sta.h" const char *wpa_supplicant_version = "wpa_supplicant v" VERSION_STR "\n" @@ -126,8 +127,8 @@ int wpa_set_wep_keys(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) } -static int wpa_supplicant_set_wpa_none_key(struct wpa_supplicant *wpa_s, - struct wpa_ssid *ssid) +int wpa_supplicant_set_wpa_none_key(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) { u8 key[32]; size_t keylen; @@ -470,6 +471,9 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) wpa_s->disallow_aps_ssid = NULL; wnm_bss_keep_alive_deinit(wpa_s); +#ifdef CONFIG_WNM + wnm_deallocate_memory(wpa_s); +#endif /* CONFIG_WNM */ ext_password_deinit(wpa_s->ext_pw); wpa_s->ext_pw = NULL; @@ -1279,8 +1283,6 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, struct wpa_driver_capa capa; int assoc_failed = 0; struct wpa_ssid *old_ssid; - u8 ext_capab[10]; - int ext_capab_len; #ifdef CONFIG_HT_OVERRIDES struct ieee80211_ht_capabilities htcaps; struct ieee80211_ht_capabilities htcaps_mask; @@ -1493,15 +1495,27 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, } #endif /* CONFIG_HS20 */ - ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab); - if (ext_capab_len > 0) { - u8 *pos = wpa_ie; - if (wpa_ie_len > 0 && pos[0] == WLAN_EID_RSN) - pos += 2 + pos[1]; - os_memmove(pos + ext_capab_len, pos, - wpa_ie_len - (pos - wpa_ie)); - wpa_ie_len += ext_capab_len; - os_memcpy(pos, ext_capab, ext_capab_len); + /* + * Workaround: Add Extended Capabilities element only if the AP + * included this element in Beacon/Probe Response frames. Some older + * APs seem to have interoperability issues if this element is + * included, so while the standard may require us to include the + * element in all cases, it is justifiable to skip it to avoid + * interoperability issues. + */ + if (!bss || wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB)) { + u8 ext_capab[10]; + int ext_capab_len; + ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab); + if (ext_capab_len > 0) { + u8 *pos = wpa_ie; + if (wpa_ie_len > 0 && pos[0] == WLAN_EID_RSN) + pos += 2 + pos[1]; + os_memmove(pos + ext_capab_len, pos, + wpa_ie_len - (pos - wpa_ie)); + wpa_ie_len += ext_capab_len; + os_memcpy(pos, ext_capab, ext_capab_len); + } } wpa_clear_keys(wpa_s, bss ? bss->bssid : NULL); @@ -3877,3 +3891,42 @@ void wpas_request_connection(struct wpa_supplicant *wpa_s) if (wpa_supplicant_fast_associate(wpa_s) != 1) wpa_supplicant_req_scan(wpa_s, 0, 0); } + + +/** + * wpas_wpa_is_in_progress - Check whether a connection is in progress + * @wpa_s: Pointer to wpa_supplicant data + * + * This function is to check if the wpa state is in beginning of the connection + * during 4-way handshake or group key handshake with WPA on any shared + * interface. + */ +int wpas_wpa_is_in_progress(struct wpa_supplicant *wpa_s) +{ + const char *rn, *rn2; + struct wpa_supplicant *ifs; + + if (!wpa_s->driver->get_radio_name) + return 0; + + rn = wpa_s->driver->get_radio_name(wpa_s->drv_priv); + if (rn == NULL || rn[0] == '\0') + return 0; + + 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) + continue; + if (ifs->wpa_state >= WPA_AUTHENTICATING && + ifs->wpa_state != WPA_COMPLETED) { + wpa_dbg(wpa_s, MSG_DEBUG, "Connection is in progress " + "on interface %s - defer scan", ifs->ifname); + return 1; + } + } + + return 0; +} diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index 49a87d93..c7623ac4 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -238,6 +238,7 @@ struct wpa_global { struct wpa_supplicant *p2p_group_formation; struct wpa_supplicant *p2p_invite_group; u8 p2p_dev_addr[ETH_ALEN]; + struct os_time p2p_go_wait_client; struct dl_list p2p_srv_bonjour; /* struct p2p_srv_bonjour */ struct dl_list p2p_srv_upnp; /* struct p2p_srv_upnp */ int p2p_disabled; @@ -350,6 +351,8 @@ struct wpa_supplicant { struct wpa_ssid_value *disallow_aps_ssid; size_t disallow_aps_ssid_count; + enum { WPA_SETBAND_AUTO, WPA_SETBAND_5G, WPA_SETBAND_2G } setband; + /* previous scan was wildcard when interleaving between * wildcard scans and specific SSID scan when max_ssids=1 */ int prev_scan_wildcard; @@ -692,6 +695,17 @@ struct wpa_supplicant { u8 last_gas_dialog_token; unsigned int no_keep_alive:1; + +#ifdef CONFIG_WNM + u8 wnm_dialog_token; + u8 wnm_reply; + u8 wnm_num_neighbor_report; + u8 wnm_mode; + u16 wnm_dissoc_timer; + u8 wnm_validity_interval; + u8 wnm_bss_termination_duration[12]; + struct neighbor_report *wnm_neighbor_report_elements; +#endif /* CONFIG_WNM */ }; @@ -704,6 +718,8 @@ void wpa_supplicant_apply_vht_overrides( struct wpa_driver_associate_params *params); int wpa_set_wep_keys(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); +int wpa_supplicant_set_wpa_none_key(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid); int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s); @@ -782,6 +798,7 @@ int disallowed_ssid(struct wpa_supplicant *wpa_s, const u8 *ssid, size_t ssid_len); void wpas_request_connection(struct wpa_supplicant *wpa_s); int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf); +int wpas_wpa_is_in_progress(struct wpa_supplicant *wpa_s); /** * wpa_supplicant_ctrl_iface_ctrl_rsp_handle - Handle a control response diff --git a/wpa_supplicant/wpa_supplicant_template.conf b/wpa_supplicant/wpa_supplicant_template.conf index a08eb33d..eee48a77 100644 --- a/wpa_supplicant/wpa_supplicant_template.conf +++ b/wpa_supplicant/wpa_supplicant_template.conf @@ -4,3 +4,5 @@ ctrl_interface=wlan0 eapol_version=1 ap_scan=1 fast_reauth=1 +### flag to avoid creating new interface for p2p +p2p_no_group_iface=1 diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c index cd518738..1a64df38 100644 --- a/wpa_supplicant/wpas_glue.c +++ b/wpa_supplicant/wpas_glue.c @@ -551,7 +551,7 @@ static int wpa_supplicant_tdls_oper(void *ctx, int oper, const u8 *peer) static int wpa_supplicant_tdls_peer_addset( - void *ctx, const u8 *peer, int add, u16 capability, + void *ctx, const u8 *peer, int add, u16 aid, u16 capability, const u8 *supp_rates, size_t supp_rates_len, const struct ieee80211_ht_capabilities *ht_capab, const struct ieee80211_vht_capabilities *vht_capab, @@ -563,7 +563,7 @@ static int wpa_supplicant_tdls_peer_addset( os_memset(¶ms, 0, sizeof(params)); params.addr = peer; - params.aid = 1; + params.aid = aid; params.capability = capability; params.flags = WPA_STA_TDLS_PEER | WPA_STA_AUTHORIZED; diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c index 9af60849..f37c5beb 100644 --- a/wpa_supplicant/wps_supplicant.c +++ b/wpa_supplicant/wps_supplicant.c @@ -219,6 +219,80 @@ static void wpas_wps_security_workaround(struct wpa_supplicant *wpa_s, } +static void wpas_wps_remove_dup_network(struct wpa_supplicant *wpa_s, + struct wpa_ssid *new_ssid) +{ + struct wpa_ssid *ssid, *next; + + for (ssid = wpa_s->conf->ssid, next = ssid ? ssid->next : NULL; ssid; + ssid = next, next = ssid ? ssid->next : NULL) { + /* + * new_ssid has already been added to the list in + * wpas_wps_add_network(), so skip it. + */ + if (ssid == new_ssid) + continue; + + if (ssid->bssid_set || new_ssid->bssid_set) { + if (ssid->bssid_set != new_ssid->bssid_set) + continue; + if (os_memcmp(ssid->bssid, new_ssid->bssid, ETH_ALEN) != + 0) + continue; + } + + /* compare SSID */ + if (ssid->ssid_len == 0 || ssid->ssid_len != new_ssid->ssid_len) + continue; + + if (ssid->ssid && new_ssid->ssid) { + if (os_memcmp(ssid->ssid, new_ssid->ssid, + ssid->ssid_len) != 0) + continue; + } else if (ssid->ssid || new_ssid->ssid) + continue; + + /* compare security parameters */ + if (ssid->auth_alg != new_ssid->auth_alg || + ssid->key_mgmt != new_ssid->key_mgmt || + ssid->proto != new_ssid->proto || + ssid->pairwise_cipher != new_ssid->pairwise_cipher || + ssid->group_cipher != new_ssid->group_cipher) + continue; + + if (ssid->passphrase && new_ssid->passphrase) { + if (os_strlen(ssid->passphrase) != + os_strlen(new_ssid->passphrase)) + continue; + if (os_strcmp(ssid->passphrase, new_ssid->passphrase) != + 0) + continue; + } else if (ssid->passphrase || new_ssid->passphrase) + continue; + + if ((ssid->psk_set || new_ssid->psk_set) && + os_memcmp(ssid->psk, new_ssid->psk, sizeof(ssid->psk)) != 0) + continue; + + if (ssid->auth_alg == WPA_ALG_WEP) { + if (ssid->wep_tx_keyidx != new_ssid->wep_tx_keyidx) + continue; + if (os_memcmp(ssid->wep_key, new_ssid->wep_key, + sizeof(ssid->wep_key))) + continue; + if (os_memcmp(ssid->wep_key_len, new_ssid->wep_key_len, + sizeof(ssid->wep_key_len))) + continue; + } + + /* Remove the duplicated older network entry. */ + wpa_printf(MSG_DEBUG, "Remove duplicate network %d", ssid->id); + wpas_notify_network_removed(wpa_s, ssid); + wpa_config_remove_network(wpa_s->conf, ssid->id); + } +} + + static int wpa_supplicant_wps_cred(void *ctx, const struct wps_credential *cred) { @@ -438,6 +512,8 @@ static int wpa_supplicant_wps_cred(void *ctx, if (cred->ap_channel) wpa_s->wps_ap_channel = cred->ap_channel; + wpas_wps_remove_dup_network(wpa_s, ssid); + #ifndef CONFIG_NO_CONFIG_WRITE if (wpa_s->conf->update_config && wpa_config_write(wpa_s->confname, wpa_s->conf)) { @@ -573,6 +649,10 @@ static void wpa_supplicant_wps_event_success(struct wpa_supplicant *wpa_s) wpa_s->wps_success = 1; wpas_notify_wps_event_success(wpa_s); + if (wpa_s->current_ssid) + wpas_clear_temp_disabled(wpa_s, wpa_s->current_ssid, 1); + wpa_s->extra_blacklist_count = 0; + /* * Enable the networks disabled during wpas_wps_reassoc after 10 * seconds. The 10 seconds timer is to allow the data connection to be |
