📄 ieee80211_output.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_output.c 3058 2007-12-13 06:21:32Z br1 $ */#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 int ieee80211_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 (BH's disabled) */intieee80211_hardstart(struct sk_buff *skb, struct net_device *dev){ struct ieee80211vap *vap = dev->priv; struct ieee80211com *ic = vap->iv_ic; struct net_device *parent = ic->ic_dev; struct ieee80211_node *ni = NULL; struct ieee80211_cb *cb; struct ether_header *eh; /* 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; } cb = (struct ieee80211_cb *) skb->cb; memset(cb, 0, sizeof(struct ieee80211_cb)); if (vap->iv_opmode == IEEE80211_M_MONITOR) { ieee80211_monitor_encap(vap, skb); ieee80211_parent_queue_xmit(skb); return 0; } if (ic->ic_flags & IEEE80211_F_SCAN) /* cancel 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; } cb->ni = 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_pwrsave(ni, skb); return 0; }#ifdef ATH_SUPERG_XR /* * broadcast/multicast packets need to be sent on XR vap in addition to * normal vap. */ /* FIXME: ieee80211_parent_queue_xmit */ if (vap->iv_xrvap && ni == vap->iv_bss && vap->iv_xrvap->iv_sta_assoc) { struct sk_buff *skb1; ni = ieee80211_find_txnode(vap->iv_xrvap, eh->ether_dhost); skb1 = skb_clone(skb,GFP_ATOMIC); if (skb1) { cb = (struct ieee80211_cb *) skb1->cb; cb->ni = ni; cb->flags = 0; cb->next = NULL; (void) dev_queue_xmit(skb1); } }#endif ieee80211_parent_queue_xmit(skb); return 0;bad: if (skb != NULL) dev_kfree_skb(skb); if (ni != NULL) ieee80211_free_node(ni); return 0;}void ieee80211_parent_queue_xmit(struct sk_buff *skb) { struct ieee80211vap *vap = skb->dev->priv; 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; (void) dev_queue_xmit(skb);}/* * 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; struct ieee80211_cb *cb = (struct ieee80211_cb *)skb->cb; KASSERT(ni != NULL, ("null node")); cb->ni = 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_bss->ni_bssid); /* XXX power management */ if ((cb->flags & M_LINK0) != 0 && ni->ni_challenge != NULL) { cb->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)) { printf("[%s] send %s on channel %u\n", ether_sprintf(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 is assumed to have setup a node reference * for use; 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; struct sk_buff *skb; struct ieee80211_cb *cb; struct ieee80211_frame *wh; u_int8_t *frm; skb = ieee80211_getmgtframe(&frm, 0); if (skb == NULL) { /* XXX debug msg */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -