mac80211: IEEE 802.11 Extended Key ID support

Add support for Extended Key ID as defined in IEEE 802.11-2016.

 - Implement the nl80211 API for Extended Key ID
 - Extend mac80211 API to allow drivers to support Extended Key ID
 - Enable Extended Key ID by default for drivers only supporting SW
   crypto (e.g. mac80211_hwsim)
 - Allow unicast Tx usage to be supressed (IEEE80211_KEY_FLAG_NO_AUTO_TX)
 - Select the decryption key based on the MPDU keyid
 - Enforce existing assumptions in the code that rekeys don't change the
   cipher

Signed-off-by: Alexander Wetzel <alexander@wetzel-home.de>
[remove module parameter]
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
Alexander Wetzel 2019-03-19 21:34:08 +01:00 committed by Johannes Berg
parent 6cdd3979a2
commit 96fc6efb9a
10 changed files with 151 additions and 60 deletions

View File

@ -1697,6 +1697,7 @@ struct wireless_dev *ieee80211_vif_to_wdev(struct ieee80211_vif *vif);
* @IEEE80211_KEY_FLAG_PUT_MIC_SPACE: This flag should be set by the driver for * @IEEE80211_KEY_FLAG_PUT_MIC_SPACE: This flag should be set by the driver for
* a TKIP key if it only requires MIC space. Do not set together with * a TKIP key if it only requires MIC space. Do not set together with
* @IEEE80211_KEY_FLAG_GENERATE_MMIC on the same key. * @IEEE80211_KEY_FLAG_GENERATE_MMIC on the same key.
* @IEEE80211_KEY_FLAG_NO_AUTO_TX: Key needs explicit Tx activation.
*/ */
enum ieee80211_key_flags { enum ieee80211_key_flags {
IEEE80211_KEY_FLAG_GENERATE_IV_MGMT = BIT(0), IEEE80211_KEY_FLAG_GENERATE_IV_MGMT = BIT(0),
@ -1708,6 +1709,7 @@ enum ieee80211_key_flags {
IEEE80211_KEY_FLAG_RX_MGMT = BIT(6), IEEE80211_KEY_FLAG_RX_MGMT = BIT(6),
IEEE80211_KEY_FLAG_RESERVE_TAILROOM = BIT(7), IEEE80211_KEY_FLAG_RESERVE_TAILROOM = BIT(7),
IEEE80211_KEY_FLAG_PUT_MIC_SPACE = BIT(8), IEEE80211_KEY_FLAG_PUT_MIC_SPACE = BIT(8),
IEEE80211_KEY_FLAG_NO_AUTO_TX = BIT(9),
}; };
/** /**
@ -2243,6 +2245,9 @@ struct ieee80211_txq {
* @IEEE80211_HW_SUPPORTS_ONLY_HE_MULTI_BSSID: Hardware supports multi BSSID * @IEEE80211_HW_SUPPORTS_ONLY_HE_MULTI_BSSID: Hardware supports multi BSSID
* only for HE APs. Applies if @IEEE80211_HW_SUPPORTS_MULTI_BSSID is set. * only for HE APs. Applies if @IEEE80211_HW_SUPPORTS_MULTI_BSSID is set.
* *
* @IEEE80211_HW_EXT_KEY_ID_NATIVE: Driver and hardware are supporting Extended
* Key ID and can handle two unicast keys per station for Rx and Tx.
*
* @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays * @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays
*/ */
enum ieee80211_hw_flags { enum ieee80211_hw_flags {
@ -2294,6 +2299,7 @@ enum ieee80211_hw_flags {
IEEE80211_HW_TX_STATUS_NO_AMPDU_LEN, IEEE80211_HW_TX_STATUS_NO_AMPDU_LEN,
IEEE80211_HW_SUPPORTS_MULTI_BSSID, IEEE80211_HW_SUPPORTS_MULTI_BSSID,
IEEE80211_HW_SUPPORTS_ONLY_HE_MULTI_BSSID, IEEE80211_HW_SUPPORTS_ONLY_HE_MULTI_BSSID,
IEEE80211_HW_EXT_KEY_ID_NATIVE,
/* keep last, obviously */ /* keep last, obviously */
NUM_IEEE80211_HW_FLAGS NUM_IEEE80211_HW_FLAGS

View File

@ -351,6 +351,36 @@ static int ieee80211_set_noack_map(struct wiphy *wiphy,
return 0; return 0;
} }
static int ieee80211_set_tx(struct ieee80211_sub_if_data *sdata,
const u8 *mac_addr, u8 key_idx)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_key *key;
struct sta_info *sta;
int ret = -EINVAL;
if (!wiphy_ext_feature_isset(local->hw.wiphy,
NL80211_EXT_FEATURE_EXT_KEY_ID))
return -EINVAL;
sta = sta_info_get_bss(sdata, mac_addr);
if (!sta)
return -EINVAL;
if (sta->ptk_idx == key_idx)
return 0;
mutex_lock(&local->key_mtx);
key = key_mtx_dereference(local, sta->ptk[key_idx]);
if (key && key->conf.flags & IEEE80211_KEY_FLAG_NO_AUTO_TX)
ret = ieee80211_set_tx_key(key);
mutex_unlock(&local->key_mtx);
return ret;
}
static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev, static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
u8 key_idx, bool pairwise, const u8 *mac_addr, u8 key_idx, bool pairwise, const u8 *mac_addr,
struct key_params *params) struct key_params *params)
@ -365,6 +395,9 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
if (!ieee80211_sdata_running(sdata)) if (!ieee80211_sdata_running(sdata))
return -ENETDOWN; return -ENETDOWN;
if (pairwise && params->mode == NL80211_KEY_SET_TX)
return ieee80211_set_tx(sdata, mac_addr, key_idx);
/* reject WEP and TKIP keys if WEP failed to initialize */ /* reject WEP and TKIP keys if WEP failed to initialize */
switch (params->cipher) { switch (params->cipher) {
case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP40:
@ -396,6 +429,9 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
if (pairwise) if (pairwise)
key->conf.flags |= IEEE80211_KEY_FLAG_PAIRWISE; key->conf.flags |= IEEE80211_KEY_FLAG_PAIRWISE;
if (params->mode == NL80211_KEY_NO_TX)
key->conf.flags |= IEEE80211_KEY_FLAG_NO_AUTO_TX;
mutex_lock(&local->sta_mtx); mutex_lock(&local->sta_mtx);
if (mac_addr) { if (mac_addr) {

View File

@ -221,6 +221,7 @@ static const char *hw_flag_names[] = {
FLAG(TX_STATUS_NO_AMPDU_LEN), FLAG(TX_STATUS_NO_AMPDU_LEN),
FLAG(SUPPORTS_MULTI_BSSID), FLAG(SUPPORTS_MULTI_BSSID),
FLAG(SUPPORTS_ONLY_HE_MULTI_BSSID), FLAG(SUPPORTS_ONLY_HE_MULTI_BSSID),
FLAG(EXT_KEY_ID_NATIVE),
#undef FLAG #undef FLAG
}; };

View File

@ -1269,7 +1269,7 @@ struct ieee80211_local {
/* /*
* Key mutex, protects sdata's key_list and sta_info's * Key mutex, protects sdata's key_list and sta_info's
* key pointers (write access, they're RCU.) * key pointers and ptk_idx (write access, they're RCU.)
*/ */
struct mutex key_mtx; struct mutex key_mtx;

View File

@ -265,9 +265,24 @@ static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key)
sta ? sta->sta.addr : bcast_addr, ret); sta ? sta->sta.addr : bcast_addr, ret);
} }
int ieee80211_set_tx_key(struct ieee80211_key *key)
{
struct sta_info *sta = key->sta;
struct ieee80211_local *local = key->local;
struct ieee80211_key *old;
assert_key_lock(local);
old = key_mtx_dereference(local, sta->ptk[sta->ptk_idx]);
sta->ptk_idx = key->conf.keyidx;
ieee80211_check_fast_xmit(sta);
return 0;
}
static int ieee80211_hw_key_replace(struct ieee80211_key *old_key, static int ieee80211_hw_key_replace(struct ieee80211_key *old_key,
struct ieee80211_key *new_key, struct ieee80211_key *new_key,
bool ptk0rekey) bool pairwise)
{ {
struct ieee80211_sub_if_data *sdata; struct ieee80211_sub_if_data *sdata;
struct ieee80211_local *local; struct ieee80211_local *local;
@ -284,8 +299,9 @@ static int ieee80211_hw_key_replace(struct ieee80211_key *old_key,
assert_key_lock(old_key->local); assert_key_lock(old_key->local);
sta = old_key->sta; sta = old_key->sta;
/* PTK only using key ID 0 needs special handling on rekey */ /* Unicast rekey without Extended Key ID needs special handling */
if (new_key && sta && ptk0rekey) { if (new_key && sta && pairwise &&
rcu_access_pointer(sta->ptk[sta->ptk_idx]) == old_key) {
local = old_key->local; local = old_key->local;
sdata = old_key->sdata; sdata = old_key->sdata;
@ -401,10 +417,6 @@ static int ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
if (old) { if (old) {
idx = old->conf.keyidx; idx = old->conf.keyidx;
/* TODO: proper implement and test "Extended Key ID for
* Individually Addressed Frames" from IEEE 802.11-2016.
* Till then always assume only key ID 0 is used for
* pairwise keys.*/
ret = ieee80211_hw_key_replace(old, new, pairwise); ret = ieee80211_hw_key_replace(old, new, pairwise);
} else { } else {
/* new must be provided in case old is not */ /* new must be provided in case old is not */
@ -421,15 +433,20 @@ static int ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
if (sta) { if (sta) {
if (pairwise) { if (pairwise) {
rcu_assign_pointer(sta->ptk[idx], new); rcu_assign_pointer(sta->ptk[idx], new);
sta->ptk_idx = idx; if (new &&
if (new) { !(new->conf.flags & IEEE80211_KEY_FLAG_NO_AUTO_TX)) {
sta->ptk_idx = idx;
clear_sta_flag(sta, WLAN_STA_BLOCK_BA); clear_sta_flag(sta, WLAN_STA_BLOCK_BA);
ieee80211_check_fast_xmit(sta); ieee80211_check_fast_xmit(sta);
} }
} else { } else {
rcu_assign_pointer(sta->gtk[idx], new); rcu_assign_pointer(sta->gtk[idx], new);
} }
if (new) /* Only needed for transition from no key -> key.
* Still triggers unnecessary when using Extended Key ID
* and installing the second key ID the first time.
*/
if (new && !old)
ieee80211_check_fast_rx(sta); ieee80211_check_fast_rx(sta);
} else { } else {
defunikey = old && defunikey = old &&
@ -745,16 +762,34 @@ int ieee80211_key_link(struct ieee80211_key *key,
* can cause warnings to appear. * can cause warnings to appear.
*/ */
bool delay_tailroom = sdata->vif.type == NL80211_IFTYPE_STATION; bool delay_tailroom = sdata->vif.type == NL80211_IFTYPE_STATION;
int ret; int ret = -EOPNOTSUPP;
mutex_lock(&sdata->local->key_mtx); mutex_lock(&sdata->local->key_mtx);
if (sta && pairwise) if (sta && pairwise) {
struct ieee80211_key *alt_key;
old_key = key_mtx_dereference(sdata->local, sta->ptk[idx]); old_key = key_mtx_dereference(sdata->local, sta->ptk[idx]);
else if (sta) alt_key = key_mtx_dereference(sdata->local, sta->ptk[idx ^ 1]);
/* The rekey code assumes that the old and new key are using
* the same cipher. Enforce the assumption for pairwise keys.
*/
if (key &&
((alt_key && alt_key->conf.cipher != key->conf.cipher) ||
(old_key && old_key->conf.cipher != key->conf.cipher)))
goto out;
} else if (sta) {
old_key = key_mtx_dereference(sdata->local, sta->gtk[idx]); old_key = key_mtx_dereference(sdata->local, sta->gtk[idx]);
else } else {
old_key = key_mtx_dereference(sdata->local, sdata->keys[idx]); old_key = key_mtx_dereference(sdata->local, sdata->keys[idx]);
}
/* Non-pairwise keys must also not switch the cipher on rekey */
if (!pairwise) {
if (key && old_key && old_key->conf.cipher != key->conf.cipher)
goto out;
}
/* /*
* Silently accept key re-installation without really installing the * Silently accept key re-installation without really installing the

View File

@ -18,6 +18,7 @@
#define NUM_DEFAULT_KEYS 4 #define NUM_DEFAULT_KEYS 4
#define NUM_DEFAULT_MGMT_KEYS 2 #define NUM_DEFAULT_MGMT_KEYS 2
#define INVALID_PTK_KEYIDX 2 /* Keyidx always pointing to a NULL key for PTK */
struct ieee80211_local; struct ieee80211_local;
struct ieee80211_sub_if_data; struct ieee80211_sub_if_data;
@ -146,6 +147,7 @@ ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
int ieee80211_key_link(struct ieee80211_key *key, int ieee80211_key_link(struct ieee80211_key *key,
struct ieee80211_sub_if_data *sdata, struct ieee80211_sub_if_data *sdata,
struct sta_info *sta); struct sta_info *sta);
int ieee80211_set_tx_key(struct ieee80211_key *key);
void ieee80211_key_free(struct ieee80211_key *key, bool delay_tailroom); void ieee80211_key_free(struct ieee80211_key *key, bool delay_tailroom);
void ieee80211_key_free_unused(struct ieee80211_key *key); void ieee80211_key_free_unused(struct ieee80211_key *key);
void ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, int idx, void ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, int idx,

View File

@ -1051,6 +1051,11 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
} }
} }
if (!local->ops->set_key ||
ieee80211_hw_check(&local->hw, EXT_KEY_ID_NATIVE))
wiphy_ext_feature_set(local->hw.wiphy,
NL80211_EXT_FEATURE_EXT_KEY_ID);
/* /*
* Calculate scan IE length -- we need this to alloc * Calculate scan IE length -- we need this to alloc
* memory and to subtract from the driver limit. It * memory and to subtract from the driver limit. It

View File

@ -1005,23 +1005,43 @@ static int ieee80211_get_mmie_keyidx(struct sk_buff *skb)
return -1; return -1;
} }
static int ieee80211_get_cs_keyid(const struct ieee80211_cipher_scheme *cs, static int ieee80211_get_keyid(struct sk_buff *skb,
struct sk_buff *skb) const struct ieee80211_cipher_scheme *cs)
{ {
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
__le16 fc; __le16 fc;
int hdrlen; int hdrlen;
int minlen;
u8 key_idx_off;
u8 key_idx_shift;
u8 keyid; u8 keyid;
fc = hdr->frame_control; fc = hdr->frame_control;
hdrlen = ieee80211_hdrlen(fc); hdrlen = ieee80211_hdrlen(fc);
if (skb->len < hdrlen + cs->hdr_len) if (cs) {
minlen = hdrlen + cs->hdr_len;
key_idx_off = hdrlen + cs->key_idx_off;
key_idx_shift = cs->key_idx_shift;
} else {
/* WEP, TKIP, CCMP and GCMP */
minlen = hdrlen + IEEE80211_WEP_IV_LEN;
key_idx_off = hdrlen + 3;
key_idx_shift = 6;
}
if (unlikely(skb->len < minlen))
return -EINVAL; return -EINVAL;
skb_copy_bits(skb, hdrlen + cs->key_idx_off, &keyid, 1); skb_copy_bits(skb, key_idx_off, &keyid, 1);
keyid &= cs->key_idx_mask;
keyid >>= cs->key_idx_shift; if (cs)
keyid &= cs->key_idx_mask;
keyid >>= key_idx_shift;
/* cs could use more than the usual two bits for the keyid */
if (unlikely(keyid >= NUM_DEFAULT_KEYS))
return -EINVAL;
return keyid; return keyid;
} }
@ -1852,9 +1872,9 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
int keyidx; int keyidx;
int hdrlen;
ieee80211_rx_result result = RX_DROP_UNUSABLE; ieee80211_rx_result result = RX_DROP_UNUSABLE;
struct ieee80211_key *sta_ptk = NULL; struct ieee80211_key *sta_ptk = NULL;
struct ieee80211_key *ptk_idx = NULL;
int mmie_keyidx = -1; int mmie_keyidx = -1;
__le16 fc; __le16 fc;
const struct ieee80211_cipher_scheme *cs = NULL; const struct ieee80211_cipher_scheme *cs = NULL;
@ -1892,21 +1912,24 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
if (rx->sta) { if (rx->sta) {
int keyid = rx->sta->ptk_idx; int keyid = rx->sta->ptk_idx;
sta_ptk = rcu_dereference(rx->sta->ptk[keyid]);
if (ieee80211_has_protected(fc) && rx->sta->cipher_scheme) { if (ieee80211_has_protected(fc)) {
cs = rx->sta->cipher_scheme; cs = rx->sta->cipher_scheme;
keyid = ieee80211_get_cs_keyid(cs, rx->skb); keyid = ieee80211_get_keyid(rx->skb, cs);
if (unlikely(keyid < 0)) if (unlikely(keyid < 0))
return RX_DROP_UNUSABLE; return RX_DROP_UNUSABLE;
ptk_idx = rcu_dereference(rx->sta->ptk[keyid]);
} }
sta_ptk = rcu_dereference(rx->sta->ptk[keyid]);
} }
if (!ieee80211_has_protected(fc)) if (!ieee80211_has_protected(fc))
mmie_keyidx = ieee80211_get_mmie_keyidx(rx->skb); mmie_keyidx = ieee80211_get_mmie_keyidx(rx->skb);
if (!is_multicast_ether_addr(hdr->addr1) && sta_ptk) { if (!is_multicast_ether_addr(hdr->addr1) && sta_ptk) {
rx->key = sta_ptk; rx->key = ptk_idx ? ptk_idx : sta_ptk;
if ((status->flag & RX_FLAG_DECRYPTED) && if ((status->flag & RX_FLAG_DECRYPTED) &&
(status->flag & RX_FLAG_IV_STRIPPED)) (status->flag & RX_FLAG_IV_STRIPPED))
return RX_CONTINUE; return RX_CONTINUE;
@ -1966,8 +1989,6 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
} }
return RX_CONTINUE; return RX_CONTINUE;
} else { } else {
u8 keyid;
/* /*
* The device doesn't give us the IV so we won't be * The device doesn't give us the IV so we won't be
* able to look up the key. That's ok though, we * able to look up the key. That's ok though, we
@ -1981,23 +2002,10 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
(status->flag & RX_FLAG_IV_STRIPPED)) (status->flag & RX_FLAG_IV_STRIPPED))
return RX_CONTINUE; return RX_CONTINUE;
hdrlen = ieee80211_hdrlen(fc); keyidx = ieee80211_get_keyid(rx->skb, cs);
if (cs) { if (unlikely(keyidx < 0))
keyidx = ieee80211_get_cs_keyid(cs, rx->skb); return RX_DROP_UNUSABLE;
if (unlikely(keyidx < 0))
return RX_DROP_UNUSABLE;
} else {
if (rx->skb->len < 8 + hdrlen)
return RX_DROP_UNUSABLE; /* TODO: count this? */
/*
* no need to call ieee80211_wep_get_keyidx,
* it verifies a bunch of things we've done already
*/
skb_copy_bits(rx->skb, hdrlen + 3, &keyid, 1);
keyidx = keyid >> 6;
}
/* check per-station GTK first, if multicast packet */ /* check per-station GTK first, if multicast packet */
if (is_multicast_ether_addr(hdr->addr1) && rx->sta) if (is_multicast_ether_addr(hdr->addr1) && rx->sta)
@ -4042,12 +4050,8 @@ void ieee80211_check_fast_rx(struct sta_info *sta)
case WLAN_CIPHER_SUITE_GCMP_256: case WLAN_CIPHER_SUITE_GCMP_256:
break; break;
default: default:
/* we also don't want to deal with WEP or cipher scheme /* We also don't want to deal with
* since those require looking up the key idx in the * WEP or cipher scheme.
* frame, rather than assuming the PTK is used
* (we need to revisit this once we implement the real
* PTK index, which is now valid in the spec, but we
* haven't implemented that part yet)
*/ */
goto clear_rcu; goto clear_rcu;
} }

