]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
wifi: ath12k: support GTK rekey offload
authorBaochen Qiang <quic_bqiang@quicinc.com>
Wed, 19 Jun 2024 14:21:12 +0000 (17:21 +0300)
committerKalle Valo <quic_kvalo@quicinc.com>
Mon, 24 Jun 2024 16:31:47 +0000 (19:31 +0300)
Host sets GTK related info to firmware before WoW is enabled, and
gets rekey replay_count and then disables GTK rekey when WoW quits.

Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0-03427-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.15378.4

Signed-off-by: Baochen Qiang <quic_bqiang@quicinc.com>
Acked-by: Jeff Johnson <quic_jjohnson@quicinc.com>
Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com>
Link: https://patch.msgid.link/20240604055407.12506-8-quic_bqiang@quicinc.com
drivers/net/wireless/ath/ath12k/core.h
drivers/net/wireless/ath/ath12k/mac.c
drivers/net/wireless/ath/ath12k/wmi.c
drivers/net/wireless/ath/ath12k/wmi.h
drivers/net/wireless/ath/ath12k/wow.c

index 81c4bfe7792fc4a036f323acd1d5c32b549df0bb..9de3df924a1c8575f5da828a97aa5730746cbd7a 100644 (file)
@@ -230,6 +230,13 @@ struct ath12k_vif_cache {
        u32 bss_conf_changed;
 };
 
