Commit 1441b2f2 authored by Manikanta Pubbisetty's avatar Manikanta Pubbisetty Committed by Kalle Valo

ath11k: enable PN offload

Enabling PN checking in the hardware; hardware checks
the PN of the received packets and reports the errors
to the host for further handling; default action is to
drop such packets. TSC (TKIP sequence counter)
validation is also offloaded.

Hardware validates PN/TSC only for unicast packets;
for group addressed packets, PN validation is done
in mac80211.

PN errors are reported to the driver via WBM RX release
ring and can be dumped by using the following command.

"cat /sys/kernel/debug/ath11k/soc_rx_stats | grep -i pn"

Sample Output:
PN check fail: 210
Signed-off-by: default avatarManikanta Pubbisetty <mpubbise@codeaurora.org>
Signed-off-by: default avatarKalle Valo <kvalo@codeaurora.org>
parent 243874c6
...@@ -333,6 +333,7 @@ struct ath11k_sta { ...@@ -333,6 +333,7 @@ struct ath11k_sta {
u32 bw; u32 bw;
u32 nss; u32 nss;
u32 smps; u32 smps;
enum hal_pn_type pn_type;
struct work_struct update_wk; struct work_struct update_wk;
struct rate_info txrate; struct rate_info txrate;
......
...@@ -58,8 +58,8 @@ int ath11k_dp_peer_setup(struct ath11k *ar, int vdev_id, const u8 *addr) ...@@ -58,8 +58,8 @@ int ath11k_dp_peer_setup(struct ath11k *ar, int vdev_id, const u8 *addr)
} }
for (tid = 0; tid <= IEEE80211_NUM_TIDS; tid++) { for (tid = 0; tid <= IEEE80211_NUM_TIDS; tid++) {
ret = ath11k_peer_rx_tid_setup(ar, addr, vdev_id, ret = ath11k_peer_rx_tid_setup(ar, addr, vdev_id, tid, 1, 0,
tid, 1, 0); HAL_PN_TYPE_NONE);
if (ret) { if (ret) {
ath11k_warn(ab, "failed to setup rxd tid queue for tid %d: %d\n", ath11k_warn(ab, "failed to setup rxd tid queue for tid %d: %d\n",
tid, ret); tid, ret);
......
...@@ -843,7 +843,8 @@ static void ath11k_dp_rx_tid_mem_free(struct ath11k_base *ab, ...@@ -843,7 +843,8 @@ static void ath11k_dp_rx_tid_mem_free(struct ath11k_base *ab,
} }
int ath11k_peer_rx_tid_setup(struct ath11k *ar, const u8 *peer_mac, int vdev_id, int ath11k_peer_rx_tid_setup(struct ath11k *ar, const u8 *peer_mac, int vdev_id,
u8 tid, u32 ba_win_sz, u16 ssn) u8 tid, u32 ba_win_sz, u16 ssn,
enum hal_pn_type pn_type)
{ {
struct ath11k_base *ab = ar->ab; struct ath11k_base *ab = ar->ab;
struct ath11k_peer *peer; struct ath11k_peer *peer;
...@@ -904,7 +905,8 @@ int ath11k_peer_rx_tid_setup(struct ath11k *ar, const u8 *peer_mac, int vdev_id, ...@@ -904,7 +905,8 @@ int ath11k_peer_rx_tid_setup(struct ath11k *ar, const u8 *peer_mac, int vdev_id,
addr_aligned = PTR_ALIGN(vaddr, HAL_LINK_DESC_ALIGN); addr_aligned = PTR_ALIGN(vaddr, HAL_LINK_DESC_ALIGN);
ath11k_hal_reo_qdesc_setup(addr_aligned, tid, ba_win_sz, ssn); ath11k_hal_reo_qdesc_setup(addr_aligned, tid, ba_win_sz,
ssn, pn_type);
paddr = dma_map_single(ab->dev, addr_aligned, hw_desc_sz, paddr = dma_map_single(ab->dev, addr_aligned, hw_desc_sz,
DMA_BIDIRECTIONAL); DMA_BIDIRECTIONAL);
...@@ -948,7 +950,7 @@ int ath11k_dp_rx_ampdu_start(struct ath11k *ar, ...@@ -948,7 +950,7 @@ int ath11k_dp_rx_ampdu_start(struct ath11k *ar,
ret = ath11k_peer_rx_tid_setup(ar, params->sta->addr, vdev_id, ret = ath11k_peer_rx_tid_setup(ar, params->sta->addr, vdev_id,
params->tid, params->buf_size, params->tid, params->buf_size,
params->ssn); params->ssn, arsta->pn_type);
if (ret) if (ret)
ath11k_warn(ab, "failed to setup rx tid %d\n", ret); ath11k_warn(ab, "failed to setup rx tid %d\n", ret);
...@@ -1001,8 +1003,80 @@ int ath11k_dp_rx_ampdu_stop(struct ath11k *ar, ...@@ -1001,8 +1003,80 @@ int ath11k_dp_rx_ampdu_stop(struct ath11k *ar,
return ret; return ret;
} }
static int ath11k_get_ppdu_user_index(struct htt_ppdu_stats *ppdu_stats, int ath11k_dp_peer_rx_pn_replay_config(struct ath11k_vif *arvif,
u16 peer_id) const u8 *peer_addr,
enum set_key_cmd key_cmd,
struct ieee80211_key_conf *key)
{
struct ath11k *ar = arvif->ar;
struct ath11k_base *ab = ar->ab;
struct ath11k_hal_reo_cmd cmd = {0};
struct ath11k_peer *peer;
struct dp_rx_tid *rx_tid;
u8 tid;
int ret = 0;
/* NOTE: Enable PN/TSC replay check offload only for unicast frames.
* We use mac80211 PN/TSC replay check functionality for bcast/mcast
* for now.
*/
if (!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE))
return 0;
cmd.flag |= HAL_REO_CMD_FLG_NEED_STATUS;
cmd.upd0 |= HAL_REO_CMD_UPD0_PN |
HAL_REO_CMD_UPD0_PN_SIZE |
HAL_REO_CMD_UPD0_PN_VALID |
HAL_REO_CMD_UPD0_PN_CHECK |
HAL_REO_CMD_UPD0_SVLD;
switch (key->cipher) {
case WLAN_CIPHER_SUITE_TKIP:
case WLAN_CIPHER_SUITE_CCMP:
case WLAN_CIPHER_SUITE_CCMP_256:
case WLAN_CIPHER_SUITE_GCMP:
case WLAN_CIPHER_SUITE_GCMP_256:
if (key_cmd == SET_KEY) {
cmd.upd1 |= HAL_REO_CMD_UPD1_PN_CHECK;
cmd.pn_size = 48;
}
break;
default:
break;
}
spin_lock_bh(&ab->base_lock);
peer = ath11k_peer_find(ab, arvif->vdev_id, peer_addr);
if (!peer) {
ath11k_warn(ab, "failed to find the peer to configure pn replay detection\n");
spin_unlock_bh(&ab->base_lock);
return -ENOENT;
}
for (tid = 0; tid <= IEEE80211_NUM_TIDS; tid++) {
rx_tid = &peer->rx_tid[tid];
if (!rx_tid->active)
continue;
cmd.addr_lo = lower_32_bits(rx_tid->paddr);
cmd.addr_hi = upper_32_bits(rx_tid->paddr);
ret = ath11k_dp_tx_send_reo_cmd(ab, rx_tid,
HAL_REO_CMD_UPDATE_RX_QUEUE,
&cmd, NULL);
if (ret) {
ath11k_warn(ab, "failed to configure rx tid %d queue for pn replay detection %d\n",
tid, ret);
break;
}
}
spin_unlock_bh(&ar->ab->base_lock);
return ret;
}
static inline int ath11k_get_ppdu_user_index(struct htt_ppdu_stats *ppdu_stats,
u16 peer_id)
{ {
int i; int i;
...@@ -1974,7 +2048,7 @@ static void ath11k_dp_rx_h_mpdu(struct ath11k *ar, ...@@ -1974,7 +2048,7 @@ static void ath11k_dp_rx_h_mpdu(struct ath11k *ar,
struct sk_buff *last_msdu; struct sk_buff *last_msdu;
struct sk_buff *msdu; struct sk_buff *msdu;
struct ath11k_skb_rxcb *last_rxcb; struct ath11k_skb_rxcb *last_rxcb;
bool is_decrypted; bool is_decrypted = false, fill_crypto_hdr;
u32 err_bitmap; u32 err_bitmap;
u8 *qos; u8 *qos;
...@@ -1991,6 +2065,9 @@ static void ath11k_dp_rx_h_mpdu(struct ath11k *ar, ...@@ -1991,6 +2065,9 @@ static void ath11k_dp_rx_h_mpdu(struct ath11k *ar,
qos[0] &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT; qos[0] &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT;
} }
/* PN for multicast packets will be checked in mac80211 */
fill_crypto_hdr = is_multicast_ether_addr(hdr->addr1);
is_decrypted = ath11k_dp_rx_h_attn_is_decrypted(rx_desc); is_decrypted = ath11k_dp_rx_h_attn_is_decrypted(rx_desc);
enctype = ath11k_dp_rx_h_mpdu_start_enctype(rx_desc); enctype = ath11k_dp_rx_h_mpdu_start_enctype(rx_desc);
...@@ -2013,14 +2090,27 @@ static void ath11k_dp_rx_h_mpdu(struct ath11k *ar, ...@@ -2013,14 +2090,27 @@ static void ath11k_dp_rx_h_mpdu(struct ath11k *ar,
if (err_bitmap & DP_RX_MPDU_ERR_TKIP_MIC) if (err_bitmap & DP_RX_MPDU_ERR_TKIP_MIC)
rx_status->flag |= RX_FLAG_MMIC_ERROR; rx_status->flag |= RX_FLAG_MMIC_ERROR;
if (is_decrypted) if (is_decrypted) {
rx_status->flag |= RX_FLAG_DECRYPTED | RX_FLAG_MMIC_STRIPPED | rx_status->flag |= RX_FLAG_DECRYPTED | RX_FLAG_MMIC_STRIPPED;
RX_FLAG_MIC_STRIPPED | RX_FLAG_ICV_STRIPPED;
if (fill_crypto_hdr)
rx_status->flag |= RX_FLAG_MIC_STRIPPED |
RX_FLAG_ICV_STRIPPED;
else
rx_status->flag |= RX_FLAG_IV_STRIPPED |
RX_FLAG_PN_VALIDATED;
}
skb_queue_walk(amsdu_list, msdu) { skb_queue_walk(amsdu_list, msdu) {
ath11k_dp_rx_h_csum_offload(msdu); ath11k_dp_rx_h_csum_offload(msdu);
ath11k_dp_rx_h_undecap(ar, msdu, rx_desc, ath11k_dp_rx_h_undecap(ar, msdu, rx_desc,
enctype, rx_status, is_decrypted); enctype, rx_status, is_decrypted);
if (!is_decrypted || fill_crypto_hdr)
continue;
hdr = (void *)msdu->data;
hdr->frame_control &= ~__cpu_to_le16(IEEE80211_FCTL_PROTECTED);
} }
} }
...@@ -3603,6 +3693,13 @@ static bool ath11k_dp_rx_h_reo_err(struct ath11k *ar, struct sk_buff *msdu, ...@@ -3603,6 +3693,13 @@ static bool ath11k_dp_rx_h_reo_err(struct ath11k *ar, struct sk_buff *msdu,
if (ath11k_dp_rx_h_null_q_desc(ar, msdu, status, msdu_list)) if (ath11k_dp_rx_h_null_q_desc(ar, msdu, status, msdu_list))
drop = true; drop = true;
break; break;
case HAL_REO_DEST_RING_ERROR_CODE_PN_CHECK_FAILED:
/* TODO: Do not drop PN failed packets in the driver;
* instead, it is good to drop such packets in mac80211
* after incrementing the replay counters.
*/
/* fall through */
default: default:
/* TODO: Review other errors and process them to mac80211 /* TODO: Review other errors and process them to mac80211
* as appropriate. * as appropriate.
......
...@@ -43,11 +43,16 @@ int ath11k_dp_rx_ampdu_start(struct ath11k *ar, ...@@ -43,11 +43,16 @@ int ath11k_dp_rx_ampdu_start(struct ath11k *ar,
struct ieee80211_ampdu_params *params); struct ieee80211_ampdu_params *params);
int ath11k_dp_rx_ampdu_stop(struct ath11k *ar, int ath11k_dp_rx_ampdu_stop(struct ath11k *ar,
struct ieee80211_ampdu_params *params); struct ieee80211_ampdu_params *params);
int ath11k_dp_peer_rx_pn_replay_config(struct ath11k_vif *arvif,
const u8 *peer_addr,
enum set_key_cmd key_cmd,
struct ieee80211_key_conf *key);
void ath11k_peer_rx_tid_cleanup(struct ath11k *ar, struct ath11k_peer *peer); void ath11k_peer_rx_tid_cleanup(struct ath11k *ar, struct ath11k_peer *peer);
void ath11k_peer_rx_tid_delete(struct ath11k *ar, void ath11k_peer_rx_tid_delete(struct ath11k *ar,
struct ath11k_peer *peer, u8 tid); struct ath11k_peer *peer, u8 tid);
int ath11k_peer_rx_tid_setup(struct ath11k *ar, const u8 *peer_mac, int vdev_id, int ath11k_peer_rx_tid_setup(struct ath11k *ar, const u8 *peer_mac, int vdev_id,
u8 tid, u32 ba_win_sz, u16 ssn); u8 tid, u32 ba_win_sz, u16 ssn,
enum hal_pn_type pn_type);
void ath11k_dp_htt_htc_t2h_msg_handler(struct ath11k_base *ab, void ath11k_dp_htt_htc_t2h_msg_handler(struct ath11k_base *ab,
struct sk_buff *skb); struct sk_buff *skb);
int ath11k_dp_pdev_reo_setup(struct ath11k_base *ab); int ath11k_dp_pdev_reo_setup(struct ath11k_base *ab);
......
...@@ -847,7 +847,7 @@ struct ath11k_hal { ...@@ -847,7 +847,7 @@ struct ath11k_hal {
u32 ath11k_hal_reo_qdesc_size(u32 ba_window_size, u8 tid); u32 ath11k_hal_reo_qdesc_size(u32 ba_window_size, u8 tid);
void ath11k_hal_reo_qdesc_setup(void *vaddr, int tid, u32 ba_window_size, void ath11k_hal_reo_qdesc_setup(void *vaddr, int tid, u32 ba_window_size,
u32 start_seqtype); u32 start_seq, enum hal_pn_type type);
void ath11k_hal_reo_init_cmd_ring(struct ath11k_base *ab, void ath11k_hal_reo_init_cmd_ring(struct ath11k_base *ab,
struct hal_srng *srng); struct hal_srng *srng);
void ath11k_hal_reo_hw_setup(struct ath11k_base *ab); void ath11k_hal_reo_hw_setup(struct ath11k_base *ab);
......
...@@ -694,7 +694,7 @@ u32 ath11k_hal_reo_qdesc_size(u32 ba_window_size, u8 tid) ...@@ -694,7 +694,7 @@ u32 ath11k_hal_reo_qdesc_size(u32 ba_window_size, u8 tid)
} }
void ath11k_hal_reo_qdesc_setup(void *vaddr, int tid, u32 ba_window_size, void ath11k_hal_reo_qdesc_setup(void *vaddr, int tid, u32 ba_window_size,
u32 start_seq) u32 start_seq, enum hal_pn_type type)
{ {
struct hal_rx_reo_queue *qdesc = (struct hal_rx_reo_queue *)vaddr; struct hal_rx_reo_queue *qdesc = (struct hal_rx_reo_queue *)vaddr;
struct hal_rx_reo_queue_ext *ext_desc; struct hal_rx_reo_queue_ext *ext_desc;
...@@ -723,6 +723,18 @@ void ath11k_hal_reo_qdesc_setup(void *vaddr, int tid, u32 ba_window_size, ...@@ -723,6 +723,18 @@ void ath11k_hal_reo_qdesc_setup(void *vaddr, int tid, u32 ba_window_size,
qdesc->info0 |= FIELD_PREP(HAL_RX_REO_QUEUE_INFO0_BA_WINDOW_SIZE, qdesc->info0 |= FIELD_PREP(HAL_RX_REO_QUEUE_INFO0_BA_WINDOW_SIZE,
ba_window_size - 1); ba_window_size - 1);
switch (type) {
case HAL_PN_TYPE_NONE:
case HAL_PN_TYPE_WAPI_EVEN:
case HAL_PN_TYPE_WAPI_UNEVEN:
break;
case HAL_PN_TYPE_WPA:
qdesc->info0 |=
FIELD_PREP(HAL_RX_REO_QUEUE_INFO0_PN_CHECK, 1) |
FIELD_PREP(HAL_RX_REO_QUEUE_INFO0_PN_SIZE,
HAL_RX_REO_QUEUE_PN_SIZE_48);
break;
}
/* TODO: Set Ignore ampdu flags based on BA window size and/or /* TODO: Set Ignore ampdu flags based on BA window size and/or
* AMPDU capabilities * AMPDU capabilities
......
...@@ -2325,6 +2325,7 @@ static int ath11k_mac_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, ...@@ -2325,6 +2325,7 @@ static int ath11k_mac_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
struct ath11k_base *ab = ar->ab; struct ath11k_base *ab = ar->ab;
struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif);
struct ath11k_peer *peer; struct ath11k_peer *peer;
struct ath11k_sta *arsta;
const u8 *peer_addr; const u8 *peer_addr;
int ret = 0; int ret = 0;
u32 flags = 0; u32 flags = 0;
...@@ -2382,6 +2383,12 @@ static int ath11k_mac_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, ...@@ -2382,6 +2383,12 @@ static int ath11k_mac_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
goto exit; goto exit;
} }
ret = ath11k_dp_peer_rx_pn_replay_config(arvif, peer_addr, cmd, key);
if (ret) {
ath11k_warn(ab, "failed to offload PN replay detection %d\n", ret);
goto exit;
}
spin_lock_bh(&ab->base_lock); spin_lock_bh(&ab->base_lock);
peer = ath11k_peer_find(ab, arvif->vdev_id, peer_addr); peer = ath11k_peer_find(ab, arvif->vdev_id, peer_addr);
if (peer && cmd == SET_KEY) { if (peer && cmd == SET_KEY) {
...@@ -2399,6 +2406,26 @@ static int ath11k_mac_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, ...@@ -2399,6 +2406,26 @@ static int ath11k_mac_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
} else if (!peer) } else if (!peer)
/* impossible unless FW goes crazy */ /* impossible unless FW goes crazy */
ath11k_warn(ab, "peer %pM disappeared!\n", peer_addr); ath11k_warn(ab, "peer %pM disappeared!\n", peer_addr);
if (sta) {
arsta = (struct ath11k_sta *)sta->drv_priv;
switch (key->cipher) {
case WLAN_CIPHER_SUITE_TKIP:
case WLAN_CIPHER_SUITE_CCMP:
case WLAN_CIPHER_SUITE_CCMP_256:
case WLAN_CIPHER_SUITE_GCMP:
case WLAN_CIPHER_SUITE_GCMP_256:
if (cmd == SET_KEY)
arsta->pn_type = HAL_PN_TYPE_WPA;
else
arsta->pn_type = HAL_PN_TYPE_NONE;
break;
default:
arsta->pn_type = HAL_PN_TYPE_NONE;
break;
}
}
spin_unlock_bh(&ab->base_lock); spin_unlock_bh(&ab->base_lock);
exit: exit:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment