luckfox-pico-sdk/sysdrv/drv_ko/wifi/atbm6441/hal_apollo/rx.c
2023-08-08 20:36:47 +08:00

1528 lines
40 KiB
C

/*
* Copyright 2002-2005, Instant802 Networks, Inc.
* Copyright 2005-2006, Devicescape Software, Inc.
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
* Copyright 2007-2010 Johannes Berg <johannes@sipsolutions.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifdef LINUX_OS
#include <linux/jiffies.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/skbuff.h>
#include <linux/if_arp.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/rcupdate.h>
#include <linux/export.h>
#include <net/atbm_mac80211.h>
#include <net/ieee80211_radiotap.h>
#endif
#include "apollo.h"
#include "ieee80211_i.h"
#include "bh.h"
static bool ieee80211_frame_eapol(u8 *data, int len);
static void ieee80211_parse_qos(struct ieee80211_rx_data *rx)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb);
int tid, seqno_idx, security_idx;
/* does the frame have a qos control field? */
if (ieee80211_is_data_qos(hdr->frame_control)) {
u8 *qc = ieee80211_get_qos_ctl(hdr);
/* frame has qos control */
tid = *qc & IEEE80211_QOS_CTL_TID_MASK;
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0))
if (*qc & IEEE80211_QOS_CONTROL_A_MSDU_PRESENT)
#else
if (*qc & IEEE80211_QOS_CTL_A_MSDU_PRESENT)
#endif
status->rx_flags |= IEEE80211_RX_AMSDU;
seqno_idx = tid;
security_idx = tid;
} else {
/*
* IEEE 802.11-2007, 7.1.3.4.1 ("Sequence Number field"):
*
* Sequence numbers for management frames, QoS data
* frames with a broadcast/multicast address in the
* Address 1 field, and all non-QoS data frames sent
* by QoS STAs are assigned using an additional single
* modulo-4096 counter, [...]
*
* We also use that counter for non-QoS STAs.
*/
seqno_idx = NUM_RX_DATA_QUEUES;
security_idx = 0;
if (ieee80211_is_mgmt(hdr->frame_control))
security_idx = NUM_RX_DATA_QUEUES;
tid = 0;
}
rx->seqno_idx = seqno_idx;
rx->security_idx = security_idx;
/* Set skb->priority to 1d tag if highest order bit of TID is not set.
* For now, set skb->priority to 0 for other cases. */
rx->skb->priority = (tid > 7) ? 0 : tid;
}
/**
* DOC: Packet alignment
*
* Drivers always need to pass packets that are aligned to two-byte boundaries
* to the stack.
*
* Additionally, should, if possible, align the payload data in a way that
* guarantees that the contained IP header is aligned to a four-byte
* boundary. In the case of regular frames, this simply means aligning the
* payload to a four-byte boundary (because either the IP header is directly
* contained, or IV/RFC1042 headers that have a length divisible by four are
* in front of it). If the payload data is not properly aligned and the
* architecture doesn't support efficient unaligned operations, mac80211
* will align the data.
*
* With A-MSDU frames, however, the payload data address must yield two modulo
* four because there are 14-byte 802.3 headers within the A-MSDU frames that
* push the IP header further back to a multiple of four again. Thankfully, the
* specs were sane enough this time around to require padding each A-MSDU
* subframe to a length that is a multiple of four.
*
* Padding like Atheros hardware adds which is between the 802.11 header and
* the payload is not supported, the driver is required to move the 802.11
* header to be directly in front of the payload in that case.
*/
static void ieee80211_verify_alignment(struct ieee80211_rx_data *rx)
{
#ifdef CONFIG_MAC80211_ATBM_VERBOSE_DEBUG
WARN_ONCE((unsigned long)rx->skb->data & 1,
"unaligned packet at 0x%p\n", rx->skb->data);
#endif
}
#define SEQ_MODULO 0x1000
#define SEQ_MASK 0xfff
static inline int seq_less(u16 sq1, u16 sq2)
{
return ((sq1 - sq2) & SEQ_MASK) > (SEQ_MODULO >> 1);
}
static inline u16 seq_inc(u16 sq)
{
return (sq + 1) & SEQ_MASK;
}
static inline u16 seq_sub(u16 sq1, u16 sq2)
{
return (sq1 - sq2) & SEQ_MASK;
}
static void ieee80211_release_reorder_frame(struct atbm_common *hw_priv,
struct tid_ampdu_rx *tid_agg_rx,
int index,struct sk_buff_head *frames)
{
struct sk_buff *skb = tid_agg_rx->reorder_buf[index];
struct ieee80211_rx_status *status;
lockdep_assert_held(&tid_agg_rx->reorder_lock);
if (!skb)
goto no_frame;
/* release the frame from the reorder ring buffer */
tid_agg_rx->stored_mpdu_num--;
tid_agg_rx->reorder_buf[index] = NULL;
status = IEEE80211_SKB_RXCB(skb);
status->rx_flags |= IEEE80211_RX_DEFERRED_RELEASE;
__atbm_skb_queue_tail(frames, skb);
no_frame:
tid_agg_rx->head_seq_num = seq_inc(tid_agg_rx->head_seq_num);
}
static void ieee80211_release_reorder_frames(struct atbm_common *hw_priv,
struct tid_ampdu_rx *tid_agg_rx,
u16 head_seq_num,struct sk_buff_head *frames)
{
int index;
lockdep_assert_held(&tid_agg_rx->reorder_lock);
while (seq_less(tid_agg_rx->head_seq_num, head_seq_num)) {
index = seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn) %
tid_agg_rx->buf_size;
ieee80211_release_reorder_frame(hw_priv, tid_agg_rx, index,frames);
}
}
/*
* Timeout (in jiffies) for skb's that are waiting in the RX reorder buffer. If
* the skb was added to the buffer longer than this time ago, the earlier
* frames that have not yet been received are assumed to be lost and the skb
* can be released for processing. This may also release other skb's from the
* reorder buffer if there are no additional gaps between the frames.
*
* Callers must hold tid_agg_rx->reorder_lock.
*/
static int HT_RX_REORDER_BUF_TIMEOUT = (HZ / 3);
static int Sta_shortTcp_RxCnt = 0;
static void ieee80211_sta_reorder_release(struct atbm_common *hw_priv,
struct tid_ampdu_rx *tid_agg_rx,struct sk_buff_head *frames)
{
int index, j;
lockdep_assert_held(&tid_agg_rx->reorder_lock);
/* release the buffer until next missing frame */
index = seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn) %
tid_agg_rx->buf_size;
if (!tid_agg_rx->reorder_buf[index] &&
tid_agg_rx->stored_mpdu_num) {
/*
* No buffers ready to be released, but check whether any
* frames in the reorder buffer have timed out.
*/
int skipped = 1;
for (j = (index + 1) % tid_agg_rx->buf_size; j != index;
j = (j + 1) % tid_agg_rx->buf_size) {
if (!tid_agg_rx->reorder_buf[j]) {
skipped++;
continue;
}
if (skipped &&
!time_after(jiffies, tid_agg_rx->reorder_time[j] +
HT_RX_REORDER_BUF_TIMEOUT))
goto set_release_timer;
if(skipped){
//printk("release an RX reorder frame due to timeout sn %x\n",tid_agg_rx->head_seq_num);
}
ieee80211_release_reorder_frame(hw_priv, tid_agg_rx, j,frames);
/*
* Increment the head seq# also for the skipped slots.
*/
tid_agg_rx->head_seq_num =
(tid_agg_rx->head_seq_num + skipped) & SEQ_MASK;
skipped = 0;
}
} else while (tid_agg_rx->reorder_buf[index]) {
ieee80211_release_reorder_frame(hw_priv, tid_agg_rx, index,frames);
index = seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn) %
tid_agg_rx->buf_size;
}
if (tid_agg_rx->stored_mpdu_num) {
j = index = seq_sub(tid_agg_rx->head_seq_num,
tid_agg_rx->ssn) % tid_agg_rx->buf_size;
for (; j != (index - 1) % tid_agg_rx->buf_size;
j = (j + 1) % tid_agg_rx->buf_size) {
if (tid_agg_rx->reorder_buf[j])
break;
}
set_release_timer:
atbm_mod_timer(&tid_agg_rx->reorder_timer,
tid_agg_rx->reorder_time[j] + 1 +
HT_RX_REORDER_BUF_TIMEOUT);
} else {
atbm_del_timer(&tid_agg_rx->reorder_timer);
}
}
/*
* As this function belongs to the RX path it must be under
* rcu_read_lock protection. It returns false if the frame
* can be processed immediately, true if it was consumed.
*/
static bool ieee80211_sta_manage_reorder_buf(struct atbm_common *hw_priv,
struct tid_ampdu_rx *tid_agg_rx,
struct sk_buff *skb,struct sk_buff_head *frames)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
u16 sc = le16_to_cpu(hdr->seq_ctrl);
u16 mpdu_seq_num = (sc & IEEE80211_SCTL_SEQ) >> 4;
u16 head_seq_num, buf_size;
int index;
bool ret = true;
bool isEapol = (ieee80211_is_data(hdr->frame_control) &&
ieee80211_frame_eapol(skb->data, skb->len));
if(skb->len < 150){
Sta_shortTcp_RxCnt++;
if(Sta_shortTcp_RxCnt > 50){
Sta_shortTcp_RxCnt = 50;
HT_RX_REORDER_BUF_TIMEOUT = HZ/4;
}
}
else {
if(Sta_shortTcp_RxCnt >= 20)
Sta_shortTcp_RxCnt -= 20;
if (Sta_shortTcp_RxCnt < 20)
{
Sta_shortTcp_RxCnt = 0;
HT_RX_REORDER_BUF_TIMEOUT = HZ/2;
}
}
spin_lock(&tid_agg_rx->reorder_lock);
buf_size = tid_agg_rx->buf_size;
head_seq_num = tid_agg_rx->head_seq_num;
/* frame with out of date sequence number */
if (seq_less(mpdu_seq_num, head_seq_num)) {
atbm_dev_kfree_skb(skb);
goto out;
}
/*
* If frame the sequence number exceeds our buffering window
* size release some previous frames to make room for this one.
*/
if (!seq_less(mpdu_seq_num, head_seq_num + buf_size)) {
head_seq_num = seq_inc(seq_sub(mpdu_seq_num, buf_size));
/* release stored frames up to new head to stack */
ieee80211_release_reorder_frames(hw_priv, tid_agg_rx, head_seq_num,frames);
}
/* Now the new frame is always in the range of the reordering buffer */
index = seq_sub(mpdu_seq_num, tid_agg_rx->ssn) % tid_agg_rx->buf_size;
/* check if we already stored this frame */
if (tid_agg_rx->reorder_buf[index]) {
atbm_dev_kfree_skb(skb);
goto out;
}
/*
* If the current MPDU is in the right order and nothing else
* is stored we can process it directly, no need to buffer it.
* If it is first but there's something stored, we may be able
* to release frames after this one.
*/
if (mpdu_seq_num == tid_agg_rx->head_seq_num &&
tid_agg_rx->stored_mpdu_num == 0) {
tid_agg_rx->head_seq_num = seq_inc(tid_agg_rx->head_seq_num);
ret = false;
goto out;
}
if (isEapol && mpdu_seq_num == (tid_agg_rx->head_seq_num + 1) &&
tid_agg_rx->stored_mpdu_num == 0) {
tid_agg_rx->head_seq_num = seq_inc(tid_agg_rx->head_seq_num);
ret = false;
goto out;
}
/* put the frame in the reordering buffer */
tid_agg_rx->reorder_buf[index] = skb;
tid_agg_rx->reorder_time[index] = jiffies;
tid_agg_rx->stored_mpdu_num++;
ieee80211_sta_reorder_release(hw_priv, tid_agg_rx,frames);
out:
spin_unlock(&tid_agg_rx->reorder_lock);
return ret;
}
/*
* Reorder MPDUs from A-MPDUs, keeping them on a buffer. Returns
* true if the MPDU was buffered, false if it should be processed.
*/
static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx,struct sk_buff_head *frames)
{
struct sk_buff *skb = rx->skb;
struct atbm_common *hw_priv = rx->hw_priv;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
struct sta_info *sta = rx->sta;
struct tid_ampdu_rx *tid_agg_rx;
u16 sc;
int tid;
if (!ieee80211_is_data_qos(hdr->frame_control))
goto dont_reorder;
/*
* filter the QoS data rx stream according to
* STA/TID and check if this STA/TID is on aggregation
*/
if (!sta)
goto dont_reorder;
tid = *(int *)ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK;
tid_agg_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[tid]);
if (!tid_agg_rx)
goto dont_reorder;
/* qos null data frames are excluded */
if (unlikely(hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_NULLFUNC)))
goto dont_reorder;
/* new, potentially un-ordered, ampdu frame - process it */
/* reset session timer */
if (tid_agg_rx->timeout)
atbm_mod_timer(&tid_agg_rx->session_timer,
TU_TO_EXP_TIME(tid_agg_rx->timeout));
/* if this mpdu is fragmented - terminate rx aggregation session */
sc = le16_to_cpu(hdr->seq_ctrl);
if (sc & IEEE80211_SCTL_FRAG) {
atbm_printk_err("%s:dont process fragment\n",__func__);
goto dont_reorder;
}
/*
* No locking needed -- we will only ever process one
* RX packet at a time, and thus own tid_agg_rx. All
* other code manipulating it needs to (and does) make
* sure that we cannot get to it any more before doing
* anything with it.
*/
if (ieee80211_sta_manage_reorder_buf(hw_priv, tid_agg_rx, skb,frames))
return;
dont_reorder:
__atbm_skb_queue_tail(frames, skb);
}
static bool ieee80211_frame_eapol(u8 *data, int len)
{
__le16 fc;
bool status = false;
#define ETH_TYPE_OFFSET 30
fc = ((struct ieee80211_hdr *)data)->frame_control;
if ((ieee80211_is_data(fc)) && (len > 34)) {
u8 eapol_index = ETH_TYPE_OFFSET;
u16 ethertype;
if (ieee80211_is_data_qos(fc))
eapol_index = ETH_TYPE_OFFSET + 2;
ethertype = ((data[eapol_index] << 8) |
(data[eapol_index + 1]));
if (ethertype == ETH_P_PAE)
status = true;
}
return status;
}
static ieee80211_rx_result
ieee80211_rx_h_check(struct ieee80211_rx_data *rx)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb);
/* Drop duplicate 802.11 retransmissions (IEEE 802.11 Chap. 9.2.9) */
if (rx->sta && !is_multicast_ether_addr(hdr->addr1)) {
if (unlikely(ieee80211_has_retry(hdr->frame_control) &&
rx->sta->last_seq_ctrl[rx->seqno_idx] ==
hdr->seq_ctrl)) {
if (status->rx_flags & IEEE80211_RX_RA_MATCH) {
;
}
return RX_DROP_UNUSABLE;
} else
rx->sta->last_seq_ctrl[rx->seqno_idx] = hdr->seq_ctrl;
}
if (unlikely(rx->skb->len < 16)) {
return RX_DROP_MONITOR;
}
if (unlikely((ieee80211_is_data(hdr->frame_control) ||
ieee80211_is_pspoll(hdr->frame_control))
&& (!rx->sta || !test_sta_flag(rx->sta, WLAN_STA_ASSOC)))) {
return RX_DROP_MONITOR;
}
return RX_CONTINUE;
}
static ieee80211_rx_result
ieee80211_crypto_wep_decrypt(struct ieee80211_rx_data *rx)
{
struct sk_buff *skb = rx->skb;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
if (!ieee80211_is_data(hdr->frame_control) &&
!ieee80211_is_auth(hdr->frame_control))
return RX_CONTINUE;
if(!(status->flag & RX_FLAG_DECRYPTED)){
atbm_printk_err("wep decr err\n");
return RX_DROP_UNUSABLE;
}
if (!(status->flag & RX_FLAG_IV_STRIPPED)) {
unsigned int hdrlen;
hdrlen = ieee80211_hdrlen(hdr->frame_control);
memmove(skb->data + WEP_IV_LEN, skb->data, hdrlen);
atbm_skb_pull(skb, WEP_IV_LEN);
/* remove ICV */
atbm_skb_trim(rx->skb, rx->skb->len - WEP_ICV_LEN);
}
return RX_CONTINUE;
}
static ieee80211_rx_result
ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data;
int hdrlen = 0;
struct sk_buff *skb = rx->skb;
hdrlen = ieee80211_hdrlen(hdr->frame_control);
if (!ieee80211_is_data(hdr->frame_control))
return RX_CONTINUE;
if (!rx->sta || skb->len - hdrlen < 12)
return RX_DROP_UNUSABLE;
/* Trim ICV */
atbm_skb_trim(skb, skb->len - TKIP_ICV_LEN);
/* Remove IV */
memmove(skb->data + TKIP_IV_LEN, skb->data, hdrlen);
atbm_skb_pull(skb, TKIP_IV_LEN);
return RX_CONTINUE;
}
static ieee80211_rx_result
ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
int hdrlen;
struct sk_buff *skb = rx->skb;
int data_len;
hdrlen = ieee80211_hdrlen(hdr->frame_control);
if (!ieee80211_is_data(hdr->frame_control) &&
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,17,0))
!ieee80211_is_robust_mgmt_frame(hdr)
#else
!ieee80211_is_robust_mgmt_frame(skb)
#endif
)
return RX_CONTINUE;
data_len = skb->len - hdrlen - CCMP_HDR_LEN - CCMP_MIC_LEN;
if (!rx->sta || data_len < 0)
return RX_DROP_UNUSABLE;
/* Remove CCMP header and MIC */
atbm_skb_trim(skb, skb->len - CCMP_MIC_LEN);
memmove(skb->data + CCMP_HDR_LEN, skb->data, hdrlen);
atbm_skb_pull(skb, CCMP_HDR_LEN);
return RX_CONTINUE;
}
static ieee80211_rx_result
ieee80211_crypto_aes_cmac_decrypt(struct ieee80211_rx_data *rx)
{
struct sk_buff *skb = rx->skb;
struct ieee80211_mmie *mmie;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
if (!ieee80211_is_mgmt(hdr->frame_control))
return RX_CONTINUE;
if (skb->len < 24 + sizeof(*mmie))
return RX_DROP_UNUSABLE;
mmie = (struct ieee80211_mmie *)
(skb->data + skb->len - sizeof(*mmie));
if (mmie->element_id != ATBM_WLAN_EID_MMIE ||
mmie->length != sizeof(*mmie) - 2)
return RX_DROP_UNUSABLE; /* Invalid MMIE */
/* Remove MMIE */
atbm_skb_trim(skb, skb->len - sizeof(*mmie));
return RX_CONTINUE;
}
static void ccmp_tkip_hdr2pn(u8 *pn, u8 *hdr, bool is_ccmp)
{
pn[0] = hdr[7];
pn[1] = hdr[6];
pn[2] = hdr[5];
pn[3] = hdr[4];
if (is_ccmp){
pn[4] = hdr[1];
pn[5] = hdr[0];
}
else {
pn[4] = hdr[0];
pn[5] = hdr[2];
}
}
static int ccmp_tkip_replaycnt(struct atbm_vif *vif, struct ieee80211_mgmt *mgmt, bool is_ccmp)
{
u8 pn[CCMP_PN_LEN];
static const u8 zero_pn[CCMP_PN_LEN]= {0};
int hdrlen = ieee80211_hdrlen(mgmt->frame_control);
u8 *qc = ieee80211_get_qos_ctl((struct ieee80211_hdr *)mgmt);
u8 tid = *qc & 0x7;//IEEE80211_QOS_CTL_TID_MASK;
u8 *pOldPN;
u8 *b_pn_init;
if (vif->type != NL80211_IFTYPE_STATION){
return 0;
}
ccmp_tkip_hdr2pn(pn,(u8 *)mgmt + hdrlen, is_ccmp);
if (is_multicast_ether_addr(mgmt->da) == 0){
if (ieee80211_is_data_qos(mgmt->frame_control)){
pOldPN = vif->ptk_pn[tid];
b_pn_init = &(vif->ptk_pn_init[tid]);
}
else {
pOldPN = vif->ptk_noqos_pn;
b_pn_init = &(vif->ptk_noqos_pn_init);
}
}
else {
pOldPN = vif->gtk_pn;
b_pn_init = &(vif->gtk_pn_init);
}
if (*b_pn_init == 0){
if (memcmp(pn, pOldPN, CCMP_PN_LEN) <= 0){
if (!((memcmp(pn, zero_pn, CCMP_PN_LEN) == 0) && (memcmp(pOldPN, zero_pn, CCMP_PN_LEN) == 0))){
atbm_printk_err("[RX]ccmp_tkip_replaycnt drop %x:%x:%x:%x:%x:%x > %x:%x:%x:%x:%x:%x\n",
pOldPN[0],pOldPN[1],pOldPN[2],pOldPN[3],pOldPN[4],pOldPN[5],
pn[0],pn[1],pn[2],pn[3],pn[4],pn[5]);
}
return -1;
}
else {
int i = 0;
for (i=0; i<CCMP_PN_LEN; i++){
if (pn[i] > pOldPN[i]){
//PN = xx,xx,xx,xx,0,0
if (i >= CCMP_PN_LEN-2){
break;
}
else {
//PN = 1,0,0,0,0,0
//pOldPN = 0,0xff,0xff,0xff,0,0
if ((pn[i] != (pOldPN[i]+1))
|| (pOldPN[i+1] != 0xff)
|| (pn[i+1] != 0)){
atbm_printk_warn("[RX]ccmp_tkip_replaycnt not update\n");
return 0;
}
}
}
}
}
}
else {
*b_pn_init = 0;
}
memcpy(pOldPN, pn, CCMP_PN_LEN);
return 0;
}
static ieee80211_rx_result
ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
{
struct sk_buff *skb = rx->skb;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
ieee80211_rx_result result = RX_DROP_UNUSABLE;
struct ieee80211_hdr *hdr = (void *)skb->data;
/*
* Key selection 101
*
* There are four types of keys:
* - GTK (group keys)
* - IGTK (group keys for management frames)
* - PTK (pairwise keys)
* - STK (station-to-station pairwise keys)
*
* When selecting a key, we have to distinguish between multicast
* (including broadcast) and unicast frames, the latter can only
* use PTKs and STKs while the former always use GTKs and IGTKs.
* Unless, of course, actual WEP keys ("pre-RSNA") are used, then
* unicast frames can also use key indices like GTKs. Hence, if we
* don't have a PTK/STK we check the key index for a WEP key.
*
* Note that in a regular BSS, multicast frames are sent by the
* AP only, associated stations unicast the frame to the AP first
* which then multicasts it on their behalf.
*
* There is also a slight problem in IBSS mode: GTKs are negotiated
* with each station, that is something we don't currently handle.
* The spec seems to expect that one negotiates the same key with
* every station but there's no such requirement; VLANs could be
* possible.
*/
/*
* No point in finding a key and decrypting if the frame is neither
* addressed to us nor a multicast frame.
*/
rx->key = NULL;
if (!(status->rx_flags & IEEE80211_RX_RA_MATCH))
return RX_CONTINUE;
if (atbm_skb_linearize(rx->skb))
return RX_DROP_UNUSABLE;
if(rx->sta == NULL)
return RX_CONTINUE;
if(rx->sta->enc == false)
return RX_CONTINUE;
if(!(status->flag & RX_FLAG_DECRYPTED))
return RX_CONTINUE;
if(status->flag & RX_FLAG_IV_STRIPPED)
return RX_CONTINUE;
/* the hdr variable is invalid now! */
if(is_multicast_ether_addr(hdr->addr1))
rx->key = &rx->vif->group_key;
else
rx->key = &rx->sta->key;
switch (rx->key->key_type) {
case IEEE80211_ENC_WEP:
result = ieee80211_crypto_wep_decrypt(rx);
break;
case IEEE80211_ENC_TKIP:
if (ccmp_tkip_replaycnt(rx->vif, (struct ieee80211_mgmt *)hdr, false))
return RX_DROP_UNUSABLE;
result = ieee80211_crypto_tkip_decrypt(rx);
break;
case IEEE80211_ENC_AES:
if (ccmp_tkip_replaycnt(rx->vif, (struct ieee80211_mgmt *)hdr, true))
return RX_DROP_UNUSABLE;
result = ieee80211_crypto_ccmp_decrypt(rx);
break;
case IEEE80211_ENC_AES_CMAC:
result = ieee80211_crypto_aes_cmac_decrypt(rx);
break;
default:
/*
* We can reach here only with HW-only algorithms
* but why didn't it decrypt the frame?!
*/
return RX_DROP_UNUSABLE;
}
/* either the frame has been decrypted or will be dropped */
status->flag |= RX_FLAG_DECRYPTED;
return result;
}
static ieee80211_rx_result
ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
{
struct sta_info *sta = rx->sta;
struct sk_buff *skb = rx->skb;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
if (!sta)
return RX_CONTINUE;
if (!(status->rx_flags & IEEE80211_RX_RA_MATCH))
return RX_CONTINUE;
/*
* Drop (qos-)data::nullfunc frames silently, since they
* are used only to control station power saving mode.
*/
if (ieee80211_is_nullfunc(hdr->frame_control) ||
ieee80211_is_qos_nullfunc(hdr->frame_control)) {
atbm_dev_kfree_skb(rx->skb);
return RX_QUEUED;
}
return RX_CONTINUE;
} /* ieee80211_rx_h_sta_process */
static ieee80211_rx_result
ieee80211_rx_h_remove_qos_control(struct ieee80211_rx_data *rx)
{
u8 *data = rx->skb->data;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)data;
if (!ieee80211_is_data_qos(hdr->frame_control))
return RX_CONTINUE;
/* remove the qos control field, update frame type and meta-data */
memmove(data + IEEE80211_QOS_CTL_LEN, data,
ieee80211_hdrlen(hdr->frame_control) - IEEE80211_QOS_CTL_LEN);
hdr = (struct ieee80211_hdr *)atbm_skb_pull(rx->skb, IEEE80211_QOS_CTL_LEN);
/* change frame type to non QOS */
hdr->frame_control &= ~cpu_to_le16(IEEE80211_STYPE_QOS_DATA);
return RX_CONTINUE;
}
static int
ieee80211_802_1x_port_control(struct ieee80211_rx_data *rx)
{
if (unlikely(!rx->sta ||
!test_sta_flag(rx->sta, WLAN_STA_AUTHORIZED)))
return -EACCES;
return 0;
}
static int
ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx, __le16 fc)
{
struct sk_buff *skb = rx->skb;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
/*
* Pass through unencrypted frames if the hardware has
* decrypted them already.
*/
if (status->flag & RX_FLAG_DECRYPTED)
return 0;
/* Drop unencrypted frames if key is set. */
if (unlikely(!ieee80211_has_protected(fc) &&
!ieee80211_is_nullfunc(fc) &&
ieee80211_is_data(fc) &&
(rx->sta->enc)))
return -EACCES;
return 0;
}
static int
__ieee80211_data_to_8023(struct ieee80211_rx_data *rx, bool *port_control)
{
struct atbm_vif *vif = rx->vif;
int ret;
*port_control = false;
ret = ieee80211_data_to_8023(rx->skb, vif->addr, vif->type);
if (ret < 0)
return ret;
return 0;
}
/*
* requires that rx->skb is a frame with ethernet header
*/
static bool ieee80211_frame_allowed(struct ieee80211_rx_data *rx, __le16 fc)
{
if (ieee80211_802_1x_port_control(rx) ||
ieee80211_drop_unencrypted(rx, fc))
return false;
return true;
}
/* TODO It should be removed before official delivery */
void dump_mem(u8 *data, int len)
{
int i;
printk(KERN_ERR "dump_mem len %d\n ",len);
for (i = 0; i < len; i+=8) {
if(len-i>=8){
printk(KERN_ERR "%02x %02x %02x %02x |%02x %02x %02x %02x ", data[i], data[i+1], data[i+2], data[i+3], data[i+4], data[i+5], data[i+6], data[i+7]);
}
else if(len-i>=7){
printk(KERN_ERR "%02x %02x %02x %02x |%02x %02x %02x ", data[i], data[i+1], data[i+2], data[i+3], data[i+4], data[i+5], data[i+6]);
}
else if(len-i>=6){
printk(KERN_ERR "%02x %02x %02x %02x |%02x %02x ", data[i], data[i+1], data[i+2], data[i+3], data[i+4], data[i+5]);
}
else if(len-i>=5){
printk(KERN_ERR "%02x %02x %02x %02x |%02x ", data[i], data[i+1], data[i+2], data[i+3], data[i+4]);
}
else if(len-i>=4){
printk(KERN_ERR "%02x %02x %02x %02x", data[i], data[i+1], data[i+2], data[i+3]);
}
else if(len-i>=3){
printk(KERN_ERR "%02x %02x %02x ", data[i], data[i+1], data[i+2]);
}
else if(len-i>=2){
printk(KERN_ERR "%02x %02x ", data[i], data[i+1]);
}
else if(len-i>=2){
printk(KERN_ERR "%02x ", data[i]);
}
}
printk(KERN_ERR "\n");
}
/*
* requires that rx->skb is a frame with ethernet header
*/
static void
ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
{
struct atbm_vif *vif = rx->vif;
struct net_device *dev = vif->ndev;
struct sk_buff *skb;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb);
skb = rx->skb;
if (skb) {
if (vif->type == NL80211_IFTYPE_STATION){
if (!(status->flag & RX_FLAG_DECRYPTED)){
if (vif->ap_info.enc){
atbm_dev_kfree_skb(skb);
return;
}
}
}
//dump_mem(skb->data,skb->len);
skb->dev = dev;
/* deliver to local stack */
skb->protocol = eth_type_trans(skb, dev);
#ifdef CHKSUM_HW_SUPPORT
if ((dev->features & NETIF_F_RXCSUM) &&(!(status->flag &RX_FLAG_HW_CHKSUM_ERROR))){
skb->ip_summed=CHECKSUM_UNNECESSARY;
}else{
skb->ip_summed=CHECKSUM_NONE;
}
#endif
//printk(KERN_ERR "%s %d,skb->pkt_type %x\n",__func__,__LINE__,skb->pkt_type );
memset(skb->cb, 0, sizeof(skb->cb));
atbm_netif_rx(skb);
}
}
static int
atbm_ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list,
const u8 *addr, enum nl80211_iftype iftype, const unsigned int extra_headroom, bool has_80211_header)
{
struct sk_buff *frame = NULL;
u16 ethertype;
u8 *payload;
const struct ethhdr *eth;
int remaining, err;
u8 dst[ETH_ALEN], src[ETH_ALEN];
if (has_80211_header){
err = ieee80211_data_to_8023(skb, addr, iftype);
if (err)
goto out;
eth = (struct ethhdr *)atbm_skb_pull(skb, sizeof(struct ethhdr));
if (!eth)
goto out;
}
else {
eth = (struct ethhdr *)(skb->data);
}
while (skb != frame){
u8 padding;
u16 len = eth->h_proto;
unsigned int subframe_len = sizeof(struct ethhdr)+ntohs(len);
remaining = skb->len;
memcpy(dst, eth->h_dest, ETH_ALEN);
memcpy(src, eth->h_source, ETH_ALEN);
padding = (4-subframe_len)&0x3;
if (subframe_len > remaining)
goto purge;
if (atbm_compare_ether_addr(eth->h_dest, rfc1042_header))
goto purge;
atbm_skb_pull(skb, sizeof(struct ethhdr));
if (remaining <= subframe_len + padding){
frame = skb;
}
else {
#ifdef LINUX_OS
unsigned int hlen = ALIGN(extra_headroom, 4);
#else
unsigned int hlen = ALIGN2(extra_headroom, 4);
#endif
frame = atbm_dev_alloc_skb(hlen + subframe_len + 2);
if (!frame)
goto purge;
atbm_skb_reserve(frame, hlen + sizeof(struct ethhdr) + 2);
memcpy(atbm_skb_put(frame, ntohs(len)), skb->data, ntohs(len));
eth = (struct ethhdr *)atbm_skb_pull(skb, ntohs(len) + padding);
if (!eth){
atbm_dev_kfree_skb(frame);
goto purge;
}
}
frame->priority = skb->priority;
payload = frame->data;
ethertype = (payload[6]<<8)|payload[7];
if (likely((atbm_compare_ether_addr(payload, rfc1042_header) == 0 &&
ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) ||
atbm_compare_ether_addr(payload, bridge_tunnel_header) == 0)){
atbm_skb_pull(frame, 6);
memcpy(atbm_skb_push(frame, ETH_ALEN), src, ETH_ALEN);
memcpy(atbm_skb_push(frame, ETH_ALEN), dst, ETH_ALEN);
}
else {
memcpy(atbm_skb_push(frame, sizeof(u16)), &len, sizeof(u16));
memcpy(atbm_skb_push(frame, ETH_ALEN), src, ETH_ALEN);
memcpy(atbm_skb_push(frame, ETH_ALEN), dst, ETH_ALEN);
}
atbm_skb_queue_tail(list, frame);
}
return 0;
purge:
atbm_skb_queue_purge(list);
out:
return -1;
}
static int
ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx)
{
struct atbm_vif *vif = rx->vif;
struct net_device *dev = vif->ndev;
struct sk_buff *skb = rx->skb;
struct sk_buff *frame;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
struct sk_buff_head frame_list;
int err;
if (unlikely(!ieee80211_is_data_present(hdr->frame_control)))
return RX_DROP_MONITOR;
if (is_multicast_ether_addr(hdr->addr1))
return RX_DROP_UNUSABLE;
if (status->flag & RX_FLAG_DECRYPTED){
switch (rx->key->key_type){
case IEEE80211_ENC_WEP:
case IEEE80211_ENC_TKIP:
return RX_DROP_UNUSABLE;
default:
break;
}
}
if (vif->type == NL80211_IFTYPE_STATION){
if (!(status->flag & RX_FLAG_DECRYPTED)){
if (vif->ap_info.enc){
return RX_DROP_UNUSABLE;
}
}
}
atbm_skb_queue_head_init(&frame_list);
err = atbm_ieee80211_amsdu_to_8023s(skb, &frame_list, vif->addr, vif->type, 0, true);
if (err)
return RX_DROP_UNUSABLE;
while (!atbm_skb_queue_empty(&frame_list)){
frame = atbm_skb_dequeue(&frame_list);
frame->dev = dev;
frame->protocol = eth_type_trans(frame, dev);
#ifdef CHKSUM_HW_SUPPORT
if ((dev->features & NETIF_F_RXCSUM) &&(!(status->flag &RX_FLAG_HW_CHKSUM_ERROR))){
frame->ip_summed=CHECKSUM_UNNECESSARY;
}else{
frame->ip_summed=CHECKSUM_NONE;
}
#endif
memset(frame->cb, 0, sizeof(frame->cb));
atbm_netif_rx(frame);
}
return 0;
}
static ieee80211_rx_result
ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
{
struct atbm_vif *vif = rx->vif;
struct net_device *dev = vif->ndev;
struct sk_buff *skb = rx->skb;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
__le16 fc = hdr->frame_control;
bool port_control;
int err;
if (unlikely(!ieee80211_is_data(hdr->frame_control)))
return RX_CONTINUE;
if (unlikely(!ieee80211_is_data_present(hdr->frame_control)))
return RX_DROP_MONITOR;
if (status->rx_flags & IEEE80211_RX_AMSDU){
err = ieee80211_rx_h_amsdu(rx);
if (err)
return RX_DROP_UNUSABLE;
else {
atbm_dev_kfree_skb(skb);
}
}
else{
err = __ieee80211_data_to_8023(rx, &port_control);
if (unlikely(err)){
atbm_printk_err("%s:to_8023 err\n",__func__);
return RX_DROP_UNUSABLE;
}
if (!ieee80211_frame_allowed(rx, fc)){
atbm_printk_err("%s:frame_allowed err\n",__func__);
return RX_DROP_MONITOR;
}
rx->skb->dev = dev;
dev->stats.rx_packets++;
dev->stats.rx_bytes += rx->skb->len;
ieee80211_deliver_skb(rx);
}
return RX_QUEUED;
}
static ieee80211_rx_result
ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
{
struct atbm_vif *vif = rx->vif;
struct atbm_ieee80211_mgmt *mgmt = (struct atbm_ieee80211_mgmt *) rx->skb->data;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb);
int len = rx->skb->len;
if (!ieee80211_is_action(mgmt->frame_control))
return RX_CONTINUE;
/* drop too small frames */
if (len < IEEE80211_MIN_ACTION_SIZE)
return RX_DROP_UNUSABLE;
if (!rx->sta && mgmt->u.action.category != WLAN_CATEGORY_PUBLIC)
return RX_DROP_UNUSABLE;
if (!(status->rx_flags & IEEE80211_RX_RA_MATCH))
return RX_DROP_UNUSABLE;
switch (mgmt->u.action.category) {
case WLAN_CATEGORY_BACK:
/*
* The aggregation code is not prepared to handle
* anything but STA/AP due to the BSSID handling;
* IBSS could work in the code but isn't supported
* by drivers or the standard.
*/
if (vif->type != NL80211_IFTYPE_STATION &&
vif->type != NL80211_IFTYPE_AP_VLAN &&
vif->type != NL80211_IFTYPE_AP)
break;
/* verify action_code is present */
if (len < IEEE80211_MIN_ACTION_SIZE + 1)
break;
switch (mgmt->u.action.u.addba_req.action_code) {
case WLAN_ACTION_ADDBA_REQ:
if (len < (IEEE80211_MIN_ACTION_SIZE +
sizeof(mgmt->u.action.u.addba_req)))
goto invalid;
break;
case WLAN_ACTION_ADDBA_RESP:
if (len < (IEEE80211_MIN_ACTION_SIZE +
sizeof(mgmt->u.action.u.addba_resp)))
goto invalid;
break;
case WLAN_ACTION_DELBA:
if (len < (IEEE80211_MIN_ACTION_SIZE +
sizeof(mgmt->u.action.u.delba)))
goto invalid;
break;
default:
goto invalid;
}
goto queue;
default:
goto handled;
}
return RX_CONTINUE;
invalid:
status->rx_flags |= IEEE80211_RX_MALFORMED_ACTION_FRM;
/* will return in the next handlers */
return RX_CONTINUE;
handled:
atbm_dev_kfree_skb(rx->skb);
return RX_QUEUED;
queue:
rx->skb->pkt_type = IEEE80211_SDATA_QUEUE_TYPE_FRAME;
atbm_skb_queue_tail(&vif->skb_queue, rx->skb);
atbm_hw_priv_queue_work(vif->hw_priv, &vif->work);
return RX_QUEUED;
}
static void ieee80211_rx_handlers_result(struct ieee80211_rx_data *rx,
ieee80211_rx_result res)
{
struct atbm_ieee80211_mgmt *mgmt;
switch (res) {
case RX_DROP_MONITOR:
/* fall through */
case RX_CONTINUE:
case RX_DROP_UNUSABLE:
mgmt = (struct atbm_ieee80211_mgmt *)rx->skb->data;
atbm_printk_err("%s:drop (%x)\n",__func__,mgmt->frame_control);
atbm_dev_kfree_skb(rx->skb);
break;
case RX_QUEUED:
break;
}
}
ieee80211_rx_result
ieee80211_rx_h_michael_mic_verify(struct ieee80211_rx_data *rx)
{
unsigned int hdrlen;
struct sk_buff *skb = rx->skb;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
if(rx->key == NULL)
return RX_CONTINUE;
if(rx->sta == NULL){
return RX_CONTINUE;
}
if( !(status->flag & RX_FLAG_DECRYPTED)){
return RX_CONTINUE;
}
if(IEEE80211_ENC_TKIP != rx->key->key_type)
return RX_CONTINUE;
/*
* it makes no sense to check for MIC errors on anything other
* than data frames.
*/
if (!ieee80211_is_data_present(hdr->frame_control))
return RX_CONTINUE;
/*
* No way to verify the MIC if the hardware stripped it or
* the IV with the key index. In this case we have solely rely
* on the driver to set RX_FLAG_MMIC_ERROR in the event of a
* MIC failure report.
*/
if (status->flag & (RX_FLAG_MMIC_STRIPPED | RX_FLAG_IV_STRIPPED)) {
goto mic_fail;
}
if (status->flag & RX_FLAG_MMIC_ERROR)
goto mic_fail;
hdrlen = ieee80211_hdrlen(hdr->frame_control);
if (skb->len < hdrlen + ATBM_MICHAEL_MIC_LEN)
return RX_DROP_UNUSABLE;
/* remove Michael MIC from payload */
atbm_skb_trim(skb, skb->len - ATBM_MICHAEL_MIC_LEN);
return RX_CONTINUE;
mic_fail:
atbm_printk_debug("Mic Err\n");
return RX_DROP_UNUSABLE;
}
static ieee80211_rx_result
ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx)
{
struct atbm_vif *vif = rx->vif;
struct atbm_ieee80211_mgmt *mgmt = (void *)rx->skb->data;
__le16 stype;
stype = mgmt->frame_control & cpu_to_le16(IEEE80211_FCTL_STYPE);
switch (stype) {
case cpu_to_le16(IEEE80211_STYPE_BEACON):
case cpu_to_le16(IEEE80211_STYPE_PROBE_RESP):
/* process for all: mesh, mlme, ibss */
break;
case cpu_to_le16(IEEE80211_STYPE_DEAUTH):
case cpu_to_le16(IEEE80211_STYPE_DISASSOC):
if (is_multicast_ether_addr(mgmt->da) &&
!is_broadcast_ether_addr(mgmt->da))
return RX_DROP_MONITOR;
/* process only for station */
if (vif->type != NL80211_IFTYPE_STATION)
return RX_DROP_MONITOR;
break;
case cpu_to_le16(IEEE80211_STYPE_PROBE_REQ):
case cpu_to_le16(IEEE80211_STYPE_AUTH):
case cpu_to_le16(IEEE80211_STYPE_ASSOC_RESP):
break;
default:
return RX_DROP_MONITOR;
}
/* queue up frame and kick off work to process it */
rx->skb->pkt_type = IEEE80211_SDATA_QUEUE_TYPE_FRAME;
atbm_skb_queue_tail(&vif->skb_queue, rx->skb);
atbm_hw_priv_queue_work(rx->hw_priv, &vif->work);
return RX_QUEUED;
}
static void ieee80211_rx_handlers(struct ieee80211_rx_data *rx,struct sk_buff_head *frames)
{
ieee80211_rx_result res = RX_DROP_MONITOR;
struct sk_buff *skb;
#define CALL_RXH(rxh) \
do { \
res = rxh(rx); \
if (res != RX_CONTINUE) \
goto rxh_next; \
} while (0)
spin_lock_bh(&rx->hw_priv->rx_path_lock);
while ((skb = __atbm_skb_dequeue(frames))) {
/*
* all the other fields are valid across frames
* that belong to an aMPDU since they are on the
* same TID from the same station
*/
rx->skb = skb;
CALL_RXH(ieee80211_rx_h_decrypt);
CALL_RXH(ieee80211_rx_h_sta_process);
CALL_RXH(ieee80211_rx_h_michael_mic_verify);
CALL_RXH(ieee80211_rx_h_remove_qos_control);
CALL_RXH(ieee80211_rx_h_data);
CALL_RXH(ieee80211_rx_h_action);
CALL_RXH(ieee80211_rx_h_mgmt);
rxh_next:
ieee80211_rx_handlers_result(rx, res);
#undef CALL_RXH
}
spin_unlock_bh(&rx->hw_priv->rx_path_lock);
}
static void ieee80211_invoke_rx_handlers(struct ieee80211_rx_data *rx)
{
ieee80211_rx_result res = RX_DROP_MONITOR;
struct sk_buff_head reorder_release;
__atbm_skb_queue_head_init(&reorder_release);
#define CALL_RXH(rxh) \
do { \
res = rxh(rx); \
if (res != RX_CONTINUE) \
goto rxh_next; \
} while (0)
CALL_RXH(ieee80211_rx_h_check);
ieee80211_rx_reorder_ampdu(rx,&reorder_release);
ieee80211_rx_handlers(rx,&reorder_release);
return;
rxh_next:
ieee80211_rx_handlers_result(rx, res);
#undef CALL_RXH
}
/*
* This function makes calls into the RX path, therefore
* it has to be invoked under RCU read lock.
*/
void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid)
{
struct ieee80211_rx_data rx = {
.sta = sta,
.vif = sta->vif,
.hw_priv = sta->hw_priv,
/* This is OK -- must be QoS data frame */
.security_idx = tid,
.seqno_idx = tid,
.flags = 0,
};
struct tid_ampdu_rx *tid_agg_rx;
struct sk_buff_head reorder_release;
__atbm_skb_queue_head_init(&reorder_release);
tid_agg_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[tid]);
if (!tid_agg_rx)
return;
spin_lock(&tid_agg_rx->reorder_lock);
ieee80211_sta_reorder_release(sta->hw_priv, tid_agg_rx,&reorder_release);
spin_unlock(&tid_agg_rx->reorder_lock);
ieee80211_rx_handlers(&rx,&reorder_release);
}
/* main receive path */
static int prepare_for_handlers(struct ieee80211_rx_data *rx,
struct ieee80211_hdr *hdr)
{
struct atbm_vif *vif = rx->vif;
struct sk_buff *skb = rx->skb;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
u8 *bssid = ieee80211_get_bssid(hdr, skb->len, vif->type);
int multicast = is_multicast_ether_addr(hdr->addr1);
switch (vif->type) {
case NL80211_IFTYPE_STATION:
if (!bssid)
return 0;
if (!multicast &&
atbm_compare_ether_addr(vif->addr, hdr->addr1) != 0) {
status->rx_flags &= ~IEEE80211_RX_RA_MATCH;
}
break;
case NL80211_IFTYPE_AP_VLAN:
case NL80211_IFTYPE_AP:
break;
default:
/* should never get here */
WARN_ON(1);
return 0;
}
return 1;
}
/*
* This function returns whether or not the SKB
* was destined for RX processing or not, which,
* if consume is true, is equivalent to whether
* or not the skb was consumed.
*/
static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx,
struct sk_buff *skb, bool consume)
{
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_hdr *hdr = (void *)skb->data;
int prepares;
rx->skb = skb;
status->rx_flags |= IEEE80211_RX_RA_MATCH;
prepares = prepare_for_handlers(rx, hdr);
if (!prepares)
{
return false;
}
if (!consume) {
skb = atbm_skb_copy(skb, GFP_ATOMIC);
if (!skb) {
if (net_ratelimit())
atbm_printk_debug("failed to copy skb for\n");
return true;
}
rx->skb = skb;
}
ieee80211_invoke_rx_handlers(rx);
return true;
}
/*
* This is the actual Rx frames handler. as it blongs to Rx path it must
* be called with rcu_read_lock protection.
*/
static void __ieee80211_rx_handle_packet(struct atbm_vif *vif,
struct sk_buff *skb)
{
struct atbm_common *hw_priv = vif->hw_priv;
struct ieee80211_hdr *hdr;
__le16 fc;
struct ieee80211_rx_data rx;
int err = 0;
fc = ((struct ieee80211_hdr *)skb->data)->frame_control;
memset(&rx, 0, sizeof(rx));
rx.skb = skb;
rx.hw_priv = hw_priv;
rx.vif = vif;
if (ieee80211_is_mgmt(fc))
err = atbm_skb_linearize(skb);
else
err = !atbm_pskb_may_pull(skb, ieee80211_hdrlen(fc));
if (err) {
atbm_dev_kfree_skb(skb);
return;
}
hdr = (struct ieee80211_hdr *)skb->data;
ieee80211_parse_qos(&rx);
ieee80211_verify_alignment(&rx);
rx.sta = sta_info_get_rx(vif,hdr->addr2);
if(rx.sta == NULL){
atbm_printk_err("%s: mac[%pM] not associate\n",__func__,hdr->addr2);
}
if(ieee80211_prepare_and_rx_handle(&rx,skb,true) == true)
return;
atbm_dev_kfree_skb(skb);
}
/*
* This is the receive path handler. It is called by a low level driver when an
* 802.11 MPDU is received from the hardware.
*/
void ieee80211_rx(struct atbm_vif *vif, struct sk_buff *skb)
{
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
WARN_ON_ONCE(softirq_count() == 0);
status->rx_flags = 0;
/*
* key references and virtual interfaces are protected using RCU
* and this requires that we are in a read-side RCU section during
* receive processing
*/
rcu_read_lock();
skb->pkt_type = 0;
__ieee80211_rx_handle_packet(vif, skb);
rcu_read_unlock();
return;
}