View File

@ -347,6 +347,15 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
sta->sta.max_rx_aggregation_subframes = sta->sta.max_rx_aggregation_subframes =
local->hw.max_rx_aggregation_subframes; local->hw.max_rx_aggregation_subframes;
/* Extended Key ID needs to install keys for keyid 0 and 1 Rx-only.
* The Tx path starts to use a key as soon as the key slot ptk_idx
* references to is not NULL. To not use the initial Rx-only key
* prematurely for Tx initialize ptk_idx to an impossible PTK keyid
* which always will refer to a NULL key.
*/
BUILD_BUG_ON(ARRAY_SIZE(sta->ptk) <= INVALID_PTK_KEYIDX);
sta->ptk_idx = INVALID_PTK_KEYIDX;
sta->local = local; sta->local = local;
sta->sdata = sdata; sta->sdata = sdata;
sta->rx_stats.last_rx = jiffies; sta->rx_stats.last_rx = jiffies;

View File

@ -3001,23 +3001,15 @@ void ieee80211_check_fast_xmit(struct sta_info *sta)
switch (build.key->conf.cipher) { switch (build.key->conf.cipher) {
case WLAN_CIPHER_SUITE_CCMP: case WLAN_CIPHER_SUITE_CCMP:
case WLAN_CIPHER_SUITE_CCMP_256: case WLAN_CIPHER_SUITE_CCMP_256:
/* add fixed key ID */ if (gen_iv)
if (gen_iv) {
(build.hdr + build.hdr_len)[3] =
0x20 | (build.key->conf.keyidx << 6);
build.pn_offs = build.hdr_len; build.pn_offs = build.hdr_len;
}
if (gen_iv || iv_spc) if (gen_iv || iv_spc)
build.hdr_len += IEEE80211_CCMP_HDR_LEN; build.hdr_len += IEEE80211_CCMP_HDR_LEN;
break; break;
case WLAN_CIPHER_SUITE_GCMP: case WLAN_CIPHER_SUITE_GCMP:
case WLAN_CIPHER_SUITE_GCMP_256: case WLAN_CIPHER_SUITE_GCMP_256:
/* add fixed key ID */ if (gen_iv)
if (gen_iv) {
(build.hdr + build.hdr_len)[3] =
0x20 | (build.key->conf.keyidx << 6);
build.pn_offs = build.hdr_len; build.pn_offs = build.hdr_len;
}
if (gen_iv || iv_spc) if (gen_iv || iv_spc)
build.hdr_len += IEEE80211_GCMP_HDR_LEN; build.hdr_len += IEEE80211_GCMP_HDR_LEN;
break; break;
@ -3388,6 +3380,7 @@ static void ieee80211_xmit_fast_finish(struct ieee80211_sub_if_data *sdata,
pn = atomic64_inc_return(&key->conf.tx_pn); pn = atomic64_inc_return(&key->conf.tx_pn);
crypto_hdr[0] = pn; crypto_hdr[0] = pn;
crypto_hdr[1] = pn >> 8; crypto_hdr[1] = pn >> 8;
crypto_hdr[3] = 0x20 | (key->conf.keyidx << 6);
crypto_hdr[4] = pn >> 16; crypto_hdr[4] = pn >> 16;
crypto_hdr[5] = pn >> 24; crypto_hdr[5] = pn >> 24;
crypto_hdr[6] = pn >> 32; crypto_hdr[6] = pn >> 32;