From: Viktor Barna <viktor.barna@xxxxxxxxxx> (Part of the split. Please, take a look at the cover letter for more details). Signed-off-by: Viktor Barna <viktor.barna@xxxxxxxxxx> --- drivers/net/wireless/celeno/cl8k/key.c | 382 +++++++++++++++++++++++++ 1 file changed, 382 insertions(+) create mode 100644 drivers/net/wireless/celeno/cl8k/key.c diff --git a/drivers/net/wireless/celeno/cl8k/key.c b/drivers/net/wireless/celeno/cl8k/key.c new file mode 100644 index 000000000000..99821b86a795 --- /dev/null +++ b/drivers/net/wireless/celeno/cl8k/key.c @@ -0,0 +1,382 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause +/* Copyright(c) 2019-2022, Celeno Communications Ltd. */ + +#include "chip.h" +#include "phy.h" +#include "fw.h" +#include "sta.h" +#include "enhanced_tim.h" +#include "key.h" + +#define DECRYPT_CCMPSUCCESS_FLAGS (RX_FLAG_DECRYPTED | RX_FLAG_MIC_STRIPPED) + +void cl_vif_key_init(struct cl_vif *cl_vif) +{ + INIT_LIST_HEAD(&cl_vif->key_list_head); +} + +static struct cl_key_conf *cl_vif_key_find(struct cl_vif *cl_vif, + struct ieee80211_key_conf *key_conf_in) +{ + struct cl_key_conf *key, *tmp, *out = NULL; + + if (!key_conf_in) + return NULL; + + list_for_each_entry_safe(key, tmp, &cl_vif->key_list_head, list) { + struct ieee80211_key_conf *key_conf = key->key_conf; + + if (key_conf_in->keyidx != key_conf->keyidx) + continue; + + out = key; + break; + } + + return out; +} + +static struct ieee80211_key_conf *cl_vif_key_conf_default(struct cl_vif *cl_vif) +{ + struct cl_key_conf *key, *tmp; + struct ieee80211_key_conf *out = NULL; + + list_for_each_entry_safe(key, tmp, &cl_vif->key_list_head, list) { + if (key->key_conf->keyidx != cl_vif->key_idx_default) + continue; + + out = key->key_conf; + break; + } + + return out; +} + +static int cl_vif_key_add(struct cl_vif *cl_vif, struct ieee80211_key_conf *key_conf) +{ + struct cl_key_conf *key = NULL, *old_key = NULL; + + key = kzalloc(sizeof(*key), GFP_KERNEL); + if (!key) + return -ENOMEM; + + if (!list_empty(&cl_vif->key_list_head)) + old_key = list_first_entry(&cl_vif->key_list_head, struct cl_key_conf, list); + + cl_vif->key_idx_default = old_key ? old_key->key_conf->keyidx : key_conf->keyidx; + key->key_conf = key_conf; + list_add_tail(&key->list, &cl_vif->key_list_head); + + return 0; +} + +static void cl_vif_key_del(struct cl_vif *cl_vif, struct ieee80211_key_conf *key_conf_in) +{ + struct cl_key_conf *key, *tmp; + + if (!key_conf_in) + return; + + list_for_each_entry_safe(key, tmp, &cl_vif->key_list_head, list) { + struct ieee80211_key_conf *key_conf = key->key_conf; + + if (key_conf_in->keyidx != key_conf->keyidx) + continue; + + list_del(&key->list); + kfree(key); + } + + if (!list_empty(&cl_vif->key_list_head)) { + struct cl_key_conf *new_key = list_first_entry(&cl_vif->key_list_head, + struct cl_key_conf, list); + + cl_vif->key_idx_default = new_key->key_conf->keyidx; + } +} + +static int cl_vif_key_check_and_add(struct cl_vif *cl_vif, + struct ieee80211_key_conf *key_conf) +{ + struct cl_key_conf *key = cl_vif_key_find(cl_vif, key_conf); + + if (key) { + cl_dbg_warn(cl_vif->cl_hw, + "[%s] error: previous key found. delete old key and add new key\n", + __func__); + cl_vif_key_del(cl_vif, key->key_conf); + } + + return cl_vif_key_add(cl_vif, key_conf); +} + +static inline void cl_ccmp_hdr2pn(u8 *pn, u8 *hdr) +{ + pn[0] = hdr[7]; + pn[1] = hdr[6]; + pn[2] = hdr[5]; + pn[3] = hdr[4]; + pn[4] = hdr[1]; + pn[5] = hdr[0]; +} + +static int cl_key_validate_pn(struct cl_hw *cl_hw, struct cl_sta *cl_sta, struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + int hdrlen = 0, res = 0; + u8 pn[IEEE80211_CCMP_PN_LEN]; + u8 tid = 0; + + hdrlen = ieee80211_hdrlen(hdr->frame_control); + tid = ieee80211_get_tid(hdr); + + cl_ccmp_hdr2pn(pn, skb->data + hdrlen); + res = memcmp(pn, cl_sta->rx_pn[tid], IEEE80211_CCMP_PN_LEN); + if (res < 0) { + cl_hw->rx_info.pkt_drop_invalid_pn++; + return -1; + } + + memcpy(cl_sta->rx_pn[tid], pn, IEEE80211_CCMP_PN_LEN); + + return 0; +} + +void cl_vif_key_deinit(struct cl_vif *cl_vif) +{ + struct cl_key_conf *key, *tmp; + + list_for_each_entry_safe(key, tmp, &cl_vif->key_list_head, list) { + list_del(&key->list); + kfree(key); + } +} + +static int cl_cmd_set_key(struct cl_hw *cl_hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + int error = 0; + struct mm_key_add_cfm *key_add_cfm; + u8 cipher_suite = 0; + + /* Retrieve the cipher suite selector */ + switch (key->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + cipher_suite = MAC_CIPHER_SUITE_WEP40; + break; + case WLAN_CIPHER_SUITE_WEP104: + cipher_suite = MAC_CIPHER_SUITE_WEP104; + break; + case WLAN_CIPHER_SUITE_TKIP: + cipher_suite = MAC_CIPHER_SUITE_TKIP; + break; + case WLAN_CIPHER_SUITE_CCMP: + cipher_suite = MAC_CIPHER_SUITE_CCMP; + break; + case WLAN_CIPHER_SUITE_GCMP: + case WLAN_CIPHER_SUITE_GCMP_256: + cipher_suite = MAC_CIPHER_SUITE_GCMP; + break; + case WLAN_CIPHER_SUITE_AES_CMAC: + return -EOPNOTSUPP; + default: + return -EINVAL; + } + + error = cl_msg_tx_key_add(cl_hw, vif, sta, key, cipher_suite); + if (error) + return error; + + key_add_cfm = (struct mm_key_add_cfm *)(cl_hw->msg_cfm_params[MM_KEY_ADD_CFM]); + if (!key_add_cfm) + return -ENOMSG; + + if (key_add_cfm->status != 0) { + cl_dbg_verbose(cl_hw, "Status Error (%u)\n", key_add_cfm->status); + cl_msg_tx_free_cfm_params(cl_hw, MM_KEY_ADD_CFM); + return -EIO; + } + + /* Save the index retrieved from firmware */ + key->hw_key_idx = key_add_cfm->hw_key_idx; + + cl_msg_tx_free_cfm_params(cl_hw, MM_KEY_ADD_CFM); + + /* + * Now inform mac80211 about our choices regarding header fields generation: + * we let mac80211 take care of all generations + */ + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; + if (key->cipher == WLAN_CIPHER_SUITE_TKIP) + key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; + + if (sta) { + struct cl_sta *cl_sta = (struct cl_sta *)sta->drv_priv; + + cl_sta->key_conf = key; + } else { + error = cl_vif_key_check_and_add((struct cl_vif *)vif->drv_priv, key); + } + + return error; +} + +static int cl_cmd_disable_key(struct cl_hw *cl_hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + int ret = 0; + struct cl_sta *cl_sta = NULL; + struct cl_tx_queue *tx_queue = &cl_hw->tx_queues->single[HIGH_PRIORITY_QUEUE]; + + if (sta) { + cl_sta = (struct cl_sta *)sta->drv_priv; + + cl_sta->key_conf = NULL; + cl_sta->key_disable = true; + + /* + * Make sure there aren't any packets in firmware before deleting the key, + * otherwise they will be transmitted without encryption. + */ + cl_sta->stop_tx = true; + cl_single_cfm_clear_tim_bit_sta(cl_hw, cl_sta->sta_idx); + cl_agg_cfm_clear_tim_bit_sta(cl_hw, cl_sta); + cl_txq_flush_sta(cl_hw, cl_sta); + cl_single_cfm_poll_empty_sta(cl_hw, cl_sta->sta_idx); + cl_agg_cfm_poll_empty_sta(cl_hw, cl_sta); + + if (!list_empty(&tx_queue->hdrs)) { + spin_lock_bh(&cl_hw->tx_lock_single); + cl_enhanced_tim_set_tx_single(cl_hw, HIGH_PRIORITY_QUEUE, + tx_queue->hw_index, + false, tx_queue->cl_sta, + tx_queue->tid); + spin_unlock_bh(&cl_hw->tx_lock_single); + } + } else { + cl_vif_key_del((struct cl_vif *)vif->drv_priv, key); + } + + ret = cl_msg_tx_key_del(cl_hw, key->hw_key_idx); + + if (cl_sta) + cl_sta->stop_tx = false; + + return ret; +} + +int cl_key_set(struct cl_hw *cl_hw, + enum set_key_cmd cmd, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + int error = 0; + + switch (cmd) { + case SET_KEY: + error = cl_cmd_set_key(cl_hw, vif, sta, key); + break; + + case DISABLE_KEY: + error = cl_cmd_disable_key(cl_hw, vif, sta, key); + break; + + default: + error = -EINVAL; + break; + } + + return error; +} + +struct ieee80211_key_conf *cl_key_get(struct cl_sta *cl_sta) +{ + if (cl_sta->key_conf) + return cl_sta->key_conf; + + if (cl_sta->cl_vif) + return cl_vif_key_conf_default(cl_sta->cl_vif); + + return NULL; +} + +bool cl_key_is_cipher_ccmp_gcmp(struct ieee80211_key_conf *keyconf) +{ + u32 cipher; + + if (!keyconf) + return false; + + cipher = keyconf->cipher; + + return ((cipher == WLAN_CIPHER_SUITE_CCMP) || + (cipher == WLAN_CIPHER_SUITE_GCMP) || + (cipher == WLAN_CIPHER_SUITE_GCMP_256)); +} + +void cl_key_ccmp_gcmp_pn_to_hdr(u8 *hdr, u64 pn, int key_id) +{ + hdr[0] = pn; + hdr[1] = pn >> 8; + hdr[2] = 0; + hdr[3] = 0x20 | (key_id << 6); + hdr[4] = pn >> 16; + hdr[5] = pn >> 24; + hdr[6] = pn >> 32; + hdr[7] = pn >> 40; +} + +u8 cl_key_get_cipher_len(struct sk_buff *skb) +{ + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); + struct ieee80211_key_conf *key_conf = tx_info->control.hw_key; + + if (key_conf) { + switch (key_conf->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + return IEEE80211_WEP_IV_LEN; + case WLAN_CIPHER_SUITE_TKIP: + return IEEE80211_TKIP_IV_LEN; + case WLAN_CIPHER_SUITE_CCMP: + return IEEE80211_CCMP_HDR_LEN; + case WLAN_CIPHER_SUITE_CCMP_256: + return IEEE80211_CCMP_256_HDR_LEN; + case WLAN_CIPHER_SUITE_GCMP: + case WLAN_CIPHER_SUITE_GCMP_256: + return IEEE80211_GCMP_HDR_LEN; + } + } + + return 0; +} + +int cl_key_handle_pn_validation(struct cl_hw *cl_hw, struct sk_buff *skb, + struct cl_sta *cl_sta) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); + + if (!ieee80211_is_data(hdr->frame_control) || + ieee80211_is_frag(hdr)) + return CL_PN_VALID_STATE_NOT_NEEDED; + + if (!(status->flag & DECRYPT_CCMPSUCCESS_FLAGS)) + return CL_PN_VALID_STATE_NOT_NEEDED; + + if (!cl_sta) + return CL_PN_VALID_STATE_NOT_NEEDED; + + if (cl_key_validate_pn(cl_hw, cl_sta, skb)) + return CL_PN_VALID_STATE_FAILED; + + status = IEEE80211_SKB_RXCB(skb); + status->flag |= RX_FLAG_PN_VALIDATED; + + return CL_PN_VALID_STATE_SUCCESS; +} -- 2.36.1