233 lines
7.4 KiB
C
233 lines
7.4 KiB
C
|
/****************************************************************************
|
|||
|
*
|
|||
|
* 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 MSDU<EFBFBD>s SN is not <EFBFBD>higher<EFBFBD> than the <EFBFBD>highest<EFBFBD> SN (i.e. modulo 4096) passed to
|
|||
|
* the upper layers so far, then only pass it if
|
|||
|
* (a) there are no <EFBFBD>lower<EFBFBD> holes or
|
|||
|
* (b) it times out or
|
|||
|
* (c) a <EFBFBD>higher<EFBFBD> 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;
|
|||
|
}
|