diff --git a/src/drivers/driver.h b/src/drivers/driver.h index ae82629ea..5d61481ae 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -794,6 +794,7 @@ struct hostapd_sta_add_params { u16 listen_interval; const struct ieee80211_ht_capabilities *ht_capabilities; u32 flags; /* bitmask of WPA_STA_* flags */ + int set; /* Set STA parameters instead of add */ }; struct hostapd_freq_params { @@ -878,6 +879,7 @@ struct wpa_bss_params { #define WPA_STA_WMM BIT(1) #define WPA_STA_SHORT_PREAMBLE BIT(2) #define WPA_STA_MFP BIT(3) +#define WPA_STA_TDLS_PEER BIT(4) /** * struct p2p_params - P2P parameters for driver-based P2P management @@ -1622,6 +1624,9 @@ struct wpa_driver_ops { * This function is used to add a station entry to the driver once the * station has completed association. This is only used if the driver * does not take care of association processing. + * + * With TDLS, this function is also used to add or set (params->set 1) + * TDLS peer entries. */ int (*sta_add)(void *priv, struct hostapd_sta_add_params *params); @@ -2313,7 +2318,7 @@ struct wpa_driver_ops { * @status_code: Status Code or Reason Code to use (if needed) * @buf: TDLS IEs to add to the message * @len: Length of buf in octets - * Returns: 0 on success, -1 on failure + * Returns: 0 on success, negative (<0) on failure * * This optional function can be used to send packet to driver which is * responsible for receiving and sending all TDLS packets. @@ -2322,6 +2327,16 @@ struct wpa_driver_ops { u8 dialog_token, u16 status_code, const u8 *buf, size_t len); + /** + * tdls_oper - Ask the driver to perform high-level TDLS operations + * @priv: Private driver interface data + * @oper: TDLS high-level operation. See %enum tdls_oper + * @peer: Destination (peer) MAC address + * Returns: 0 on success, negative (<0) on failure + * + * This optional function can be used to send high-level TDLS commands + * to the driver. + */ int (*tdls_oper)(void *priv, enum tdls_oper oper, const u8 *peer); /** diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index a81892313..d962ac988 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -4409,6 +4409,8 @@ static u32 sta_flags_nl80211(int flags) f |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE); if (flags & WPA_STA_MFP) f |= BIT(NL80211_STA_FLAG_MFP); + if (flags & WPA_STA_TDLS_PEER) + f |= BIT(NL80211_STA_FLAG_TDLS_PEER); return f; } @@ -4423,19 +4425,26 @@ static int wpa_driver_nl80211_sta_add(void *priv, struct nl80211_sta_flag_update upd; int ret = -ENOBUFS; + if ((params->flags & WPA_STA_TDLS_PEER) && + !(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)) + return -EOPNOTSUPP; + msg = nlmsg_alloc(); if (!msg) return -ENOMEM; - nl80211_cmd(drv, msg, 0, NL80211_CMD_NEW_STATION); + nl80211_cmd(drv, msg, 0, params->set ? NL80211_CMD_SET_STATION : + NL80211_CMD_NEW_STATION); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->addr); - NLA_PUT_U16(msg, NL80211_ATTR_STA_AID, params->aid); NLA_PUT(msg, NL80211_ATTR_STA_SUPPORTED_RATES, params->supp_rates_len, params->supp_rates); - NLA_PUT_U16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL, - params->listen_interval); + if (!params->set) { + NLA_PUT_U16(msg, NL80211_ATTR_STA_AID, params->aid); + NLA_PUT_U16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL, + params->listen_interval); + } if (params->ht_capabilities) { NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY, sizeof(*params->ht_capabilities), @@ -4449,8 +4458,9 @@ static int wpa_driver_nl80211_sta_add(void *priv, ret = send_and_recv_msgs(drv, msg, NULL, NULL); if (ret) - wpa_printf(MSG_DEBUG, "nl80211: NL80211_CMD_NEW_STATION " - "result: %d (%s)", ret, strerror(-ret)); + wpa_printf(MSG_DEBUG, "nl80211: NL80211_CMD_%s_STATION " + "result: %d (%s)", params->set ? "SET" : "NEW", ret, + strerror(-ret)); if (ret == -EEXIST) ret = 0; nla_put_failure: @@ -5138,6 +5148,9 @@ static int wpa_driver_nl80211_sta_set_flags(void *priv, const u8 *addr, if (total_flags & WPA_STA_MFP) NLA_PUT_FLAG(flags, NL80211_STA_FLAG_MFP); + if (total_flags & WPA_STA_TDLS_PEER) + NLA_PUT_FLAG(flags, NL80211_STA_FLAG_TDLS_PEER); + if (nla_put_nested(msg, NL80211_ATTR_STA_FLAGS, flags)) goto nla_put_failure; diff --git a/src/rsn_supp/tdls.c b/src/rsn_supp/tdls.c index 45db2b346..490fcbbd3 100644 --- a/src/rsn_supp/tdls.c +++ b/src/rsn_supp/tdls.c @@ -1624,8 +1624,14 @@ skip_rsn: wpa_tdls_generate_tpk(peer, sm->own_addr, sm->bssid); skip_rsn_check: + /* add the peer to the driver as a "setup in progress" peer */ + wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, NULL, 0); + wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Response / TPK M2"); - wpa_tdls_send_tpk_m2(sm, src_addr, dtoken, lnkid, peer); + if (wpa_tdls_send_tpk_m2(sm, src_addr, dtoken, lnkid, peer) < 0) { + wpa_tdls_disable_link(sm, peer->addr); + goto error; + } return 0; @@ -1658,6 +1664,11 @@ static void wpa_tdls_enable_link(struct wpa_sm *sm, struct wpa_tdls_peer *peer) } #endif /* CONFIG_TDLS_TESTING */ } + + /* add supported rates and capabilities to the TDLS peer */ + wpa_sm_tdls_peer_addset(sm, peer->addr, 0, peer->capability, + peer->supp_rates, peer->supp_rates_len); + wpa_sm_tdls_oper(sm, TDLS_ENABLE_LINK, peer->addr); } @@ -2059,7 +2070,15 @@ int wpa_tdls_start(struct wpa_sm *sm, const u8 *addr) peer->initiator = 1; - return wpa_tdls_send_tpk_m1(sm, peer); + /* add the peer to the driver as a "setup in progress" peer */ + wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, NULL, 0); + + if (wpa_tdls_send_tpk_m1(sm, peer) < 0) { + wpa_tdls_disable_link(sm, peer->addr); + return -1; + } + + return 0; } diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h index 492fd349b..d4ae09c1a 100644 --- a/src/rsn_supp/wpa.h +++ b/src/rsn_supp/wpa.h @@ -62,6 +62,9 @@ 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, + u16 capability, const u8 *supp_rates, + size_t supp_rates_len); #endif /* CONFIG_TDLS */ void (*set_rekey_offload)(void *ctx, const u8 *kek, const u8 *kck, const u8 *replay_ctr); diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h index 67c339027..39124c499 100644 --- a/src/rsn_supp/wpa_i.h +++ b/src/rsn_supp/wpa_i.h @@ -291,6 +291,18 @@ static inline int wpa_sm_tdls_oper(struct wpa_sm *sm, int oper, return sm->ctx->tdls_oper(sm->ctx->ctx, oper, peer); return -1; } + +static inline int +wpa_sm_tdls_peer_addset(struct wpa_sm *sm, const u8 *addr, int add, + u16 capability, const u8 *supp_rates, + size_t supp_rates_len) +{ + if (sm->ctx->tdls_peer_addset) + return sm->ctx->tdls_peer_addset(sm->ctx->ctx, addr, add, + capability, supp_rates, + supp_rates_len); + return -1; +} #endif /* CONFIG_TDLS */ void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c index 94948c639..c02fdd09a 100644 --- a/wpa_supplicant/wpas_glue.c +++ b/wpa_supplicant/wpas_glue.c @@ -563,6 +563,27 @@ static int wpa_supplicant_tdls_oper(void *ctx, int oper, const u8 *peer) return wpa_drv_tdls_oper(wpa_s, oper, peer); } + +static int wpa_supplicant_tdls_peer_addset( + void *ctx, const u8 *peer, int add, u16 capability, + const u8 *supp_rates, size_t supp_rates_len) +{ + struct wpa_supplicant *wpa_s = ctx; + struct hostapd_sta_add_params params; + + params.addr = peer; + params.aid = 1; + params.capability = capability; + params.flags = WPA_STA_TDLS_PEER | WPA_STA_AUTHORIZED; + params.ht_capabilities = NULL; + params.listen_interval = 0; + params.supp_rates = supp_rates; + params.supp_rates_len = supp_rates_len; + params.set = !add; + + return wpa_drv_sta_add(wpa_s, ¶ms); +} + #endif /* CONFIG_TDLS */ @@ -724,6 +745,7 @@ int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s) ctx->tdls_get_capa = wpa_supplicant_tdls_get_capa; ctx->send_tdls_mgmt = wpa_supplicant_send_tdls_mgmt; ctx->tdls_oper = wpa_supplicant_tdls_oper; + ctx->tdls_peer_addset = wpa_supplicant_tdls_peer_addset; #endif /* CONFIG_TDLS */ ctx->set_rekey_offload = wpa_supplicant_set_rekey_offload;