wifi: rtw89: wow: prepare PTK GTK info from mac80211
authorChih-Kang Chang <gary.chang@realtek.com>
Thu, 2 May 2024 02:24:57 +0000 (10:24 +0800)
committerPing-Ke Shih <pkshih@realtek.com>
Fri, 3 May 2024 23:59:55 +0000 (07:59 +0800)
Get the PTK and PTK TRX PN value and transfer to IV value, these
values will used by firmware to generate packets with correct IV value.

Signed-off-by: Chih-Kang Chang <gary.chang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://msgid.link/20240502022505.28966-5-pkshih@realtek.com
drivers/net/wireless/realtek/rtw89/core.h
drivers/net/wireless/realtek/rtw89/fw.c
drivers/net/wireless/realtek/rtw89/wow.c
drivers/net/wireless/realtek/rtw89/wow.h

index b8683a9075ad200d559a5c20e6b7821f3b3f099b..22146713d01f0483f8223278d05d012d66f5c415 100644 (file)
@@ -5205,11 +5205,25 @@ struct rtw89_wow_cam_info {
        bool valid;
 };
 
+struct rtw89_wow_key_info {
+       u8 ptk_tx_iv[8];
+       u8 valid_check;
+       u8 symbol_check_en;
+       u8 gtk_keyidx;
+       u8 rsvd[5];
+       u8 ptk_rx_iv[8];
+       u8 gtk_rx_iv[4][8];
+} __packed;
+
 struct rtw89_wow_param {
        struct ieee80211_vif *wow_vif;
        DECLARE_BITMAP(flags, RTW89_WOW_FLAG_NUM);
        struct rtw89_wow_cam_info patterns[RTW89_MAX_PATTERN_NUM];
+       struct rtw89_wow_key_info key_info;
        u8 pattern_cnt;
+       u8 ptk_alg;
+       u8 gtk_alg;
+       u8 ptk_keyidx;
        u8 akm;
 };
 
index d9ab781fa0e64d9b90ccde46ba3aeeb3c6e77e03..0bc7fcd5b27d50afb8d15d71c7240bcae6db212c 100644 (file)
@@ -6402,7 +6402,7 @@ int rtw89_fw_h2c_wow_wakeup_ctrl(struct rtw89_dev *rtwdev,
 
        skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_WAKEUP_CTRL_LEN);
        if (!skb) {
-               rtw89_err(rtwdev, "failed to alloc skb for keep alive\n");
+               rtw89_err(rtwdev, "failed to alloc skb for wakeup ctrl\n");
                return -ENOMEM;
        }
 
index dcae75128c7127aabffb96b0a5e0db8b822ddbf5..185e24626691c787d4011e230869c1905d2bd8c4 100644 (file)
@@ -27,6 +27,192 @@ void rtw89_wow_parse_akm(struct rtw89_dev *rtwdev, struct sk_buff *skb)
        rtw_wow->akm = rsn_ie->akm_cipher_suite.type;
 }
 
