* published by the Free Software Foundation.
*/
+#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <net/ieee80211_radiotap.h>
#include "ieee80211_i.h"
-#include "ieee80211_led.h"
-#include "ieee80211_common.h"
+#include "led.h"
+#include "mesh.h"
#include "wep.h"
#include "wpa.h"
#include "tkip.h"
#include "wme.h"
-/* pre-rx handlers
+u8 ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,
+ struct tid_ampdu_rx *tid_agg_rx,
+ struct sk_buff *skb, u16 mpdu_seq_num,
+ int bar_req);
+/*
+ * monitor mode reception
*
- * these don't have dev/sdata fields in the rx data
- * The sta value should also not be used because it may
- * be NULL even though a STA (in IBSS mode) will be added.
+ * This function cleans up the SKB, i.e. it removes all the stuff
+ * only useful for monitoring.
*/
-
-static ieee80211_txrx_result
-ieee80211_rx_h_parse_qos(struct ieee80211_txrx_data *rx)
+static struct sk_buff *remove_monitor_info(struct ieee80211_local *local,
+ struct sk_buff *skb,
+ int rtap_len)
{
- u8 *data = rx->skb->data;
- int tid;
-
- /* does the frame have a qos control field? */
- if (WLAN_FC_IS_QOS_DATA(rx->fc)) {
- u8 *qc = data + ieee80211_get_hdrlen(rx->fc) - QOS_CONTROL_LEN;
- /* frame has qos control */
- tid = qc[0] & QOS_CONTROL_TID_MASK;
- } else {
- if (unlikely((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT)) {
- /* Separate TID for management frames */
- tid = NUM_RX_DATA_QUEUES - 1;
- } else {
- /* no qos control present */
- tid = 0; /* 802.1d - Best Effort */
+ skb_pull(skb, rtap_len);
+
+ if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS) {
+ if (likely(skb->len > FCS_LEN))
+ skb_trim(skb, skb->len - FCS_LEN);
+ else {
+ /* driver bug */
+ WARN_ON(1);
+ dev_kfree_skb(skb);
+ skb = NULL;
}
}
- I802_DEBUG_INC(rx->local->wme_rx_queue[tid]);
- /* only a debug counter, sta might not be assigned properly yet */
- if (rx->sta)
- I802_DEBUG_INC(rx->sta->wme_rx_queue[tid]);
+ return skb;
+}
- rx->u.rx.queue = tid;
- /* 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;
+static inline int should_drop_frame(struct ieee80211_rx_status *status,
+ struct sk_buff *skb,
+ int present_fcs_len,
+ int radiotap_len)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+
+ if (status->flag & (RX_FLAG_FAILED_FCS_CRC | RX_FLAG_FAILED_PLCP_CRC))
+ return 1;
+ if (unlikely(skb->len < 16 + present_fcs_len + radiotap_len))
+ return 1;
+ if (ieee80211_is_ctl(hdr->frame_control) &&
+ !ieee80211_is_pspoll(hdr->frame_control) &&
+ !ieee80211_is_back_req(hdr->frame_control))
+ return 1;
+ return 0;
+}
+
+static int
+ieee80211_rx_radiotap_len(struct ieee80211_local *local,
+ struct ieee80211_rx_status *status)
+{
+ int len;
- return TXRX_CONTINUE;
+ /* always present fields */
+ len = sizeof(struct ieee80211_radiotap_header) + 9;
+
+ if (status->flag & RX_FLAG_TSFT)
+ len += 8;
+ if (local->hw.flags & IEEE80211_HW_SIGNAL_DB ||
+ local->hw.flags & IEEE80211_HW_SIGNAL_DBM)
+ len += 1;
+ if (local->hw.flags & IEEE80211_HW_NOISE_DBM)
+ len += 1;
+
+ if (len & 1) /* padding for RX_FLAGS if necessary */
+ len++;
+
+ /* make sure radiotap starts at a naturally aligned address */
+ if (len % 8)
+ len = roundup(len, 8);
+
+ return len;
}
-static ieee80211_txrx_result
-ieee80211_rx_h_load_stats(struct ieee80211_txrx_data *rx)
+/**
+ * ieee80211_add_rx_radiotap_header - add radiotap header
+ *
+ * add a radiotap header containing all the fields which the hardware provided.
+ */
+static void
+ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
+ struct sk_buff *skb,
+ struct ieee80211_rx_status *status,
+ struct ieee80211_rate *rate,
+ int rtap_len)
{
- struct ieee80211_local *local = rx->local;
- struct sk_buff *skb = rx->skb;
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
- u32 load = 0, hdrtime;
- struct ieee80211_rate *rate;
- struct ieee80211_hw_mode *mode = local->hw.conf.mode;
- int i;
+ struct ieee80211_radiotap_header *rthdr;
+ unsigned char *pos;
- /* Estimate total channel use caused by this frame */
+ rthdr = (struct ieee80211_radiotap_header *)skb_push(skb, rtap_len);
+ memset(rthdr, 0, rtap_len);
- if (unlikely(mode->num_rates < 0))
- return TXRX_CONTINUE;
+ /* radiotap header, set always present flags */
+ rthdr->it_present =
+ cpu_to_le32((1 << IEEE80211_RADIOTAP_FLAGS) |
+ (1 << IEEE80211_RADIOTAP_RATE) |
+ (1 << IEEE80211_RADIOTAP_CHANNEL) |
+ (1 << IEEE80211_RADIOTAP_ANTENNA) |
+ (1 << IEEE80211_RADIOTAP_RX_FLAGS));
+ rthdr->it_len = cpu_to_le16(rtap_len);
- rate = &mode->rates[0];
- for (i = 0; i < mode->num_rates; i++) {
- if (mode->rates[i].val == rx->u.rx.status->rate) {
- rate = &mode->rates[i];
- break;
- }
- }
+ pos = (unsigned char *)(rthdr+1);
- /* 1 bit at 1 Mbit/s takes 1 usec; in channel_use values,
- * 1 usec = 1/8 * (1080 / 10) = 13.5 */
+ /* the order of the following fields is important */
- if (mode->mode == MODE_IEEE80211A ||
- (mode->mode == MODE_IEEE80211G &&
- rate->flags & IEEE80211_RATE_ERP))
- hdrtime = CHAN_UTIL_HDR_SHORT;
- else
- hdrtime = CHAN_UTIL_HDR_LONG;
+ /* IEEE80211_RADIOTAP_TSFT */
+ if (status->flag & RX_FLAG_TSFT) {
+ *(__le64 *)pos = cpu_to_le64(status->mactime);
+ rthdr->it_present |=
+ cpu_to_le32(1 << IEEE80211_RADIOTAP_TSFT);
+ pos += 8;
+ }
- load = hdrtime;
- if (!is_multicast_ether_addr(hdr->addr1))
- load += hdrtime;
+ /* IEEE80211_RADIOTAP_FLAGS */
+ if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS)
+ *pos |= IEEE80211_RADIOTAP_F_FCS;
+ pos++;
+
+ /* IEEE80211_RADIOTAP_RATE */
+ *pos = rate->bitrate / 5;
+ pos++;
+
+ /* IEEE80211_RADIOTAP_CHANNEL */
+ *(__le16 *)pos = cpu_to_le16(status->freq);
+ pos += 2;
+ if (status->band == IEEE80211_BAND_5GHZ)
+ *(__le16 *)pos = cpu_to_le16(IEEE80211_CHAN_OFDM |
+ IEEE80211_CHAN_5GHZ);
+ else
+ *(__le16 *)pos = cpu_to_le16(IEEE80211_CHAN_DYN |
+ IEEE80211_CHAN_2GHZ);
+ pos += 2;
+
+ /* IEEE80211_RADIOTAP_DBM_ANTSIGNAL */
+ if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) {
+ *pos = status->signal;
+ rthdr->it_present |=
+ cpu_to_le32(1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL);
+ pos++;
+ }
- load += skb->len * rate->rate_inv;
+ /* IEEE80211_RADIOTAP_DBM_ANTNOISE */
+ if (local->hw.flags & IEEE80211_HW_NOISE_DBM) {
+ *pos = status->noise;
+ rthdr->it_present |=
+ cpu_to_le32(1 << IEEE80211_RADIOTAP_DBM_ANTNOISE);
+ pos++;
+ }
- /* Divide channel_use by 8 to avoid wrapping around the counter */
- load >>= CHAN_UTIL_SHIFT;
- local->channel_use_raw += load;
- rx->u.rx.load = load;
+ /* IEEE80211_RADIOTAP_LOCK_QUALITY is missing */
- return TXRX_CONTINUE;
-}
+ /* IEEE80211_RADIOTAP_ANTENNA */
+ *pos = status->antenna;
+ pos++;
-ieee80211_rx_handler ieee80211_rx_pre_handlers[] =
-{
- ieee80211_rx_h_parse_qos,
- ieee80211_rx_h_load_stats,
- NULL
-};
+ /* IEEE80211_RADIOTAP_DB_ANTSIGNAL */
+ if (local->hw.flags & IEEE80211_HW_SIGNAL_DB) {
+ *pos = status->signal;
+ rthdr->it_present |=
+ cpu_to_le32(1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL);
+ pos++;
+ }
-/* rx handlers */
+ /* IEEE80211_RADIOTAP_DB_ANTNOISE is not used */
-static ieee80211_txrx_result
-ieee80211_rx_h_if_stats(struct ieee80211_txrx_data *rx)
-{
- if (rx->sta)
- rx->sta->channel_use_raw += rx->u.rx.load;
- rx->sdata->channel_use_raw += rx->u.rx.load;
- return TXRX_CONTINUE;
+ /* IEEE80211_RADIOTAP_RX_FLAGS */
+ /* ensure 2 byte alignment for the 2 byte field as required */
+ if ((pos - (unsigned char *)rthdr) & 1)
+ pos++;
+ /* FIXME: when radiotap gets a 'bad PLCP' flag use it here */
+ if (status->flag & (RX_FLAG_FAILED_FCS_CRC | RX_FLAG_FAILED_PLCP_CRC))
+ *(__le16 *)pos |= cpu_to_le16(IEEE80211_RADIOTAP_F_RX_BADFCS);
+ pos += 2;
}
-static void
-ieee80211_rx_monitor(struct net_device *dev, struct sk_buff *skb,
- struct ieee80211_rx_status *status)
+/*
+ * This function copies a received frame to all monitor interfaces and
+ * returns a cleaned-up SKB that no longer includes the FCS nor the
+ * radiotap header the driver might have added.
+ */
+static struct sk_buff *
+ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
+ struct ieee80211_rx_status *status,
+ struct ieee80211_rate *rate)
{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_rate *rate;
- struct ieee80211_rtap_hdr {
- struct ieee80211_radiotap_header hdr;
- u8 flags;
- u8 rate;
- __le16 chan_freq;
- __le16 chan_flags;
- u8 antsignal;
- u8 padding_for_rxflags;
- __le16 rx_flags;
- } __attribute__ ((packed)) *rthdr;
-
- skb->dev = dev;
+ struct ieee80211_sub_if_data *sdata;
+ int needed_headroom = 0;
+ struct sk_buff *skb, *skb2;
+ struct net_device *prev_dev = NULL;
+ int present_fcs_len = 0;
+ int rtap_len = 0;
+ /*
+ * First, we may need to make a copy of the skb because
+ * (1) we need to modify it for radiotap (if not present), and
+ * (2) the other RX handlers will modify the skb we got.
+ *
+ * We don't need to, of course, if we aren't going to return
+ * the SKB because it has a bad FCS/PLCP checksum.
+ */
if (status->flag & RX_FLAG_RADIOTAP)
- goto out;
+ rtap_len = ieee80211_get_radiotap_len(origskb->data);
+ else
+ /* room for the radiotap header based on driver features */
+ needed_headroom = ieee80211_rx_radiotap_len(local, status);
- if (skb_headroom(skb) < sizeof(*rthdr)) {
- I802_DEBUG_INC(local->rx_expand_skb_head);
- if (pskb_expand_head(skb, sizeof(*rthdr), 0, GFP_ATOMIC)) {
- dev_kfree_skb(skb);
- return;
+ if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS)
+ present_fcs_len = FCS_LEN;
+
+ if (!local->monitors) {
+ if (should_drop_frame(status, origskb, present_fcs_len,
+ rtap_len)) {
+ dev_kfree_skb(origskb);
+ return NULL;
}
+
+ return remove_monitor_info(local, origskb, rtap_len);
}
- rthdr = (struct ieee80211_rtap_hdr *) skb_push(skb, sizeof(*rthdr));
- memset(rthdr, 0, sizeof(*rthdr));
- rthdr->hdr.it_len = cpu_to_le16(sizeof(*rthdr));
- rthdr->hdr.it_present =
- cpu_to_le32((1 << IEEE80211_RADIOTAP_FLAGS) |
- (1 << IEEE80211_RADIOTAP_RATE) |
- (1 << IEEE80211_RADIOTAP_CHANNEL) |
- (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL) |
- (1 << IEEE80211_RADIOTAP_RX_FLAGS));
- rthdr->flags = local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS ?
- IEEE80211_RADIOTAP_F_FCS : 0;
+ if (should_drop_frame(status, origskb, present_fcs_len, rtap_len)) {
+ /* only need to expand headroom if necessary */
+ skb = origskb;
+ origskb = NULL;
- /* FIXME: when radiotap gets a 'bad PLCP' flag use it here */
- rthdr->rx_flags = 0;
- if (status->flag &
- (RX_FLAG_FAILED_FCS_CRC | RX_FLAG_FAILED_PLCP_CRC))
- rthdr->rx_flags |= cpu_to_le16(IEEE80211_RADIOTAP_F_RX_BADFCS);
+ /*
+ * This shouldn't trigger often because most devices have an
+ * RX header they pull before we get here, and that should
+ * be big enough for our radiotap information. We should
+ * probably export the length to drivers so that we can have
+ * them allocate enough headroom to start with.
+ */
+ if (skb_headroom(skb) < needed_headroom &&
+ pskb_expand_head(skb, needed_headroom, 0, GFP_ATOMIC)) {
+ dev_kfree_skb(skb);
+ return NULL;
+ }
+ } else {
+ /*
+ * Need to make a copy and possibly remove radiotap header
+ * and FCS from the original.
+ */
+ skb = skb_copy_expand(origskb, needed_headroom, 0, GFP_ATOMIC);
- rate = ieee80211_get_rate(local, status->phymode, status->rate);
- if (rate)
- rthdr->rate = rate->rate / 5;
+ origskb = remove_monitor_info(local, origskb, rtap_len);
- rthdr->chan_freq = cpu_to_le16(status->freq);
- rthdr->chan_flags =
- status->phymode == MODE_IEEE80211A ?
- cpu_to_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ) :
- cpu_to_le16(IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ);
- rthdr->antsignal = status->ssi;
+ if (!skb)
+ return origskb;
+ }
- out:
- dev->stats.rx_packets++;
- dev->stats.rx_bytes += skb->len;
+ /* if necessary, prepend radiotap information */
+ if (!(status->flag & RX_FLAG_RADIOTAP))
+ ieee80211_add_rx_radiotap_header(local, skb, status, rate,
+ needed_headroom);
- skb_set_mac_header(skb, 0);
+ skb_reset_mac_header(skb);
skb->ip_summed = CHECKSUM_UNNECESSARY;
skb->pkt_type = PACKET_OTHERHOST;
skb->protocol = htons(ETH_P_802_2);
- memset(skb->cb, 0, sizeof(skb->cb));
- netif_rx(skb);
+
+ list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+ if (!netif_running(sdata->dev))
+ continue;
+
+ if (sdata->vif.type != IEEE80211_IF_TYPE_MNTR)
+ continue;
+
+ if (sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES)
+ continue;
+
+ if (prev_dev) {
+ skb2 = skb_clone(skb, GFP_ATOMIC);
+ if (skb2) {
+ skb2->dev = prev_dev;
+ netif_rx(skb2);
+ }
+ }
+
+ prev_dev = sdata->dev;
+ sdata->dev->stats.rx_packets++;
+ sdata->dev->stats.rx_bytes += skb->len;
+ }
+
+ if (prev_dev) {
+ skb->dev = prev_dev;
+ netif_rx(skb);
+ } else
+ dev_kfree_skb(skb);
+
+ return origskb;
}
-static ieee80211_txrx_result
-ieee80211_rx_h_monitor(struct ieee80211_txrx_data *rx)
+
+static void ieee80211_parse_qos(struct ieee80211_rx_data *rx)
{
- if (rx->sdata->type == IEEE80211_IF_TYPE_MNTR) {
- ieee80211_rx_monitor(rx->dev, rx->skb, rx->u.rx.status);
- return TXRX_QUEUED;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
+ int tid;
+
+ /* 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 (*qc & IEEE80211_QOS_CONTROL_A_MSDU_PRESENT)
+ rx->flags |= IEEE80211_RX_AMSDU;
+ else
+ rx->flags &= ~IEEE80211_RX_AMSDU;
+ } else {
+ if (unlikely(ieee80211_is_mgmt(hdr->frame_control))) {
+ /* Separate TID for management frames */
+ tid = NUM_RX_DATA_QUEUES - 1;
+ } else {
+ /* no qos control present */
+ tid = 0; /* 802.1d - Best Effort */
+ }
}
+ rx->queue = tid;
+ /* 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;
+}
+
+static void ieee80211_verify_ip_alignment(struct ieee80211_rx_data *rx)
+{
+#ifdef CONFIG_MAC80211_DEBUG_PACKET_ALIGNMENT
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
+ int hdrlen;
+
+ if (!ieee80211_is_data_present(hdr->frame_control))
+ return;
+
/*
- * Drop frames with failed FCS/PLCP checksums here, they are only
- * relevant for monitor mode, the rest of the stack should never
- * see them.
+ * Drivers are required to 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.
+ *
+ * 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 inbetween the 802.11
+ * header and the payload is not supported, the driver is required
+ * to move the 802.11 header further back in that case.
*/
- if (rx->u.rx.status->flag &
- (RX_FLAG_FAILED_FCS_CRC | RX_FLAG_FAILED_PLCP_CRC))
- return TXRX_DROP;
+ hdrlen = ieee80211_hdrlen(hdr->frame_control);
+ if (rx->flags & IEEE80211_RX_AMSDU)
+ hdrlen += ETH_HLEN;
+ WARN_ON_ONCE(((unsigned long)(rx->skb->data + hdrlen)) & 3);
+#endif
+}
- if (rx->u.rx.status->flag & RX_FLAG_RADIOTAP)
- skb_pull(rx->skb, ieee80211_get_radiotap_len(rx->skb->data));
- return TXRX_CONTINUE;
-}
+/* rx handlers */
-static ieee80211_txrx_result
-ieee80211_rx_h_passive_scan(struct ieee80211_txrx_data *rx)
+static ieee80211_rx_result debug_noinline
+ieee80211_rx_h_passive_scan(struct ieee80211_rx_data *rx)
{
struct ieee80211_local *local = rx->local;
struct sk_buff *skb = rx->skb;
- if (unlikely(local->sta_scanning != 0)) {
- ieee80211_sta_rx_scan(rx->dev, skb, rx->u.rx.status);
- return TXRX_QUEUED;
+ if (unlikely(local->sta_hw_scanning))
+ return ieee80211_sta_rx_scan(rx->dev, skb, rx->status);
+
+ if (unlikely(local->sta_sw_scanning)) {
+ /* drop all the other packets during a software scan anyway */
+ if (ieee80211_sta_rx_scan(rx->dev, skb, rx->status)
+ != RX_QUEUED)
+ dev_kfree_skb(skb);
+ return RX_QUEUED;
}
- if (unlikely(rx->flags & IEEE80211_TXRXD_RXIN_SCAN)) {
+ if (unlikely(rx->flags & IEEE80211_RX_IN_SCAN)) {
/* scanning finished during invoking of handlers */
I802_DEBUG_INC(local->rx_handlers_drop_passive_scan);
- return TXRX_DROP;
+ return RX_DROP_UNUSABLE;
}
- return TXRX_CONTINUE;
+ return RX_CONTINUE;
}
-static ieee80211_txrx_result
-ieee80211_rx_h_check(struct ieee80211_txrx_data *rx)
+static ieee80211_rx_result
+ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
{
- struct ieee80211_hdr *hdr;
- hdr = (struct ieee80211_hdr *) rx->skb->data;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
+ unsigned int hdrlen = ieee80211_hdrlen(hdr->frame_control);
+
+ if (ieee80211_is_data(hdr->frame_control)) {
+ if (!ieee80211_has_a4(hdr->frame_control))
+ return RX_DROP_MONITOR;
+ if (memcmp(hdr->addr4, rx->dev->dev_addr, ETH_ALEN) == 0)
+ return RX_DROP_MONITOR;
+ }
+
+ /* If there is not an established peer link and this is not a peer link
+ * establisment frame, beacon or probe, drop the frame.
+ */
+
+ if (!rx->sta || sta_plink_state(rx->sta) != PLINK_ESTAB) {
+ struct ieee80211_mgmt *mgmt;
+
+ if (!ieee80211_is_mgmt(hdr->frame_control))
+ return RX_DROP_MONITOR;
+
+ if (ieee80211_is_action(hdr->frame_control)) {
+ mgmt = (struct ieee80211_mgmt *)hdr;
+ if (mgmt->u.action.category != PLINK_CATEGORY)
+ return RX_DROP_MONITOR;
+ return RX_CONTINUE;
+ }
+
+ if (ieee80211_is_probe_req(hdr->frame_control) ||
+ ieee80211_is_probe_resp(hdr->frame_control) ||
+ ieee80211_is_beacon(hdr->frame_control))
+ return RX_CONTINUE;
+
+ return RX_DROP_MONITOR;
+
+ }
+
+#define msh_h_get(h, l) ((struct ieee80211s_hdr *) ((u8 *)h + l))
+
+ if (ieee80211_is_data(hdr->frame_control) &&
+ is_multicast_ether_addr(hdr->addr1) &&
+ mesh_rmc_check(hdr->addr4, msh_h_get(hdr, hdrlen), rx->dev))
+ return RX_DROP_MONITOR;
+#undef msh_h_get
+
+ return RX_CONTINUE;
+}
+
+
+static ieee80211_rx_result debug_noinline
+ieee80211_rx_h_check(struct ieee80211_rx_data *rx)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
/* Drop duplicate 802.11 retransmissions (IEEE 802.11 Chap. 9.2.9) */
if (rx->sta && !is_multicast_ether_addr(hdr->addr1)) {
- if (unlikely(rx->fc & IEEE80211_FCTL_RETRY &&
- rx->sta->last_seq_ctrl[rx->u.rx.queue] ==
+ if (unlikely(ieee80211_has_retry(hdr->frame_control) &&
+ rx->sta->last_seq_ctrl[rx->queue] ==
hdr->seq_ctrl)) {
- if (rx->flags & IEEE80211_TXRXD_RXRA_MATCH) {
+ if (rx->flags & IEEE80211_RX_RA_MATCH) {
rx->local->dot11FrameDuplicateCount++;
rx->sta->num_duplicates++;
}
- return TXRX_DROP;
+ return RX_DROP_MONITOR;
} else
- rx->sta->last_seq_ctrl[rx->u.rx.queue] = hdr->seq_ctrl;
+ rx->sta->last_seq_ctrl[rx->queue] = hdr->seq_ctrl;
}
- if ((rx->local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS) &&
- rx->skb->len > FCS_LEN)
- skb_trim(rx->skb, rx->skb->len - FCS_LEN);
-
if (unlikely(rx->skb->len < 16)) {
I802_DEBUG_INC(rx->local->rx_handlers_drop_short);
- return TXRX_DROP;
+ return RX_DROP_MONITOR;
}
- if (!(rx->flags & IEEE80211_TXRXD_RXRA_MATCH))
- rx->skb->pkt_type = PACKET_OTHERHOST;
- else if (compare_ether_addr(rx->dev->dev_addr, hdr->addr1) == 0)
- rx->skb->pkt_type = PACKET_HOST;
- else if (is_multicast_ether_addr(hdr->addr1)) {
- if (is_broadcast_ether_addr(hdr->addr1))
- rx->skb->pkt_type = PACKET_BROADCAST;
- else
- rx->skb->pkt_type = PACKET_MULTICAST;
- } else
- rx->skb->pkt_type = PACKET_OTHERHOST;
-
/* Drop disallowed frame classes based on STA auth/assoc state;
* IEEE 802.11, Chap 5.5.
*
* deauth/disassoc frames when needed. In addition, hostapd is
* responsible for filtering on both auth and assoc states.
*/
- if (unlikely(((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA ||
- ((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL &&
- (rx->fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PSPOLL)) &&
- rx->sdata->type != IEEE80211_IF_TYPE_IBSS &&
- (!rx->sta || !(rx->sta->flags & WLAN_STA_ASSOC)))) {
- if ((!(rx->fc & IEEE80211_FCTL_FROMDS) &&
- !(rx->fc & IEEE80211_FCTL_TODS) &&
- (rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA)
- || !(rx->flags & IEEE80211_TXRXD_RXRA_MATCH)) {
+
+ if (ieee80211_vif_is_mesh(&rx->sdata->vif))
+ return ieee80211_rx_mesh_check(rx);
+
+ if (unlikely((ieee80211_is_data(hdr->frame_control) ||
+ ieee80211_is_pspoll(hdr->frame_control)) &&
+ rx->sdata->vif.type != IEEE80211_IF_TYPE_IBSS &&
+ (!rx->sta || !test_sta_flags(rx->sta, WLAN_STA_ASSOC)))) {
+ if ((!ieee80211_has_fromds(hdr->frame_control) &&
+ !ieee80211_has_tods(hdr->frame_control) &&
+ ieee80211_is_data(hdr->frame_control)) ||
+ !(rx->flags & IEEE80211_RX_RA_MATCH)) {
/* Drop IBSS frames and frames for other hosts
* silently. */
- return TXRX_DROP;
+ return RX_DROP_MONITOR;
}
- if (!rx->local->apdev)
- return TXRX_DROP;
-
- ieee80211_rx_mgmt(rx->local, rx->skb, rx->u.rx.status,
- ieee80211_msg_sta_not_assoc);
- return TXRX_QUEUED;
+ return RX_DROP_MONITOR;
}
- return TXRX_CONTINUE;
+ return RX_CONTINUE;
}
-static ieee80211_txrx_result
-ieee80211_rx_h_load_key(struct ieee80211_txrx_data *rx)
+static ieee80211_rx_result debug_noinline
+ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
{
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
int keyidx;
int hdrlen;
+ ieee80211_rx_result result = RX_DROP_UNUSABLE;
struct ieee80211_key *stakey = NULL;
/*
* possible.
*/
- if (!(rx->fc & IEEE80211_FCTL_PROTECTED))
- return TXRX_CONTINUE;
+ if (!ieee80211_has_protected(hdr->frame_control))
+ return RX_CONTINUE;
/*
- * No point in finding a key if the frame is neither
+ * No point in finding a key and decrypting if the frame is neither
* addressed to us nor a multicast frame.
*/
- if (!(rx->flags & IEEE80211_TXRXD_RXRA_MATCH))
- return TXRX_CONTINUE;
+ if (!(rx->flags & IEEE80211_RX_RA_MATCH))
+ return RX_CONTINUE;
if (rx->sta)
stakey = rcu_dereference(rx->sta->key);
* we somehow allow the driver to tell us which key
* the hardware used if this flag is set?
*/
- if ((rx->u.rx.status->flag & RX_FLAG_DECRYPTED) &&
- (rx->u.rx.status->flag & RX_FLAG_IV_STRIPPED))
- return TXRX_CONTINUE;
+ if ((rx->status->flag & RX_FLAG_DECRYPTED) &&
+ (rx->status->flag & RX_FLAG_IV_STRIPPED))
+ return RX_CONTINUE;
- hdrlen = ieee80211_get_hdrlen(rx->fc);
+ hdrlen = ieee80211_hdrlen(hdr->frame_control);
if (rx->skb->len < 8 + hdrlen)
- return TXRX_DROP; /* TODO: count this? */
+ return RX_DROP_UNUSABLE; /* TODO: count this? */
/*
* no need to call ieee80211_wep_get_keyidx,
if (rx->key) {
rx->key->tx_rx_count++;
/* TODO: add threshold stuff again */
+ } else {
+ return RX_DROP_MONITOR;
+ }
+
+ /* Check for weak IVs if possible */
+ if (rx->sta && rx->key->conf.alg == ALG_WEP &&
+ ieee80211_is_data(hdr->frame_control) &&
+ (!(rx->status->flag & RX_FLAG_IV_STRIPPED) ||
+ !(rx->status->flag & RX_FLAG_DECRYPTED)) &&
+ ieee80211_wep_is_weak_iv(rx->skb, rx->key))
+ rx->sta->wep_weak_iv_count++;
+
+ switch (rx->key->conf.alg) {
+ case ALG_WEP:
+ result = ieee80211_crypto_wep_decrypt(rx);
+ break;
+ case ALG_TKIP:
+ result = ieee80211_crypto_tkip_decrypt(rx);
+ break;
+ case ALG_CCMP:
+ result = ieee80211_crypto_ccmp_decrypt(rx);
+ break;
}
- return TXRX_CONTINUE;
+ /* either the frame has been decrypted or will be dropped */
+ rx->status->flag |= RX_FLAG_DECRYPTED;
+
+ return result;
}
static void ap_sta_ps_start(struct net_device *dev, struct sta_info *sta)
struct ieee80211_sub_if_data *sdata;
DECLARE_MAC_BUF(mac);
- sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
+ sdata = sta->sdata;
- if (sdata->bss)
- atomic_inc(&sdata->bss->num_sta_ps);
- sta->flags |= WLAN_STA_PS;
- sta->pspoll = 0;
+ atomic_inc(&sdata->bss->num_sta_ps);
+ set_and_clear_sta_flags(sta, WLAN_STA_PS, WLAN_STA_PSPOLL);
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
printk(KERN_DEBUG "%s: STA %s aid %d enters power save mode\n",
dev->name, print_mac(mac, sta->addr), sta->aid);
struct sk_buff *skb;
int sent = 0;
struct ieee80211_sub_if_data *sdata;
- struct ieee80211_tx_packet_data *pkt_data;
+ struct ieee80211_tx_info *info;
DECLARE_MAC_BUF(mac);
- sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
- if (sdata->bss)
- atomic_dec(&sdata->bss->num_sta_ps);
- sta->flags &= ~(WLAN_STA_PS | WLAN_STA_TIM);
- sta->pspoll = 0;
- if (!skb_queue_empty(&sta->ps_tx_buf)) {
- if (local->ops->set_tim)
- local->ops->set_tim(local_to_hw(local), sta->aid, 0);
- if (sdata->bss)
- bss_tim_clear(local, sdata->bss, sta->aid);
- }
+ sdata = sta->sdata;
+
+ atomic_dec(&sdata->bss->num_sta_ps);
+
+ clear_sta_flags(sta, WLAN_STA_PS | WLAN_STA_PSPOLL);
+
+ if (!skb_queue_empty(&sta->ps_tx_buf))
+ sta_info_clear_tim_bit(sta);
+
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
printk(KERN_DEBUG "%s: STA %s aid %d exits power save mode\n",
dev->name, print_mac(mac, sta->addr), sta->aid);
#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
+
/* Send all buffered frames to the station */
while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) {
- pkt_data = (struct ieee80211_tx_packet_data *) skb->cb;
+ info = IEEE80211_SKB_CB(skb);
sent++;
- pkt_data->flags |= IEEE80211_TXPD_REQUEUE;
+ info->flags |= IEEE80211_TX_CTL_REQUEUE;
dev_queue_xmit(skb);
}
while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) {
- pkt_data = (struct ieee80211_tx_packet_data *) skb->cb;
+ info = IEEE80211_SKB_CB(skb);
local->total_ps_buffered--;
sent++;
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
"since STA not sleeping anymore\n", dev->name,
print_mac(mac, sta->addr), sta->aid);
#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
- pkt_data->flags |= IEEE80211_TXPD_REQUEUE;
+ info->flags |= IEEE80211_TX_CTL_REQUEUE;
dev_queue_xmit(skb);
}
return sent;
}
-static ieee80211_txrx_result
-ieee80211_rx_h_sta_process(struct ieee80211_txrx_data *rx)
+static ieee80211_rx_result debug_noinline
+ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
{
struct sta_info *sta = rx->sta;
struct net_device *dev = rx->dev;
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
if (!sta)
- return TXRX_CONTINUE;
+ return RX_CONTINUE;
/* Update last_rx only for IBSS packets which are for the current
* BSSID to avoid keeping the current IBSS network alive in cases where
* other STAs are using different BSSID. */
- if (rx->sdata->type == IEEE80211_IF_TYPE_IBSS) {
- u8 *bssid = ieee80211_get_bssid(hdr, rx->skb->len);
+ if (rx->sdata->vif.type == IEEE80211_IF_TYPE_IBSS) {
+ u8 *bssid = ieee80211_get_bssid(hdr, rx->skb->len,
+ IEEE80211_IF_TYPE_IBSS);
if (compare_ether_addr(bssid, rx->sdata->u.sta.bssid) == 0)
sta->last_rx = jiffies;
} else
if (!is_multicast_ether_addr(hdr->addr1) ||
- rx->sdata->type == IEEE80211_IF_TYPE_STA) {
+ rx->sdata->vif.type == IEEE80211_IF_TYPE_STA) {
/* Update last_rx only for unicast frames in order to prevent
* the Probe Request frames (the only broadcast frames from a
* STA in infrastructure mode) from keeping a connection alive.
+ * Mesh beacons will update last_rx when if they are found to
+ * match the current local configuration when processed.
*/
sta->last_rx = jiffies;
}
- if (!(rx->flags & IEEE80211_TXRXD_RXRA_MATCH))
- return TXRX_CONTINUE;
+ if (!(rx->flags & IEEE80211_RX_RA_MATCH))
+ return RX_CONTINUE;
sta->rx_fragments++;
sta->rx_bytes += rx->skb->len;
- sta->last_rssi = rx->u.rx.status->ssi;
- sta->last_signal = rx->u.rx.status->signal;
- sta->last_noise = rx->u.rx.status->noise;
+ sta->last_signal = rx->status->signal;
+ sta->last_qual = rx->status->qual;
+ sta->last_noise = rx->status->noise;
- if (!(rx->fc & IEEE80211_FCTL_MOREFRAGS)) {
+ if (!ieee80211_has_morefrags(hdr->frame_control) &&
+ (rx->sdata->vif.type == IEEE80211_IF_TYPE_AP ||
+ rx->sdata->vif.type == IEEE80211_IF_TYPE_VLAN)) {
/* Change STA power saving mode only in the end of a frame
* exchange sequence */
- if ((sta->flags & WLAN_STA_PS) && !(rx->fc & IEEE80211_FCTL_PM))
- rx->u.rx.sent_ps_buffered += ap_sta_ps_end(dev, sta);
- else if (!(sta->flags & WLAN_STA_PS) &&
- (rx->fc & IEEE80211_FCTL_PM))
+ if (test_sta_flags(sta, WLAN_STA_PS) &&
+ !ieee80211_has_pm(hdr->frame_control))
+ rx->sent_ps_buffered += ap_sta_ps_end(dev, sta);
+ else if (!test_sta_flags(sta, WLAN_STA_PS) &&
+ ieee80211_has_pm(hdr->frame_control))
ap_sta_ps_start(dev, sta);
}
/* Drop data::nullfunc frames silently, since they are used only to
* control station power saving mode. */
- if ((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA &&
- (rx->fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_NULLFUNC) {
+ if (ieee80211_is_nullfunc(hdr->frame_control)) {
I802_DEBUG_INC(rx->local->rx_handlers_drop_nullfunc);
/* Update counter and free packet here to avoid counting this
* as a dropped packed. */
sta->rx_packets++;
dev_kfree_skb(rx->skb);
- return TXRX_QUEUED;
+ return RX_QUEUED;
}
- return TXRX_CONTINUE;
+ return RX_CONTINUE;
} /* ieee80211_rx_h_sta_process */
-static ieee80211_txrx_result
-ieee80211_rx_h_wep_weak_iv_detection(struct ieee80211_txrx_data *rx)
-{
- if (!rx->sta || !(rx->fc & IEEE80211_FCTL_PROTECTED) ||
- (rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA ||
- !rx->key || rx->key->conf.alg != ALG_WEP ||
- !(rx->flags & IEEE80211_TXRXD_RXRA_MATCH))
- return TXRX_CONTINUE;
-
- /* Check for weak IVs, if hwaccel did not remove IV from the frame */
- if (!(rx->u.rx.status->flag & RX_FLAG_IV_STRIPPED) ||
- !(rx->u.rx.status->flag & RX_FLAG_DECRYPTED))
- if (ieee80211_wep_is_weak_iv(rx->skb, rx->key))
- rx->sta->wep_weak_iv_count++;
-
- return TXRX_CONTINUE;
-}
-
-static ieee80211_txrx_result
-ieee80211_rx_h_wep_decrypt(struct ieee80211_txrx_data *rx)
-{
- if ((rx->key && rx->key->conf.alg != ALG_WEP) ||
- !(rx->fc & IEEE80211_FCTL_PROTECTED) ||
- ((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA &&
- ((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT ||
- (rx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_AUTH)))
- return TXRX_CONTINUE;
-
- if (!rx->key) {
- if (net_ratelimit())
- printk(KERN_DEBUG "%s: RX WEP frame, but no key set\n",
- rx->dev->name);
- return TXRX_DROP;
- }
-
- if (!(rx->u.rx.status->flag & RX_FLAG_DECRYPTED)) {
- if (ieee80211_wep_decrypt(rx->local, rx->skb, rx->key)) {
- if (net_ratelimit())
- printk(KERN_DEBUG "%s: RX WEP frame, decrypt "
- "failed\n", rx->dev->name);
- return TXRX_DROP;
- }
- } else if (!(rx->u.rx.status->flag & RX_FLAG_IV_STRIPPED)) {
- ieee80211_wep_remove_iv(rx->local, rx->skb, rx->key);
- /* remove ICV */
- skb_trim(rx->skb, rx->skb->len - 4);
- }
-
- return TXRX_CONTINUE;
-}
-
static inline struct ieee80211_fragment_entry *
ieee80211_reassemble_add(struct ieee80211_sub_if_data *sdata,
unsigned int frag, unsigned int seq, int rx_queue,
sdata->fragment_next = 0;
if (!skb_queue_empty(&entry->skb_list)) {
-#ifdef CONFIG_MAC80211_DEBUG
+#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
struct ieee80211_hdr *hdr =
(struct ieee80211_hdr *) entry->skb_list.next->data;
DECLARE_MAC_BUF(mac);
jiffies - entry->first_frag_time, entry->seq,
entry->last_frag, print_mac(mac, hdr->addr1),
print_mac(mac2, hdr->addr2));
-#endif /* CONFIG_MAC80211_DEBUG */
+#endif
__skb_queue_purge(&entry->skb_list);
}
compare_ether_addr(hdr->addr2, f_hdr->addr2) != 0)
continue;
- if (entry->first_frag_time + 2 * HZ < jiffies) {
+ if (time_after(jiffies, entry->first_frag_time + 2 * HZ)) {
__skb_queue_purge(&entry->skb_list);
continue;
}
return NULL;
}
-static ieee80211_txrx_result
-ieee80211_rx_h_defragment(struct ieee80211_txrx_data *rx)
+static ieee80211_rx_result debug_noinline
+ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
{
struct ieee80211_hdr *hdr;
u16 sc;
if (frag == 0) {
/* This is the first fragment of a new frame. */
entry = ieee80211_reassemble_add(rx->sdata, frag, seq,
- rx->u.rx.queue, &(rx->skb));
+ rx->queue, &(rx->skb));
if (rx->key && rx->key->conf.alg == ALG_CCMP &&
(rx->fc & IEEE80211_FCTL_PROTECTED)) {
/* Store CCMP PN so that we can verify that the next
* fragment has a sequential PN value. */
entry->ccmp = 1;
memcpy(entry->last_pn,
- rx->key->u.ccmp.rx_pn[rx->u.rx.queue],
+ rx->key->u.ccmp.rx_pn[rx->queue],
CCMP_PN_LEN);
}
- return TXRX_QUEUED;
+ return RX_QUEUED;
}
/* This is a fragment for a frame that should already be pending in
* fragment cache. Add this fragment to the end of the pending entry.
*/
entry = ieee80211_reassemble_find(rx->sdata, rx->fc, frag, seq,
- rx->u.rx.queue, hdr);
+ rx->queue, hdr);
if (!entry) {
I802_DEBUG_INC(rx->local->rx_handlers_drop_defrag);
- return TXRX_DROP;
+ return RX_DROP_MONITOR;
}
/* Verify that MPDUs within one MSDU have sequential PN values.
int i;
u8 pn[CCMP_PN_LEN], *rpn;
if (!rx->key || rx->key->conf.alg != ALG_CCMP)
- return TXRX_DROP;
+ return RX_DROP_UNUSABLE;
memcpy(pn, entry->last_pn, CCMP_PN_LEN);
for (i = CCMP_PN_LEN - 1; i >= 0; i--) {
pn[i]++;
if (pn[i])
break;
}
- rpn = rx->key->u.ccmp.rx_pn[rx->u.rx.queue];
- if (memcmp(pn, rpn, CCMP_PN_LEN) != 0) {
- if (net_ratelimit())
- printk(KERN_DEBUG "%s: defrag: CCMP PN not "
- "sequential A2=%s"
- " PN=%02x%02x%02x%02x%02x%02x "
- "(expected %02x%02x%02x%02x%02x%02x)\n",
- rx->dev->name, print_mac(mac, hdr->addr2),
- rpn[0], rpn[1], rpn[2], rpn[3], rpn[4],
- rpn[5], pn[0], pn[1], pn[2], pn[3],
- pn[4], pn[5]);
- return TXRX_DROP;
- }
+ rpn = rx->key->u.ccmp.rx_pn[rx->queue];
+ if (memcmp(pn, rpn, CCMP_PN_LEN))
+ return RX_DROP_UNUSABLE;
memcpy(entry->last_pn, pn, CCMP_PN_LEN);
}
entry->extra_len += rx->skb->len;
if (rx->fc & IEEE80211_FCTL_MOREFRAGS) {
rx->skb = NULL;
- return TXRX_QUEUED;
+ return RX_QUEUED;
}
rx->skb = __skb_dequeue(&entry->skb_list);
GFP_ATOMIC))) {
I802_DEBUG_INC(rx->local->rx_handlers_drop_defrag);
__skb_queue_purge(&entry->skb_list);
- return TXRX_DROP;
+ return RX_DROP_UNUSABLE;
}
}
while ((skb = __skb_dequeue(&entry->skb_list))) {
}
/* Complete frame has been reassembled - process it now */
- rx->flags |= IEEE80211_TXRXD_FRAGMENTED;
+ rx->flags |= IEEE80211_RX_FRAGMENTED;
out:
if (rx->sta)
rx->local->dot11MulticastReceivedFrameCount++;
else
ieee80211_led_rx(rx->local);
- return TXRX_CONTINUE;
+ return RX_CONTINUE;
}
-static ieee80211_txrx_result
-ieee80211_rx_h_ps_poll(struct ieee80211_txrx_data *rx)
+static ieee80211_rx_result debug_noinline
+ieee80211_rx_h_ps_poll(struct ieee80211_rx_data *rx)
{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
struct sk_buff *skb;
int no_pending_pkts;
DECLARE_MAC_BUF(mac);
if (likely(!rx->sta ||
(rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_CTL ||
(rx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_PSPOLL ||
- !(rx->flags & IEEE80211_TXRXD_RXRA_MATCH)))
- return TXRX_CONTINUE;
+ !(rx->flags & IEEE80211_RX_RA_MATCH)))
+ return RX_CONTINUE;
+
+ if ((sdata->vif.type != IEEE80211_IF_TYPE_AP) &&
+ (sdata->vif.type != IEEE80211_IF_TYPE_VLAN))
+ return RX_DROP_UNUSABLE;
skb = skb_dequeue(&rx->sta->tx_filtered);
if (!skb) {
struct ieee80211_hdr *hdr =
(struct ieee80211_hdr *) skb->data;
- /* tell TX path to send one frame even though the STA may
- * still remain is PS mode after this frame exchange */
- rx->sta->pspoll = 1;
+ /*
+ * Tell TX path to send one frame even though the STA may
+ * still remain is PS mode after this frame exchange.
+ */
+ set_sta_flags(rx->sta, WLAN_STA_PSPOLL);
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
printk(KERN_DEBUG "STA %s aid %d: PS Poll (entries after %d)\n",
/* Use MoreData flag to indicate whether there are more
* buffered frames for this STA */
- if (no_pending_pkts) {
+ if (no_pending_pkts)
hdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_MOREDATA);
- rx->sta->flags &= ~WLAN_STA_TIM;
- } else
+ else
hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA);
dev_queue_xmit(skb);
- if (no_pending_pkts) {
- if (rx->local->ops->set_tim)
- rx->local->ops->set_tim(local_to_hw(rx->local),
- rx->sta->aid, 0);
- if (rx->sdata->bss)
- bss_tim_clear(rx->local, rx->sdata->bss, rx->sta->aid);
- }
+ if (no_pending_pkts)
+ sta_info_clear_tim_bit(rx->sta);
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
- } else if (!rx->u.rx.sent_ps_buffered) {
+ } else if (!rx->sent_ps_buffered) {
+ /*
+ * FIXME: This can be the result of a race condition between
+ * us expiring a frame and the station polling for it.
+ * Should we send it a null-func frame indicating we
+ * have nothing buffered for it?
+ */
printk(KERN_DEBUG "%s: STA %s sent PS Poll even "
- "though there is no buffered frames for it\n",
+ "though there are no buffered frames for it\n",
rx->dev->name, print_mac(mac, rx->sta->addr));
#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
-
}
- /* Free PS Poll skb here instead of returning TXRX_DROP that would
+ /* Free PS Poll skb here instead of returning RX_DROP that would
* count as an dropped frame. */
dev_kfree_skb(rx->skb);
- return TXRX_QUEUED;
+ return RX_QUEUED;
}
-static ieee80211_txrx_result
-ieee80211_rx_h_remove_qos_control(struct ieee80211_txrx_data *rx)
+static ieee80211_rx_result debug_noinline
+ieee80211_rx_h_remove_qos_control(struct ieee80211_rx_data *rx)
{
- u16 fc = rx->fc;
u8 *data = rx->skb->data;
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) data;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)data;
- if (!WLAN_FC_IS_QOS_DATA(fc))
- return TXRX_CONTINUE;
+ if (!ieee80211_is_data_qos(hdr->frame_control))
+ return RX_CONTINUE;
/* remove the qos control field, update frame type and meta-data */
- memmove(data + 2, data, ieee80211_get_hdrlen(fc) - 2);
- hdr = (struct ieee80211_hdr *) skb_pull(rx->skb, 2);
+ memmove(data + IEEE80211_QOS_CTL_LEN, data,
+ ieee80211_hdrlen(hdr->frame_control) - IEEE80211_QOS_CTL_LEN);
+ hdr = (struct ieee80211_hdr *)skb_pull(rx->skb, IEEE80211_QOS_CTL_LEN);
/* change frame type to non QOS */
- rx->fc = fc &= ~IEEE80211_STYPE_QOS_DATA;
- hdr->frame_control = cpu_to_le16(fc);
+ rx->fc &= ~IEEE80211_STYPE_QOS_DATA;
+ hdr->frame_control &= ~cpu_to_le16(IEEE80211_STYPE_QOS_DATA);
- return TXRX_CONTINUE;
+ return RX_CONTINUE;
}
-static ieee80211_txrx_result
-ieee80211_rx_h_802_1x_pae(struct ieee80211_txrx_data *rx)
+static int
+ieee80211_802_1x_port_control(struct ieee80211_rx_data *rx)
{
- if (rx->sdata->eapol && ieee80211_is_eapol(rx->skb) &&
- rx->sdata->type != IEEE80211_IF_TYPE_STA &&
- (rx->flags & IEEE80211_TXRXD_RXRA_MATCH)) {
- /* Pass both encrypted and unencrypted EAPOL frames to user
- * space for processing. */
- if (!rx->local->apdev)
- return TXRX_DROP;
- ieee80211_rx_mgmt(rx->local, rx->skb, rx->u.rx.status,
- ieee80211_msg_normal);
- return TXRX_QUEUED;
- }
-
- if (unlikely(rx->sdata->ieee802_1x &&
- (rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA &&
- (rx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_NULLFUNC &&
- (!rx->sta || !(rx->sta->flags & WLAN_STA_AUTHORIZED)) &&
- !ieee80211_is_eapol(rx->skb))) {
-#ifdef CONFIG_MAC80211_DEBUG
- struct ieee80211_hdr *hdr =
- (struct ieee80211_hdr *) rx->skb->data;
- DECLARE_MAC_BUF(mac);
- printk(KERN_DEBUG "%s: dropped frame from %s"
- " (unauthorized port)\n", rx->dev->name,
- print_mac(mac, hdr->addr2));
-#endif /* CONFIG_MAC80211_DEBUG */
- return TXRX_DROP;
- }
+ if (unlikely(!rx->sta ||
+ !test_sta_flags(rx->sta, WLAN_STA_AUTHORIZED)))
+ return -EACCES;
- return TXRX_CONTINUE;
+ return 0;
}
-static ieee80211_txrx_result
-ieee80211_rx_h_drop_unencrypted(struct ieee80211_txrx_data *rx)
+static int
+ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx)
{
/*
* Pass through unencrypted frames if the hardware has
* decrypted them already.
*/
- if (rx->u.rx.status->flag & RX_FLAG_DECRYPTED)
- return TXRX_CONTINUE;
+ if (rx->status->flag & RX_FLAG_DECRYPTED)
+ return 0;
/* Drop unencrypted frames if key is set. */
if (unlikely(!(rx->fc & IEEE80211_FCTL_PROTECTED) &&
(rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA &&
(rx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_NULLFUNC &&
- (rx->key || rx->sdata->drop_unencrypted) &&
- (rx->sdata->eapol == 0 ||
- !ieee80211_is_eapol(rx->skb)))) {
- if (net_ratelimit())
- printk(KERN_DEBUG "%s: RX non-WEP frame, but expected "
- "encryption\n", rx->dev->name);
- return TXRX_DROP;
- }
- return TXRX_CONTINUE;
+ (rx->key || rx->sdata->drop_unencrypted)))
+ return -EACCES;
+
+ return 0;
}
-static ieee80211_txrx_result
-ieee80211_rx_h_data(struct ieee80211_txrx_data *rx)
+static int
+ieee80211_data_to_8023(struct ieee80211_rx_data *rx)
{
struct net_device *dev = rx->dev;
- struct ieee80211_local *local = rx->local;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data;
u16 fc, hdrlen, ethertype;
u8 *payload;
u8 dst[ETH_ALEN];
- u8 src[ETH_ALEN];
- struct sk_buff *skb = rx->skb, *skb2;
+ u8 src[ETH_ALEN] __aligned(2);
+ struct sk_buff *skb = rx->skb;
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
DECLARE_MAC_BUF(mac);
DECLARE_MAC_BUF(mac2);
DECLARE_MAC_BUF(mac4);
fc = rx->fc;
- if (unlikely((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA))
- return TXRX_CONTINUE;
if (unlikely(!WLAN_FC_DATA_PRESENT(fc)))
- return TXRX_DROP;
+ return -1;
hdrlen = ieee80211_get_hdrlen(fc);
+ if (ieee80211_vif_is_mesh(&sdata->vif)) {
+ int meshhdrlen = ieee80211_get_mesh_hdrlen(
+ (struct ieee80211s_hdr *) (skb->data + hdrlen));
+ /* Copy on cb:
+ * - mesh header: to be used for mesh forwarding
+ * decision. It will also be used as mesh header template at
+ * tx.c:ieee80211_subif_start_xmit() if interface
+ * type is mesh and skb->pkt_type == PACKET_OTHERHOST
+ * - ta: to be used if a RERR needs to be sent.
+ */
+ memcpy(skb->cb, skb->data + hdrlen, meshhdrlen);
+ memcpy(MESH_PREQ(skb), hdr->addr2, ETH_ALEN);
+ hdrlen += meshhdrlen;
+ }
+
/* convert IEEE 802.11 header + possible LLC headers into Ethernet
* header
* IEEE 802.11 address fields:
memcpy(dst, hdr->addr3, ETH_ALEN);
memcpy(src, hdr->addr2, ETH_ALEN);
- if (unlikely(sdata->type != IEEE80211_IF_TYPE_AP &&
- sdata->type != IEEE80211_IF_TYPE_VLAN)) {
- if (net_ratelimit())
- printk(KERN_DEBUG "%s: dropped ToDS frame "
- "(BSSID=%s SA=%s DA=%s)\n",
- dev->name,
- print_mac(mac, hdr->addr1),
- print_mac(mac2, hdr->addr2),
- print_mac(mac3, hdr->addr3));
- return TXRX_DROP;
- }
+ if (unlikely(sdata->vif.type != IEEE80211_IF_TYPE_AP &&
+ sdata->vif.type != IEEE80211_IF_TYPE_VLAN))
+ return -1;
break;
case (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS):
/* RA TA DA SA */
memcpy(dst, hdr->addr3, ETH_ALEN);
memcpy(src, hdr->addr4, ETH_ALEN);
- if (unlikely(sdata->type != IEEE80211_IF_TYPE_WDS)) {
- if (net_ratelimit())
- printk(KERN_DEBUG "%s: dropped FromDS&ToDS "
- "frame (RA=%s TA=%s DA=%s SA=%s)\n",
- rx->dev->name,
- print_mac(mac, hdr->addr1),
- print_mac(mac2, hdr->addr2),
- print_mac(mac3, hdr->addr3),
- print_mac(mac4, hdr->addr4));
- return TXRX_DROP;
- }
+ if (unlikely(sdata->vif.type != IEEE80211_IF_TYPE_WDS &&
+ sdata->vif.type != IEEE80211_IF_TYPE_MESH_POINT))
+ return -1;
break;
case IEEE80211_FCTL_FROMDS:
/* DA BSSID SA */
memcpy(dst, hdr->addr1, ETH_ALEN);
memcpy(src, hdr->addr3, ETH_ALEN);
- if (sdata->type != IEEE80211_IF_TYPE_STA ||
+ if (sdata->vif.type != IEEE80211_IF_TYPE_STA ||
(is_multicast_ether_addr(dst) &&
!compare_ether_addr(src, dev->dev_addr)))
- return TXRX_DROP;
+ return -1;
break;
case 0:
/* DA SA BSSID */
memcpy(dst, hdr->addr1, ETH_ALEN);
memcpy(src, hdr->addr2, ETH_ALEN);
- if (sdata->type != IEEE80211_IF_TYPE_IBSS) {
- if (net_ratelimit()) {
- printk(KERN_DEBUG "%s: dropped IBSS frame "
- "(DA=%s SA=%s BSSID=%s)\n",
- dev->name,
- print_mac(mac, hdr->addr1),
- print_mac(mac2, hdr->addr2),
- print_mac(mac3, hdr->addr3));
- }
- return TXRX_DROP;
- }
+ if (sdata->vif.type != IEEE80211_IF_TYPE_IBSS)
+ return -1;
break;
}
- payload = skb->data + hdrlen;
-
- if (unlikely(skb->len - hdrlen < 8)) {
- if (net_ratelimit()) {
- printk(KERN_DEBUG "%s: RX too short data frame "
- "payload\n", dev->name);
- }
- return TXRX_DROP;
- }
+ if (unlikely(skb->len - hdrlen < 8))
+ return -1;
+ payload = skb->data + hdrlen;
ethertype = (payload[6] << 8) | payload[7];
if (likely((compare_ether_addr(payload, rfc1042_header) == 0 &&
} else {
struct ethhdr *ehdr;
__be16 len;
+
skb_pull(skb, hdrlen);
len = htons(skb->len);
ehdr = (struct ethhdr *) skb_push(skb, sizeof(struct ethhdr));
memcpy(ehdr->h_source, src, ETH_ALEN);
ehdr->h_proto = len;
}
- skb->dev = dev;
+ return 0;
+}
- skb2 = NULL;
+/*
+ * requires that rx->skb is a frame with ethernet header
+ */
+static bool ieee80211_frame_allowed(struct ieee80211_rx_data *rx)
+{
+ static const u8 pae_group_addr[ETH_ALEN] __aligned(2)
+ = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x03 };
+ struct ethhdr *ehdr = (struct ethhdr *) rx->skb->data;
- dev->stats.rx_packets++;
- dev->stats.rx_bytes += skb->len;
+ /*
+ * Allow EAPOL frames to us/the PAE group address regardless
+ * of whether the frame was encrypted or not.
+ */
+ if (ehdr->h_proto == htons(ETH_P_PAE) &&
+ (compare_ether_addr(ehdr->h_dest, rx->dev->dev_addr) == 0 ||
+ compare_ether_addr(ehdr->h_dest, pae_group_addr) == 0))
+ return true;
- if (local->bridge_packets && (sdata->type == IEEE80211_IF_TYPE_AP
- || sdata->type == IEEE80211_IF_TYPE_VLAN) &&
- (rx->flags & IEEE80211_TXRXD_RXRA_MATCH)) {
- if (is_multicast_ether_addr(skb->data)) {
- /* send multicast frames both to higher layers in
- * local net stack and back to the wireless media */
- skb2 = skb_copy(skb, GFP_ATOMIC);
- if (!skb2 && net_ratelimit())
+ if (ieee80211_802_1x_port_control(rx) ||
+ ieee80211_drop_unencrypted(rx))
+ return false;
+
+ return true;
+}
+
+/*
+ * requires that rx->skb is a frame with ethernet header
+ */
+static void
+ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
+{
+ struct net_device *dev = rx->dev;
+ struct ieee80211_local *local = rx->local;
+ struct sk_buff *skb, *xmit_skb;
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ethhdr *ehdr = (struct ethhdr *) rx->skb->data;
+ struct sta_info *dsta;
+
+ skb = rx->skb;
+ xmit_skb = NULL;
+
+ if (local->bridge_packets && (sdata->vif.type == IEEE80211_IF_TYPE_AP ||
+ sdata->vif.type == IEEE80211_IF_TYPE_VLAN) &&
+ (rx->flags & IEEE80211_RX_RA_MATCH)) {
+ if (is_multicast_ether_addr(ehdr->h_dest)) {
+ /*
+ * send multicast frames both to higher layers in
+ * local net stack and back to the wireless medium
+ */
+ xmit_skb = skb_copy(skb, GFP_ATOMIC);
+ if (!xmit_skb && net_ratelimit())
printk(KERN_DEBUG "%s: failed to clone "
"multicast frame\n", dev->name);
} else {
- struct sta_info *dsta;
dsta = sta_info_get(local, skb->data);
- if (dsta && !dsta->dev) {
- if (net_ratelimit())
- printk(KERN_DEBUG "Station with null "
- "dev structure!\n");
- } else if (dsta && dsta->dev == dev) {
- /* Destination station is associated to this
- * AP, so send the frame directly to it and
- * do not pass the frame to local net stack.
+ if (dsta && dsta->sdata->dev == dev) {
+ /*
+ * The destination station is associated to
+ * this AP (in this VLAN), so send the frame
+ * directly to it and do not pass it to local
+ * net stack.
*/
- skb2 = skb;
+ xmit_skb = skb;
+ skb = NULL;
+ }
+ }
+ }
+
+ /* Mesh forwarding */
+ if (ieee80211_vif_is_mesh(&sdata->vif)) {
+ u8 *mesh_ttl = &((struct ieee80211s_hdr *)skb->cb)->ttl;
+ (*mesh_ttl)--;
+
+ if (is_multicast_ether_addr(skb->data)) {
+ if (*mesh_ttl > 0) {
+ xmit_skb = skb_copy(skb, GFP_ATOMIC);
+ if (xmit_skb)
+ xmit_skb->pkt_type = PACKET_OTHERHOST;
+ else if (net_ratelimit())
+ printk(KERN_DEBUG "%s: failed to clone "
+ "multicast frame\n", dev->name);
+ } else
+ IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.sta,
+ dropped_frames_ttl);
+ } else if (skb->pkt_type != PACKET_OTHERHOST &&
+ compare_ether_addr(dev->dev_addr, skb->data) != 0) {
+ if (*mesh_ttl == 0) {
+ IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.sta,
+ dropped_frames_ttl);
+ dev_kfree_skb(skb);
skb = NULL;
+ } else {
+ xmit_skb = skb;
+ xmit_skb->pkt_type = PACKET_OTHERHOST;
+ if (!(dev->flags & IFF_PROMISC))
+ skb = NULL;
}
- if (dsta)
- sta_info_put(dsta);
}
}
netif_rx(skb);
}
- if (skb2) {
+ if (xmit_skb) {
/* send to wireless media */
- skb2->protocol = __constant_htons(ETH_P_802_3);
- skb_set_network_header(skb2, 0);
- skb_set_mac_header(skb2, 0);
- dev_queue_xmit(skb2);
+ xmit_skb->protocol = htons(ETH_P_802_3);
+ skb_reset_network_header(xmit_skb);
+ skb_reset_mac_header(xmit_skb);
+ dev_queue_xmit(xmit_skb);
}
-
- return TXRX_QUEUED;
}
-static ieee80211_txrx_result
-ieee80211_rx_h_mgmt(struct ieee80211_txrx_data *rx)
+static ieee80211_rx_result debug_noinline
+ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx)
{
- struct ieee80211_sub_if_data *sdata;
+ struct net_device *dev = rx->dev;
+ struct ieee80211_local *local = rx->local;
+ u16 fc, ethertype;
+ u8 *payload;
+ struct sk_buff *skb = rx->skb, *frame = NULL;
+ const struct ethhdr *eth;
+ int remaining, err;
+ u8 dst[ETH_ALEN];
+ u8 src[ETH_ALEN];
+ DECLARE_MAC_BUF(mac);
+
+ fc = rx->fc;
+ if (unlikely((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA))
+ return RX_CONTINUE;
- if (!(rx->flags & IEEE80211_TXRXD_RXRA_MATCH))
- return TXRX_DROP;
+ if (unlikely(!WLAN_FC_DATA_PRESENT(fc)))
+ return RX_DROP_MONITOR;
- sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
- if ((sdata->type == IEEE80211_IF_TYPE_STA ||
- sdata->type == IEEE80211_IF_TYPE_IBSS) &&
- !rx->local->user_space_mlme) {
- ieee80211_sta_rx_mgmt(rx->dev, rx->skb, rx->u.rx.status);
- } else {
- /* Management frames are sent to hostapd for processing */
- if (!rx->local->apdev)
- return TXRX_DROP;
- ieee80211_rx_mgmt(rx->local, rx->skb, rx->u.rx.status,
- ieee80211_msg_normal);
+ if (!(rx->flags & IEEE80211_RX_AMSDU))
+ return RX_CONTINUE;
+
+ err = ieee80211_data_to_8023(rx);
+ if (unlikely(err))
+ return RX_DROP_UNUSABLE;
+
+ skb->dev = dev;
+
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += skb->len;
+
+ /* skip the wrapping header */
+ eth = (struct ethhdr *) skb_pull(skb, sizeof(struct ethhdr));
+ if (!eth)
+ return RX_DROP_UNUSABLE;
+
+ while (skb != frame) {
+ u8 padding;
+ __be16 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);
+ /* the last MSDU has no padding */
+ if (subframe_len > remaining)
+ return RX_DROP_UNUSABLE;
+
+ skb_pull(skb, sizeof(struct ethhdr));
+ /* if last subframe reuse skb */
+ if (remaining <= subframe_len + padding)
+ frame = skb;
+ else {
+ frame = dev_alloc_skb(local->hw.extra_tx_headroom +
+ subframe_len);
+
+ if (frame == NULL)
+ return RX_DROP_UNUSABLE;
+
+ skb_reserve(frame, local->hw.extra_tx_headroom +
+ sizeof(struct ethhdr));
+ memcpy(skb_put(frame, ntohs(len)), skb->data,
+ ntohs(len));
+
+ eth = (struct ethhdr *) skb_pull(skb, ntohs(len) +
+ padding);
+ if (!eth) {
+ dev_kfree_skb(frame);
+ return RX_DROP_UNUSABLE;
+ }
+ }
+
+ skb_reset_network_header(frame);
+ frame->dev = dev;
+ frame->priority = skb->priority;
+ rx->skb = frame;
+
+ payload = frame->data;
+ ethertype = (payload[6] << 8) | payload[7];
+
+ if (likely((compare_ether_addr(payload, rfc1042_header) == 0 &&
+ ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) ||
+ compare_ether_addr(payload,
+ bridge_tunnel_header) == 0)) {
+ /* remove RFC1042 or Bridge-Tunnel
+ * encapsulation and replace EtherType */
+ skb_pull(frame, 6);
+ memcpy(skb_push(frame, ETH_ALEN), src, ETH_ALEN);
+ memcpy(skb_push(frame, ETH_ALEN), dst, ETH_ALEN);
+ } else {
+ memcpy(skb_push(frame, sizeof(__be16)),
+ &len, sizeof(__be16));
+ memcpy(skb_push(frame, ETH_ALEN), src, ETH_ALEN);
+ memcpy(skb_push(frame, ETH_ALEN), dst, ETH_ALEN);
+ }
+
+ if (!ieee80211_frame_allowed(rx)) {
+ if (skb == frame) /* last frame */
+ return RX_DROP_UNUSABLE;
+ dev_kfree_skb(frame);
+ continue;
+ }
+
+ ieee80211_deliver_skb(rx);
}
- return TXRX_QUEUED;
+
+ return RX_QUEUED;
}
-static inline ieee80211_txrx_result __ieee80211_invoke_rx_handlers(
- struct ieee80211_local *local,
- ieee80211_rx_handler *handlers,
- struct ieee80211_txrx_data *rx,
- struct sta_info *sta)
+static ieee80211_rx_result debug_noinline
+ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
{
- ieee80211_rx_handler *handler;
- ieee80211_txrx_result res = TXRX_DROP;
+ struct net_device *dev = rx->dev;
+ u16 fc;
+ int err;
+
+ fc = rx->fc;
+ if (unlikely((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA))
+ return RX_CONTINUE;
+
+ if (unlikely(!WLAN_FC_DATA_PRESENT(fc)))
+ return RX_DROP_MONITOR;
- for (handler = handlers; *handler != NULL; handler++) {
- res = (*handler)(rx);
+ err = ieee80211_data_to_8023(rx);
+ if (unlikely(err))
+ return RX_DROP_UNUSABLE;
- switch (res) {
- case TXRX_CONTINUE:
- continue;
- case TXRX_DROP:
- I802_DEBUG_INC(local->rx_handlers_drop);
- if (sta)
- sta->rx_dropped++;
- break;
- case TXRX_QUEUED:
- I802_DEBUG_INC(local->rx_handlers_queued);
- break;
+ if (!ieee80211_frame_allowed(rx))
+ 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 debug_noinline
+ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx)
+{
+ struct ieee80211_local *local = rx->local;
+ struct ieee80211_hw *hw = &local->hw;
+ struct sk_buff *skb = rx->skb;
+ struct ieee80211_bar *bar = (struct ieee80211_bar *)skb->data;
+ struct tid_ampdu_rx *tid_agg_rx;
+ u16 start_seq_num;
+ u16 tid;
+
+ if (likely(!ieee80211_is_ctl(bar->frame_control)))
+ return RX_CONTINUE;
+
+ if (ieee80211_is_back_req(bar->frame_control)) {
+ if (!rx->sta)
+ return RX_CONTINUE;
+ tid = le16_to_cpu(bar->control) >> 12;
+ if (rx->sta->ampdu_mlme.tid_state_rx[tid]
+ != HT_AGG_STATE_OPERATIONAL)
+ return RX_CONTINUE;
+ tid_agg_rx = rx->sta->ampdu_mlme.tid_rx[tid];
+
+ start_seq_num = le16_to_cpu(bar->start_seq_num) >> 4;
+
+ /* reset session timer */
+ if (tid_agg_rx->timeout) {
+ unsigned long expires =
+ jiffies + (tid_agg_rx->timeout / 1000) * HZ;
+ mod_timer(&tid_agg_rx->session_timer, expires);
}
- break;
+
+ /* manage reordering buffer according to requested */
+ /* sequence number */
+ rcu_read_lock();
+ ieee80211_sta_manage_reorder_buf(hw, tid_agg_rx, NULL,
+ start_seq_num, 1);
+ rcu_read_unlock();
+ return RX_DROP_UNUSABLE;
}
- if (res == TXRX_DROP)
- dev_kfree_skb(rx->skb);
- return res;
+ return RX_CONTINUE;
}
-static inline void ieee80211_invoke_rx_handlers(struct ieee80211_local *local,
- ieee80211_rx_handler *handlers,
- struct ieee80211_txrx_data *rx,
- struct sta_info *sta)
+static ieee80211_rx_result debug_noinline
+ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx)
{
- if (__ieee80211_invoke_rx_handlers(local, handlers, rx, sta) ==
- TXRX_CONTINUE)
- dev_kfree_skb(rx->skb);
+ struct ieee80211_sub_if_data *sdata;
+
+ if (!(rx->flags & IEEE80211_RX_RA_MATCH))
+ return RX_DROP_MONITOR;
+
+ sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
+ if ((sdata->vif.type == IEEE80211_IF_TYPE_STA ||
+ sdata->vif.type == IEEE80211_IF_TYPE_IBSS ||
+ sdata->vif.type == IEEE80211_IF_TYPE_MESH_POINT) &&
+ !(sdata->flags & IEEE80211_SDATA_USERSPACE_MLME))
+ ieee80211_sta_rx_mgmt(rx->dev, rx->skb, rx->status);
+ else
+ return RX_DROP_MONITOR;
+
+ return RX_QUEUED;
}
static void ieee80211_rx_michael_mic_report(struct net_device *dev,
struct ieee80211_hdr *hdr,
- struct sta_info *sta,
- struct ieee80211_txrx_data *rx)
+ struct ieee80211_rx_data *rx)
{
- int keyidx, hdrlen;
+ int keyidx;
+ unsigned int hdrlen;
DECLARE_MAC_BUF(mac);
DECLARE_MAC_BUF(mac2);
- hdrlen = ieee80211_get_hdrlen_from_skb(rx->skb);
+ hdrlen = ieee80211_hdrlen(hdr->frame_control);
if (rx->skb->len >= hdrlen + 4)
keyidx = rx->skb->data[hdrlen + 3] >> 6;
else
keyidx = -1;
- if (net_ratelimit())
- printk(KERN_DEBUG "%s: TKIP hwaccel reported Michael MIC "
- "failure from %s to %s keyidx=%d\n",
- dev->name, print_mac(mac, hdr->addr2),
- print_mac(mac2, hdr->addr1), keyidx);
-
- if (!sta) {
+ if (!rx->sta) {
/*
* Some hardware seem to generate incorrect Michael MIC
* reports; ignore them to avoid triggering countermeasures.
*/
- if (net_ratelimit())
- printk(KERN_DEBUG "%s: ignored spurious Michael MIC "
- "error for unknown address %s\n",
- dev->name, print_mac(mac, hdr->addr2));
goto ignore;
}
- if (!(rx->fc & IEEE80211_FCTL_PROTECTED)) {
- if (net_ratelimit())
- printk(KERN_DEBUG "%s: ignored spurious Michael MIC "
- "error for a frame with no PROTECTED flag (src "
- "%s)\n", dev->name, print_mac(mac, hdr->addr2));
+ if (!ieee80211_has_protected(hdr->frame_control))
goto ignore;
- }
- if (rx->sdata->type == IEEE80211_IF_TYPE_AP && keyidx) {
+ if (rx->sdata->vif.type == IEEE80211_IF_TYPE_AP && keyidx) {
/*
* APs with pairwise keys should never receive Michael MIC
* errors for non-zero keyidx because these are reserved for
* group keys and only the AP is sending real multicast
* frames in the BSS.
*/
- if (net_ratelimit())
- printk(KERN_DEBUG "%s: ignored Michael MIC error for "
- "a frame with non-zero keyidx (%d)"
- " (src %s)\n", dev->name, keyidx,
- print_mac(mac, hdr->addr2));
goto ignore;
}
- if ((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA &&
- ((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT ||
- (rx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_AUTH)) {
- if (net_ratelimit())
- printk(KERN_DEBUG "%s: ignored spurious Michael MIC "
- "error for a frame that cannot be encrypted "
- "(fc=0x%04x) (src %s)\n",
- dev->name, rx->fc, print_mac(mac, hdr->addr2));
+ if (!ieee80211_is_data(hdr->frame_control) &&
+ !ieee80211_is_auth(hdr->frame_control))
goto ignore;
- }
mac80211_ev_michael_mic_failure(rx->dev, keyidx, hdr);
ignore:
rx->skb = NULL;
}
-ieee80211_rx_handler ieee80211_rx_handlers[] =
+/* TODO: use IEEE80211_RX_FRAGMENTED */
+static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx)
{
- ieee80211_rx_h_if_stats,
- ieee80211_rx_h_monitor,
- ieee80211_rx_h_passive_scan,
- ieee80211_rx_h_check,
- ieee80211_rx_h_load_key,
- ieee80211_rx_h_sta_process,
- ieee80211_rx_h_ccmp_decrypt,
- ieee80211_rx_h_tkip_decrypt,
- ieee80211_rx_h_wep_weak_iv_detection,
- ieee80211_rx_h_wep_decrypt,
- ieee80211_rx_h_defragment,
- ieee80211_rx_h_ps_poll,
- ieee80211_rx_h_michael_mic_verify,
- /* this must be after decryption - so header is counted in MPDU mic
- * must be before pae and data, so QOS_DATA format frames
- * are not passed to user space by these functions
- */
- ieee80211_rx_h_remove_qos_control,
- ieee80211_rx_h_802_1x_pae,
- ieee80211_rx_h_drop_unencrypted,
- ieee80211_rx_h_data,
- ieee80211_rx_h_mgmt,
- NULL
-};
+ struct ieee80211_sub_if_data *sdata;
+ struct ieee80211_local *local = rx->local;
+ struct ieee80211_rtap_hdr {
+ struct ieee80211_radiotap_header hdr;
+ u8 flags;
+ u8 rate;
+ __le16 chan_freq;
+ __le16 chan_flags;
+ } __attribute__ ((packed)) *rthdr;
+ struct sk_buff *skb = rx->skb, *skb2;
+ struct net_device *prev_dev = NULL;
+ struct ieee80211_rx_status *status = rx->status;
+
+ if (rx->flags & IEEE80211_RX_CMNTR_REPORTED)
+ goto out_free_skb;
+
+ if (skb_headroom(skb) < sizeof(*rthdr) &&
+ pskb_expand_head(skb, sizeof(*rthdr), 0, GFP_ATOMIC))
+ goto out_free_skb;
+
+ rthdr = (void *)skb_push(skb, sizeof(*rthdr));
+ memset(rthdr, 0, sizeof(*rthdr));
+ rthdr->hdr.it_len = cpu_to_le16(sizeof(*rthdr));
+ rthdr->hdr.it_present =
+ cpu_to_le32((1 << IEEE80211_RADIOTAP_FLAGS) |
+ (1 << IEEE80211_RADIOTAP_RATE) |
+ (1 << IEEE80211_RADIOTAP_CHANNEL));
+
+ rthdr->rate = rx->rate->bitrate / 5;
+ rthdr->chan_freq = cpu_to_le16(status->freq);
+
+ if (status->band == IEEE80211_BAND_5GHZ)
+ rthdr->chan_flags = cpu_to_le16(IEEE80211_CHAN_OFDM |
+ IEEE80211_CHAN_5GHZ);
+ else
+ rthdr->chan_flags = cpu_to_le16(IEEE80211_CHAN_DYN |
+ IEEE80211_CHAN_2GHZ);
+
+ skb_set_mac_header(skb, 0);
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ skb->pkt_type = PACKET_OTHERHOST;
+ skb->protocol = htons(ETH_P_802_2);
+
+ list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+ if (!netif_running(sdata->dev))
+ continue;
+
+ if (sdata->vif.type != IEEE80211_IF_TYPE_MNTR ||
+ !(sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES))
+ continue;
+
+ if (prev_dev) {
+ skb2 = skb_clone(skb, GFP_ATOMIC);
+ if (skb2) {
+ skb2->dev = prev_dev;
+ netif_rx(skb2);
+ }
+ }
+
+ prev_dev = sdata->dev;
+ sdata->dev->stats.rx_packets++;
+ sdata->dev->stats.rx_bytes += skb->len;
+ }
+
+ if (prev_dev) {
+ skb->dev = prev_dev;
+ netif_rx(skb);
+ skb = NULL;
+ } else
+ goto out_free_skb;
+
+ rx->flags |= IEEE80211_RX_CMNTR_REPORTED;
+ return;
+
+ out_free_skb:
+ dev_kfree_skb(skb);
+}
+
+
+static void ieee80211_invoke_rx_handlers(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_rx_data *rx,
+ struct sk_buff *skb)
+{
+ ieee80211_rx_result res = RX_DROP_MONITOR;
+
+ rx->skb = skb;
+ rx->sdata = sdata;
+ rx->dev = sdata->dev;
+
+#define CALL_RXH(rxh) \
+ res = rxh(rx); \
+ if (res != RX_CONTINUE) \
+ goto rxh_done;
+
+ CALL_RXH(ieee80211_rx_h_passive_scan)
+ CALL_RXH(ieee80211_rx_h_check)
+ CALL_RXH(ieee80211_rx_h_decrypt)
+ CALL_RXH(ieee80211_rx_h_sta_process)
+ CALL_RXH(ieee80211_rx_h_defragment)
+ CALL_RXH(ieee80211_rx_h_ps_poll)
+ CALL_RXH(ieee80211_rx_h_michael_mic_verify)
+ /* must be after MMIC verify so header is counted in MPDU mic */
+ CALL_RXH(ieee80211_rx_h_remove_qos_control)
+ CALL_RXH(ieee80211_rx_h_amsdu)
+ CALL_RXH(ieee80211_rx_h_data)
+ CALL_RXH(ieee80211_rx_h_ctrl)
+ CALL_RXH(ieee80211_rx_h_mgmt)
+
+#undef CALL_RXH
+
+ rxh_done:
+ switch (res) {
+ case RX_DROP_MONITOR:
+ I802_DEBUG_INC(sdata->local->rx_handlers_drop);
+ if (rx->sta)
+ rx->sta->rx_dropped++;
+ /* fall through */
+ case RX_CONTINUE:
+ ieee80211_rx_cooked_monitor(rx);
+ break;
+ case RX_DROP_UNUSABLE:
+ I802_DEBUG_INC(sdata->local->rx_handlers_drop);
+ if (rx->sta)
+ rx->sta->rx_dropped++;
+ dev_kfree_skb(rx->skb);
+ break;
+ case RX_QUEUED:
+ I802_DEBUG_INC(sdata->local->rx_handlers_queued);
+ break;
+ }
+}
/* main receive path */
static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata,
- u8 *bssid, struct ieee80211_txrx_data *rx,
+ u8 *bssid, struct ieee80211_rx_data *rx,
struct ieee80211_hdr *hdr)
{
int multicast = is_multicast_ether_addr(hdr->addr1);
- switch (sdata->type) {
+ switch (sdata->vif.type) {
case IEEE80211_IF_TYPE_STA:
if (!bssid)
return 0;
if (!ieee80211_bssid_match(bssid, sdata->u.sta.bssid)) {
- if (!(rx->flags & IEEE80211_TXRXD_RXIN_SCAN))
+ if (!(rx->flags & IEEE80211_RX_IN_SCAN))
return 0;
- rx->flags &= ~IEEE80211_TXRXD_RXRA_MATCH;
+ rx->flags &= ~IEEE80211_RX_RA_MATCH;
} else if (!multicast &&
compare_ether_addr(sdata->dev->dev_addr,
hdr->addr1) != 0) {
if (!(sdata->dev->flags & IFF_PROMISC))
return 0;
- rx->flags &= ~IEEE80211_TXRXD_RXRA_MATCH;
+ rx->flags &= ~IEEE80211_RX_RA_MATCH;
}
break;
case IEEE80211_IF_TYPE_IBSS:
if (!bssid)
return 0;
- if (!ieee80211_bssid_match(bssid, sdata->u.sta.bssid)) {
- if (!(rx->flags & IEEE80211_TXRXD_RXIN_SCAN))
+ if (ieee80211_is_beacon(hdr->frame_control)) {
+ if (!rx->sta)
+ rx->sta = ieee80211_ibss_add_sta(sdata->dev,
+ rx->skb, bssid, hdr->addr2,
+ BIT(rx->status->rate_idx));
+ return 1;
+ }
+ else if (!ieee80211_bssid_match(bssid, sdata->u.sta.bssid)) {
+ if (!(rx->flags & IEEE80211_RX_IN_SCAN))
return 0;
- rx->flags &= ~IEEE80211_TXRXD_RXRA_MATCH;
+ rx->flags &= ~IEEE80211_RX_RA_MATCH;
} else if (!multicast &&
compare_ether_addr(sdata->dev->dev_addr,
hdr->addr1) != 0) {
if (!(sdata->dev->flags & IFF_PROMISC))
return 0;
- rx->flags &= ~IEEE80211_TXRXD_RXRA_MATCH;
+ rx->flags &= ~IEEE80211_RX_RA_MATCH;
} else if (!rx->sta)
rx->sta = ieee80211_ibss_add_sta(sdata->dev, rx->skb,
- bssid, hdr->addr2);
+ bssid, hdr->addr2,
+ BIT(rx->status->rate_idx));
+ break;
+ case IEEE80211_IF_TYPE_MESH_POINT:
+ if (!multicast &&
+ compare_ether_addr(sdata->dev->dev_addr,
+ hdr->addr1) != 0) {
+ if (!(sdata->dev->flags & IFF_PROMISC))
+ return 0;
+
+ rx->flags &= ~IEEE80211_RX_RA_MATCH;
+ }
break;
+ case IEEE80211_IF_TYPE_VLAN:
case IEEE80211_IF_TYPE_AP:
if (!bssid) {
if (compare_ether_addr(sdata->dev->dev_addr,
return 0;
} else if (!ieee80211_bssid_match(bssid,
sdata->dev->dev_addr)) {
- if (!(rx->flags & IEEE80211_TXRXD_RXIN_SCAN))
+ if (!(rx->flags & IEEE80211_RX_IN_SCAN))
return 0;
- rx->flags &= ~IEEE80211_TXRXD_RXRA_MATCH;
+ rx->flags &= ~IEEE80211_RX_RA_MATCH;
}
- if (sdata->dev == sdata->local->mdev &&
- !(rx->flags & IEEE80211_TXRXD_RXIN_SCAN))
- /* do not receive anything via
- * master device when not scanning */
- return 0;
break;
case IEEE80211_IF_TYPE_WDS:
- if (bssid ||
- (rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA)
+ if (bssid || !ieee80211_is_data(hdr->frame_control))
return 0;
if (compare_ether_addr(sdata->u.wds.remote_addr, hdr->addr2))
return 0;
break;
+ case IEEE80211_IF_TYPE_MNTR:
+ /* take everything */
+ break;
+ case IEEE80211_IF_TYPE_INVALID:
+ /* should never get here */
+ WARN_ON(1);
+ break;
}
return 1;
}
/*
- * This is the receive path handler. It is called by a low level driver when an
- * 802.11 MPDU is received from the hardware.
+ * This is the actual Rx frames handler. as it blongs to Rx path it must
+ * be called with rcu_read_lock protection.
*/
-void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb,
- struct ieee80211_rx_status *status)
+static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
+ struct sk_buff *skb,
+ struct ieee80211_rx_status *status,
+ struct ieee80211_rate *rate)
{
struct ieee80211_local *local = hw_to_local(hw);
struct ieee80211_sub_if_data *sdata;
- struct sta_info *sta;
struct ieee80211_hdr *hdr;
- struct ieee80211_txrx_data rx;
+ struct ieee80211_rx_data rx;
u16 type;
- int radiotap_len = 0, prepres;
+ int prepares;
struct ieee80211_sub_if_data *prev = NULL;
struct sk_buff *skb_new;
u8 *bssid;
- int bogon;
-
- if (status->flag & RX_FLAG_RADIOTAP) {
- radiotap_len = ieee80211_get_radiotap_len(skb->data);
- skb_pull(skb, radiotap_len);
- }
-
- /*
- * 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();
hdr = (struct ieee80211_hdr *) skb->data;
memset(&rx, 0, sizeof(rx));
rx.skb = skb;
rx.local = local;
- rx.u.rx.status = status;
- rx.fc = skb->len >= 2 ? le16_to_cpu(hdr->frame_control) : 0;
+ rx.status = status;
+ rx.rate = rate;
+ rx.fc = le16_to_cpu(hdr->frame_control);
type = rx.fc & IEEE80211_FCTL_FTYPE;
- bogon = status->flag & (RX_FLAG_FAILED_FCS_CRC |
- RX_FLAG_FAILED_PLCP_CRC);
-
- if (!bogon && (type == IEEE80211_FTYPE_DATA ||
- type == IEEE80211_FTYPE_MGMT))
+ if (type == IEEE80211_FTYPE_DATA || type == IEEE80211_FTYPE_MGMT)
local->dot11ReceivedFragmentCount++;
- if (!bogon && skb->len >= 16) {
- sta = rx.sta = sta_info_get(local, hdr->addr2);
- if (sta) {
- rx.dev = rx.sta->dev;
- rx.sdata = IEEE80211_DEV_TO_SUB_IF(rx.dev);
- }
- } else
- sta = rx.sta = NULL;
+ rx.sta = sta_info_get(local, hdr->addr2);
+ if (rx.sta) {
+ rx.sdata = rx.sta->sdata;
+ rx.dev = rx.sta->sdata->dev;
+ }
if ((status->flag & RX_FLAG_MMIC_ERROR)) {
- ieee80211_rx_michael_mic_report(local->mdev, hdr, sta, &rx);
- goto end;
+ ieee80211_rx_michael_mic_report(local->mdev, hdr, &rx);
+ return;
}
- if (unlikely(local->sta_scanning))
- rx.flags |= IEEE80211_TXRXD_RXIN_SCAN;
+ if (unlikely(local->sta_sw_scanning || local->sta_hw_scanning))
+ rx.flags |= IEEE80211_RX_IN_SCAN;
- if (__ieee80211_invoke_rx_handlers(local, local->rx_pre_handlers, &rx,
- sta) != TXRX_CONTINUE)
- goto end;
- skb = rx.skb;
-
- skb_push(skb, radiotap_len);
- if (sta && !(sta->flags & (WLAN_STA_WDS | WLAN_STA_ASSOC_AP)) &&
- !local->iff_promiscs && !is_multicast_ether_addr(hdr->addr1)) {
- rx.flags |= IEEE80211_TXRXD_RXRA_MATCH;
- ieee80211_invoke_rx_handlers(local, local->rx_handlers, &rx,
- rx.sta);
- sta_info_put(sta);
- rcu_read_unlock();
- return;
- }
+ ieee80211_parse_qos(&rx);
+ ieee80211_verify_ip_alignment(&rx);
- bssid = ieee80211_get_bssid(hdr, skb->len - radiotap_len);
+ skb = rx.skb;
list_for_each_entry_rcu(sdata, &local->interfaces, list) {
- rx.flags |= IEEE80211_TXRXD_RXRA_MATCH;
-
if (!netif_running(sdata->dev))
continue;
- prepres = prepare_for_handlers(sdata, bssid, &rx, hdr);
- /* prepare_for_handlers can change sta */
- sta = rx.sta;
+ if (sdata->vif.type == IEEE80211_IF_TYPE_MNTR)
+ continue;
+
+ bssid = ieee80211_get_bssid(hdr, skb->len, sdata->vif.type);
+ rx.flags |= IEEE80211_RX_RA_MATCH;
+ prepares = prepare_for_handlers(sdata, bssid, &rx, hdr);
- if (!prepres)
+ if (!prepares)
continue;
/*
if (!skb_new) {
if (net_ratelimit())
printk(KERN_DEBUG "%s: failed to copy "
- "multicast frame for %s",
+ "multicast frame for %s\n",
wiphy_name(local->hw.wiphy),
prev->dev->name);
continue;
}
- rx.skb = skb_new;
- rx.dev = prev->dev;
- rx.sdata = prev;
- ieee80211_invoke_rx_handlers(local, local->rx_handlers,
- &rx, sta);
+ rx.fc = le16_to_cpu(hdr->frame_control);
+ ieee80211_invoke_rx_handlers(prev, &rx, skb_new);
prev = sdata;
}
if (prev) {
- rx.skb = skb;
- rx.dev = prev->dev;
- rx.sdata = prev;
- ieee80211_invoke_rx_handlers(local, local->rx_handlers,
- &rx, sta);
+ rx.fc = le16_to_cpu(hdr->frame_control);
+ ieee80211_invoke_rx_handlers(prev, &rx, skb);
} else
dev_kfree_skb(skb);
+}
- end:
- rcu_read_unlock();
+#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);
+}
+
+
+/*
+ * As it function blongs to Rx path it must be called with
+ * the proper rcu_read_lock protection for its flow.
+ */
+u8 ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,
+ struct tid_ampdu_rx *tid_agg_rx,
+ struct sk_buff *skb, u16 mpdu_seq_num,
+ int bar_req)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+ struct ieee80211_rx_status status;
+ u16 head_seq_num, buf_size;
+ int index;
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_rate *rate;
+
+ 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)) {
+ dev_kfree_skb(skb);
+ return 1;
+ }
+
+ /* if frame sequence number exceeds our buffering window size or
+ * block Ack Request arrived - release stored frames */
+ if ((!seq_less(mpdu_seq_num, head_seq_num + buf_size)) || (bar_req)) {
+ /* new head to the ordering buffer */
+ if (bar_req)
+ head_seq_num = mpdu_seq_num;
+ else
+ head_seq_num =
+ seq_inc(seq_sub(mpdu_seq_num, buf_size));
+ /* release stored frames up to new head to stack */
+ 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;
+
+ if (tid_agg_rx->reorder_buf[index]) {
+ /* release the reordered frames to stack */
+ memcpy(&status,
+ tid_agg_rx->reorder_buf[index]->cb,
+ sizeof(status));
+ sband = local->hw.wiphy->bands[status.band];
+ rate = &sband->bitrates[status.rate_idx];
+ __ieee80211_rx_handle_packet(hw,
+ tid_agg_rx->reorder_buf[index],
+ &status, rate);
+ tid_agg_rx->stored_mpdu_num--;
+ tid_agg_rx->reorder_buf[index] = NULL;
+ }
+ tid_agg_rx->head_seq_num =
+ seq_inc(tid_agg_rx->head_seq_num);
+ }
+ if (bar_req)
+ return 1;
+ }
- if (sta)
- sta_info_put(sta);
+ /* now the new frame is always in the range of the reordering */
+ /* buffer window */
+ 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]) {
+ dev_kfree_skb(skb);
+ return 1;
+ }
+
+ /* if arrived mpdu is in the right order and nothing else stored */
+ /* release it immediately */
+ 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);
+ return 0;
+ }
+
+ /* put the frame in the reordering buffer */
+ tid_agg_rx->reorder_buf[index] = skb;
+ tid_agg_rx->stored_mpdu_num++;
+ /* 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;
+ while (tid_agg_rx->reorder_buf[index]) {
+ /* release the reordered frame back to stack */
+ memcpy(&status, tid_agg_rx->reorder_buf[index]->cb,
+ sizeof(status));
+ sband = local->hw.wiphy->bands[status.band];
+ rate = &sband->bitrates[status.rate_idx];
+ __ieee80211_rx_handle_packet(hw, tid_agg_rx->reorder_buf[index],
+ &status, rate);
+ tid_agg_rx->stored_mpdu_num--;
+ tid_agg_rx->reorder_buf[index] = NULL;
+ tid_agg_rx->head_seq_num = seq_inc(tid_agg_rx->head_seq_num);
+ index = seq_sub(tid_agg_rx->head_seq_num,
+ tid_agg_rx->ssn) % tid_agg_rx->buf_size;
+ }
+ return 1;
+}
+
+static u8 ieee80211_rx_reorder_ampdu(struct ieee80211_local *local,
+ struct sk_buff *skb)
+{
+ struct ieee80211_hw *hw = &local->hw;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+ struct sta_info *sta;
+ struct tid_ampdu_rx *tid_agg_rx;
+ u16 sc;
+ u16 mpdu_seq_num;
+ u8 ret = 0;
+ int tid;
+
+ sta = sta_info_get(local, hdr->addr2);
+ if (!sta)
+ return ret;
+
+ /* filter the QoS data rx stream according to
+ * STA/TID and check if this STA/TID is on aggregation */
+ if (!ieee80211_is_data_qos(hdr->frame_control))
+ goto end_reorder;
+
+ tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK;
+
+ if (sta->ampdu_mlme.tid_state_rx[tid] != HT_AGG_STATE_OPERATIONAL)
+ goto end_reorder;
+
+ tid_agg_rx = sta->ampdu_mlme.tid_rx[tid];
+
+ /* null data frames are excluded */
+ if (unlikely(ieee80211_is_nullfunc(hdr->frame_control)))
+ goto end_reorder;
+
+ /* new un-ordered ampdu frame - process it */
+
+ /* reset session timer */
+ if (tid_agg_rx->timeout) {
+ unsigned long expires =
+ jiffies + (tid_agg_rx->timeout / 1000) * HZ;
+ mod_timer(&tid_agg_rx->session_timer, expires);
+ }
+
+ /* if this mpdu is fragmented - terminate rx aggregation session */
+ sc = le16_to_cpu(hdr->seq_ctrl);
+ if (sc & IEEE80211_SCTL_FRAG) {
+ ieee80211_sta_stop_rx_ba_session(sta->sdata->dev, sta->addr,
+ tid, 0, WLAN_REASON_QSTA_REQUIRE_SETUP);
+ ret = 1;
+ goto end_reorder;
+ }
+
+ /* according to mpdu sequence number deal with reordering buffer */
+ mpdu_seq_num = (sc & IEEE80211_SCTL_SEQ) >> 4;
+ ret = ieee80211_sta_manage_reorder_buf(hw, tid_agg_rx, skb,
+ mpdu_seq_num, 0);
+ end_reorder:
+ return ret;
+}
+
+/*
+ * 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 ieee80211_hw *hw, struct sk_buff *skb,
+ struct ieee80211_rx_status *status)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+ struct ieee80211_rate *rate = NULL;
+ struct ieee80211_supported_band *sband;
+
+ if (status->band < 0 ||
+ status->band >= IEEE80211_NUM_BANDS) {
+ WARN_ON(1);
+ return;
+ }
+
+ sband = local->hw.wiphy->bands[status->band];
+
+ if (!sband ||
+ status->rate_idx < 0 ||
+ status->rate_idx >= sband->n_bitrates) {
+ WARN_ON(1);
+ return;
+ }
+
+ rate = &sband->bitrates[status->rate_idx];
+
+ /*
+ * 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();
+
+ /*
+ * Frames with failed FCS/PLCP checksum are not returned,
+ * all other frames are returned without radiotap header
+ * if it was previously present.
+ * Also, frames with less than 16 bytes are dropped.
+ */
+ skb = ieee80211_rx_monitor(local, skb, status, rate);
+ if (!skb) {
+ rcu_read_unlock();
+ return;
+ }
+
+ if (!ieee80211_rx_reorder_ampdu(local, skb))
+ __ieee80211_rx_handle_packet(hw, skb, status, rate);
+
+ rcu_read_unlock();
}
EXPORT_SYMBOL(__ieee80211_rx);