+struct ath12k_rekey_data {
+       u8 kck[NL80211_KCK_LEN];
+       u8 kek[NL80211_KCK_LEN];
+       u64 replay_ctr;
+       bool enable_offload;
+};
+
 struct ath12k_vif {
        u32 vdev_id;
        enum wmi_vdev_type vdev_type;
@@ -286,6 +293,7 @@ struct ath12k_vif {
        u32 punct_bitmap;
        bool ps;
        struct ath12k_vif_cache *cache;
+       struct ath12k_rekey_data rekey_data;
 };
 
 struct ath12k_vif_iter {
index 13186496728b81a091aab9bd83953cd09a0a3c21..261859ad967cb6f32aeae6d4af50f0ed82e5f5f2 100644 (file)
@@ -2842,6 +2842,7 @@ static void ath12k_bss_assoc(struct ath12k *ar,
        }
 
        arvif->is_up = true;
+       arvif->rekey_data.enable_offload = false;
 
        ath12k_dbg(ar->ab, ATH12K_DBG_MAC,
                   "mac vdev %d up (associated) bssid %pM aid %d\n",
@@ -2889,6 +2890,8 @@ static void ath12k_bss_disassoc(struct ath12k *ar,
 
        arvif->is_up = false;
 
+       memset(&arvif->rekey_data, 0, sizeof(arvif->rekey_data));
+
        cancel_delayed_work(&arvif->connection_loss_work);
 }
 
@@ -8545,6 +8548,40 @@ exit:
        return ret;
 }
 
+static void ath12k_mac_op_set_rekey_data(struct ieee80211_hw *hw,
+                                        struct ieee80211_vif *vif,
+                                        struct cfg80211_gtk_rekey_data *data)
+{
+       struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif);
+       struct ath12k_rekey_data *rekey_data = &arvif->rekey_data;
+       struct ath12k_hw *ah = ath12k_hw_to_ah(hw);
+       struct ath12k *ar = ath12k_ah_to_ar(ah, 0);
+
+       ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac set rekey data vdev %d\n",
+                  arvif->vdev_id);
+
+       mutex_lock(&ar->conf_mutex);
+
+       memcpy(rekey_data->kck, data->kck, NL80211_KCK_LEN);
+       memcpy(rekey_data->kek, data->kek, NL80211_KEK_LEN);
+
+       /* The supplicant works on big-endian, the firmware expects it on
+        * little endian.
+        */
+       rekey_data->replay_ctr = get_unaligned_be64(data->replay_ctr);
+
+       arvif->rekey_data.enable_offload = true;
+
+       ath12k_dbg_dump(ar->ab, ATH12K_DBG_MAC, "kck", NULL,
+                       rekey_data->kck, NL80211_KCK_LEN);
+       ath12k_dbg_dump(ar->ab, ATH12K_DBG_MAC, "kek", NULL,
+                       rekey_data->kck, NL80211_KEK_LEN);
+       ath12k_dbg_dump(ar->ab, ATH12K_DBG_MAC, "replay ctr", NULL,
+                       &rekey_data->replay_ctr, sizeof(rekey_data->replay_ctr));
+
+       mutex_unlock(&ar->conf_mutex);
+}
+
 static const struct ieee80211_ops ath12k_ops = {
        .tx                             = ath12k_mac_op_tx,
        .wake_tx_queue                  = ieee80211_handle_wake_tx_queue,
@@ -8560,6 +8597,7 @@ static const struct ieee80211_ops ath12k_ops = {
        .hw_scan                        = ath12k_mac_op_hw_scan,
        .cancel_hw_scan                 = ath12k_mac_op_cancel_hw_scan,
        .set_key                        = ath12k_mac_op_set_key,
+       .set_rekey_data                 = ath12k_mac_op_set_rekey_data,
        .sta_state                      = ath12k_mac_op_sta_state,
        .sta_set_txpwr                  = ath12k_mac_op_sta_set_txpwr,
        .sta_rc_update                  = ath12k_mac_op_sta_rc_update,
index 8b80d5b6bc0160b9f62eaf4120432b56111c188d..0e41386bca45532f1e962209ae5faadcf7b06100 100644 (file)
@@ -7091,6 +7091,56 @@ static void ath12k_wmi_event_wow_wakeup_host(struct ath12k_base *ab, struct sk_b
        complete(&ab->wow.wakeup_completed);
 }
 
+static void ath12k_wmi_gtk_offload_status_event(struct ath12k_base *ab,
+                                               struct sk_buff *skb)
+{
+       const struct wmi_gtk_offload_status_event *ev;
+       struct ath12k_vif *arvif;
+       __be64 replay_ctr_be;
+       u64 replay_ctr;
+       const void **tb;
+       int ret;
+
+       tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
+       if (IS_ERR(tb)) {
+               ret = PTR_ERR(tb);
+               ath12k_warn(ab, "failed to parse tlv: %d\n", ret);
+               return;
+       }
+
+       ev = tb[WMI_TAG_GTK_OFFLOAD_STATUS_EVENT];
+       if (!ev) {
+               ath12k_warn(ab, "failed to fetch gtk offload status ev");
+               kfree(tb);
+               return;
+       }
+
+       rcu_read_lock();
+       arvif = ath12k_mac_get_arvif_by_vdev_id(ab, le32_to_cpu(ev->vdev_id));
+       if (!arvif) {
+               rcu_read_unlock();
+               ath12k_warn(ab, "failed to get arvif for vdev_id:%d\n",
+                           le32_to_cpu(ev->vdev_id));
+               kfree(tb);
+               return;
+       }
+
+       replay_ctr = le64_to_cpu(ev->replay_ctr);
+       arvif->rekey_data.replay_ctr = replay_ctr;
+       ath12k_dbg(ab, ATH12K_DBG_WMI, "wmi gtk offload event refresh_cnt %d replay_ctr %llu\n",
+                  le32_to_cpu(ev->refresh_cnt), replay_ctr);
+
+       /* supplicant expects big-endian replay counter */
+       replay_ctr_be = cpu_to_be64(replay_ctr);
+
+       ieee80211_gtk_rekey_notify(arvif->vif, arvif->bssid,
+                                  (void *)&replay_ctr_be, GFP_ATOMIC);
+
+       rcu_read_unlock();
+
+       kfree(tb);
+}
+
 static void ath12k_wmi_op_rx(struct ath12k_base *ab, struct sk_buff *skb)
 {
        struct wmi_cmd_hdr *cmd_hdr;
@@ -7214,6 +7264,9 @@ static void ath12k_wmi_op_rx(struct ath12k_base *ab, struct sk_buff *skb)
        case WMI_WOW_WAKEUP_HOST_EVENTID:
                ath12k_wmi_event_wow_wakeup_host(ab, skb);
                break;
+       case WMI_GTK_OFFLOAD_STATUS_EVENTID:
+               ath12k_wmi_gtk_offload_status_event(ab, skb);
+               break;
        /* TODO: Add remaining events */
        default:
                ath12k_dbg(ab, ATH12K_DBG_WMI, "Unknown eventid: 0x%x\n", id);
@@ -7931,3 +7984,62 @@ int ath12k_wmi_arp_ns_offload(struct ath12k *ar,
 
        return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_SET_ARP_NS_OFFLOAD_CMDID);
 }
+
+int ath12k_wmi_gtk_rekey_offload(struct ath12k *ar,
+                                struct ath12k_vif *arvif, bool enable)
+{
+       struct ath12k_rekey_data *rekey_data = &arvif->rekey_data;
+       struct wmi_gtk_rekey_offload_cmd *cmd;
+       struct sk_buff *skb;
+       __le64 replay_ctr;
+       int len;
+
+       len = sizeof(*cmd);
+       skb =  ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len);
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_gtk_rekey_offload_cmd *)skb->data;
+       cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_GTK_OFFLOAD_CMD, sizeof(*cmd));
+       cmd->vdev_id = cpu_to_le32(arvif->vdev_id);
+
+       if (enable) {
+               cmd->flags = cpu_to_le32(GTK_OFFLOAD_ENABLE_OPCODE);
+
+               /* the length in rekey_data and cmd is equal */
+               memcpy(cmd->kck, rekey_data->kck, sizeof(cmd->kck));
+               memcpy(cmd->kek, rekey_data->kek, sizeof(cmd->kek));
+
+               replay_ctr = cpu_to_le64(rekey_data->replay_ctr);
+               memcpy(cmd->replay_ctr, &replay_ctr,
+                      sizeof(replay_ctr));
+       } else {
+               cmd->flags = cpu_to_le32(GTK_OFFLOAD_DISABLE_OPCODE);
+       }
+
+       ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "offload gtk rekey vdev: %d %d\n",
+                  arvif->vdev_id, enable);
+       return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_GTK_OFFLOAD_CMDID);
+}
+
+int ath12k_wmi_gtk_rekey_getinfo(struct ath12k *ar,
+                                struct ath12k_vif *arvif)
+{
+       struct wmi_gtk_rekey_offload_cmd *cmd;
+       struct sk_buff *skb;
+       int len;
+
+       len = sizeof(*cmd);
+       skb =  ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len);
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_gtk_rekey_offload_cmd *)skb->data;
+       cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_GTK_OFFLOAD_CMD, sizeof(*cmd));
+       cmd->vdev_id = cpu_to_le32(arvif->vdev_id);
+       cmd->flags = cpu_to_le32(GTK_OFFLOAD_REQUEST_STATUS_OPCODE);
+
+       ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "get gtk rekey vdev_id: %d\n",
+                  arvif->vdev_id);
+       return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_GTK_OFFLOAD_CMDID);
+}
index ac0517f46b726f253d8f86420dfe433890ee1ab9..32f74993b12973fe7b8abab2d8b24a307329f5eb 100644 (file)
@@ -5374,6 +5374,41 @@ struct wmi_set_arp_ns_offload_cmd {
         */
 } __packed;
 