+static const struct rtw89_cipher_info rtw89_cipher_info_defs[] = {
+       {WLAN_CIPHER_SUITE_WEP40,       .fw_alg = 1,    .len = WLAN_KEY_LEN_WEP40,},
+       {WLAN_CIPHER_SUITE_WEP104,      .fw_alg = 2,    .len = WLAN_KEY_LEN_WEP104,},
+       {WLAN_CIPHER_SUITE_TKIP,        .fw_alg = 3,    .len = WLAN_KEY_LEN_TKIP,},
+       {WLAN_CIPHER_SUITE_CCMP,        .fw_alg = 6,    .len = WLAN_KEY_LEN_CCMP,},
+       {WLAN_CIPHER_SUITE_GCMP,        .fw_alg = 8,    .len = WLAN_KEY_LEN_GCMP,},
+       {WLAN_CIPHER_SUITE_CCMP_256,    .fw_alg = 7,    .len = WLAN_KEY_LEN_CCMP_256,},
+       {WLAN_CIPHER_SUITE_GCMP_256,    .fw_alg = 23,   .len = WLAN_KEY_LEN_GCMP_256,},
+};
+
+static const
+struct rtw89_cipher_info *rtw89_cipher_alg_recognize(u32 cipher)
+{
+       const struct rtw89_cipher_info *cipher_info_defs;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(rtw89_cipher_info_defs); i++) {
+               cipher_info_defs = &rtw89_cipher_info_defs[i];
+               if (cipher_info_defs->cipher == cipher)
+                       return cipher_info_defs;
+       }
+
+       return NULL;
+}
+
+static int _pn_to_iv(struct rtw89_dev *rtwdev, struct ieee80211_key_conf *key,
+                    u8 *iv, u64 pn, u8 key_idx)
+{
+       switch (key->cipher) {
+       case WLAN_CIPHER_SUITE_TKIP:
+               iv[0] = u64_get_bits(pn, RTW89_KEY_PN_1);
+               iv[1] = (u64_get_bits(pn, RTW89_KEY_PN_1) | 0x20) & 0x7f;
+               iv[2] = u64_get_bits(pn, RTW89_KEY_PN_0);
+               break;
+       case WLAN_CIPHER_SUITE_CCMP:
+       case WLAN_CIPHER_SUITE_GCMP:
+       case WLAN_CIPHER_SUITE_CCMP_256:
+       case WLAN_CIPHER_SUITE_GCMP_256:
+               iv[0] = u64_get_bits(pn, RTW89_KEY_PN_0);
+               iv[1] = u64_get_bits(pn, RTW89_KEY_PN_1);
+               iv[2] = 0;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       iv[3] = BIT(5) | ((key_idx & 0x3) << 6);
+       iv[4] = u64_get_bits(pn, RTW89_KEY_PN_2);
+       iv[5] = u64_get_bits(pn, RTW89_KEY_PN_3);
+       iv[6] = u64_get_bits(pn, RTW89_KEY_PN_4);
+       iv[7] = u64_get_bits(pn, RTW89_KEY_PN_5);
+
+       return 0;
+}
+
+static int rtw89_rx_pn_to_iv(struct rtw89_dev *rtwdev,
+                            struct ieee80211_key_conf *key,
+                            u8 *iv)
+{
+       struct ieee80211_key_seq seq;
+       int err;
+       u64 pn;
+
+       ieee80211_get_key_rx_seq(key, 0, &seq);
+
+       /* seq.ccmp.pn[] is BE order array */
+       pn = u64_encode_bits(seq.ccmp.pn[0], RTW89_KEY_PN_5) |
+            u64_encode_bits(seq.ccmp.pn[1], RTW89_KEY_PN_4) |
+            u64_encode_bits(seq.ccmp.pn[2], RTW89_KEY_PN_3) |
+            u64_encode_bits(seq.ccmp.pn[3], RTW89_KEY_PN_2) |
+            u64_encode_bits(seq.ccmp.pn[4], RTW89_KEY_PN_1) |
+            u64_encode_bits(seq.ccmp.pn[5], RTW89_KEY_PN_0);
+
+       err = _pn_to_iv(rtwdev, key, iv, pn, key->keyidx);
+       if (err)
+               return err;
+
+       rtw89_debug(rtwdev, RTW89_DBG_WOW, "%s key %d pn-%llx to iv-%*ph\n",
+                   __func__, key->keyidx, pn, 8, iv);
+
+       return 0;
+}
+
+static int rtw89_tx_pn_to_iv(struct rtw89_dev *rtwdev,
+                            struct ieee80211_key_conf *key,
+                            u8 *iv)
+{
+       int err;
+       u64 pn;
+
+       pn = atomic64_inc_return(&key->tx_pn);
+       err = _pn_to_iv(rtwdev, key, iv, pn, key->keyidx);
+       if (err)
+               return err;
+
+       rtw89_debug(rtwdev, RTW89_DBG_WOW, "%s key %d pn-%llx to iv-%*ph\n",
+                   __func__, key->keyidx, pn, 8, iv);
+
+       return 0;
+}
+
+static void rtw89_wow_get_key_info_iter(struct ieee80211_hw *hw,
+                                       struct ieee80211_vif *vif,
+                                       struct ieee80211_sta *sta,
+                                       struct ieee80211_key_conf *key,
+                                       void *data)
+{
+       struct rtw89_dev *rtwdev = hw->priv;
+       struct rtw89_wow_param *rtw_wow = &rtwdev->wow;
+       struct rtw89_wow_key_info *key_info = &rtw_wow->key_info;
+       const struct rtw89_cipher_info *cipher_info;
+       bool *err = data;
+       int ret;
+
+       cipher_info = rtw89_cipher_alg_recognize(key->cipher);
+
+       switch (key->cipher) {
+       case WLAN_CIPHER_SUITE_TKIP:
+       case WLAN_CIPHER_SUITE_CCMP:
+       case WLAN_CIPHER_SUITE_GCMP:
+       case WLAN_CIPHER_SUITE_CCMP_256:
+       case WLAN_CIPHER_SUITE_GCMP_256:
+               if (sta) {
+                       ret = rtw89_tx_pn_to_iv(rtwdev, key,
+                                               key_info->ptk_tx_iv);
+                       if (ret)
+                               goto err;
+                       ret = rtw89_rx_pn_to_iv(rtwdev, key,
+                                               key_info->ptk_rx_iv);
+                       if (ret)
+                               goto err;
+
+                       rtw_wow->ptk_alg = cipher_info->fw_alg;
+                       rtw_wow->ptk_keyidx = key->keyidx;
+               } else {
+                       ret = rtw89_rx_pn_to_iv(rtwdev, key,
+                                               key_info->gtk_rx_iv[key->keyidx]);
+                       if (ret)
+                               goto err;
+
+                       rtw_wow->gtk_alg = cipher_info->fw_alg;
+                       key_info->gtk_keyidx = key->keyidx;
+               }
+               break;
+       default:
+               rtw89_debug(rtwdev, RTW89_DBG_WOW, "unsupport cipher %x\n",
+                           key->cipher);
+               goto err;
+       }
+
+       return;
+err:
+       *err = true;
+}
+
+static void rtw89_wow_key_clear(struct rtw89_dev *rtwdev)
+{
+       struct rtw89_wow_param *rtw_wow = &rtwdev->wow;
+
+       memset(&rtw_wow->key_info, 0, sizeof(rtw_wow->key_info));
+       rtw_wow->ptk_alg = 0;
+       rtw_wow->gtk_alg = 0;
+}
+
+static void rtw89_wow_construct_key_info(struct rtw89_dev *rtwdev)
+{
+       struct rtw89_wow_param *rtw_wow = &rtwdev->wow;
+       struct rtw89_wow_key_info *key_info = &rtw_wow->key_info;
+       struct ieee80211_vif *wow_vif = rtwdev->wow.wow_vif;
+       bool err = false;
+
+       rcu_read_lock();
+       ieee80211_iter_keys_rcu(rtwdev->hw, wow_vif,
+                               rtw89_wow_get_key_info_iter, &err);
+       rcu_read_unlock();
+
+       if (err) {
+               rtw89_wow_key_clear(rtwdev);
+               return;
+       }
+
+       key_info->valid_check = RTW89_WOW_VALID_CHECK;
+       key_info->symbol_check_en = RTW89_WOW_SYMBOL_CHK_PTK |
+                                   RTW89_WOW_SYMBOL_CHK_GTK;
+}
+
 static void rtw89_wow_leave_deep_ps(struct rtw89_dev *rtwdev)
 {
        __rtw89_leave_ps_mode(rtwdev);
@@ -645,6 +831,7 @@ static int rtw89_wow_fw_start(struct rtw89_dev *rtwdev)
        int ret;
 
        rtw89_wow_pattern_write(rtwdev);
+       rtw89_wow_construct_key_info(rtwdev);
 
        ret = rtw89_fw_h2c_keep_alive(rtwdev, rtwvif, true);
        if (ret) {
index d9cfd2beda397832f9973669153ba1f9195fbaf1..165c75083721b1dba4efa340fef2fe47b84e5caa 100644 (file)
@@ -5,6 +5,17 @@
 #ifndef __RTW89_WOW_H__
 #define __RTW89_WOW_H__
 
+#define RTW89_KEY_PN_0 GENMASK_ULL(7, 0)
+#define RTW89_KEY_PN_1 GENMASK_ULL(15, 8)
+#define RTW89_KEY_PN_2 GENMASK_ULL(23, 16)
+#define RTW89_KEY_PN_3 GENMASK_ULL(31, 24)
+#define RTW89_KEY_PN_4 GENMASK_ULL(39, 32)
+#define RTW89_KEY_PN_5 GENMASK_ULL(47, 40)
+
+#define RTW89_WOW_VALID_CHECK 0xDD
+#define RTW89_WOW_SYMBOL_CHK_PTK BIT(0)
+#define RTW89_WOW_SYMBOL_CHK_GTK BIT(1)
+
 enum rtw89_wake_reason {
        RTW89_WOW_RSN_RX_PTK_REKEY = 0x1,
        RTW89_WOW_RSN_RX_GTK_REKEY = 0x2,
@@ -31,6 +42,12 @@ struct rtw89_rsn_ie {
        struct rtw89_cipher_suite akm_cipher_suite;
 } __packed;
 
+struct rtw89_cipher_info {
+       u32 cipher;
+       u8 fw_alg;
+       enum ieee80211_key_len len;
+};
+
 #ifdef CONFIG_PM
 int rtw89_wow_suspend(struct rtw89_dev *rtwdev, struct cfg80211_wowlan *wowlan);
 int rtw89_wow_resume(struct rtw89_dev *rtwdev);