kernel_samsung_a53x/drivers/net/wireless/scsc/ba_replay.c
2024-06-15 16:02:09 -03:00

232 lines
7.4 KiB
C
Executable file
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/****************************************************************************
*
* Copyright (c) 2012 - 2021 Samsung Electronics Co., Ltd. All rights reserved
*
****************************************************************************/
#include "debug.h"
#include "dev.h"
#include "ba.h"
static bool ba_replay_check_enable = false;
module_param(ba_replay_check_enable, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(ba_replay_check_enable, "Replay check (1: enable (default), 0: disable)");
static uint ba_replay_check_option = 1;
module_param(ba_replay_check_option, uint, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(ba_replay_check_option, "Replay check option type (1 (default), 2");
static bool ba_replay_check_option_1(struct net_device *dev, struct slsi_ba_session_rx *ba_session_rx, struct slsi_ba_frame_desc *frame_desc);
static bool ba_replay_check_option_2(struct net_device *dev, struct slsi_ba_session_rx *ba_session_rx, struct slsi_ba_frame_desc *frame_desc);
struct ba_replay_check_type {
const u8 option_num;
bool (*replay_check_fn)(struct net_device *dev, struct slsi_ba_session_rx *ba_session_rx, struct slsi_ba_frame_desc *frame_desc);
};
/* NOTES: Replay detection Options
*
* Option 1:
* If an MSDU is a candidate to be passed to upper layers, either as result
* of reordering or as result of reordering timeout, then only pass it if it's
* PN is more than the highest PN passed to upper layer so far.
*
* Option 2:
* If:
* An MSDU's SN is "higher" than the "highest" SN (i.e modulo 4096) passed to
* upper layers so far, then only pass it if
* (a) there are no lower holes or
* (b) it times out
* (c) it's PN is more than the highest PN passed to UL so far
*
* An MSDUs SN is not “higher” than the “highest” SN (i.e. modulo 4096) passed to
* the upper layers so far, then only pass it if
* (a) there are no “lower” holes or
* (b) it times out or
* (c) a “higher” SN has already timed out and been passed to the UL, and if
* (d) its PN is less than the highest PN passed to the UL so far
*
*/
static const struct ba_replay_check_type replay_check_types[] = {
{ 1, ba_replay_check_option_1 },
{ 2, ba_replay_check_option_2 }
};
void slsi_ba_replay_reset_pn(struct net_device *dev, struct slsi_peer *peer)
{
u8 i = 0;
SLSI_NET_DBG4(dev, SLSI_RX_BA, "reset PNs for peer %pM\n", peer->address);
for (i = 0; i <= FAPI_PRIORITY_QOS_UP7; i++)
memset(peer->rx_pn[i], 0, SLSI_RX_PN_LEN);
}
void slsi_ba_replay_get_pn(struct net_device *dev, struct slsi_peer *peer, struct sk_buff *skb, struct slsi_ba_frame_desc *frame_desc)
{
struct slsi_skb_cb *skb_cb;
if (ba_replay_check_enable) {
skb_cb = (struct slsi_skb_cb *)skb->cb;
if (skb_cb->is_ciphered) {
frame_desc->pn[0] = skb_cb->keyrsc[7];
frame_desc->pn[1] = skb_cb->keyrsc[6];
frame_desc->pn[2] = skb_cb->keyrsc[5];
frame_desc->pn[3] = skb_cb->keyrsc[4];
frame_desc->pn[4] = skb_cb->keyrsc[1];
frame_desc->pn[5] = skb_cb->keyrsc[0];
SLSI_NET_DBG4(dev, SLSI_RX_BA, "received: tid=%d sn=%d previous PN %pm received PN %pm\n",
frame_desc->tid,
frame_desc->sn,
peer->rx_pn[frame_desc->tid],
frame_desc->pn);
}
}
}
void slsi_ba_replay_store_pn(struct net_device *dev, struct slsi_peer *peer, struct sk_buff *skb)
{
struct slsi_skb_cb *skb_cb;
if (ba_replay_check_enable) {
skb_cb = (struct slsi_skb_cb *)skb->cb;
if (skb_cb->is_ciphered) {
if (skb_cb->tid <= FAPI_PRIORITY_QOS_UP7) {
peer->rx_pn[skb_cb->tid][0] = skb_cb->keyrsc[7];
peer->rx_pn[skb_cb->tid][1] = skb_cb->keyrsc[6];
peer->rx_pn[skb_cb->tid][2] = skb_cb->keyrsc[5];
peer->rx_pn[skb_cb->tid][3] = skb_cb->keyrsc[4];
peer->rx_pn[skb_cb->tid][4] = skb_cb->keyrsc[1];
peer->rx_pn[skb_cb->tid][5] = skb_cb->keyrsc[0];
SLSI_NET_DBG4(dev, SLSI_RX_BA, "store: tid=%d sn=%d received PN %pm\n",
skb_cb->tid,
skb_cb->seq_num,
peer->rx_pn[skb_cb->tid]);
}
}
}
}
bool ba_replay_check_option_1(struct net_device *dev, struct slsi_ba_session_rx *ba_session_rx, struct slsi_ba_frame_desc *frame_desc)
{
struct slsi_peer *peer = ba_session_rx->peer;
if (frame_desc->flag_old_sn) {
SLSI_NET_WARN(dev, "old frame, drop: sn=%d, expected_sn=%d\n", frame_desc->sn, ba_session_rx->expected_sn);
ba_session_rx->ba_drops_old++;
return true;
}
if (memcmp(frame_desc->pn, peer->rx_pn[frame_desc->tid], SLSI_RX_PN_LEN) <= 0) {
SLSI_NET_WARN(dev, "replay detected: tid=%d sn=%d previous PN %pm received PN %pm\n",
frame_desc->tid,
frame_desc->sn,
peer->rx_pn[frame_desc->tid],
frame_desc->pn);
ba_session_rx->ba_drops_replay++;
return true;
}
memcpy(peer->rx_pn[frame_desc->tid], frame_desc->pn, SLSI_RX_PN_LEN);
return false;
}
bool ba_replay_check_option_2(struct net_device *dev, struct slsi_ba_session_rx *ba_session_rx, struct slsi_ba_frame_desc *frame_desc)
{
struct slsi_peer *peer = ba_session_rx->peer;
u8 i = 0;
if (!frame_desc->flag_old_sn) {
if (memcmp(frame_desc->pn, peer->rx_pn[frame_desc->tid], SLSI_RX_PN_LEN) <= 0) {
SLSI_NET_WARN(dev, "replay detected: tid=%d sn=%d previous PN %pm received PN %pm\n",
frame_desc->tid,
frame_desc->sn,
peer->rx_pn[frame_desc->tid],
frame_desc->pn);
ba_session_rx->ba_drops_replay++;
return true;
}
memcpy(peer->rx_pn[frame_desc->tid], frame_desc->pn, SLSI_RX_PN_LEN);
ba_session_rx->ba_window[i].sn = frame_desc->sn;
ba_session_rx->ba_window[i].sent = true;
return false;
}
/* is it a duplicate */
i = SN_TO_INDEX(ba_session_rx, frame_desc->sn);
if (ba_session_rx->ba_window[i].sent) {
SLSI_NET_WARN(dev, "duplicate: tid=%d sn=%d highest_sn=%d, previous PN %pm received PN %pm\n",
frame_desc->tid,
frame_desc->sn,
ba_session_rx->highest_received_sn,
peer->rx_pn[frame_desc->tid],
frame_desc->pn);
ba_session_rx->ba_drops_old++;
return true;
}
/* check if SN is more than highest SN passed so far, if not */
if (IS_SN_LESS(ba_session_rx->highest_received_sn, frame_desc->sn)) {
/* check if its PN is more than highest PN paseed to the UL so far */
if (memcmp(frame_desc->pn, peer->rx_pn[frame_desc->tid], SLSI_RX_PN_LEN) <= 0) {
SLSI_NET_WARN(dev, "replay detected: tid=%d sn=%d, previous PN %pm received PN %pm\n",
frame_desc->tid,
frame_desc->sn,
peer->rx_pn[frame_desc->tid],
frame_desc->pn);
ba_session_rx->ba_drops_replay++;
return true;
}
} else {
/* check if its PN is less than the highest PN passed to the UL so far */
if (memcmp(frame_desc->pn, peer->rx_pn[frame_desc->tid], SLSI_RX_PN_LEN) >= 0) {
SLSI_NET_WARN(dev, "replay detected: tid=%d sn=%d, previous PN %pm received PN %pm\n",
frame_desc->tid,
frame_desc->sn,
peer->rx_pn[frame_desc->tid],
frame_desc->pn);
ba_session_rx->ba_drops_replay++;
return true;
}
}
/* update current BA window */
ba_session_rx->ba_window[i].sn = frame_desc->sn;
ba_session_rx->ba_window[i].sent = true;
return false;
}
bool slsi_ba_replay_check_pn(struct net_device *dev, struct slsi_ba_session_rx *ba_session_rx, struct slsi_ba_frame_desc *frame_desc)
{
struct slsi_skb_cb *skb_cb;
u8 i = 0;
bool ret = false;
if (!ba_replay_check_enable)
return false;
if (frame_desc->flag_old_tdls)
return false;
skb_cb = (struct slsi_skb_cb *)frame_desc->signal->cb;
if (!skb_cb->is_ciphered)
return false;
for (i = 0; i < ARRAY_SIZE(replay_check_types); i++) {
if (replay_check_types[i].option_num == ba_replay_check_option) {
ret = replay_check_types[i].replay_check_fn(dev, ba_session_rx, frame_desc);
}
}
return ret;
}