📄 ieee80211_output.c.svn-base
字号:
/*- * 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$ */#ifndef EXPORT_SYMTAB#define EXPORT_SYMTAB#endif/* * IEEE 802.11 output handling. */#ifndef AUTOCONF_INCLUDED#include <linux/config.h>#endif#include <linux/version.h>#include <linux/module.h>#include <linux/skbuff.h>#include <linux/netdevice.h>#include <linux/if_vlan.h>#include <linux/ip.h> /* XXX for TOS */#include "if_llc.h"#include "if_ethersubr.h"#include "if_media.h"#include <net80211/ieee80211_var.h>#include <net80211/ieee80211_monitor.h>#include <net80211/if_athproto.h>#ifdef IEEE80211_DEBUG/* * Decide if an outbound 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){ if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP) return (vap->iv_opmode == IEEE80211_M_IBSS); return 1;}#endif/* * Determine the priority based on VLAN and/or IP TOS. Priority is * written into the skb->priority field. On success, returns 0. Failure * due to bad or mis-matched vlan tag is indicated by non-zero return. */static intieee80211_classify(struct ieee80211_node *ni, struct sk_buff *skb){ struct ieee80211vap *vap = ni->ni_vap; struct ether_header *eh = (struct ether_header *)skb->data; int v_wme_ac = 0, d_wme_ac = 0; /* default priority */ skb->priority = WME_AC_BE; if (!(ni->ni_flags & IEEE80211_NODE_QOS)) return 0; /* * If node has a vlan tag then all traffic * to it must have a matching vlan id. */ if (ni->ni_vlan != 0 && vlan_tx_tag_present(skb)) { u_int32_t tag=0; int v_pri; if (vap->iv_vlgrp == NULL) { IEEE80211_NODE_STAT(ni, tx_novlantag); ni->ni_stats.ns_tx_novlantag++; return 1; } if (((tag = vlan_tx_tag_get(skb)) & VLAN_VID_MASK) != (ni->ni_vlan & VLAN_VID_MASK)) { IEEE80211_NODE_STAT(ni, tx_vlanmismatch); ni->ni_stats.ns_tx_vlanmismatch++; return 1; } if (ni->ni_flags & IEEE80211_NODE_QOS) { v_pri = (tag >> VLAN_PRI_SHIFT) & VLAN_PRI_MASK; switch (v_pri) { case 1: case 2: /* Background (BK) */ v_wme_ac = WME_AC_BK; break; case 0: case 3: /* Best Effort (BE) */ v_wme_ac = WME_AC_BE; break; case 4: case 5: /* Video (VI) */ v_wme_ac = WME_AC_VI; break; case 6: case 7: /* Voice (VO) */ v_wme_ac = WME_AC_VO; break; } } } if (eh->ether_type == __constant_htons(ETHERTYPE_IP)) { const struct iphdr *ip = (struct iphdr *) (skb->data + sizeof (struct ether_header)); /* * IP frame, map the TOS field. * * XXX: fill out these mappings??? */ switch (ip->tos) { case 0x08: /* Background */ case 0x20: d_wme_ac = WME_AC_BK; break; case 0x28: /* Video */ case 0xa0: d_wme_ac = WME_AC_VI; break; case 0x30: /* Voice */ case 0xe0: case 0x88: /* XXX UPSD */ case 0xb8: d_wme_ac = WME_AC_VO; break; default: /* All others */ d_wme_ac = WME_AC_BE; break; } } else { d_wme_ac = WME_AC_BE; } skb->priority = d_wme_ac; if (v_wme_ac > d_wme_ac) skb->priority = v_wme_ac; /* Applying ACM policy */ if (vap->iv_opmode == IEEE80211_M_STA) { struct ieee80211com *ic = ni->ni_ic; while (skb->priority != WME_AC_BK && ic->ic_wme.wme_wmeBssChanParams.cap_wmeParams[skb->priority].wmep_acm) { switch (skb->priority) { case WME_AC_BE: skb->priority = WME_AC_BK; break; case WME_AC_VI: skb->priority = WME_AC_BE; break; case WME_AC_VO: skb->priority = WME_AC_VI; break; default: skb->priority = WME_AC_BK; break; } } } return 0;}/* * Context: process context (BHs disabled) * It must return either NETDEV_TX_OK or NETDEV_TX_BUSY */intieee80211_hardstart(struct sk_buff *skb, struct net_device *dev){ struct ieee80211vap *vap = netdev_priv(dev); struct ieee80211com *ic = vap->iv_ic; struct net_device *parent = ic->ic_dev; struct ieee80211_node *ni = NULL; struct ether_header *eh; /* Reset the SKB of new frames reaching this layer BEFORE * we invoke ieee80211_skb_track. */ memset(SKB_CB(skb), 0, sizeof(struct ieee80211_cb)); /* If an SKB is passed in directly from the kernel, * we take responsibility for the reference. */ ieee80211_skb_track(skb); /* NB: parent must be up and running. */ if ((parent->flags & (IFF_RUNNING|IFF_UP)) != (IFF_RUNNING|IFF_UP)) goto bad; /* No data frames go out unless we're running. */ if (vap->iv_state != IEEE80211_S_RUN) { IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT, "%s: ignore data packet, state %u\n", __func__, vap->iv_state);#if 0 vap->iv_stats.ist_tx_discard++;#endif goto bad; } if (vap->iv_opmode == IEEE80211_M_MONITOR) { ieee80211_monitor_encap(vap, skb); ieee80211_parent_queue_xmit(skb); return NETDEV_TX_OK; } /* Cancel any running BG scan */ ieee80211_cancel_scan(vap); /* Find the node for the destination so we can do * things like power save. */ eh = (struct ether_header *)skb->data; if (vap->iv_opmode == IEEE80211_M_WDS) ni = ieee80211_find_txnode(vap, vap->wds_mac); else ni = ieee80211_find_txnode(vap, eh->ether_dhost); if (ni == NULL) { /* NB: ieee80211_find_txnode does stat+msg */ goto bad; } /* Calculate priority so drivers can find the TX queue. */ if (ieee80211_classify(ni, skb)) { IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, ni, "%s: discard, classification failure", __func__); goto bad; } SKB_NI(skb) = ieee80211_ref_node(ni); /* Power-save checks. */ if (WME_UAPSD_AC_CAN_TRIGGER(skb->priority, ni)) { /* U-APSD power save queue */ /* XXXAPSD: assuming triggerable means deliverable */ M_FLAG_SET(skb, M_UAPSD); } else if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT)) { /* Station in power save mode; stick the frame * on the STA's power save queue and continue. * We'll get the frame back when the time is right. */ ieee80211_unref_node(&ni); return ieee80211_pwrsave(skb); } dev->trans_start = jiffies;#ifdef ATH_SUPERG_XR /* Broadcast/multicast packets need to be sent on XR vap in addition to * normal vap. */ if (vap->iv_xrvap && (ni == vap->iv_bss) && vap->iv_xrvap->iv_sta_assoc) { struct sk_buff *skb1 = skb_copy(skb, GFP_ATOMIC); if (skb1) { memset(SKB_CB(skb1), 0, sizeof(struct ieee80211_cb));#ifdef IEEE80211_DEBUG_REFCNT M_FLAG_SET(skb1, M_SKB_TRACKED);#endif /* #ifdef IEEE80211_DEBUG_REFCNT */ SKB_NI(skb1) = ieee80211_find_txnode(vap->iv_xrvap, eh->ether_dhost); /* Ignore this return code. */ ieee80211_parent_queue_xmit(skb1); } }#endif ieee80211_unref_node(&ni); ieee80211_parent_queue_xmit(skb); return NETDEV_TX_OK;bad: if (skb != NULL) ieee80211_dev_kfree_skb(&skb); if (ni != NULL) ieee80211_unref_node(&ni); return NETDEV_TX_OK;}/* * SKB is consumed in all cases. */void ieee80211_parent_queue_xmit(struct sk_buff *skb) { struct ieee80211vap *vap = netdev_priv(skb->dev); vap->iv_devstats.tx_packets++; vap->iv_devstats.tx_bytes += skb->len; vap->iv_ic->ic_lastdata = jiffies; /* Dispatch the packet to the parent device */ skb->dev = vap->iv_ic->ic_dev; if (dev_queue_xmit(skb) == NET_XMIT_DROP) vap->iv_devstats.tx_dropped++;}/* * Set the direction field and address fields of an outgoing * non-QoS frame. Note this should be called early on in * constructing a frame as it sets i_fc[1]; other bits can * then be or'd in. */static voidieee80211_send_setup(struct ieee80211vap *vap, struct ieee80211_node *ni, struct ieee80211_frame *wh, int type, const u_int8_t sa[IEEE80211_ADDR_LEN], const u_int8_t da[IEEE80211_ADDR_LEN], const u_int8_t bssid[IEEE80211_ADDR_LEN]){#define WH4(wh) ((struct ieee80211_frame_addr4 *)wh) wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | type; if ((type & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_DATA) { switch (vap->iv_opmode) { case IEEE80211_M_STA: wh->i_fc[1] = IEEE80211_FC1_DIR_TODS; IEEE80211_ADDR_COPY(wh->i_addr1, bssid); IEEE80211_ADDR_COPY(wh->i_addr2, sa); IEEE80211_ADDR_COPY(wh->i_addr3, da); break; case IEEE80211_M_IBSS: case IEEE80211_M_AHDEMO: wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; IEEE80211_ADDR_COPY(wh->i_addr1, da); IEEE80211_ADDR_COPY(wh->i_addr2, sa); IEEE80211_ADDR_COPY(wh->i_addr3, bssid); break; case IEEE80211_M_HOSTAP: wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS; IEEE80211_ADDR_COPY(wh->i_addr1, da); IEEE80211_ADDR_COPY(wh->i_addr2, bssid); IEEE80211_ADDR_COPY(wh->i_addr3, sa); break; case IEEE80211_M_WDS: wh->i_fc[1] = IEEE80211_FC1_DIR_DSTODS; /* XXX cheat, bssid holds RA */ IEEE80211_ADDR_COPY(wh->i_addr1, bssid); IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr); IEEE80211_ADDR_COPY(wh->i_addr3, da); IEEE80211_ADDR_COPY(WH4(wh)->i_addr4, sa); break; case IEEE80211_M_MONITOR: /* NB: to quiet compiler */ break; } } else { wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; IEEE80211_ADDR_COPY(wh->i_addr1, da); IEEE80211_ADDR_COPY(wh->i_addr2, sa); IEEE80211_ADDR_COPY(wh->i_addr3, bssid); } wh->i_dur = 0; /* NB: use non-QoS tid */ *(__le16 *)&wh->i_seq[0] = htole16(ni->ni_txseqs[0] << IEEE80211_SEQ_SEQ_SHIFT); ni->ni_txseqs[0]++;#undef WH4}/* * Send a management frame to the specified node. The node pointer * must have a reference as the pointer will be passed to the driver * and potentially held for a long time. If the frame is successfully * dispatched to the driver, then it is responsible for freeing the * reference (and potentially freeing up any associated storage). */static voidieee80211_mgmt_output(struct ieee80211_node *ni, struct sk_buff *skb, int type){ struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; struct ieee80211_frame *wh; KASSERT(ni != NULL, ("null node")); SKB_NI(skb) = ni; wh = (struct ieee80211_frame *) skb_push(skb, sizeof(struct ieee80211_frame)); ieee80211_send_setup(vap, ni, wh, IEEE80211_FC0_TYPE_MGT | type, vap->iv_myaddr, ni->ni_macaddr, vap->iv_bssid); /* XXX power management */ if ((SKB_CB(skb)->flags & M_LINK0) != 0 && ni->ni_challenge != NULL) { SKB_CB(skb)->flags &= ~M_LINK0; IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_AUTH, wh->i_addr1, "encrypting frame (%s)", __func__); wh->i_fc[1] |= IEEE80211_FC1_PROT; } if (IEEE80211_VAP_IS_SLEEPING(ni->ni_vap)) wh->i_fc[1] |= IEEE80211_FC1_PWR_MGT;#ifdef IEEE80211_DEBUG /* avoid printing too many frames */ if ((ieee80211_msg_debug(vap) && doprint(vap, type)) || ieee80211_msg_dumppkts(vap)) { printk(KERN_DEBUG "[" MAC_FMT "] send %s on channel %u\n", MAC_ADDR(wh->i_addr1), ieee80211_mgt_subtype_name[ (type & IEEE80211_FC0_SUBTYPE_MASK) >> IEEE80211_FC0_SUBTYPE_SHIFT], ieee80211_chan2ieee(ic, ic->ic_curchan)); }#endif IEEE80211_NODE_STAT(ni, tx_mgmt); (void) ic->ic_mgtstart(ic, skb);}/* Send a null data frame to the specified node. * * NB: the caller provides us with our own node reference this must not be * leaked; this is necessary to deal with a race condition when * probing for inactive stations. */intieee80211_send_nulldata(struct ieee80211_node *ni){ struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -