wifi: mac80211: add support for SPP A-MSDUs
authorJohannes Berg <johannes.berg@intel.com>
Tue, 2 Jan 2024 19:35:40 +0000 (21:35 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Fri, 26 Jan 2024 08:39:49 +0000 (09:39 +0100)
If software crypto is used, simply add support for SPP A-MSDUs
(and use it whenever enabled as required by the cfg80211 API).

If hardware crypto is used, leave it up to the driver to set
the NL80211_EXT_FEATURE_SPP_AMSDU_SUPPORT flag and then check
sta->spp_amsdu or the IEEE80211_KEY_FLAG_SPP_AMSDU key flag.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Daniel Gabay <daniel.gabay@intel.com>
Reviewed-by: Gregory Greenman <gregory.greenman@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
Link: https://msgid.link/20240102213313.b8ada4514e2b.I1ac25d5f158165b5a88062a5a5e4c4fbeecf9a5d@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
include/net/mac80211.h
net/mac80211/cfg.c
net/mac80211/ieee80211_i.h
net/mac80211/key.c
net/mac80211/main.c
net/mac80211/mlme.c
net/mac80211/wpa.c

index 84cc66dd93c14c0ef194b0e0bd4d33fea38e2409..8d6ae22c09bfbed364af6bda6b8c0451c85f6260 100644 (file)
@@ -2081,6 +2081,8 @@ static inline bool lockdep_vif_wiphy_mutex_held(struct ieee80211_vif *vif)
  * @IEEE80211_KEY_FLAG_GENERATE_MMIE: This flag should be set by the driver
  *     for a AES_CMAC key to indicate that it requires sequence number
  *     generation only
+ * @IEEE80211_KEY_FLAG_SPP_AMSDU: SPP A-MSDUs can be used with this key
+ *     (set by mac80211 from the sta->spp_amsdu flag)
  */
 enum ieee80211_key_flags {
        IEEE80211_KEY_FLAG_GENERATE_IV_MGMT     = BIT(0),
@@ -2094,6 +2096,7 @@ enum ieee80211_key_flags {
        IEEE80211_KEY_FLAG_PUT_MIC_SPACE        = BIT(8),
        IEEE80211_KEY_FLAG_NO_AUTO_TX           = BIT(9),
        IEEE80211_KEY_FLAG_GENERATE_MMIE        = BIT(10),
+       IEEE80211_KEY_FLAG_SPP_AMSDU            = BIT(11),
 };
 
 /**
@@ -2392,6 +2395,7 @@ struct ieee80211_link_sta {
  *     would be assigned to link[link_id] where link_id is the id assigned
  *     by the AP.
  * @valid_links: bitmap of valid links, or 0 for non-MLO
+ * @spp_amsdu: indicates whether the STA uses SPP A-MSDU or not.
  */
 struct ieee80211_sta {
        u8 addr[ETH_ALEN];
@@ -2405,6 +2409,7 @@ struct ieee80211_sta {
        bool tdls_initiator;
        bool mfp;
        bool mlo;
+       bool spp_amsdu;
        u8 max_amsdu_subframes;
 
        struct ieee80211_sta_aggregates *cur;
index 1e8da6372da29ad4292d03a928a441e3e784231a..dd237081dbe336cbe135f90077d210ab0a5a800c 100644 (file)
@@ -1942,6 +1942,9 @@ static int sta_apply_parameters(struct ieee80211_local *local,
                        clear_sta_flag(sta, WLAN_STA_TDLS_PEER);
        }
 
+       if (mask & BIT(NL80211_STA_FLAG_SPP_AMSDU))
+               sta->sta.spp_amsdu = set & BIT(NL80211_STA_FLAG_SPP_AMSDU);
+
        /* mark TDLS channel switch support, if the AP allows it */
        if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) &&
            !sdata->deflink.u.mgd.tdls_chan_switch_prohibited &&
index 79a5067ce82be9c1fc5e2d784b4e4c85ce9244e5..eb32174984c3e5383d97a5aaf6e5e4d26b2e0862 100644 (file)
@@ -441,6 +441,7 @@ struct ieee80211_mgd_assoc_data {
        bool timeout_started;
        bool comeback; /* whether the AP has requested association comeback */
        bool s1g;
+       bool spp_amsdu;
 
        unsigned int assoc_link_id;
 
index af74d7f9d94d0404298b14b2fed608b57a70546d..a2cce62c97b70840d180ec1694f1a33553dd0dd8 100644 (file)
@@ -925,6 +925,10 @@ int ieee80211_key_link(struct ieee80211_key *key,
         */
        key->color = atomic_inc_return(&key_color);
 
+       /* keep this flag for easier access later */
+       if (sta && sta->sta.spp_amsdu)
+               key->conf.flags |= IEEE80211_KEY_FLAG_SPP_AMSDU;
+
        increment_tailroom_need_count(sdata);
 
        ret = ieee80211_key_replace(sdata, link, sta, pairwise, old_key, key);
index 13c417eda28106faaee7bc61a91e239abe924b21..d48fa1147c14cc55e60ce8b5bb11cb0177433144 100644 (file)
@@ -735,8 +735,11 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
                                      NL80211_EXT_FEATURE_SCAN_MIN_PREQ_CONTENT);
        }
 
-       if (!ops->set_key)
+       if (!ops->set_key) {
                wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
+               wiphy_ext_feature_set(wiphy,
+                                     NL80211_EXT_FEATURE_SPP_AMSDU_SUPPORT);
+       }
 
        wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_TXQS);
        wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_RRM);
index 5279af09fd5302bb03b97087721179a702bca827..fba596d8128063984d5954e8a1600aa27fdf1490 100644 (file)
@@ -5132,6 +5132,8 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
        if (WARN_ON(!sta))
                goto out_err;
 
+       sta->sta.spp_amsdu = assoc_data->spp_amsdu;
+
        if (ieee80211_vif_is_mld(&sdata->vif)) {
                for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) {
                        if (!assoc_data->link[link_id].bss)
@@ -8216,6 +8218,8 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
        assoc_data->wmm = bss->wmm_used &&
                          (local->hw.queues >= IEEE80211_NUM_ACS);
 
+       assoc_data->spp_amsdu = req->flags & ASSOC_REQ_SPP_AMSDU;
+
        /*
         * IEEE802.11n does not allow TKIP/WEP as pairwise ciphers in HT mode.
         * We still associate in non-HT mode (11a/b/g) if any one of these
index 94dae7cb6dbd326558c8fabd5aeb2805c508507f..e40529b8c5c9eaf6e133c7fcf4c6cc81584b9fbc 100644 (file)
@@ -315,7 +315,7 @@ ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx)
  * 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)
+static u8 ccmp_gcmp_aad(struct sk_buff *skb, u8 *aad, bool spp_amsdu)
 {
        struct ieee80211_hdr *hdr = (void *)skb->data;
        __le16 mask_fc;
@@ -340,7 +340,14 @@ static u8 ccmp_gcmp_aad(struct sk_buff *skb, u8 *aad)
                len_a += 6;
 
        if (ieee80211_is_data_qos(hdr->frame_control)) {
-               qos_tid = ieee80211_get_tid(hdr);
+               qos_tid = *ieee80211_get_qos_ctl(hdr);
+
+               if (spp_amsdu)
+                       qos_tid &= IEEE80211_QOS_CTL_TID_MASK |
+                                  IEEE80211_QOS_CTL_A_MSDU_PRESENT;
+               else
+                       qos_tid &= IEEE80211_QOS_CTL_TID_MASK;
+
                mask_fc &= ~cpu_to_le16(IEEE80211_FCTL_ORDER);
                len_a += 2;
        } else {
@@ -369,10 +376,11 @@ static u8 ccmp_gcmp_aad(struct sk_buff *skb, u8 *aad)
        return qos_tid;
 }
 
-static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *b_0, u8 *aad)
+static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *b_0, u8 *aad,
+                               bool spp_amsdu)
 {
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
-       u8 qos_tid = ccmp_gcmp_aad(skb, aad);
+       u8 qos_tid = ccmp_gcmp_aad(skb, aad, spp_amsdu);
 
        /* In CCM, the initial vectors (IV) used for CTR mode encryption and CBC
         * mode authentication are not allowed to collide, yet both are derived
@@ -479,7 +487,8 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb,
                return 0;
 
        pos += IEEE80211_CCMP_HDR_LEN;
-       ccmp_special_blocks(skb, pn, b_0, aad);
+       ccmp_special_blocks(skb, pn, b_0, aad,
+                           key->conf.flags & IEEE80211_KEY_FLAG_SPP_AMSDU);
        return ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, b_0, aad, pos, len,
                                         skb_put(skb, mic_len));
 }
@@ -557,7 +566,8 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx,
                        u8 aad[2 * AES_BLOCK_SIZE];
                        u8 b_0[AES_BLOCK_SIZE];
                        /* hardware didn't decrypt/verify MIC */
-                       ccmp_special_blocks(skb, pn, b_0, aad);
+                       ccmp_special_blocks(skb, pn, b_0, aad,
+                                           key->conf.flags & IEEE80211_KEY_FLAG_SPP_AMSDU);
 
                        if (ieee80211_aes_ccm_decrypt(
                                    key->u.ccmp.tfm, b_0, aad,
@@ -581,7 +591,8 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx,
        return RX_CONTINUE;
 }
 
-static void gcmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *j_0, u8 *aad)
+static void gcmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *j_0, u8 *aad,
+                               bool spp_amsdu)
 {
        struct ieee80211_hdr *hdr = (void *)skb->data;
 
@@ -591,7 +602,7 @@ static void gcmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *j_0, u8 *aad)
        j_0[14] = 0;
        j_0[AES_BLOCK_SIZE - 1] = 0x01;
 
-       ccmp_gcmp_aad(skb, aad);
+       ccmp_gcmp_aad(skb, aad, spp_amsdu);
 }
 
 static inline void gcmp_pn2hdr(u8 *hdr, const u8 *pn, int key_id)
@@ -680,7 +691,8 @@ static int gcmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
                return 0;
 
        pos += IEEE80211_GCMP_HDR_LEN;
-       gcmp_special_blocks(skb, pn, j_0, aad);
+       gcmp_special_blocks(skb, pn, j_0, aad,
+                           key->conf.flags & IEEE80211_KEY_FLAG_SPP_AMSDU);
        return ieee80211_aes_gcm_encrypt(key->u.gcmp.tfm, j_0, aad, pos, len,
                                         skb_put(skb, IEEE80211_GCMP_MIC_LEN));
 }
@@ -753,7 +765,8 @@ ieee80211_crypto_gcmp_decrypt(struct ieee80211_rx_data *rx)
                        u8 aad[2 * AES_BLOCK_SIZE];
                        u8 j_0[AES_BLOCK_SIZE];
                        /* hardware didn't decrypt/verify MIC */
-                       gcmp_special_blocks(skb, pn, j_0, aad);
+                       gcmp_special_blocks(skb, pn, j_0, aad,
+                                           key->conf.flags & IEEE80211_KEY_FLAG_SPP_AMSDU);
 
                        if (ieee80211_aes_gcm_decrypt(
                                    key->u.gcmp.tfm, j_0, aad,