+#define GTK_OFFLOAD_OPCODE_MASK             0xFF000000
+#define GTK_OFFLOAD_ENABLE_OPCODE           0x01000000
+#define GTK_OFFLOAD_DISABLE_OPCODE          0x02000000
+#define GTK_OFFLOAD_REQUEST_STATUS_OPCODE   0x04000000
+
+#define GTK_OFFLOAD_KEK_BYTES       16
+#define GTK_OFFLOAD_KCK_BYTES       16
+#define GTK_REPLAY_COUNTER_BYTES    8
+#define WMI_MAX_KEY_LEN             32
+#define IGTK_PN_SIZE                6
+
+struct wmi_gtk_offload_status_event {
+       __le32 vdev_id;
+       __le32 flags;
+       __le32 refresh_cnt;
+       __le64 replay_ctr;
+       u8 igtk_key_index;
+       u8 igtk_key_length;
+       u8 igtk_key_rsc[IGTK_PN_SIZE];
+       u8 igtk_key[WMI_MAX_KEY_LEN];
+       u8 gtk_key_index;
+       u8 gtk_key_length;
+       u8 gtk_key_rsc[GTK_REPLAY_COUNTER_BYTES];
+       u8 gtk_key[WMI_MAX_KEY_LEN];
+} __packed;
+
+struct wmi_gtk_rekey_offload_cmd {
+       __le32 tlv_header;
+       __le32 vdev_id;
+       __le32 flags;
+       u8 kek[GTK_OFFLOAD_KEK_BYTES];
+       u8 kck[GTK_OFFLOAD_KCK_BYTES];
+       u8 replay_ctr[GTK_REPLAY_COUNTER_BYTES];
+} __packed;
+
 void ath12k_wmi_init_qcn9274(struct ath12k_base *ab,
                             struct ath12k_wmi_resource_config_arg *config);
 void ath12k_wmi_init_wcn7850(struct ath12k_base *ab,
@@ -5543,5 +5578,9 @@ int ath12k_wmi_arp_ns_offload(struct ath12k *ar,
                              struct ath12k_vif *arvif,
                              struct wmi_arp_ns_offload_arg *offload,
                              bool enable);
+int ath12k_wmi_gtk_rekey_offload(struct ath12k *ar,
+                                struct ath12k_vif *arvif, bool enable);
+int ath12k_wmi_gtk_rekey_getinfo(struct ath12k *ar,
+                                struct ath12k_vif *arvif);
 
 #endif
index 8dfa7853e7a4e21db7b78911929f3db4d039a25b..5a7001edafe0149f985ac7a58b2f77ad59c7af44 100644 (file)
@@ -21,7 +21,9 @@
 
 static const struct wiphy_wowlan_support ath12k_wowlan_support = {
        .flags = WIPHY_WOWLAN_DISCONNECT |
-                WIPHY_WOWLAN_MAGIC_PKT,
+                WIPHY_WOWLAN_MAGIC_PKT |
+                WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
+                WIPHY_WOWLAN_GTK_REKEY_FAILURE,
        .pattern_min_len = WOW_MIN_PATTERN_SIZE,
        .pattern_max_len = WOW_MAX_PATTERN_SIZE,
        .max_pkt_offset = WOW_MAX_PKT_OFFSET,
@@ -762,6 +764,41 @@ static int ath12k_wow_arp_ns_offload(struct ath12k *ar, bool enable)
        return 0;
 }
 
+static int ath12k_gtk_rekey_offload(struct ath12k *ar, bool enable)
+{
+       struct ath12k_vif *arvif;
+       int ret;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       list_for_each_entry(arvif, &ar->arvifs, list) {
+               if (arvif->vdev_type != WMI_VDEV_TYPE_STA ||
+                   !arvif->is_up ||
+                   !arvif->rekey_data.enable_offload)
+                       continue;
+
+               /* get rekey info before disable rekey offload */
+               if (!enable) {
+                       ret = ath12k_wmi_gtk_rekey_getinfo(ar, arvif);
+                       if (ret) {
+                               ath12k_warn(ar->ab, "failed to request rekey info vdev %i, ret %d\n",
+                                           arvif->vdev_id, ret);
+                               return ret;
+                       }
+               }
+
+               ret = ath12k_wmi_gtk_rekey_offload(ar, arvif, enable);
+
+               if (ret) {
+                       ath12k_warn(ar->ab, "failed to offload gtk reky vdev %i: enable %d, ret %d\n",
+                                   arvif->vdev_id, enable, ret);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
 static int ath12k_wow_protocol_offload(struct ath12k *ar, bool enable)
 {
        int ret;
@@ -773,6 +810,13 @@ static int ath12k_wow_protocol_offload(struct ath12k *ar, bool enable)
                return ret;
        }
 
+       ret = ath12k_gtk_rekey_offload(ar, enable);
+       if (ret) {
+               ath12k_warn(ar->ab, "failed to offload gtk rekey %d %d\n",
+                           enable, ret);
+               return ret;
+       }
+
        return 0;
 }