return NULL;
}
+static int mac80211_hwsim_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key)
+{
+ switch (key->cipher) {
+ case WLAN_CIPHER_SUITE_CCMP:
+ case WLAN_CIPHER_SUITE_CCMP_256:
+ case WLAN_CIPHER_SUITE_GCMP:
+ case WLAN_CIPHER_SUITE_GCMP_256:
+ break;
+ default:
+ return 1;
+ }
+
+ key->flags |= IEEE80211_KEY_FLAG_RESERVE_TAILROOM;
+ return 0;
+}
+
static void mac80211_hwsim_tx(struct ieee80211_hw *hw,
struct ieee80211_tx_control *control,
struct sk_buff *skb)
struct ieee80211_chanctx_conf *chanctx_conf;
struct ieee80211_channel *channel;
struct ieee80211_vif *vif = txi->control.vif;
- bool ack;
+ bool ack, unicast_data;
enum nl80211_chan_width confbw = NL80211_CHAN_WIDTH_20_NOHT;
u32 _portid, i;
return;
}
+ unicast_data = is_unicast_ether_addr(hdr->addr1) &&
+ ieee80211_is_data(hdr->frame_control);
+
+ if (unicast_data && ieee80211_encrypt_tx_skb(skb) < 0) {
+ ieee80211_free_txskb(hw, skb);
+ return;
+ }
+ /* re-assign hdr since skb data may have shifted after encryption */
+ hdr = (void *)skb->data;
+
if (vif && vif->type == NL80211_IFTYPE_NAN && !data->tmp_chan) {
/* For NAN Device simulation purposes, assume that NAN is always
* on channel 6 or channel 149, unless a ROC is in progress (for
}
}
+ if (!unicast_data && ieee80211_encrypt_tx_skb(skb) < 0) {
+ ieee80211_free_txskb(hw, skb);
+ return;
+ }
+ /* re-assign hdr since skb data may have shifted after encryption */
+ hdr = (void *)skb->data;
+
if (WARN(!channel, "TX w/o channel - queue = %d\n", txi->hw_queue)) {
ieee80211_free_txskb(hw, skb);
return;
.stop_nan = mac80211_hwsim_stop_nan, \
.nan_change_conf = mac80211_hwsim_change_nan_config, \
.set_radar_background = mac80211_hwsim_set_radar_background, \
+ .set_key = mac80211_hwsim_set_key, \
HWSIM_DEBUGFS_OPS
#define HWSIM_NON_MLO_OPS \
WIPHY_FLAG_AP_UAPSD |
WIPHY_FLAG_SUPPORTS_5_10_MHZ |
WIPHY_FLAG_HAS_CHANNEL_SWITCH;
+ hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
hw->wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR |
NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE |
NL80211_FEATURE_STATIC_SMPS |
NL80211_EXT_FEATURE_SCAN_MIN_PREQ_CONTENT);
wiphy_ext_feature_set(hw->wiphy,
NL80211_EXT_FEATURE_BSS_COLOR);
+ wiphy_ext_feature_set(hw->wiphy,
+ NL80211_EXT_FEATURE_SPP_AMSDU_SUPPORT);
+ wiphy_ext_feature_set(hw->wiphy,
+ NL80211_EXT_FEATURE_CAN_REPLACE_PTK0);
+ wiphy_ext_feature_set(hw->wiphy,
+ NL80211_EXT_FEATURE_EXT_KEY_ID);
hw->wiphy->interface_modes = param->iftypes;
return 0;
}
+int ieee80211_encrypt_tx_skb(struct sk_buff *skb)
+{
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_sub_if_data *sdata;
+ struct sk_buff *check_skb;
+ struct ieee80211_tx_data tx;
+ ieee80211_tx_result res;
+
+ if (!info->control.hw_key)
+ return 0;
+
+ memset(&tx, 0, sizeof(tx));
+ tx.key = container_of(info->control.hw_key, struct ieee80211_key, conf);
+ /* NULL it out now so we do full SW crypto */
+ info->control.hw_key = NULL;
+ __skb_queue_head_init(&tx.skbs);
+ __skb_queue_tail(&tx.skbs, skb);
+
+ sdata = IEEE80211_DEV_TO_SUB_IF(skb->dev);
+ tx.sdata = sdata;
+ tx.local = sdata->local;
+ res = ieee80211_tx_h_encrypt(&tx);
+ check_skb = __skb_dequeue(&tx.skbs);
+ /* we may crash after this, but it'd be a bug in crypto */
+ WARN_ON(check_skb != skb);
+ if (WARN_ON_ONCE(res != TX_CONTINUE))
+ return -EINVAL;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ieee80211_encrypt_tx_skb);
+
static void
ieee80211_beacon_get_finish(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
* Calculate AAD for CCMP/GCMP, returning qos_tid since we
* need that in CCMP also for b_0.
*/
-static u8 ccmp_gcmp_aad(struct sk_buff *skb, u8 *aad, bool spp_amsdu)
+static u8 ccmp_gcmp_aad(struct sk_buff *skb, u8 *aad, bool spp_amsdu,
+ bool aad_nonce_computed)
{
struct ieee80211_hdr *hdr = (void *)skb->data;
__le16 mask_fc;
* FC | A1 | A2 | A3 | SC | [A4] | [QC] */
put_unaligned_be16(len_a, &aad[0]);
put_unaligned(mask_fc, (__le16 *)&aad[2]);
- memcpy(&aad[4], &hdr->addrs, 3 * ETH_ALEN);
+ if (!aad_nonce_computed)
+ memcpy(&aad[4], &hdr->addrs, 3 * ETH_ALEN);
/* Mask Seq#, leave Frag# */
aad[22] = *((u8 *) &hdr->seq_ctrl) & 0x0f;
}
static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *b_0, u8 *aad,
- bool spp_amsdu)
+ bool spp_amsdu, bool aad_nonce_computed)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
- u8 qos_tid = ccmp_gcmp_aad(skb, aad, spp_amsdu);
+ u8 qos_tid = ccmp_gcmp_aad(skb, aad, spp_amsdu, aad_nonce_computed);
/* In CCM, the initial vectors (IV) used for CTR mode encryption and CBC
* mode authentication are not allowed to collide, yet both are derived
* Nonce Flags: Priority (b0..b3) | Management (b4) | Reserved (b5..b7)
*/
b_0[1] = qos_tid | (ieee80211_is_mgmt(hdr->frame_control) << 4);
- memcpy(&b_0[2], hdr->addr2, ETH_ALEN);
+ if (!aad_nonce_computed)
+ memcpy(&b_0[2], hdr->addr2, ETH_ALEN);
memcpy(&b_0[8], pn, IEEE80211_CCMP_PN_LEN);
}
pos += IEEE80211_CCMP_HDR_LEN;
ccmp_special_blocks(skb, pn, b_0, aad,
- key->conf.flags & IEEE80211_KEY_FLAG_SPP_AMSDU);
+ key->conf.flags & IEEE80211_KEY_FLAG_SPP_AMSDU,
+ false);
return ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, b_0, aad, pos, len,
skb_put(skb, mic_len));
}
if (!(status->flag & RX_FLAG_DECRYPTED)) {
u8 aad[2 * AES_BLOCK_SIZE];
u8 b_0[AES_BLOCK_SIZE];
+ bool aad_nonce_computed = false;
+
+ if (is_unicast_ether_addr(hdr->addr1) &&
+ !ieee80211_is_data(hdr->frame_control)) {
+ /* AAD computation */
+ memcpy(&aad[4], rx->link_addrs, 3 * ETH_ALEN);
+ /* Nonce computation */
+ ether_addr_copy(&b_0[2],
+ &rx->link_addrs[ETH_ALEN]);
+ aad_nonce_computed = true;
+ }
+
/* hardware didn't decrypt/verify MIC */
ccmp_special_blocks(skb, pn, b_0, aad,
- key->conf.flags & IEEE80211_KEY_FLAG_SPP_AMSDU);
+ key->conf.flags & IEEE80211_KEY_FLAG_SPP_AMSDU,
+ aad_nonce_computed);
if (ieee80211_aes_ccm_decrypt(
key->u.ccmp.tfm, b_0, aad,
}
static void gcmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *j_0, u8 *aad,
- bool spp_amsdu)
+ bool spp_amsdu, bool aad_nonce_computed)
{
struct ieee80211_hdr *hdr = (void *)skb->data;
- memcpy(j_0, hdr->addr2, ETH_ALEN);
+ if (!aad_nonce_computed)
+ memcpy(j_0, hdr->addr2, ETH_ALEN);
memcpy(&j_0[ETH_ALEN], pn, IEEE80211_GCMP_PN_LEN);
- ccmp_gcmp_aad(skb, aad, spp_amsdu);
+ ccmp_gcmp_aad(skb, aad, spp_amsdu, aad_nonce_computed);
}
static inline void gcmp_pn2hdr(u8 *hdr, const u8 *pn, int key_id)
pos += IEEE80211_GCMP_HDR_LEN;
gcmp_special_blocks(skb, pn, j_0, aad,
- key->conf.flags & IEEE80211_KEY_FLAG_SPP_AMSDU);
+ key->conf.flags & IEEE80211_KEY_FLAG_SPP_AMSDU,
+ false);
return ieee80211_aes_gcm_encrypt(key->u.gcmp.tfm, j_0, aad, pos, len,
skb_put(skb, IEEE80211_GCMP_MIC_LEN));
}
if (!(status->flag & RX_FLAG_DECRYPTED)) {
u8 aad[2 * AES_BLOCK_SIZE];
u8 j_0[AES_BLOCK_SIZE];
+ bool aad_nonce_computed = false;
+
+ if (is_unicast_ether_addr(hdr->addr1) &&
+ !ieee80211_is_data(hdr->frame_control)) {
+ /* AAD computation */
+ memcpy(&aad[4], rx->link_addrs, 3 * ETH_ALEN);
+ /* Nonce computation */
+ ether_addr_copy(&j_0[0],
+ &rx->link_addrs[ETH_ALEN]);
+ aad_nonce_computed = true;
+ }
/* hardware didn't decrypt/verify MIC */
gcmp_special_blocks(skb, pn, j_0, aad,
- key->conf.flags & IEEE80211_KEY_FLAG_SPP_AMSDU);
+ key->conf.flags & IEEE80211_KEY_FLAG_SPP_AMSDU,
+ aad_nonce_computed);
if (ieee80211_aes_gcm_decrypt(
key->u.gcmp.tfm, j_0, aad,