Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 13e0fe70 authored by Samuel Ortiz's avatar Samuel Ortiz Committed by John W. Linville
Browse files

iwmc3200wifi: cfg80211 key hooks implemetation



This patch implements the new cfg80211 privacy related hooks: add/get/set_key
and the set_default_key one.
With this implementation we can now call the wext-compat *encode* routines and
reduce our own wext code.

Signed-off-by: default avatarSamuel Ortiz <samuel.ortiz@intel.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent a70742f1
Loading
Loading
Loading
Loading
+172 −0
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@

#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/wireless.h>
#include <linux/ieee80211.h>
#include <net/cfg80211.h>
@@ -130,6 +131,173 @@ static struct ieee80211_supported_band iwm_band_5ghz = {
	.n_bitrates = iwm_a_rates_size,
};

static int iwm_key_init(struct iwm_key *key, u8 key_index,
			const u8 *mac_addr, struct key_params *params)
{
	key->hdr.key_idx = key_index;
	if (!mac_addr || is_broadcast_ether_addr(mac_addr)) {
		key->hdr.multicast = 1;
		memset(key->hdr.mac, 0xff, ETH_ALEN);
	} else {
		key->hdr.multicast = 0;
		memcpy(key->hdr.mac, mac_addr, ETH_ALEN);
	}

	if (params) {
		if (params->key_len > WLAN_MAX_KEY_LEN ||
		    params->seq_len > IW_ENCODE_SEQ_MAX_SIZE)
			return -EINVAL;

		key->cipher = params->cipher;
		key->key_len = params->key_len;
		key->seq_len = params->seq_len;
		memcpy(key->key, params->key, key->key_len);
		memcpy(key->seq, params->seq, key->seq_len);
	}

	return 0;
}

static int iwm_reset_profile(struct iwm_priv *iwm)
{
	int ret;

	if (!iwm->umac_profile_active)
		return 0;

	/*
	 * If there is a current active profile, but no
	 * default key, it's not worth trying to associate again.
	 */
	if (iwm->default_key < 0)
		return 0;

	/*
	 * Here we have an active profile, but a key setting changed.
	 * We thus have to invalidate the current profile, and push the
	 * new one. Keys will be pushed when association takes place.
	 */
	ret = iwm_invalidate_mlme_profile(iwm);
	if (ret < 0) {
		IWM_ERR(iwm, "Couldn't invalidate profile\n");
		return ret;
	}

	return iwm_send_mlme_profile(iwm);
}

static int iwm_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
				u8 key_index, const u8 *mac_addr,
				struct key_params *params)
{
	struct iwm_priv *iwm = ndev_to_iwm(ndev);
	struct iwm_key *key = &iwm->keys[key_index];
	int ret;

	IWM_DBG_WEXT(iwm, DBG, "Adding key for %pM\n", mac_addr);

	memset(key, 0, sizeof(struct iwm_key));
	ret = iwm_key_init(key, key_index, mac_addr, params);
	if (ret < 0) {
		IWM_ERR(iwm, "Invalid key_params\n");
		return ret;
	}

	/*
	 * The WEP keys can be set before or after setting the essid.
	 * We need to handle both cases by simply pushing the keys after
	 * we send the profile.
	 * If the profile is not set yet (i.e. we're pushing keys before
	 * the essid), we set the cipher appropriately.
	 * If the profile is set, we havent associated yet because our
	 * cipher was incorrectly set. So we invalidate and send the
	 * profile again.
	 */
	if (key->cipher == WLAN_CIPHER_SUITE_WEP40 ||
	    key->cipher == WLAN_CIPHER_SUITE_WEP104) {
		u8 *ucast_cipher = &iwm->umac_profile->sec.ucast_cipher;
		u8 *mcast_cipher = &iwm->umac_profile->sec.mcast_cipher;

		IWM_DBG_WEXT(iwm, DBG, "WEP key\n");

		if (key->cipher == WLAN_CIPHER_SUITE_WEP40)
			*ucast_cipher = *mcast_cipher = UMAC_CIPHER_TYPE_WEP_40;
		if (key->cipher == WLAN_CIPHER_SUITE_WEP104)
			*ucast_cipher = *mcast_cipher =
				UMAC_CIPHER_TYPE_WEP_104;

		return iwm_reset_profile(iwm);
	}

	return iwm_set_key(iwm, 0, key);
}

static int iwm_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
				u8 key_index, const u8 *mac_addr, void *cookie,
				void (*callback)(void *cookie,
						 struct key_params*))
{
	struct iwm_priv *iwm = ndev_to_iwm(ndev);
	struct iwm_key *key = &iwm->keys[key_index];
	struct key_params params;

	IWM_DBG_WEXT(iwm, DBG, "Getting key %d\n", key_index);

	memset(&params, 0, sizeof(params));

	params.cipher = key->cipher;
	params.key_len = key->key_len;
	params.seq_len = key->seq_len;
	params.seq = key->seq;
	params.key = key->key;

	callback(cookie, &params);

	return key->key_len ? 0 : -ENOENT;
}


static int iwm_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
				u8 key_index, const u8 *mac_addr)
{
	struct iwm_priv *iwm = ndev_to_iwm(ndev);
	struct iwm_key *key = &iwm->keys[key_index];

	if (!iwm->keys[key_index].key_len) {
		IWM_DBG_WEXT(iwm, DBG, "Key %d not used\n", key_index);
		return 0;
	}

	if (key_index == iwm->default_key)
		iwm->default_key = -1;

	return iwm_set_key(iwm, 1, key);
}

static int iwm_cfg80211_set_default_key(struct wiphy *wiphy,
					struct net_device *ndev,
					u8 key_index)
{
	struct iwm_priv *iwm = ndev_to_iwm(ndev);
	int ret;

	IWM_DBG_WEXT(iwm, DBG, "Default key index is: %d\n", key_index);

	if (!iwm->keys[key_index].key_len) {
		IWM_ERR(iwm, "Key %d not used\n", key_index);
		return -EINVAL;
	}

	ret = iwm_set_tx_key(iwm, key_index);
	if (ret < 0)
		return ret;

	iwm->default_key = key_index;

	return iwm_reset_profile(iwm);
}


int iwm_cfg80211_inform_bss(struct iwm_priv *iwm)
{
	struct wiphy *wiphy = iwm_to_wiphy(iwm);
@@ -326,6 +494,10 @@ static int iwm_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev)

static struct cfg80211_ops iwm_cfg80211_ops = {
	.change_virtual_intf = iwm_cfg80211_change_iface,
	.add_key = iwm_cfg80211_add_key,
	.get_key = iwm_cfg80211_get_key,
	.del_key = iwm_cfg80211_del_key,
	.set_default_key = iwm_cfg80211_set_default_key,
	.scan = iwm_cfg80211_scan,
	.set_wiphy_params = iwm_cfg80211_set_wiphy_params,
	.join_ibss = iwm_cfg80211_join_ibss,
+25 −47
Original line number Diff line number Diff line
@@ -524,9 +524,6 @@ int iwm_set_tx_key(struct iwm_priv *iwm, u8 key_idx)
{
	struct iwm_umac_tx_key_id tx_key_id;

	if (!iwm->default_key || !iwm->default_key->in_use)
		return -EINVAL;

	tx_key_id.hdr.oid = UMAC_WIFI_IF_CMD_GLOBAL_TX_KEY_ID;
	tx_key_id.hdr.buf_size = cpu_to_le16(sizeof(struct iwm_umac_tx_key_id) -
					     sizeof(struct iwm_umac_wifi_if));
@@ -569,10 +566,9 @@ static int iwm_check_profile(struct iwm_priv *iwm)
	return 0;
}

int iwm_set_key(struct iwm_priv *iwm, bool remove, bool set_tx_key,
		struct iwm_key *key)
int iwm_set_key(struct iwm_priv *iwm, bool remove, struct iwm_key *key)
{
	int ret;
	int ret = 0;
	u8 cmd[64], *sta_addr, *key_data, key_len;
	s8 key_idx;
	u16 cmd_size = 0;
@@ -582,9 +578,6 @@ int iwm_set_key(struct iwm_priv *iwm, bool remove, bool set_tx_key,
	struct iwm_umac_key_tkip *tkip = (struct iwm_umac_key_tkip *)cmd;
	struct iwm_umac_key_ccmp *ccmp = (struct iwm_umac_key_ccmp *)cmd;

	if (set_tx_key)
		iwm->default_key = key;

	/*
	 * We check if our current profile is valid.
	 * If not, we dont push the key, we just cache them,
@@ -603,8 +596,7 @@ int iwm_set_key(struct iwm_priv *iwm, bool remove, bool set_tx_key,
	key_idx = key->hdr.key_idx;

	if (!remove) {
		IWM_DBG_WEXT(iwm, DBG, "key_idx:%d set tx key:%d\n",
			     key_idx, set_tx_key);
		IWM_DBG_WEXT(iwm, DBG, "key_idx:%d\n", key_idx);
		IWM_DBG_WEXT(iwm, DBG, "key_len:%d\n", key_len);
		IWM_DBG_WEXT(iwm, DBG, "MAC:%pM, idx:%d, multicast:%d\n",
		       key_hdr->mac, key_hdr->key_idx, key_hdr->multicast);
@@ -616,8 +608,8 @@ int iwm_set_key(struct iwm_priv *iwm, bool remove, bool set_tx_key,
			     iwm->umac_profile->sec.auth_type,
			     iwm->umac_profile->sec.flags);

		switch (key->alg) {
		case UMAC_CIPHER_TYPE_WEP_40:
		switch (key->cipher) {
		case WLAN_CIPHER_SUITE_WEP40:
			wep40->hdr.oid = UMAC_WIFI_IF_CMD_ADD_WEP40_KEY;
			wep40->hdr.buf_size =
				cpu_to_le16(sizeof(struct iwm_umac_key_wep40) -
@@ -631,7 +623,7 @@ int iwm_set_key(struct iwm_priv *iwm, bool remove, bool set_tx_key,
			cmd_size = sizeof(struct iwm_umac_key_wep40);
			break;

		case UMAC_CIPHER_TYPE_WEP_104:
		case WLAN_CIPHER_SUITE_WEP104:
			wep104->hdr.oid = UMAC_WIFI_IF_CMD_ADD_WEP104_KEY;
			wep104->hdr.buf_size =
				cpu_to_le16(sizeof(struct iwm_umac_key_wep104) -
@@ -645,7 +637,7 @@ int iwm_set_key(struct iwm_priv *iwm, bool remove, bool set_tx_key,
			cmd_size = sizeof(struct iwm_umac_key_wep104);
			break;

		case UMAC_CIPHER_TYPE_CCMP:
		case WLAN_CIPHER_SUITE_CCMP:
			key_hdr->key_idx++;
			ccmp->hdr.oid = UMAC_WIFI_IF_CMD_ADD_CCMP_KEY;
			ccmp->hdr.buf_size =
@@ -657,13 +649,13 @@ int iwm_set_key(struct iwm_priv *iwm, bool remove, bool set_tx_key,

			memcpy(ccmp->key, key_data, key_len);

			if (key->flags & IW_ENCODE_EXT_RX_SEQ_VALID)
				memcpy(ccmp->iv_count, key->rx_seq, 6);
			if (key->seq_len)
				memcpy(ccmp->iv_count, key->seq, key->seq_len);

			cmd_size = sizeof(struct iwm_umac_key_ccmp);
			break;

		case UMAC_CIPHER_TYPE_TKIP:
		case WLAN_CIPHER_SUITE_TKIP:
			key_hdr->key_idx++;
			tkip->hdr.oid = UMAC_WIFI_IF_CMD_ADD_TKIP_KEY;
			tkip->hdr.buf_size =
@@ -680,8 +672,8 @@ int iwm_set_key(struct iwm_priv *iwm, bool remove, bool set_tx_key,
			       key_data + IWM_TKIP_KEY_SIZE + IWM_TKIP_MIC_SIZE,
			       IWM_TKIP_MIC_SIZE);

			if (key->flags & IW_ENCODE_EXT_RX_SEQ_VALID)
				memcpy(ccmp->iv_count, key->rx_seq, 6);
			if (key->seq_len)
				memcpy(ccmp->iv_count, key->seq, key->seq_len);

			cmd_size = sizeof(struct iwm_umac_key_tkip);
			break;
@@ -690,8 +682,8 @@ int iwm_set_key(struct iwm_priv *iwm, bool remove, bool set_tx_key,
			return -ENOTSUPP;
		}

		if ((key->alg == UMAC_CIPHER_TYPE_CCMP) ||
		    (key->alg == UMAC_CIPHER_TYPE_TKIP))
		if ((key->cipher == WLAN_CIPHER_SUITE_TKIP) ||
		    (key->cipher == WLAN_CIPHER_SUITE_CCMP))
			/*
			 * UGLY_UGLY_UGLY
			 * Copied HACK from the MWG driver.
@@ -702,23 +694,11 @@ int iwm_set_key(struct iwm_priv *iwm, bool remove, bool set_tx_key,
			schedule_timeout_interruptible(usecs_to_jiffies(300));

		ret =  iwm_send_wifi_if_cmd(iwm, cmd, cmd_size, 1);
		if (ret < 0)
			goto err;

		/*
		 * We need a default key only if it is set and
		 * if we're doing WEP.
		 */
		if (iwm->default_key == key &&
			((key->alg == UMAC_CIPHER_TYPE_WEP_40) ||
			 (key->alg == UMAC_CIPHER_TYPE_WEP_104))) {
			ret = iwm_set_tx_key(iwm, key_idx);
			if (ret < 0)
				goto err;
		}
	} else {
		struct iwm_umac_key_remove key_remove;

		IWM_DBG_WEXT(iwm, ERR, "Removing key_idx:%d\n", key_idx);

		key_remove.hdr.oid = UMAC_WIFI_IF_CMD_REMOVE_KEY;
		key_remove.hdr.buf_size =
			cpu_to_le16(sizeof(struct iwm_umac_key_remove) -
@@ -732,13 +712,9 @@ int iwm_set_key(struct iwm_priv *iwm, bool remove, bool set_tx_key,
		if (ret < 0)
			return ret;

		iwm->keys[key_idx].in_use = 0;
		iwm->keys[key_idx].key_len = 0;
	}

	return 0;

 err:
	kfree(key);
	return ret;
}

@@ -761,22 +737,24 @@ int iwm_send_mlme_profile(struct iwm_priv *iwm)
	}

	for (i = 0; i < IWM_NUM_KEYS; i++)
		if (iwm->keys[i].in_use) {
			int default_key = 0;
		if (iwm->keys[i].key_len) {
			struct iwm_key *key = &iwm->keys[i];

			if (key == iwm->default_key)
				default_key = 1;

			/* Wait for the profile before sending the keys */
			wait_event_interruptible_timeout(iwm->mlme_queue,
			     (test_bit(IWM_STATUS_ASSOCIATING, &iwm->status) ||
			      test_bit(IWM_STATUS_ASSOCIATED, &iwm->status)),
							 3 * HZ);

			ret = iwm_set_key(iwm, 0, default_key, key);
			ret = iwm_set_key(iwm, 0, key);
			if (ret < 0)
				return ret;

			if (iwm->default_key == i) {
				ret = iwm_set_tx_key(iwm, i);
				if (ret < 0)
					return ret;
			}
		}

	return 0;
+1 −2
Original line number Diff line number Diff line
@@ -406,8 +406,7 @@ int iwm_send_mlme_profile(struct iwm_priv *iwm);
int iwm_invalidate_mlme_profile(struct iwm_priv *iwm);
int iwm_send_packet(struct iwm_priv *iwm, struct sk_buff *skb, int pool_id);
int iwm_set_tx_key(struct iwm_priv *iwm, u8 key_idx);
int iwm_set_key(struct iwm_priv *iwm, bool remove, bool set_tx_key,
		struct iwm_key *key);
int iwm_set_key(struct iwm_priv *iwm, bool remove, struct iwm_key *key);
int iwm_send_umac_stats_req(struct iwm_priv *iwm, u32 flags);
int iwm_send_umac_channel_list(struct iwm_priv *iwm);
int iwm_scan_ssids(struct iwm_priv *iwm, struct cfg80211_ssid *ssids,
+6 −8
Original line number Diff line number Diff line
@@ -162,13 +162,11 @@ struct iwm_umac_key_hdr {

struct iwm_key {
	struct iwm_umac_key_hdr hdr;
	u8 in_use;
	u8 alg;
	u32 flags;
	u8 tx_seq[IW_ENCODE_SEQ_MAX_SIZE];
	u8 rx_seq[IW_ENCODE_SEQ_MAX_SIZE];
	u8 key_len;
	u8 key[32];
	u32 cipher;
	u8 key[WLAN_MAX_KEY_LEN];
	u8 seq[IW_ENCODE_SEQ_MAX_SIZE];
	int key_len;
	int seq_len;
};

#define IWM_RX_ID_HASH  0xff
@@ -276,7 +274,7 @@ struct iwm_priv {
	struct iwm_tx_queue txq[IWM_TX_QUEUES];

	struct iwm_key keys[IWM_NUM_KEYS];
	struct iwm_key *default_key;
	s8 default_key;

	DECLARE_BITMAP(wifi_ntfy, WIFI_IF_NTFY_MAX);
	wait_queue_head_t wifi_ntfy_queue;
+2 −2
Original line number Diff line number Diff line
@@ -230,7 +230,7 @@ int iwm_priv_init(struct iwm_priv *iwm)
	for (i = 0; i < IWM_NUM_KEYS; i++)
		memset(&iwm->keys[i], 0, sizeof(struct iwm_key));

	iwm->default_key = NULL;
	iwm->default_key = -1;

	init_timer(&iwm->watchdog);
	iwm->watchdog.function = iwm_watchdog;
@@ -709,7 +709,7 @@ int __iwm_down(struct iwm_priv *iwm)
	iwm->umac_profile = NULL;
	iwm_bss_list_clean(iwm);

	iwm->default_key = NULL;
	iwm->default_key = -1;
	iwm->core_enabled = 0;

	ret = iwm_bus_disable(iwm);
Loading