Commit 0739a7d7 authored by Avraham Stern's avatar Avraham Stern Committed by Luca Coelho
Browse files

iwlwifi: mvm: initiator: add option for adding a PASN responder



Add an option for adding a PASN responder, specifying the HLTK and
TK (if not associated). When a receiving a range request for a
PASN responder, the driver will ask for a secured measurement with
the specified HLTK and TK.

Signed-off-by: default avatarAvraham Stern <avraham.stern@intel.com>
Signed-off-by: default avatarLuca Coelho <luciano.coelho@intel.com>
Link: https://lore.kernel.org/r/iwlwifi.20200930161256.28c5f5266000.I2d58b72ff92c47ac33a6aacc27fbf3790b6dfc51@changeid


Signed-off-by: default avatarLuca Coelho <luciano.coelho@intel.com>
parent 68ad2474
Loading
Loading
Loading
Loading
+173 −4
Original line number Diff line number Diff line
@@ -83,6 +83,96 @@ struct iwl_mvm_smooth_entry {
	u64 host_time;
};

struct iwl_mvm_ftm_pasn_entry {
	struct list_head list;
	u8 addr[ETH_ALEN];
	u8 hltk[HLTK_11AZ_LEN];
	u8 tk[TK_11AZ_LEN];
	u8 cipher;
	u8 tx_pn[IEEE80211_CCMP_PN_LEN];
	u8 rx_pn[IEEE80211_CCMP_PN_LEN];
};

int iwl_mvm_ftm_add_pasn_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
			     u8 *addr, u32 cipher, u8 *tk, u32 tk_len,
			     u8 *hltk, u32 hltk_len)
{
	struct iwl_mvm_ftm_pasn_entry *pasn = kzalloc(sizeof(*pasn),
						      GFP_KERNEL);
	u32 expected_tk_len;

	lockdep_assert_held(&mvm->mutex);

	if (!pasn)
		return -ENOBUFS;

	pasn->cipher = iwl_mvm_cipher_to_location_cipher(cipher);

	switch (pasn->cipher) {
	case IWL_LOCATION_CIPHER_CCMP_128:
	case IWL_LOCATION_CIPHER_GCMP_128:
		expected_tk_len = WLAN_KEY_LEN_CCMP;
		break;
	case IWL_LOCATION_CIPHER_GCMP_256:
		expected_tk_len = WLAN_KEY_LEN_GCMP_256;
		break;
	default:
		goto out;
	}

	/*
	 * If associated to this AP and already have security context,
	 * the TK is already configured for this station, so it
	 * shouldn't be set again here.
	 */
	if (vif->bss_conf.assoc &&
	    !memcmp(addr, vif->bss_conf.bssid, ETH_ALEN)) {
		struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
		struct ieee80211_sta *sta;

		rcu_read_lock();
		sta = rcu_dereference(mvm->fw_id_to_mac_id[mvmvif->ap_sta_id]);
		if (!IS_ERR_OR_NULL(sta) && sta->mfp)
			expected_tk_len = 0;
		rcu_read_unlock();
	}

	if (tk_len != expected_tk_len || hltk_len != sizeof(pasn->hltk)) {
		IWL_ERR(mvm, "Invalid key length: tk_len=%u hltk_len=%u\n",
			tk_len, hltk_len);
		goto out;
	}

	memcpy(pasn->addr, addr, sizeof(pasn->addr));
	memcpy(pasn->hltk, hltk, sizeof(pasn->hltk));

	if (tk && tk_len)
		memcpy(pasn->tk, tk, sizeof(pasn->tk));

	list_add_tail(&pasn->list, &mvm->ftm_initiator.pasn_list);
	return 0;
out:
	kfree(pasn);
	return -EINVAL;
}

void iwl_mvm_ftm_remove_pasn_sta(struct iwl_mvm *mvm, u8 *addr)
{
	struct iwl_mvm_ftm_pasn_entry *entry, *prev;

	lockdep_assert_held(&mvm->mutex);

	list_for_each_entry_safe(entry, prev, &mvm->ftm_initiator.pasn_list,
				 list) {
		if (memcmp(entry->addr, addr, sizeof(entry->addr)))
			continue;

		list_del(&entry->list);
		kfree(entry);
		return;
	}
}

