?? ieee80211_input.c
字號:
/*- * Copyright (c) 2001 Atsushi Onoe * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * Alternatively, this software may be distributed under the terms of the * GNU General Public License ("GPL") version 2 as published by the Free * Software Foundation. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $Id: ieee80211_input.c 1676 2006-07-06 03:23:08Z brian $ */#ifndef EXPORT_SYMTAB#define EXPORT_SYMTAB#endif/* * IEEE 802.11 input handling. */#include <linux/config.h>#include <linux/version.h>#include <linux/module.h>#include <linux/skbuff.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/random.h>#include <linux/if_vlan.h>#include <net/iw_handler.h> /* wireless_send_event(..) */#include <linux/wireless.h> /* SIOCGIWTHRSPY */#include <linux/if_arp.h> /* ARPHRD_ETHER */#include "if_llc.h"#include "if_ethersubr.h"#include "if_media.h"#include "if_athproto.h"#include <net80211/ieee80211_var.h>#ifdef IEEE80211_DEBUG/* * Decide if a received management frame should be * printed when debugging is enabled. This filters some * of the less interesting frames that come frequently * (e.g. beacons). */static __inline intdoprint(struct ieee80211vap *vap, int subtype){ switch (subtype) { case IEEE80211_FC0_SUBTYPE_BEACON: return (vap->iv_ic->ic_flags & IEEE80211_F_SCAN); case IEEE80211_FC0_SUBTYPE_PROBE_REQ: return (vap->iv_opmode == IEEE80211_M_IBSS); } return 1;}/* * Emit a debug message about discarding a frame or information * element. One format is for extracting the mac address from * the frame header; the other is for when a header is not * available or otherwise appropriate. */#define IEEE80211_DISCARD(_vap, _m, _wh, _type, _fmt, ...) do { \ if ((_vap)->iv_debug & (_m)) \ ieee80211_discard_frame(_vap, _wh, _type, _fmt, __VA_ARGS__);\} while (0)#define IEEE80211_DISCARD_IE(_vap, _m, _wh, _type, _fmt, ...) do { \ if ((_vap)->iv_debug & (_m)) \ ieee80211_discard_ie(_vap, _wh, _type, _fmt, __VA_ARGS__);\} while (0)#define IEEE80211_DISCARD_MAC(_vap, _m, _mac, _type, _fmt, ...) do { \ if ((_vap)->iv_debug & (_m)) \ ieee80211_discard_mac(_vap, _mac, _type, _fmt, __VA_ARGS__);\} while (0)static const u_int8_t *ieee80211_getbssid(struct ieee80211vap *, const struct ieee80211_frame *);static void ieee80211_discard_frame(struct ieee80211vap *, const struct ieee80211_frame *, const char *, const char *, ...);static void ieee80211_discard_ie(struct ieee80211vap *, const struct ieee80211_frame *, const char *, const char *, ...);static void ieee80211_discard_mac(struct ieee80211vap *, const u_int8_t mac[IEEE80211_ADDR_LEN], const char *, const char *, ...);#else#define IEEE80211_DISCARD(_vap, _m, _wh, _type, _fmt, ...)#define IEEE80211_DISCARD_IE(_vap, _m, _wh, _type, _fmt, ...)#define IEEE80211_DISCARD_MAC(_vap, _m, _mac, _type, _fmt, ...)#endif /* IEEE80211_DEBUG */static struct sk_buff *ieee80211_defrag(struct ieee80211_node *, struct sk_buff *, int);static void ieee80211_deliver_data(struct ieee80211_node *, struct sk_buff *);static struct sk_buff *ieee80211_decap(struct ieee80211vap *, struct sk_buff *, int);static void ieee80211_send_error(struct ieee80211_node *, const u_int8_t *, int, int);static void ieee80211_recv_pspoll(struct ieee80211_node *, struct sk_buff *);static int accept_data_frame(struct ieee80211vap *, struct ieee80211_node *, struct ieee80211_key *, struct sk_buff *, struct ether_header *);#ifdef ATH_SUPERG_FFstatic void athff_decap(struct sk_buff *);#endif#ifdef USE_HEADERLEN_RESVstatic unsigned short ath_eth_type_trans(struct sk_buff *, struct net_device *);#endif/* Enhanced iwspy support */#ifdef CONFIG_NET_WIRELESS#if WIRELESS_EXT >= 16#ifndef IW_QUAL_QUAL_UPDATED#define IW_QUAL_QUAL_UPDATED 0x01#define IW_QUAL_LEVEL_UPDATED 0x02#define IW_QUAL_NOISE_UPDATED 0x04#endif /* IW_QUAL_QUAL_UPDATED *//** * This function is a clone of set_quality(..) in ieee80211_wireless.c */static voidset_quality(struct iw_quality *iq, u_int rssi){ iq->qual = rssi; /* NB: max is 94 because noise is hardcoded to 161 */ if (iq->qual > 94) iq->qual = 94; iq->noise = 161; /* -95dBm */ iq->level = iq->noise + iq->qual; iq->updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED | IW_QUAL_NOISE_UPDATED;}/** * Given a node and the rssi value of a just received frame from the node, this * function checks if to raise an iwspy event because we iwspy the node and rssi * exceeds threshold (if active). * * @param vap: vap * @param ni: sender node * @param rssi: rssi value of received frame */static voidiwspy_event(struct ieee80211vap *vap, struct ieee80211_node *ni, u_int rssi){ if (vap->iv_spy.thr_low && vap->iv_spy.num && ni && (rssi < vap->iv_spy.thr_low || rssi > vap->iv_spy.thr_high)) { int i; for (i = 0; i < vap->iv_spy.num; i++) { if (IEEE80211_ADDR_EQ(ni->ni_macaddr, &(vap->iv_spy.mac[i * IEEE80211_ADDR_LEN]))) { union iwreq_data wrq; struct iw_thrspy thr; IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG, "%s: we spy %s, threshold is active " "and rssi exceeds it -> raise an iwspy" " event\n", __func__, ether_sprintf( ni->ni_macaddr)); memset(&wrq, 0, sizeof(wrq)); wrq.data.length = 1; memset(&thr, 0, sizeof(struct iw_thrspy)); memcpy(thr.addr.sa_data, ni->ni_macaddr, IEEE80211_ADDR_LEN); thr.addr.sa_family = ARPHRD_ETHER; set_quality(&thr.qual, rssi); set_quality(&thr.low, vap->iv_spy.thr_low); set_quality(&thr.high, vap->iv_spy.thr_high); wireless_send_event(vap->iv_dev, SIOCGIWTHRSPY, &wrq, (char*) &thr); break; } } }}#else#define iwspy_event(_vap, _ni, _rssi)#endif /* WIRELESS_EXT >= 16 */#else#define iwspy_event(_vap, _ni, _rssi)#endif /* CONFIG_NET_WIRELESS *//* * Process a received frame. The node associated with the sender * should be supplied. If nothing was found in the node table then * the caller is assumed to supply a reference to ic_bss instead. * The RSSI and a timestamp are also supplied. The RSSI data is used * during AP scanning to select a AP to associate with; it can have * any units so long as values have consistent units and higher values * mean ``better signal''. The receive timestamp is currently not used * by the 802.11 layer. * * Context: softIRQ (tasklet) */intieee80211_input(struct ieee80211_node *ni, struct sk_buff *skb, int rssi, u_int32_t rstamp){#define HAS_SEQ(type) ((type & 0x4) == 0) struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = vap->iv_ic; struct ieee80211_node *ni_wds = NULL; struct net_device *dev = vap->iv_dev; struct ieee80211_frame *wh; struct ieee80211_key *key; struct ether_header *eh;#ifdef ATH_SUPERG_FF struct llc *llc;#endif int hdrspace; u_int8_t dir, type, subtype; u_int8_t *bssid; u_int16_t rxseq; KASSERT(ni != NULL, ("null node")); ni->ni_inact = ni->ni_inact_reload; KASSERT(skb->len >= sizeof(struct ieee80211_frame_min), ("frame length too short: %u", skb->len)); /* XXX adjust device in sk_buff? */ type = -1; /* undefined */ /* * In monitor mode, send everything directly to bpf. * Also do not process frames w/o i_addr2 any further. * XXX may want to include the CRC */ if (vap->iv_opmode == IEEE80211_M_MONITOR) goto out; if (skb->len < sizeof(struct ieee80211_frame_min)) { IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, ni->ni_macaddr, NULL, "too short (1): len %u", skb->len); vap->iv_stats.is_rx_tooshort++; goto out; } /* * Bit of a cheat here, we use a pointer for a 3-address * frame format but don't reference fields past outside * ieee80211_frame_min w/o first validating the data is * present. */ wh = (struct ieee80211_frame *)skb->data; if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) != IEEE80211_FC0_VERSION_0) { IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, ni->ni_macaddr, NULL, "wrong version %x", wh->i_fc[0]); vap->iv_stats.is_rx_badversion++; goto err; } dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK; type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) { switch (vap->iv_opmode) { case IEEE80211_M_STA: bssid = wh->i_addr2; if (!IEEE80211_ADDR_EQ(bssid, ni->ni_bssid)) { /* not interested in */ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, bssid, NULL, "%s", "not to bss"); vap->iv_stats.is_rx_wrongbss++; goto out; } iwspy_event(vap, ni, rssi); break; case IEEE80211_M_IBSS: case IEEE80211_M_AHDEMO: if (dir != IEEE80211_FC1_DIR_NODS) bssid = wh->i_addr1; else if (type == IEEE80211_FC0_TYPE_CTL) bssid = wh->i_addr1; else { if (skb->len < sizeof(struct ieee80211_frame)) { IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, ni->ni_macaddr, NULL, "too short (2): len %u", skb->len); vap->iv_stats.is_rx_tooshort++; goto out; } bssid = wh->i_addr3; } if (type == IEEE80211_FC0_TYPE_DATA && ni == vap->iv_bss) { /* * Try to find sender in local node table. */ ni = ieee80211_find_node(ni->ni_table, wh->i_addr2); if (ni == NULL) { /* * Fake up a node for this newly discovered * member of the IBSS. This should probably * done after an ACL check. */ ni = ieee80211_fakeup_adhoc_node(vap, wh->i_addr2); if (ni == NULL) { /* NB: stat kept for alloc failure */ goto err; } } } iwspy_event(vap, ni, rssi); break; case IEEE80211_M_HOSTAP: if (dir != IEEE80211_FC1_DIR_NODS) bssid = wh->i_addr1; else if (type == IEEE80211_FC0_TYPE_CTL) bssid = wh->i_addr1; else { if (skb->len < sizeof(struct ieee80211_frame)) { IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, ni->ni_macaddr, NULL, "too short (2): len %u", skb->len); vap->iv_stats.is_rx_tooshort++; goto out; } bssid = wh->i_addr3; } /* * Validate the bssid. */#ifdef ATH_SUPERG_XR if (!IEEE80211_ADDR_EQ(bssid, vap->iv_bss->ni_bssid) && !IEEE80211_ADDR_EQ(bssid, dev->broadcast)) { /* * allow MGT frames to vap->iv_xrvap. * this will allow roaming between XR and normal vaps * without station dis associating from previous vap. */ if (!(vap->iv_xrvap && IEEE80211_ADDR_EQ(bssid, vap->iv_xrvap->iv_bss->ni_bssid) && type == IEEE80211_FC0_TYPE_MGT && ni != vap->iv_bss)) { /* not interested in */ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, bssid, NULL, "%s", "not to bss or xrbss"); vap->iv_stats.is_rx_wrongbss++; goto out; } }#else if (!IEEE80211_ADDR_EQ(bssid, vap->iv_bss->ni_bssid) && !IEEE80211_ADDR_EQ(bssid, dev->broadcast)) { /* not interested in */ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, bssid, NULL, "%s", "not to bss"); vap->iv_stats.is_rx_wrongbss++; goto out; }#endif break; case IEEE80211_M_WDS: if (skb->len < sizeof(struct ieee80211_frame_addr4)) { IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, ni->ni_macaddr, NULL, "too short (3): len %u", skb->len); vap->iv_stats.is_rx_tooshort++; goto out; } bssid = wh->i_addr1; if (!IEEE80211_ADDR_EQ(bssid, vap->iv_bss->ni_bssid) && !IEEE80211_ADDR_EQ(bssid, dev->broadcast)) { /* not interested in */ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, bssid, NULL, "%s", "not to bss"); vap->iv_stats.is_rx_wrongbss++; goto out; } if (!IEEE80211_ADDR_EQ(wh->i_addr2, vap->wds_mac)) { /* not interested in */ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, wh->i_addr2, NULL, "%s", "not from DS"); vap->iv_stats.is_rx_wrongbss++; goto out; } break; default: /* XXX catch bad values */ goto out; } ni->ni_rssi = rssi; ni->ni_rstamp = rstamp; ni->ni_last_rx = jiffies; if (HAS_SEQ(type)) { u_int8_t tid; if (IEEE80211_QOS_HAS_SEQ(wh)) { tid = ((struct ieee80211_qosframe *)wh)-> i_qos[0] & IEEE80211_QOS_TID; if (TID_TO_WME_AC(tid) >= WME_AC_VI) ic->ic_wme.wme_hipri_traffic++; tid++; } else tid = 0; rxseq = le16toh(*(u_int16_t *)wh->i_seq); if ((wh->i_fc[1] & IEEE80211_FC1_RETRY) && IEEE80211_SEQ_LEQ(rxseq, ni->ni_rxseqs[tid])) { /* duplicate, discard */ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, bssid, "duplicate", "seqno <%u,%u> fragno <%u,%u> tid %u", rxseq >> IEEE80211_SEQ_SEQ_SHIFT, ni->ni_rxseqs[tid] >> IEEE80211_SEQ_SEQ_SHIFT, rxseq & IEEE80211_SEQ_FRAG_MASK, ni->ni_rxseqs[tid] & IEEE80211_SEQ_FRAG_MASK, tid); vap->iv_stats.is_rx_dup++; IEEE80211_NODE_STAT(ni, rx_dup); goto out; }
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -