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

Commit 2475b1cc authored by Max Stepanov's avatar Max Stepanov Committed by Johannes Berg
Browse files

mac80211: add generic cipher scheme support



This adds generic cipher scheme support to mac80211, such schemes
are fully under control by the driver. On hw registration drivers
may specify additional HW ciphers with a scheme how these ciphers
have to be handled by mac80211 TX/RR. A cipher scheme specifies a
cipher suite value, a size of the security header to be added to
or stripped from frames and how the PN is to be verified on RX.

Signed-off-by: default avatarMax Stepanov <Max.Stepanov@intel.com>
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 6bc54fbc
Loading
Loading
Loading
Loading
+36 −0
Original line number Diff line number Diff line
@@ -1228,6 +1228,36 @@ struct ieee80211_key_conf {
	u8 key[0];
};

/**
 * struct ieee80211_cipher_scheme - cipher scheme
 *
 * This structure contains a cipher scheme information defining
 * the secure packet crypto handling.
 *
 * @cipher: a cipher suite selector
 * @iftype: a cipher iftype bit mask indicating an allowed cipher usage
 * @hdr_len: a length of a security header used the cipher
 * @pn_len: a length of a packet number in the security header
 * @pn_off: an offset of pn from the beginning of the security header
 * @key_idx_off: an offset of key index byte in the security header
 * @key_idx_mask: a bit mask of key_idx bits
 * @key_idx_shift: a bit shift needed to get key_idx
 *     key_idx value calculation:
 *      (sec_header_base[key_idx_off] & key_idx_mask) >> key_idx_shift
 * @mic_len: a mic length in bytes
 */
struct ieee80211_cipher_scheme {
	u32 cipher;
	u16 iftype;
	u8 hdr_len;
	u8 pn_len;
	u8 pn_off;
	u8 key_idx_off;
	u8 key_idx_mask;
	u8 key_idx_shift;
	u8 mic_len;
};

/**
 * enum set_key_cmd - key command
 *
@@ -1636,6 +1666,10 @@ enum ieee80211_hw_flags {
 * @uapsd_max_sp_len: maximum number of total buffered frames the WMM AP may
 *	deliver to a WMM STA during any Service Period triggered by the WMM STA.
 *	Use IEEE80211_WMM_IE_STA_QOSINFO_SP_* for correct values.
 *
 * @n_cipher_schemes: a size of an array of cipher schemes definitions.
 * @cipher_schemes: a pointer to an array of cipher scheme definitions
 *	supported by HW.
 */
struct ieee80211_hw {
	struct ieee80211_conf conf;
@@ -1663,6 +1697,8 @@ struct ieee80211_hw {
	netdev_features_t netdev_features;
	u8 uapsd_queues;
	u8 uapsd_max_sp_len;
	u8 n_cipher_schemes;
	const struct ieee80211_cipher_scheme *cipher_schemes;
};

/**
+25 −6
Original line number Diff line number Diff line
@@ -133,7 +133,9 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
			     struct key_params *params)
{
	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
	struct ieee80211_local *local = sdata->local;
	struct sta_info *sta = NULL;
	const struct ieee80211_cipher_scheme *cs = NULL;
	struct ieee80211_key *key;
	int err;

@@ -145,22 +147,28 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
	case WLAN_CIPHER_SUITE_WEP40:
	case WLAN_CIPHER_SUITE_TKIP:
	case WLAN_CIPHER_SUITE_WEP104:
		if (IS_ERR(sdata->local->wep_tx_tfm))
		if (IS_ERR(local->wep_tx_tfm))
			return -EINVAL;
		break;
	case WLAN_CIPHER_SUITE_CCMP:
	case WLAN_CIPHER_SUITE_AES_CMAC:
	case WLAN_CIPHER_SUITE_GCMP:
		break;
	default:
		cs = ieee80211_cs_get(local, params->cipher, sdata->vif.type);
		break;
	}

	key = ieee80211_key_alloc(params->cipher, key_idx, params->key_len,
				  params->key, params->seq_len, params->seq);
				  params->key, params->seq_len, params->seq,
				  cs);
	if (IS_ERR(key))
		return PTR_ERR(key);

	if (pairwise)
		key->conf.flags |= IEEE80211_KEY_FLAG_PAIRWISE;

	mutex_lock(&sdata->local->sta_mtx);
	mutex_lock(&local->sta_mtx);

	if (mac_addr) {
		if (ieee80211_vif_is_mesh(&sdata->vif))
@@ -216,10 +224,13 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
		break;
	}

	if (sta)
		sta->cipher_scheme = cs;

	err = ieee80211_key_link(key, sdata, sta);

 out_unlock:
	mutex_unlock(&sdata->local->sta_mtx);
	mutex_unlock(&local->sta_mtx);

	return err;
}
@@ -244,7 +255,7 @@ static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,
			goto out_unlock;

		if (pairwise)
			key = key_mtx_dereference(local, sta->ptk);
			key = key_mtx_dereference(local, sta->ptk[key_idx]);
		else
			key = key_mtx_dereference(local, sta->gtk[key_idx]);
	} else
@@ -291,7 +302,7 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
			goto out;

		if (pairwise)
			key = rcu_dereference(sta->ptk);
			key = rcu_dereference(sta->ptk[key_idx]);
		else if (key_idx < NUM_DEFAULT_KEYS)
			key = rcu_dereference(sta->gtk[key_idx]);
	} else
@@ -968,11 +979,19 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
	 */
	sdata->control_port_protocol = params->crypto.control_port_ethertype;
	sdata->control_port_no_encrypt = params->crypto.control_port_no_encrypt;
	sdata->encrypt_headroom = ieee80211_cs_headroom(sdata->local,
							&params->crypto,
							sdata->vif.type);

	list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) {
		vlan->control_port_protocol =
			params->crypto.control_port_ethertype;
		vlan->control_port_no_encrypt =
			params->crypto.control_port_no_encrypt;
		vlan->encrypt_headroom =
			ieee80211_cs_headroom(sdata->local,
					      &params->crypto,
					      vlan->vif.type);
	}

	sdata->vif.bss_conf.beacon_int = params->beacon_interval;
+10 −0
Original line number Diff line number Diff line
@@ -728,6 +728,7 @@ struct ieee80211_sub_if_data {
	u16 sequence_number;
	__be16 control_port_protocol;
	bool control_port_no_encrypt;
	int encrypt_headroom;

	struct ieee80211_tx_queue_params tx_conf[IEEE80211_NUM_ACS];

@@ -1749,6 +1750,15 @@ void ieee80211_dfs_radar_detected_work(struct work_struct *work);
int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata,
			      struct cfg80211_csa_settings *csa_settings);

bool ieee80211_cs_valid(const struct ieee80211_cipher_scheme *cs);
bool ieee80211_cs_list_valid(const struct ieee80211_cipher_scheme *cs, int n);
const struct ieee80211_cipher_scheme *
ieee80211_cs_get(struct ieee80211_local *local, u32 cipher,
		 enum nl80211_iftype iftype);
int ieee80211_cs_headroom(struct ieee80211_local *local,
			  struct cfg80211_crypto_settings *crypto,
			  enum nl80211_iftype iftype);

#ifdef CONFIG_MAC80211_NOINLINE
#define debug_noinline noinline
#else
+5 −0
Original line number Diff line number Diff line
@@ -401,6 +401,8 @@ int ieee80211_add_virtual_monitor(struct ieee80211_local *local)
	snprintf(sdata->name, IFNAMSIZ, "%s-monitor",
		 wiphy_name(local->hw.wiphy));

	sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM;

	ieee80211_set_default_queues(sdata);

	ret = drv_add_interface(local, sdata);
