mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-08 00:29:36 -04:00
wifi: ath12k: support GTK rekey offload
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
This commit is contained in:
committed by
Kalle Valo
parent
1666108c74
commit
aab4ae566f
@@ -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 {
|
||||
|
||||
@@ -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 @@ static int ath12k_mac_op_remain_on_channel(struct ieee80211_hw *hw,
|
||||
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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user