static void iwl_mvm_ftm_reset(struct iwl_mvm *mvm)
{
	struct iwl_mvm_loc_entry *e, *t;
@@ -595,6 +685,63 @@ static int iwl_mvm_ftm_start_v9(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
	return iwl_mvm_ftm_send_cmd(mvm, &hcmd);
}

static void iter(struct ieee80211_hw *hw,
		 struct ieee80211_vif *vif,
		 struct ieee80211_sta *sta,
		 struct ieee80211_key_conf *key,
		 void *data)
{
	struct iwl_tof_range_req_ap_entry_v6 *target = data;

	if (!sta || memcmp(sta->addr, target->bssid, ETH_ALEN))
		return;

	WARN_ON(!sta->mfp);

	if (WARN_ON(key->keylen > sizeof(target->tk)))
		return;

	memcpy(target->tk, key->key, key->keylen);
	target->cipher = iwl_mvm_cipher_to_location_cipher(key->cipher);
	WARN_ON(target->cipher == IWL_LOCATION_CIPHER_INVALID);
}

static void
iwl_mvm_ftm_set_secured_ranging(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
				struct iwl_tof_range_req_ap_entry_v7 *target)
{
	struct iwl_mvm_ftm_pasn_entry *entry;
	u32 flags = le32_to_cpu(target->initiator_ap_flags);

	if (!(flags & (IWL_INITIATOR_AP_FLAGS_NON_TB |
		       IWL_INITIATOR_AP_FLAGS_TB)))
		return;

	lockdep_assert_held(&mvm->mutex);

	list_for_each_entry(entry, &mvm->ftm_initiator.pasn_list, list) {
		if (memcmp(entry->addr, target->bssid, sizeof(entry->addr)))
			continue;

		target->cipher = entry->cipher;
		memcpy(target->hltk, entry->hltk, sizeof(target->hltk));

		if (vif->bss_conf.assoc &&
		    !memcmp(vif->bss_conf.bssid, target->bssid,
			    sizeof(target->bssid)))
			ieee80211_iter_keys(mvm->hw, vif, iter, target);
		else
			memcpy(target->tk, entry->tk, sizeof(target->tk));

		memcpy(target->rx_pn, entry->rx_pn, sizeof(target->rx_pn));
		memcpy(target->tx_pn, entry->tx_pn, sizeof(target->tx_pn));

		target->initiator_ap_flags |=
			cpu_to_le32(IWL_INITIATOR_AP_FLAGS_SECURED);
		return;
	}
}

static int iwl_mvm_ftm_start_v11(struct iwl_mvm *mvm,
				 struct ieee80211_vif *vif,
				 struct cfg80211_pmsr_request *req)
@@ -618,6 +765,8 @@ static int iwl_mvm_ftm_start_v11(struct iwl_mvm *mvm,
		err = iwl_mvm_ftm_put_target(mvm, vif, peer, (void *)target);
		if (err)
			return err;

		iwl_mvm_ftm_set_secured_ranging(mvm, vif, target);
	}

	return iwl_mvm_ftm_send_cmd(mvm, &hcmd);
@@ -868,6 +1017,24 @@ static void iwl_mvm_debug_range_resp(struct iwl_mvm *mvm, u8 index,
	IWL_DEBUG_INFO(mvm, "\tdistance: %lld\n", rtt_avg);
}

static void
iwl_mvm_ftm_pasn_update_pn(struct iwl_mvm *mvm,
			   struct iwl_tof_range_rsp_ap_entry_ntfy_v6 *fw_ap)
{
	struct iwl_mvm_ftm_pasn_entry *entry;

	lockdep_assert_held(&mvm->mutex);

	list_for_each_entry(entry, &mvm->ftm_initiator.pasn_list, list) {
		if (memcmp(fw_ap->bssid, entry->addr, sizeof(entry->addr)))
			continue;

		memcpy(entry->rx_pn, fw_ap->rx_pn, sizeof(entry->rx_pn));
		memcpy(entry->tx_pn, fw_ap->tx_pn, sizeof(entry->tx_pn));
		return;
	}
}

void iwl_mvm_ftm_range_resp(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
{
	struct iwl_rx_packet *pkt = rxb_addr(rxb);
@@ -912,13 +1079,15 @@ void iwl_mvm_ftm_range_resp(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
		int peer_idx;

		if (new_api) {
			if (mvm->cmd_ver.range_resp == 8)
			if (mvm->cmd_ver.range_resp == 8) {
				fw_ap = &fw_resp_v8->ap[i];
			else if (fw_has_api(&mvm->fw->ucode_capa,
					    IWL_UCODE_TLV_API_FTM_RTT_ACCURACY))
				iwl_mvm_ftm_pasn_update_pn(mvm, fw_ap);
			} else if (fw_has_api(&mvm->fw->ucode_capa,
					      IWL_UCODE_TLV_API_FTM_RTT_ACCURACY)) {
				fw_ap = (void *)&fw_resp_v7->ap[i];
			else
			} else {
				fw_ap = (void *)&fw_resp_v6->ap[i];
			}

			result.final = fw_ap->last_burst;
			result.ap_tsf = le32_to_cpu(fw_ap->start_tsf);
+5 −0
Original line number Diff line number Diff line
@@ -1110,6 +1110,7 @@ struct iwl_mvm {
		struct {
			struct list_head resp;
		} smooth;
		struct list_head pasn_list;
	} ftm_initiator;

	struct list_head resp_pasn_list;
@@ -2016,6 +2017,10 @@ int iwl_mvm_ftm_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
void iwl_mvm_ftm_abort(struct iwl_mvm *mvm, struct cfg80211_pmsr_request *req);
void iwl_mvm_ftm_initiator_smooth_config(struct iwl_mvm *mvm);
void iwl_mvm_ftm_initiator_smooth_stop(struct iwl_mvm *mvm);
int iwl_mvm_ftm_add_pasn_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
			     u8 *addr, u32 cipher, u8 *tk, u32 tk_len,
			     u8 *hltk, u32 hltk_len);
void iwl_mvm_ftm_remove_pasn_sta(struct iwl_mvm *mvm, u8 *addr);

/* TDLS */

+1 −0
Original line number Diff line number Diff line
@@ -695,6 +695,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
	INIT_LIST_HEAD(&mvm->async_handlers_list);
	spin_lock_init(&mvm->time_event_lock);
	INIT_LIST_HEAD(&mvm->ftm_initiator.loc_list);
	INIT_LIST_HEAD(&mvm->ftm_initiator.pasn_list);
	INIT_LIST_HEAD(&mvm->resp_pasn_list);

	INIT_WORK(&mvm->async_handlers_wk, iwl_mvm_async_handlers_wk);