@@ -1273,6 +1275,7 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,

	sdata->control_port_protocol = cpu_to_be16(ETH_P_PAE);
	sdata->control_port_no_encrypt = false;
	sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM;

	sdata->noack_map = 0;

@@ -1689,6 +1692,8 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
	sdata->ap_power_level = IEEE80211_UNSET_POWER_LEVEL;
	sdata->user_power_level = local->user_power_level;

	sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM;

	/* setup type-dependent data */
	ieee80211_setup_sdata(sdata, type);

+37 −21
Original line number Diff line number Diff line
@@ -267,15 +267,6 @@ static void ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
	if (new)
		list_add_tail(&new->list, &sdata->key_list);

	if (sta && pairwise) {
		rcu_assign_pointer(sta->ptk, new);
	} else if (sta) {
		if (old)
			idx = old->conf.keyidx;
		else
			idx = new->conf.keyidx;
		rcu_assign_pointer(sta->gtk[idx], new);
	} else {
	WARN_ON(new && old && new->conf.keyidx != old->conf.keyidx);

	if (old)
@@ -283,6 +274,15 @@ static void ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
	else
		idx = new->conf.keyidx;

	if (sta) {
		if (pairwise) {
			rcu_assign_pointer(sta->ptk[idx], new);
			sta->ptk_idx = idx;
		} else {
			rcu_assign_pointer(sta->gtk[idx], new);
			sta->gtk_idx = idx;
		}
	} else {
		defunikey = old &&
			old == key_mtx_dereference(sdata->local,
						sdata->default_unicast_key);
@@ -316,9 +316,11 @@ static void ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
		list_del(&old->list);
}

struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
struct ieee80211_key *
ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
		    const u8 *key_data,
					  size_t seq_len, const u8 *seq)
		    size_t seq_len, const u8 *seq,
		    const struct ieee80211_cipher_scheme *cs)
{
	struct ieee80211_key *key;
	int i, j, err;
@@ -397,6 +399,18 @@ struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
			return ERR_PTR(err);
		}
		break;
	default:
		if (cs) {
			size_t len = (seq_len > MAX_PN_LEN) ?
						MAX_PN_LEN : seq_len;

			key->conf.iv_len = cs->hdr_len;
			key->conf.icv_len = cs->mic_len;
			for (i = 0; i < IEEE80211_NUM_TIDS + 1; i++)
				for (j = 0; j < len; j++)
					key->u.gen.rx_pn[i][j] =
							seq[len - j - 1];
		}
	}
	memcpy(key->conf.key, key_data, key_len);
	INIT_LIST_HEAD(&key->list);
@@ -479,7 +493,7 @@ int ieee80211_key_link(struct ieee80211_key *key,
	mutex_lock(&sdata->local->key_mtx);

	if (sta && pairwise)
		old_key = key_mtx_dereference(sdata->local, sta->ptk);
		old_key = key_mtx_dereference(sdata->local, sta->ptk[idx]);
	else if (sta)
		old_key = key_mtx_dereference(sdata->local, sta->gtk[idx]);
	else
@@ -629,8 +643,10 @@ void ieee80211_free_sta_keys(struct ieee80211_local *local,
		list_add(&key->list, &keys);
	}

	key = key_mtx_dereference(local, sta->ptk);
	if (key) {
	for (i = 0; i < NUM_DEFAULT_KEYS; i++) {
		key = key_mtx_dereference(local, sta->ptk[i]);
		if (!key)
			continue;
		ieee80211_key_replace(key->sdata, key->sta,
				key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE,
				key, NULL);
@@ -881,7 +897,7 @@ ieee80211_gtk_rekey_add(struct ieee80211_vif *vif,

	key = ieee80211_key_alloc(keyconf->cipher, keyconf->keyidx,
				  keyconf->keylen, keyconf->key,
				  0, NULL);
				  0, NULL, NULL);
	if (IS_ERR(key))
		return ERR_CAST(key);

Loading