diff options
author | Christian Mauderer <Christian.Mauderer@embedded-brains.de> | 2016-11-14 13:46:13 +0100 |
---|---|---|
committer | Christian Mauderer <Christian.Mauderer@embedded-brains.de> | 2017-01-17 12:50:57 +0100 |
commit | a241ea8e924154a217768b6e4910a62e0d25cfe2 (patch) | |
tree | 404fef7dab5f6dd06c0c3889dbc96214a5e226d4 /freebsd/sys/net80211/ieee80211_output.c | |
parent | Use thread name support (diff) | |
download | rtems-libbsd-a241ea8e924154a217768b6e4910a62e0d25cfe2.tar.bz2 |
Import IEEE 802.11 from FreeBSD.
Diffstat (limited to '')
-rw-r--r-- | freebsd/sys/net80211/ieee80211_output.c | 1296 |
1 files changed, 898 insertions, 398 deletions
diff --git a/freebsd/sys/net80211/ieee80211_output.c b/freebsd/sys/net80211/ieee80211_output.c index 229c87ea..c9251796 100644 --- a/freebsd/sys/net80211/ieee80211_output.c +++ b/freebsd/sys/net80211/ieee80211_output.c @@ -35,8 +35,9 @@ __FBSDID("$FreeBSD$"); #include <rtems/bsd/sys/param.h> #include <sys/systm.h> -#include <sys/mbuf.h> #include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/mbuf.h> #include <sys/endian.h> #include <sys/socket.h> @@ -44,6 +45,7 @@ __FBSDID("$FreeBSD$"); #include <net/bpf.h> #include <net/ethernet.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_llc.h> #include <net/if_media.h> #include <net/if_vlan_var.h> @@ -77,18 +79,6 @@ __FBSDID("$FreeBSD$"); #define ETHER_HEADER_COPY(dst, src) \ memcpy(dst, src, sizeof(struct ether_header)) -/* unalligned little endian access */ -#define LE_WRITE_2(p, v) do { \ - ((uint8_t *)(p))[0] = (v) & 0xff; \ - ((uint8_t *)(p))[1] = ((v) >> 8) & 0xff; \ -} while (0) -#define LE_WRITE_4(p, v) do { \ - ((uint8_t *)(p))[0] = (v) & 0xff; \ - ((uint8_t *)(p))[1] = ((v) >> 8) & 0xff; \ - ((uint8_t *)(p))[2] = ((v) >> 16) & 0xff; \ - ((uint8_t *)(p))[3] = ((v) >> 24) & 0xff; \ -} while (0) - static int ieee80211_fragment(struct ieee80211vap *, struct mbuf *, u_int hdrsize, u_int ciphdrsize, u_int mtu); static void ieee80211_tx_mgt_cb(struct ieee80211_node *, void *, int); @@ -112,219 +102,92 @@ doprint(struct ieee80211vap *vap, int subtype) #endif /* - * Start method for vap's. All packets from the stack come - * through here. We handle common processing of the packets - * before dispatching them to the underlying device. + * Transmit a frame to the given destination on the given VAP. + * + * It's up to the caller to figure out the details of who this + * is going to and resolving the node. + * + * This routine takes care of queuing it for power save, + * A-MPDU state stuff, fast-frames state stuff, encapsulation + * if required, then passing it up to the driver layer. + * + * This routine (for now) consumes the mbuf and frees the node + * reference; it ideally will return a TX status which reflects + * whether the mbuf was consumed or not, so the caller can + * free the mbuf (if appropriate) and the node reference (again, + * if appropriate.) */ -void -ieee80211_start(struct ifnet *ifp) +int +ieee80211_vap_pkt_send_dest(struct ieee80211vap *vap, struct mbuf *m, + struct ieee80211_node *ni) { -#define IS_DWDS(vap) \ - (vap->iv_opmode == IEEE80211_M_WDS && \ - (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY) == 0) - struct ieee80211vap *vap = ifp->if_softc; struct ieee80211com *ic = vap->iv_ic; - struct ifnet *parent = ic->ic_ifp; - struct ieee80211_node *ni; - struct mbuf *m; - struct ether_header *eh; - int error; + struct ifnet *ifp = vap->iv_ifp; +#ifdef IEEE80211_SUPPORT_SUPERG + int mcast; +#endif - /* NB: parent must be up and running */ - if (!IFNET_IS_UP_RUNNING(parent)) { - IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT, - "%s: ignore queue, parent %s not up+running\n", - __func__, parent->if_xname); - /* XXX stat */ - return; - } - if (vap->iv_state == IEEE80211_S_SLEEP) { + if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) && + (m->m_flags & M_PWR_SAV) == 0) { /* - * In power save, wakeup device for transmit. + * Station in power save mode; pass the frame + * to the 802.11 layer and continue. We'll get + * the frame back when the time is right. + * XXX lose WDS vap linkage? */ - ieee80211_new_state(vap, IEEE80211_S_RUN, 0); - return; + if (ieee80211_pwrsave(ni, m) != 0) + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); + ieee80211_free_node(ni); + + /* + * We queued it fine, so tell the upper layer + * that we consumed it. + */ + return (0); + } + /* calculate priority so drivers can find the tx queue */ + if (ieee80211_classify(ni, m)) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_OUTPUT, + ni->ni_macaddr, NULL, + "%s", "classification failure"); + vap->iv_stats.is_tx_classify++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); + m_freem(m); + ieee80211_free_node(ni); + + /* XXX better status? */ + return (0); } /* - * No data frames go out unless we're running. - * Note in particular this covers CAC and CSA - * states (though maybe we should check muting - * for CSA). + * Stash the node pointer. Note that we do this after + * any call to ieee80211_dwds_mcast because that code + * uses any existing value for rcvif to identify the + * interface it (might have been) received on. */ - if (vap->iv_state != IEEE80211_S_RUN) { - IEEE80211_LOCK(ic); - /* re-check under the com lock to avoid races */ - if (vap->iv_state != IEEE80211_S_RUN) { - IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT, - "%s: ignore queue, in %s state\n", - __func__, ieee80211_state_name[vap->iv_state]); - vap->iv_stats.is_tx_badstate++; - ifp->if_drv_flags |= IFF_DRV_OACTIVE; - IEEE80211_UNLOCK(ic); - return; - } - IEEE80211_UNLOCK(ic); - } - for (;;) { - IFQ_DEQUEUE(&ifp->if_snd, m); - if (m == NULL) - break; - /* - * Sanitize mbuf flags for net80211 use. We cannot - * clear M_PWR_SAV or M_MORE_DATA because these may - * be set for frames that are re-submitted from the - * power save queue. - * - * NB: This must be done before ieee80211_classify as - * it marks EAPOL in frames with M_EAPOL. - */ - m->m_flags &= ~(M_80211_TX - M_PWR_SAV - M_MORE_DATA); - /* - * Cancel any background scan. - */ - if (ic->ic_flags & IEEE80211_F_SCAN) - ieee80211_cancel_anyscan(vap); - /* - * Find the node for the destination so we can do - * things like power save and fast frames aggregation. - * - * NB: past this point various code assumes the first - * mbuf has the 802.3 header present (and contiguous). - */ - ni = NULL; - if (m->m_len < sizeof(struct ether_header) && - (m = m_pullup(m, sizeof(struct ether_header))) == NULL) { - IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT, - "discard frame, %s\n", "m_pullup failed"); - vap->iv_stats.is_tx_nobuf++; /* XXX */ - ifp->if_oerrors++; - continue; - } - eh = mtod(m, struct ether_header *); - if (ETHER_IS_MULTICAST(eh->ether_dhost)) { - if (IS_DWDS(vap)) { - /* - * Only unicast frames from the above go out - * DWDS vaps; multicast frames are handled by - * dispatching the frame as it comes through - * the AP vap (see below). - */ - IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_WDS, - eh->ether_dhost, "mcast", "%s", "on DWDS"); - vap->iv_stats.is_dwds_mcast++; - m_freem(m); - continue; - } - if (vap->iv_opmode == IEEE80211_M_HOSTAP) { - /* - * Spam DWDS vap's w/ multicast traffic. - */ - /* XXX only if dwds in use? */ - ieee80211_dwds_mcast(vap, m); - } - } -#ifdef IEEE80211_SUPPORT_MESH - if (vap->iv_opmode != IEEE80211_M_MBSS) { -#endif - ni = ieee80211_find_txnode(vap, eh->ether_dhost); - if (ni == NULL) { - /* NB: ieee80211_find_txnode does stat+msg */ - ifp->if_oerrors++; - m_freem(m); - continue; - } - if (ni->ni_associd == 0 && - (ni->ni_flags & IEEE80211_NODE_ASSOCID)) { - IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_OUTPUT, - eh->ether_dhost, NULL, - "sta not associated (type 0x%04x)", - htons(eh->ether_type)); - vap->iv_stats.is_tx_notassoc++; - ifp->if_oerrors++; - m_freem(m); - ieee80211_free_node(ni); - continue; - } -#ifdef IEEE80211_SUPPORT_MESH - } else { - if (!IEEE80211_ADDR_EQ(eh->ether_shost, vap->iv_myaddr)) { - /* - * Proxy station only if configured. - */ - if (!ieee80211_mesh_isproxyena(vap)) { - IEEE80211_DISCARD_MAC(vap, - IEEE80211_MSG_OUTPUT | - IEEE80211_MSG_MESH, - eh->ether_dhost, NULL, - "%s", "proxy not enabled"); - vap->iv_stats.is_mesh_notproxy++; - ifp->if_oerrors++; - m_freem(m); - continue; - } - ieee80211_mesh_proxy_check(vap, eh->ether_shost); - } - ni = ieee80211_mesh_discover(vap, eh->ether_dhost, m); - if (ni == NULL) { - /* - * NB: ieee80211_mesh_discover holds/disposes - * frame (e.g. queueing on path discovery). - */ - ifp->if_oerrors++; - continue; - } - } + m->m_pkthdr.rcvif = (void *)ni; +#ifdef IEEE80211_SUPPORT_SUPERG + mcast = (m->m_flags & (M_MCAST | M_BCAST)) ? 1: 0; #endif - if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) && - (m->m_flags & M_PWR_SAV) == 0) { - /* - * Station in power save mode; pass the frame - * to the 802.11 layer and continue. We'll get - * the frame back when the time is right. - * XXX lose WDS vap linkage? - */ - (void) ieee80211_pwrsave(ni, m); - ieee80211_free_node(ni); - continue; - } - /* calculate priority so drivers can find the tx queue */ - if (ieee80211_classify(ni, m)) { - IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_OUTPUT, - eh->ether_dhost, NULL, - "%s", "classification failure"); - vap->iv_stats.is_tx_classify++; - ifp->if_oerrors++; - m_freem(m); - ieee80211_free_node(ni); - continue; - } - /* - * Stash the node pointer. Note that we do this after - * any call to ieee80211_dwds_mcast because that code - * uses any existing value for rcvif to identify the - * interface it (might have been) received on. - */ - m->m_pkthdr.rcvif = (void *)ni; - BPF_MTAP(ifp, m); /* 802.3 tx */ - - /* - * Check if A-MPDU tx aggregation is setup or if we - * should try to enable it. The sta must be associated - * with HT and A-MPDU enabled for use. When the policy - * routine decides we should enable A-MPDU we issue an - * ADDBA request and wait for a reply. The frame being - * encapsulated will go out w/o using A-MPDU, or possibly - * it might be collected by the driver and held/retransmit. - * The default ic_ampdu_enable routine handles staggering - * ADDBA requests in case the receiver NAK's us or we are - * otherwise unable to establish a BA stream. - */ - if ((ni->ni_flags & IEEE80211_NODE_AMPDU_TX) && - (vap->iv_flags_ht & IEEE80211_FHT_AMPDU_TX) && - (m->m_flags & M_EAPOL) == 0) { - const int ac = M_WME_GETAC(m); - struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[ac]; + BPF_MTAP(ifp, m); /* 802.3 tx */ + + /* + * Check if A-MPDU tx aggregation is setup or if we + * should try to enable it. The sta must be associated + * with HT and A-MPDU enabled for use. When the policy + * routine decides we should enable A-MPDU we issue an + * ADDBA request and wait for a reply. The frame being + * encapsulated will go out w/o using A-MPDU, or possibly + * it might be collected by the driver and held/retransmit. + * The default ic_ampdu_enable routine handles staggering + * ADDBA requests in case the receiver NAK's us or we are + * otherwise unable to establish a BA stream. + */ + if ((ni->ni_flags & IEEE80211_NODE_AMPDU_TX) && + (vap->iv_flags_ht & IEEE80211_FHT_AMPDU_TX)) { + if ((m->m_flags & M_EAPOL) == 0) { + int tid = WME_AC_TO_TID(M_WME_GETAC(m)); + struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[tid]; ieee80211_txampdu_count_packet(tap); if (IEEE80211_AMPDU_RUNNING(tap)) { @@ -343,37 +206,347 @@ ieee80211_start(struct ifnet *ifp) /* XXX hold frame for reply? */ } } + } + #ifdef IEEE80211_SUPPORT_SUPERG - else if (IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_FF)) { + /* + * Check for AMSDU/FF; queue for aggregation + * + * Note: we don't bother trying to do fast frames or + * A-MSDU encapsulation for 802.3 drivers. Now, we + * likely could do it for FF (because it's a magic + * atheros tunnel LLC type) but I don't think we're going + * to really need to. For A-MSDU we'd have to set the + * A-MSDU QoS bit in the wifi header, so we just plain + * can't do it. + * + * Strictly speaking, we could actually /do/ A-MSDU / FF + * with A-MPDU together which for certain circumstances + * is beneficial (eg A-MSDU of TCK ACKs.) However, + * I'll ignore that for now so existing behaviour is maintained. + * Later on it would be good to make "amsdu + ampdu" configurable. + */ + else if (__predict_true((vap->iv_caps & IEEE80211_C_8023ENCAP) == 0)) { + if ((! mcast) && ieee80211_amsdu_tx_ok(ni)) { + m = ieee80211_amsdu_check(ni, m); + if (m == NULL) { + /* NB: any ni ref held on stageq */ + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG, + "%s: amsdu_check queued frame\n", + __func__); + return (0); + } + } else if ((! mcast) && IEEE80211_ATH_CAP(vap, ni, + IEEE80211_NODE_FF)) { m = ieee80211_ff_check(ni, m); if (m == NULL) { /* NB: any ni ref held on stageq */ - continue; + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG, + "%s: ff_check queued frame\n", + __func__); + return (0); } } + } #endif /* IEEE80211_SUPPORT_SUPERG */ - if (__predict_true((vap->iv_caps & IEEE80211_C_8023ENCAP) == 0)) { + + /* + * Grab the TX lock - serialise the TX process from this + * point (where TX state is being checked/modified) + * through to driver queue. + */ + IEEE80211_TX_LOCK(ic); + + /* + * XXX make the encap and transmit code a separate function + * so things like the FF (and later A-MSDU) path can just call + * it for flushed frames. + */ + if (__predict_true((vap->iv_caps & IEEE80211_C_8023ENCAP) == 0)) { + /* + * Encapsulate the packet in prep for transmission. + */ + m = ieee80211_encap(vap, ni, m); + if (m == NULL) { + /* NB: stat+msg handled in ieee80211_encap */ + IEEE80211_TX_UNLOCK(ic); + ieee80211_free_node(ni); + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); + return (ENOBUFS); + } + } + (void) ieee80211_parent_xmitpkt(ic, m); + + /* + * Unlock at this point - no need to hold it across + * ieee80211_free_node() (ie, the comlock) + */ + IEEE80211_TX_UNLOCK(ic); + ic->ic_lastdata = ticks; + + return (0); +} + + + +/* + * Send the given mbuf through the given vap. + * + * This consumes the mbuf regardless of whether the transmit + * was successful or not. + * + * This does none of the initial checks that ieee80211_start() + * does (eg CAC timeout, interface wakeup) - the caller must + * do this first. + */ +static int +ieee80211_start_pkt(struct ieee80211vap *vap, struct mbuf *m) +{ +#define IS_DWDS(vap) \ + (vap->iv_opmode == IEEE80211_M_WDS && \ + (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY) == 0) + struct ieee80211com *ic = vap->iv_ic; + struct ifnet *ifp = vap->iv_ifp; + struct ieee80211_node *ni; + struct ether_header *eh; + + /* + * Cancel any background scan. + */ + if (ic->ic_flags & IEEE80211_F_SCAN) + ieee80211_cancel_anyscan(vap); + /* + * Find the node for the destination so we can do + * things like power save and fast frames aggregation. + * + * NB: past this point various code assumes the first + * mbuf has the 802.3 header present (and contiguous). + */ + ni = NULL; + if (m->m_len < sizeof(struct ether_header) && + (m = m_pullup(m, sizeof(struct ether_header))) == NULL) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT, + "discard frame, %s\n", "m_pullup failed"); + vap->iv_stats.is_tx_nobuf++; /* XXX */ + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); + return (ENOBUFS); + } + eh = mtod(m, struct ether_header *); + if (ETHER_IS_MULTICAST(eh->ether_dhost)) { + if (IS_DWDS(vap)) { /* - * Encapsulate the packet in prep for transmission. + * Only unicast frames from the above go out + * DWDS vaps; multicast frames are handled by + * dispatching the frame as it comes through + * the AP vap (see below). */ - m = ieee80211_encap(vap, ni, m); - if (m == NULL) { - /* NB: stat+msg handled in ieee80211_encap */ - ieee80211_free_node(ni); - continue; - } + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_WDS, + eh->ether_dhost, "mcast", "%s", "on DWDS"); + vap->iv_stats.is_dwds_mcast++; + m_freem(m); + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); + /* XXX better status? */ + return (ENOBUFS); } - - error = parent->if_transmit(parent, m); - if (error != 0) { - /* NB: IFQ_HANDOFF reclaims mbuf */ + if (vap->iv_opmode == IEEE80211_M_HOSTAP) { + /* + * Spam DWDS vap's w/ multicast traffic. + */ + /* XXX only if dwds in use? */ + ieee80211_dwds_mcast(vap, m); + } + } +#ifdef IEEE80211_SUPPORT_MESH + if (vap->iv_opmode != IEEE80211_M_MBSS) { +#endif + ni = ieee80211_find_txnode(vap, eh->ether_dhost); + if (ni == NULL) { + /* NB: ieee80211_find_txnode does stat+msg */ + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); + m_freem(m); + /* XXX better status? */ + return (ENOBUFS); + } + if (ni->ni_associd == 0 && + (ni->ni_flags & IEEE80211_NODE_ASSOCID)) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_OUTPUT, + eh->ether_dhost, NULL, + "sta not associated (type 0x%04x)", + htons(eh->ether_type)); + vap->iv_stats.is_tx_notassoc++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); + m_freem(m); ieee80211_free_node(ni); - } else { - ifp->if_opackets++; + /* XXX better status? */ + return (ENOBUFS); } +#ifdef IEEE80211_SUPPORT_MESH + } else { + if (!IEEE80211_ADDR_EQ(eh->ether_shost, vap->iv_myaddr)) { + /* + * Proxy station only if configured. + */ + if (!ieee80211_mesh_isproxyena(vap)) { + IEEE80211_DISCARD_MAC(vap, + IEEE80211_MSG_OUTPUT | + IEEE80211_MSG_MESH, + eh->ether_dhost, NULL, + "%s", "proxy not enabled"); + vap->iv_stats.is_mesh_notproxy++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); + m_freem(m); + /* XXX better status? */ + return (ENOBUFS); + } + IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT, + "forward frame from DS SA(%6D), DA(%6D)\n", + eh->ether_shost, ":", + eh->ether_dhost, ":"); + ieee80211_mesh_proxy_check(vap, eh->ether_shost); + } + ni = ieee80211_mesh_discover(vap, eh->ether_dhost, m); + if (ni == NULL) { + /* + * NB: ieee80211_mesh_discover holds/disposes + * frame (e.g. queueing on path discovery). + */ + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); + /* XXX better status? */ + return (ENOBUFS); + } + } +#endif + + /* + * We've resolved the sender, so attempt to transmit it. + */ + + if (vap->iv_state == IEEE80211_S_SLEEP) { + /* + * In power save; queue frame and then wakeup device + * for transmit. + */ ic->ic_lastdata = ticks; + if (ieee80211_pwrsave(ni, m) != 0) + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); + ieee80211_free_node(ni); + ieee80211_new_state(vap, IEEE80211_S_RUN, 0); + return (0); + } + + if (ieee80211_vap_pkt_send_dest(vap, m, ni) != 0) + return (ENOBUFS); + return (0); +#undef IS_DWDS +} + +/* + * Start method for vap's. All packets from the stack come + * through here. We handle common processing of the packets + * before dispatching them to the underlying device. + * + * if_transmit() requires that the mbuf be consumed by this call + * regardless of the return condition. + */ +int +ieee80211_vap_transmit(struct ifnet *ifp, struct mbuf *m) +{ + struct ieee80211vap *vap = ifp->if_softc; + struct ieee80211com *ic = vap->iv_ic; + + /* + * No data frames go out unless we're running. + * Note in particular this covers CAC and CSA + * states (though maybe we should check muting + * for CSA). + */ + if (vap->iv_state != IEEE80211_S_RUN && + vap->iv_state != IEEE80211_S_SLEEP) { + IEEE80211_LOCK(ic); + /* re-check under the com lock to avoid races */ + if (vap->iv_state != IEEE80211_S_RUN && + vap->iv_state != IEEE80211_S_SLEEP) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT, + "%s: ignore queue, in %s state\n", + __func__, ieee80211_state_name[vap->iv_state]); + vap->iv_stats.is_tx_badstate++; + IEEE80211_UNLOCK(ic); + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + m_freem(m); + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); + return (ENETDOWN); + } + IEEE80211_UNLOCK(ic); } -#undef IS_DWDS + + /* + * Sanitize mbuf flags for net80211 use. We cannot + * clear M_PWR_SAV or M_MORE_DATA because these may + * be set for frames that are re-submitted from the + * power save queue. + * + * NB: This must be done before ieee80211_classify as + * it marks EAPOL in frames with M_EAPOL. + */ + m->m_flags &= ~(M_80211_TX - M_PWR_SAV - M_MORE_DATA); + + /* + * Bump to the packet transmission path. + * The mbuf will be consumed here. + */ + return (ieee80211_start_pkt(vap, m)); +} + +void +ieee80211_vap_qflush(struct ifnet *ifp) +{ + + /* Empty for now */ +} + +/* + * 802.11 raw output routine. + * + * XXX TODO: this (and other send routines) should correctly + * XXX keep the pwr mgmt bit set if it decides to call into the + * XXX driver to send a frame whilst the state is SLEEP. + * + * Otherwise the peer may decide that we're awake and flood us + * with traffic we are still too asleep to receive! + */ +int +ieee80211_raw_output(struct ieee80211vap *vap, struct ieee80211_node *ni, + struct mbuf *m, const struct ieee80211_bpf_params *params) +{ + struct ieee80211com *ic = vap->iv_ic; + int error; + + /* + * Set node - the caller has taken a reference, so ensure + * that the mbuf has the same node value that + * it would if it were going via the normal path. + */ + m->m_pkthdr.rcvif = (void *)ni; + + /* + * Attempt to add bpf transmit parameters. + * + * For now it's ok to fail; the raw_xmit api still takes + * them as an option. + * + * Later on when ic_raw_xmit() has params removed, + * they'll have to be added - so fail the transmit if + * they can't be. + */ + if (params) + (void) ieee80211_add_xmit_params(m, params); + + error = ic->ic_raw_xmit(ni, m, params); + if (error) { + if_inc_counter(vap->iv_ifp, IFCOUNTER_OERRORS, 1); + ieee80211_free_node(ni); + } + return (error); } /* @@ -383,13 +556,15 @@ ieee80211_start(struct ifnet *ifp) */ int ieee80211_output(struct ifnet *ifp, struct mbuf *m, - struct sockaddr *dst, struct route *ro) + const struct sockaddr *dst, struct route *ro) { #define senderr(e) do { error = (e); goto bad;} while (0) struct ieee80211_node *ni = NULL; struct ieee80211vap *vap; struct ieee80211_frame *wh; + struct ieee80211com *ic = NULL; int error; + int ret; if (ifp->if_drv_flags & IFF_DRV_OACTIVE) { /* @@ -403,6 +578,7 @@ ieee80211_output(struct ifnet *ifp, struct mbuf *m, senderr(ENETDOWN); } vap = ifp->if_softc; + ic = vap->iv_ic; /* * Hand to the 802.3 code if not tagged as * a raw 802.11 frame. @@ -434,6 +610,8 @@ ieee80211_output(struct ifnet *ifp, struct mbuf *m, if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) != IEEE80211_FC0_VERSION_0) senderr(EIO); /* XXX */ + if (m->m_pkthdr.len < ieee80211_anyhdrsize(wh)) + senderr(EIO); /* XXX */ /* locate destination node */ switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) { @@ -443,8 +621,6 @@ ieee80211_output(struct ifnet *ifp, struct mbuf *m, break; case IEEE80211_FC1_DIR_TODS: case IEEE80211_FC1_DIR_DSTODS: - if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) - senderr(EIO); /* XXX */ ni = ieee80211_find_txnode(vap, wh->i_addr3); break; default: @@ -473,7 +649,6 @@ ieee80211_output(struct ifnet *ifp, struct mbuf *m, if (ieee80211_classify(ni, m)) senderr(EIO); /* XXX */ - ifp->if_opackets++; IEEE80211_NODE_STAT(ni, tx_data); if (IEEE80211_IS_MULTICAST(wh->i_addr1)) { IEEE80211_NODE_STAT(ni, tx_mcast); @@ -483,21 +658,25 @@ ieee80211_output(struct ifnet *ifp, struct mbuf *m, /* NB: ieee80211_encap does not include 802.11 header */ IEEE80211_NODE_STAT_ADD(ni, tx_bytes, m->m_pkthdr.len); + IEEE80211_TX_LOCK(ic); + /* * NB: DLT_IEEE802_11_RADIO identifies the parameters are * present by setting the sa_len field of the sockaddr (yes, * this is a hack). * NB: we assume sa_data is suitably aligned to cast. */ - return vap->iv_ic->ic_raw_xmit(ni, m, + ret = ieee80211_raw_output(vap, ni, m, (const struct ieee80211_bpf_params *)(dst->sa_len ? dst->sa_data : NULL)); + IEEE80211_TX_UNLOCK(ic); + return (ret); bad: if (m != NULL) m_freem(m); if (ni != NULL) ieee80211_free_node(ni); - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); return error; #undef senderr } @@ -522,6 +701,8 @@ ieee80211_send_setup( struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *); ieee80211_seq seqno; + IEEE80211_TX_LOCK_ASSERT(ni->ni_ic); + wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | type; if ((type & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_DATA) { switch (vap->iv_opmode) { @@ -553,7 +734,6 @@ ieee80211_send_setup( break; case IEEE80211_M_MBSS: #ifdef IEEE80211_SUPPORT_MESH - /* XXX add support for proxied addresses */ if (IEEE80211_IS_MULTICAST(da)) { wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS; /* XXX next hop */ @@ -586,11 +766,16 @@ ieee80211_send_setup( } *(uint16_t *)&wh->i_dur[0] = 0; - tap = &ni->ni_tx_ampdu[TID_TO_WME_AC(tid)]; + tap = &ni->ni_tx_ampdu[tid]; if (tid != IEEE80211_NONQOS_TID && IEEE80211_AMPDU_RUNNING(tap)) m->m_flags |= M_AMPDU_MPDU; else { - seqno = ni->ni_txseqs[tid]++; + if (IEEE80211_HAS_SEQ(type & IEEE80211_FC0_TYPE_MASK, + type & IEEE80211_FC0_SUBTYPE_MASK)) + seqno = ni->ni_txseqs[tid]++; + else + seqno = 0; + *(uint16_t *)&wh->i_seq[0] = htole16(seqno << IEEE80211_SEQ_SEQ_SHIFT); M_SEQNO_SET(m, seqno); @@ -616,27 +801,28 @@ ieee80211_mgmt_output(struct ieee80211_node *ni, struct mbuf *m, int type, struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; struct ieee80211_frame *wh; + int ret; KASSERT(ni != NULL, ("null node")); if (vap->iv_state == IEEE80211_S_CAC) { IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT | IEEE80211_MSG_DOTH, ni, "block %s frame in CAC state", - ieee80211_mgt_subtype_name[ - (type & IEEE80211_FC0_SUBTYPE_MASK) >> - IEEE80211_FC0_SUBTYPE_SHIFT]); + ieee80211_mgt_subtype_name(type)); vap->iv_stats.is_tx_badstate++; ieee80211_free_node(ni); m_freem(m); return EIO; /* XXX */ } - M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT); + M_PREPEND(m, sizeof(struct ieee80211_frame), M_NOWAIT); if (m == NULL) { ieee80211_free_node(ni); return ENOMEM; } + IEEE80211_TX_LOCK(ic); + wh = mtod(m, struct ieee80211_frame *); ieee80211_send_setup(ni, m, IEEE80211_FC0_TYPE_MGT | type, IEEE80211_NONQOS_TID, @@ -644,7 +830,7 @@ ieee80211_mgmt_output(struct ieee80211_node *ni, struct mbuf *m, int type, if (params->ibp_flags & IEEE80211_BPF_CRYPTO) { IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_AUTH, wh->i_addr1, "encrypting frame (%s)", __func__); - wh->i_fc[1] |= IEEE80211_FC1_WEP; + wh->i_fc[1] |= IEEE80211_FC1_PROTECTED; } m->m_flags |= M_ENCAP; /* mark encapsulated */ @@ -657,15 +843,24 @@ ieee80211_mgmt_output(struct ieee80211_node *ni, struct mbuf *m, int 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_mgt_subtype_name(type), ieee80211_chan2ieee(ic, ic->ic_curchan)); } #endif IEEE80211_NODE_STAT(ni, tx_mgmt); - return ic->ic_raw_xmit(ni, m, params); + ret = ieee80211_raw_output(vap, ni, m, params); + IEEE80211_TX_UNLOCK(ic); + return (ret); +} + +static void +ieee80211_nulldata_transmitted(struct ieee80211_node *ni, void *arg, + int status) +{ + struct ieee80211vap *vap = ni->ni_vap; + + wakeup(vap); } /* @@ -689,6 +884,7 @@ ieee80211_send_nulldata(struct ieee80211_node *ni) struct ieee80211_frame *wh; int hdrlen; uint8_t *frm; + int ret; if (vap->iv_state == IEEE80211_S_CAC) { IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT | IEEE80211_MSG_DOTH, @@ -717,13 +913,15 @@ ieee80211_send_nulldata(struct ieee80211_node *ni) } KASSERT(M_LEADINGSPACE(m) >= hdrlen, ("leading space %zd", M_LEADINGSPACE(m))); - M_PREPEND(m, hdrlen, M_DONTWAIT); + M_PREPEND(m, hdrlen, M_NOWAIT); if (m == NULL) { /* NB: cannot happen */ ieee80211_free_node(ni); return ENOMEM; } + IEEE80211_TX_LOCK(ic); + wh = mtod(m, struct ieee80211_frame *); /* NB: a little lie */ if (ni->ni_flags & IEEE80211_NODE_QOS) { const int tid = WME_AC_TO_TID(WME_AC_BE); @@ -753,6 +951,11 @@ ieee80211_send_nulldata(struct ieee80211_node *ni) vap->iv_opmode != IEEE80211_M_HOSTAP) wh->i_fc[1] |= IEEE80211_FC1_PWR_MGT; } + if ((ic->ic_flags & IEEE80211_F_SCAN) && + (ni->ni_flags & IEEE80211_NODE_PWR_MGT)) { + ieee80211_add_callback(m, ieee80211_nulldata_transmitted, + NULL); + } m->m_len = m->m_pkthdr.len = hdrlen; m->m_flags |= M_ENCAP; /* mark encapsulated */ @@ -766,7 +969,9 @@ ieee80211_send_nulldata(struct ieee80211_node *ni) ieee80211_chan2ieee(ic, ic->ic_curchan), wh->i_fc[1] & IEEE80211_FC1_PWR_MGT ? "ena" : "dis"); - return ic->ic_raw_xmit(ni, m, NULL); + ret = ieee80211_raw_output(vap, ni, m, NULL); + IEEE80211_TX_UNLOCK(ic); + return (ret); } /* @@ -934,7 +1139,7 @@ ieee80211_mbuf_adjust(struct ieee80211vap *vap, int hdrsize, return NULL; } KASSERT(needed_space <= MHLEN, - ("not enough room, need %u got %zu\n", needed_space, MHLEN)); + ("not enough room, need %u got %d\n", needed_space, MHLEN)); /* * Setup new mbuf to have leading space to prepend the * 802.11 header and any crypto header bits that are @@ -1013,10 +1218,13 @@ ieee80211_encap(struct ieee80211vap *vap, struct ieee80211_node *ni, struct mbuf *m) { #define WH4(wh) ((struct ieee80211_frame_addr4 *)(wh)) +#define MC01(mc) ((struct ieee80211_meshcntl_ae01 *)mc) struct ieee80211com *ic = ni->ni_ic; #ifdef IEEE80211_SUPPORT_MESH struct ieee80211_mesh_state *ms = vap->iv_mesh; struct ieee80211_meshcntl_ae10 *mc; + struct ieee80211_mesh_route *rt = NULL; + int dir = -1; #endif struct ether_header eh; struct ieee80211_frame *wh; @@ -1026,6 +1234,9 @@ ieee80211_encap(struct ieee80211vap *vap, struct ieee80211_node *ni, ieee80211_seq seqno; int meshhdrsize, meshae; uint8_t *qos; + int is_amsdu = 0; + + IEEE80211_TX_LOCK_ASSERT(ic); /* * Copy existing Ethernet header to a safe place. The @@ -1071,9 +1282,11 @@ ieee80211_encap(struct ieee80211vap *vap, struct ieee80211_node *ni, * ap's require all data frames to be QoS-encapsulated * once negotiated in which case we'll need to make this * configurable. + * NB: mesh data frames are QoS. */ - addqos = (ni->ni_flags & (IEEE80211_NODE_QOS|IEEE80211_NODE_HT)) && - (m->m_flags & M_EAPOL) == 0; + addqos = ((ni->ni_flags & (IEEE80211_NODE_QOS|IEEE80211_NODE_HT)) || + (vap->iv_opmode == IEEE80211_M_MBSS)) && + (m->m_flags & M_EAPOL) == 0; if (addqos) hdrsize = sizeof(struct ieee80211_qosframe); else @@ -1095,21 +1308,40 @@ ieee80211_encap(struct ieee80211vap *vap, struct ieee80211_node *ni, * w/ 4-address format and address extension mode 10 */ is4addr = 0; /* NB: don't use, disable */ - if (!IEEE80211_IS_MULTICAST(eh.ether_dhost)) - hdrsize += IEEE80211_ADDR_LEN; /* unicast are 4-addr */ - meshhdrsize = sizeof(struct ieee80211_meshcntl); - /* XXX defines for AE modes */ - if (IEEE80211_ADDR_EQ(eh.ether_shost, vap->iv_myaddr)) { - if (!IEEE80211_IS_MULTICAST(eh.ether_dhost)) - meshae = 0; - else - meshae = 4; /* NB: pseudo */ - } else if (IEEE80211_IS_MULTICAST(eh.ether_dhost)) { - meshae = 1; - meshhdrsize += 1*IEEE80211_ADDR_LEN; + if (!IEEE80211_IS_MULTICAST(eh.ether_dhost)) { + rt = ieee80211_mesh_rt_find(vap, eh.ether_dhost); + KASSERT(rt != NULL, ("route is NULL")); + dir = IEEE80211_FC1_DIR_DSTODS; + hdrsize += IEEE80211_ADDR_LEN; + if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY) { + if (IEEE80211_ADDR_EQ(rt->rt_mesh_gate, + vap->iv_myaddr)) { + IEEE80211_NOTE_MAC(vap, + IEEE80211_MSG_MESH, + eh.ether_dhost, + "%s", "trying to send to ourself"); + goto bad; + } + meshae = IEEE80211_MESH_AE_10; + meshhdrsize = + sizeof(struct ieee80211_meshcntl_ae10); + } else { + meshae = IEEE80211_MESH_AE_00; + meshhdrsize = + sizeof(struct ieee80211_meshcntl); + } } else { - meshae = 2; - meshhdrsize += 2*IEEE80211_ADDR_LEN; + dir = IEEE80211_FC1_DIR_FROMDS; + if (!IEEE80211_ADDR_EQ(eh.ether_shost, vap->iv_myaddr)) { + /* proxy group */ + meshae = IEEE80211_MESH_AE_01; + meshhdrsize = + sizeof(struct ieee80211_meshcntl_ae01); + } else { + /* group */ + meshae = IEEE80211_MESH_AE_00; + meshhdrsize = sizeof(struct ieee80211_meshcntl); + } } } else { #endif @@ -1157,16 +1389,26 @@ ieee80211_encap(struct ieee80211vap *vap, struct ieee80211_node *ni, } else { #ifdef IEEE80211_SUPPORT_SUPERG /* - * Aggregated frame. + * Aggregated frame. Check if it's for AMSDU or FF. + * + * XXX TODO: IEEE80211_NODE_AMSDU* isn't implemented + * anywhere for some reason. But, since 11n requires + * AMSDU RX, we can just assume "11n" == "AMSDU". */ - m = ieee80211_ff_encap(vap, m, hdrspace + meshhdrsize, key); + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG, "%s: called; M_FF\n", __func__); + if (ieee80211_amsdu_tx_ok(ni)) { + m = ieee80211_amsdu_encap(vap, m, hdrspace + meshhdrsize, key); + is_amsdu = 1; + } else { + m = ieee80211_ff_encap(vap, m, hdrspace + meshhdrsize, key); + } if (m == NULL) #endif goto bad; } datalen = m->m_pkthdr.len; /* NB: w/o 802.11 header */ - M_PREPEND(m, hdrspace + meshhdrsize, M_DONTWAIT); + M_PREPEND(m, hdrspace + meshhdrsize, M_NOWAIT); if (m == NULL) { vap->iv_stats.is_tx_nobuf++; goto bad; @@ -1210,44 +1452,52 @@ ieee80211_encap(struct ieee80211vap *vap, struct ieee80211_node *ni, /* NB: offset by hdrspace to deal with DATAPAD */ mc = (struct ieee80211_meshcntl_ae10 *) (mtod(m, uint8_t *) + hdrspace); + wh->i_fc[1] = dir; switch (meshae) { - case 0: /* ucast, no proxy */ - wh->i_fc[1] = IEEE80211_FC1_DIR_DSTODS; - IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr); - IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr); - IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_dhost); - IEEE80211_ADDR_COPY(WH4(wh)->i_addr4, eh.ether_shost); + case IEEE80211_MESH_AE_00: /* no proxy */ mc->mc_flags = 0; - qos = ((struct ieee80211_qosframe_addr4 *) wh)->i_qos; - break; - case 4: /* mcast, no proxy */ - wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS; - IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost); - IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr); - IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_shost); - mc->mc_flags = 0; /* NB: AE is really 0 */ - qos = ((struct ieee80211_qosframe *) wh)->i_qos; + if (dir == IEEE80211_FC1_DIR_DSTODS) { /* ucast */ + IEEE80211_ADDR_COPY(wh->i_addr1, + ni->ni_macaddr); + IEEE80211_ADDR_COPY(wh->i_addr2, + vap->iv_myaddr); + IEEE80211_ADDR_COPY(wh->i_addr3, + eh.ether_dhost); + IEEE80211_ADDR_COPY(WH4(wh)->i_addr4, + eh.ether_shost); + qos =((struct ieee80211_qosframe_addr4 *) + wh)->i_qos; + } else if (dir == IEEE80211_FC1_DIR_FROMDS) { + /* mcast */ + IEEE80211_ADDR_COPY(wh->i_addr1, + eh.ether_dhost); + IEEE80211_ADDR_COPY(wh->i_addr2, + vap->iv_myaddr); + IEEE80211_ADDR_COPY(wh->i_addr3, + eh.ether_shost); + qos = ((struct ieee80211_qosframe *) + wh)->i_qos; + } break; - case 1: /* mcast, proxy */ + case IEEE80211_MESH_AE_01: /* mcast, proxy */ wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS; IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost); IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr); IEEE80211_ADDR_COPY(wh->i_addr3, vap->iv_myaddr); mc->mc_flags = 1; - IEEE80211_ADDR_COPY(mc->mc_addr4, eh.ether_shost); + IEEE80211_ADDR_COPY(MC01(mc)->mc_addr4, + eh.ether_shost); qos = ((struct ieee80211_qosframe *) wh)->i_qos; break; - case 2: /* ucast, proxy */ - wh->i_fc[1] = IEEE80211_FC1_DIR_DSTODS; - IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr); + case IEEE80211_MESH_AE_10: /* ucast, proxy */ + KASSERT(rt != NULL, ("route is NULL")); + IEEE80211_ADDR_COPY(wh->i_addr1, rt->rt_nexthop); IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr); - /* XXX not right, need MeshDA */ - IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_dhost); - /* XXX assume are MeshSA */ + IEEE80211_ADDR_COPY(wh->i_addr3, rt->rt_mesh_gate); IEEE80211_ADDR_COPY(WH4(wh)->i_addr4, vap->iv_myaddr); - mc->mc_flags = 2; - IEEE80211_ADDR_COPY(mc->mc_addr4, eh.ether_dhost); - IEEE80211_ADDR_COPY(mc->mc_addr5, eh.ether_shost); + mc->mc_flags = IEEE80211_MESH_AE_10; + IEEE80211_ADDR_COPY(mc->mc_addr5, eh.ether_dhost); + IEEE80211_ADDR_COPY(mc->mc_addr6, eh.ether_shost); qos = ((struct ieee80211_qosframe_addr4 *) wh)->i_qos; break; default: @@ -1256,7 +1506,7 @@ ieee80211_encap(struct ieee80211vap *vap, struct ieee80211_node *ni, } mc->mc_ttl = ms->ms_ttl; ms->ms_seq++; - LE_WRITE_4(mc->mc_seq, ms->ms_seq); + le32enc(mc->mc_seq, ms->ms_seq); break; #endif case IEEE80211_M_WDS: /* NB: is4addr should always be true */ @@ -1279,9 +1529,21 @@ ieee80211_encap(struct ieee80211vap *vap, struct ieee80211_node *ni, qos[0] = tid & IEEE80211_QOS_TID; if (ic->ic_wme.wme_wmeChanParams.cap_wmeParams[ac].wmep_noackPolicy) qos[0] |= IEEE80211_QOS_ACKPOLICY_NOACK; - qos[1] = 0; +#ifdef IEEE80211_SUPPORT_MESH + if (vap->iv_opmode == IEEE80211_M_MBSS) + qos[1] = IEEE80211_QOS_MC; + else +#endif + qos[1] = 0; wh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_QOS; + /* + * If this is an A-MSDU then ensure we set the + * relevant field. + */ + if (is_amsdu) + qos[0] |= IEEE80211_QOS_AMSDU; + if ((m->m_flags & M_AMPDU_MPDU) == 0) { /* * NB: don't assign a sequence # to potential @@ -1305,6 +1567,14 @@ ieee80211_encap(struct ieee80211vap *vap, struct ieee80211_node *ni, *(uint16_t *)wh->i_seq = htole16(seqno << IEEE80211_SEQ_SEQ_SHIFT); M_SEQNO_SET(m, seqno); + + /* + * XXX TODO: we shouldn't allow EAPOL, etc that would + * be forced to be non-QoS traffic to be A-MSDU encapsulated. + */ + if (is_amsdu) + printf("%s: XXX ERROR: is_amsdu set; not QoS!\n", + __func__); } @@ -1323,7 +1593,7 @@ ieee80211_encap(struct ieee80211vap *vap, struct ieee80211_node *ni, (vap->iv_opmode == IEEE80211_M_STA ? !IEEE80211_KEY_UNDEFINED(key) : !IEEE80211_KEY_UNDEFINED(&ni->ni_ucastkey)))) { - wh->i_fc[1] |= IEEE80211_FC1_WEP; + wh->i_fc[1] |= IEEE80211_FC1_PROTECTED; if (!ieee80211_crypto_enmic(vap, key, m, txfrag)) { IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_OUTPUT, eh.ether_dhost, @@ -1353,6 +1623,22 @@ bad: m_freem(m); return NULL; #undef WH4 +#undef MC01 +} + +void +ieee80211_free_mbuf(struct mbuf *m) +{ + struct mbuf *next; + + if (m == NULL) + return; + + do { + next = m->m_nextpkt; + m->m_nextpkt = NULL; + m_freem(m); + } while ((m = next) != NULL); } /* @@ -1367,33 +1653,35 @@ static int ieee80211_fragment(struct ieee80211vap *vap, struct mbuf *m0, u_int hdrsize, u_int ciphdrsize, u_int mtu) { + struct ieee80211com *ic = vap->iv_ic; struct ieee80211_frame *wh, *whf; - struct mbuf *m, *prev, *next; + struct mbuf *m, *prev; u_int totalhdrsize, fragno, fragsize, off, remainder, payload; + u_int hdrspace; KASSERT(m0->m_nextpkt == NULL, ("mbuf already chained?")); KASSERT(m0->m_pkthdr.len > mtu, ("pktlen %u mtu %u", m0->m_pkthdr.len, mtu)); + /* + * Honor driver DATAPAD requirement. + */ + if (ic->ic_flags & IEEE80211_F_DATAPAD) + hdrspace = roundup(hdrsize, sizeof(uint32_t)); + else + hdrspace = hdrsize; + wh = mtod(m0, struct ieee80211_frame *); /* NB: mark the first frag; it will be propagated below */ wh->i_fc[1] |= IEEE80211_FC1_MORE_FRAG; - totalhdrsize = hdrsize + ciphdrsize; + totalhdrsize = hdrspace + ciphdrsize; fragno = 1; off = mtu - ciphdrsize; remainder = m0->m_pkthdr.len - off; prev = m0; do { - fragsize = totalhdrsize + remainder; - if (fragsize > mtu) - fragsize = mtu; - /* XXX fragsize can be >2048! */ - KASSERT(fragsize < MCLBYTES, - ("fragment size %u too big!", fragsize)); - if (fragsize > MHLEN) - m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); - else - m = m_gethdr(M_DONTWAIT, MT_DATA); + fragsize = MIN(totalhdrsize + remainder, mtu); + m = m_get2(fragsize, M_NOWAIT, MT_DATA, M_PKTHDR); if (m == NULL) goto bad; /* leave room to prepend any cipher header */ @@ -1404,9 +1692,20 @@ ieee80211_fragment(struct ieee80211vap *vap, struct mbuf *m0, * we mark the first fragment with the MORE_FRAG bit * it automatically is propagated to each fragment; we * need only clear it on the last fragment (done below). + * NB: frag 1+ dont have Mesh Control field present. */ whf = mtod(m, struct ieee80211_frame *); memcpy(whf, wh, hdrsize); +#ifdef IEEE80211_SUPPORT_MESH + if (vap->iv_opmode == IEEE80211_M_MBSS) { + if (IEEE80211_IS_DSTODS(wh)) + ((struct ieee80211_qosframe_addr4 *) + whf)->i_qos[1] &= ~IEEE80211_QOS_MC; + else + ((struct ieee80211_qosframe *) + whf)->i_qos[1] &= ~IEEE80211_QOS_MC; + } +#endif *(uint16_t *)&whf->i_seq[0] |= htole16( (fragno & IEEE80211_SEQ_FRAG_MASK) << IEEE80211_SEQ_FRAG_SHIFT); @@ -1414,9 +1713,10 @@ ieee80211_fragment(struct ieee80211vap *vap, struct mbuf *m0, payload = fragsize - totalhdrsize; /* NB: destination is known to be contiguous */ - m_copydata(m0, off, payload, mtod(m, uint8_t *) + hdrsize); - m->m_len = hdrsize + payload; - m->m_pkthdr.len = hdrsize + payload; + + m_copydata(m0, off, payload, mtod(m, uint8_t *) + hdrspace); + m->m_len = hdrspace + payload; + m->m_pkthdr.len = hdrspace + payload; m->m_flags |= M_FRAG; /* chain up the fragment */ @@ -1442,11 +1742,7 @@ ieee80211_fragment(struct ieee80211vap *vap, struct mbuf *m0, return 1; bad: /* reclaim fragments but leave original frame for caller to free */ - for (m = m0->m_nextpkt; m != NULL; m = next) { - next = m->m_nextpkt; - m->m_nextpkt = NULL; /* XXX paranoid */ - m_freem(m); - } + ieee80211_free_mbuf(m0->m_nextpkt); m0->m_nextpkt = NULL; return 0; } @@ -1490,7 +1786,7 @@ ieee80211_add_xrates(uint8_t *frm, const struct ieee80211_rateset *rs) /* * Add an ssid element to a frame. */ -static uint8_t * +uint8_t * ieee80211_add_ssid(uint8_t *frm, const uint8_t *ssid, u_int len) { *frm++ = IEEE80211_ELEMID_SSID; @@ -1527,7 +1823,7 @@ static uint8_t * ieee80211_add_cfparms(uint8_t *frm, struct ieee80211com *ic) { #define ADDSHORT(frm, v) do { \ - LE_WRITE_2(frm, v); \ + le16enc(frm, v); \ frm += 2; \ } while (0) *frm++ = IEEE80211_ELEMID_CFPARMS; @@ -1558,7 +1854,7 @@ add_ie(uint8_t *frm, const uint8_t *ie) /* * Add a WME information element to a frame. */ -static uint8_t * +uint8_t * ieee80211_add_wme_info(uint8_t *frm, struct ieee80211_wme_state *wme) { static const struct ieee80211_wme_info info = { @@ -1582,7 +1878,7 @@ ieee80211_add_wme_param(uint8_t *frm, struct ieee80211_wme_state *wme) { #define SM(_v, _f) (((_v) << _f##_S) & _f) #define ADDSHORT(frm, v) do { \ - LE_WRITE_2(frm, v); \ + le16enc(frm, v); \ frm += 2; \ } while (0) /* NB: this works 'cuz a param has an info at the front */ @@ -1663,6 +1959,33 @@ ieee80211_add_supportedchannels(uint8_t *frm, struct ieee80211com *ic) } /* + * Add an 11h Quiet time element to a frame. + */ +static uint8_t * +ieee80211_add_quiet(uint8_t *frm, struct ieee80211vap *vap) +{ + struct ieee80211_quiet_ie *quiet = (struct ieee80211_quiet_ie *) frm; + + quiet->quiet_ie = IEEE80211_ELEMID_QUIET; + quiet->len = 6; + if (vap->iv_quiet_count_value == 1) + vap->iv_quiet_count_value = vap->iv_quiet_count; + else if (vap->iv_quiet_count_value > 1) + vap->iv_quiet_count_value--; + + if (vap->iv_quiet_count_value == 0) { + /* value 0 is reserved as per 802.11h standerd */ + vap->iv_quiet_count_value = 1; + } + + quiet->tbttcount = vap->iv_quiet_count_value; + quiet->period = vap->iv_quiet_period; + quiet->duration = htole16(vap->iv_quiet_duration); + quiet->offset = htole16(vap->iv_quiet_offset); + return frm + sizeof(*quiet); +} + +/* * Add an 11h Channel Switch Announcement element to a frame. * Note that we use the per-vap CSA count to adjust the global * counter so we can use this routine to form probe response @@ -1697,7 +2020,7 @@ ieee80211_add_countryie(uint8_t *frm, struct ieee80211com *ic) * re-calculation. */ if (ic->ic_countryie != NULL) - free(ic->ic_countryie, M_80211_NODE_IE); + IEEE80211_FREE(ic->ic_countryie, M_80211_NODE_IE); ic->ic_countryie = ieee80211_alloc_countryie(ic); if (ic->ic_countryie == NULL) return frm; @@ -1706,6 +2029,40 @@ ieee80211_add_countryie(uint8_t *frm, struct ieee80211com *ic) return add_appie(frm, ic->ic_countryie); } +uint8_t * +ieee80211_add_wpa(uint8_t *frm, const struct ieee80211vap *vap) +{ + if (vap->iv_flags & IEEE80211_F_WPA1 && vap->iv_wpa_ie != NULL) + return (add_ie(frm, vap->iv_wpa_ie)); + else { + /* XXX else complain? */ + return (frm); + } +} + +uint8_t * +ieee80211_add_rsn(uint8_t *frm, const struct ieee80211vap *vap) +{ + if (vap->iv_flags & IEEE80211_F_WPA2 && vap->iv_rsn_ie != NULL) + return (add_ie(frm, vap->iv_rsn_ie)); + else { + /* XXX else complain? */ + return (frm); + } +} + +uint8_t * +ieee80211_add_qos(uint8_t *frm, const struct ieee80211_node *ni) +{ + if (ni->ni_flags & IEEE80211_NODE_QOS) { + *frm++ = IEEE80211_ELEMID_QOS; + *frm++ = 1; + *frm++ = 0; + } + + return (frm); +} + /* * Send a probe request frame with the specified ssid * and any optional information element data. @@ -1719,17 +2076,21 @@ ieee80211_send_probereq(struct ieee80211_node *ni, { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; + struct ieee80211_node *bss; const struct ieee80211_txparam *tp; struct ieee80211_bpf_params params; - struct ieee80211_frame *wh; const struct ieee80211_rateset *rs; struct mbuf *m; uint8_t *frm; + int ret; + + bss = ieee80211_ref_node(vap->iv_bss); if (vap->iv_state == IEEE80211_S_CAC) { IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, ni, "block %s frame in CAC state", "probe request"); vap->iv_stats.is_tx_badstate++; + ieee80211_free_node(bss); return EIO; /* XXX */ } @@ -1751,6 +2112,7 @@ ieee80211_send_probereq(struct ieee80211_node *ni, * [tlv] supported rates * [tlv] RSN (optional) * [tlv] extended supported rates + * [tlv] HT cap (optional) * [tlv] WPA (optional) * [tlv] user-specified ie's */ @@ -1758,6 +2120,8 @@ ieee80211_send_probereq(struct ieee80211_node *ni, ic->ic_headroom + sizeof(struct ieee80211_frame), 2 + IEEE80211_NWID_LEN + 2 + IEEE80211_RATE_SIZE + + sizeof(struct ieee80211_ie_htcap) + + sizeof(struct ieee80211_ie_htinfo) + sizeof(struct ieee80211_ie_wpa) + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) + sizeof(struct ieee80211_ie_wpa) @@ -1767,37 +2131,52 @@ ieee80211_send_probereq(struct ieee80211_node *ni, if (m == NULL) { vap->iv_stats.is_tx_nobuf++; ieee80211_free_node(ni); + ieee80211_free_node(bss); return ENOMEM; } frm = ieee80211_add_ssid(frm, ssid, ssidlen); rs = ieee80211_get_suprates(ic, ic->ic_curchan); frm = ieee80211_add_rates(frm, rs); - if (vap->iv_flags & IEEE80211_F_WPA2) { - if (vap->iv_rsn_ie != NULL) - frm = add_ie(frm, vap->iv_rsn_ie); - /* XXX else complain? */ - } + frm = ieee80211_add_rsn(frm, vap); frm = ieee80211_add_xrates(frm, rs); - if (vap->iv_flags & IEEE80211_F_WPA1) { - if (vap->iv_wpa_ie != NULL) - frm = add_ie(frm, vap->iv_wpa_ie); - /* XXX else complain? */ + + /* + * Note: we can't use bss; we don't have one yet. + * + * So, we should announce our capabilities + * in this channel mode (2g/5g), not the + * channel details itself. + */ + if ((vap->iv_opmode == IEEE80211_M_IBSS) && + (vap->iv_flags_ht & IEEE80211_FHT_HT)) { + struct ieee80211_channel *c; + + /* + * Get the HT channel that we should try upgrading to. + * If we can do 40MHz then this'll upgrade it appropriately. + */ + c = ieee80211_ht_adjust_channel(ic, ic->ic_curchan, + vap->iv_flags_ht); + frm = ieee80211_add_htcap_ch(frm, vap, c); } + + frm = ieee80211_add_wpa(frm, vap); if (vap->iv_appie_probereq != NULL) frm = add_appie(frm, vap->iv_appie_probereq); m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); KASSERT(M_LEADINGSPACE(m) >= sizeof(struct ieee80211_frame), ("leading space %zd", M_LEADINGSPACE(m))); - M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT); + M_PREPEND(m, sizeof(struct ieee80211_frame), M_NOWAIT); if (m == NULL) { /* NB: cannot happen */ ieee80211_free_node(ni); + ieee80211_free_node(bss); return ENOMEM; } - wh = mtod(m, struct ieee80211_frame *); + IEEE80211_TX_LOCK(ic); ieee80211_send_setup(ni, m, IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_REQ, IEEE80211_NONQOS_TID, sa, da, bssid); @@ -1810,8 +2189,11 @@ ieee80211_send_probereq(struct ieee80211_node *ni, IEEE80211_NODE_STAT(ni, tx_mgmt); IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS, - "send probe req on channel %u bssid %s ssid \"%.*s\"\n", - ieee80211_chan2ieee(ic, ic->ic_curchan), ether_sprintf(bssid), + "send probe req on channel %u bssid %s sa %6D da %6D ssid \"%.*s\"\n", + ieee80211_chan2ieee(ic, ic->ic_curchan), + ether_sprintf(bssid), + sa, ":", + da, ":", ssidlen, ssid); memset(¶ms, 0, sizeof(params)); @@ -1824,7 +2206,10 @@ ieee80211_send_probereq(struct ieee80211_node *ni, } else params.ibp_try0 = tp->maxretry; params.ibp_power = ni->ni_txpower; - return ic->ic_raw_xmit(ni, m, ¶ms); + ret = ieee80211_raw_output(vap, ni, m, ¶ms); + IEEE80211_TX_UNLOCK(ic); + ieee80211_free_node(bss); + return (ret); } /* @@ -1956,7 +2341,8 @@ ieee80211_send_mgmt(struct ieee80211_node *ni, int type, int arg) case IEEE80211_FC0_SUBTYPE_DEAUTH: IEEE80211_NOTE(vap, IEEE80211_MSG_AUTH, ni, - "send station deauthenticate (reason %d)", arg); + "send station deauthenticate (reason: %d (%s))", arg, + ieee80211_reason_to_string(arg)); m = ieee80211_getmgtframe(&frm, ic->ic_headroom + sizeof(struct ieee80211_frame), sizeof(uint16_t)); @@ -2046,33 +2432,40 @@ ieee80211_send_mgmt(struct ieee80211_node *ni, int type, int arg) frm = ieee80211_add_ssid(frm, ni->ni_essid, ni->ni_esslen); frm = ieee80211_add_rates(frm, &ni->ni_rates); - if (vap->iv_flags & IEEE80211_F_WPA2) { - if (vap->iv_rsn_ie != NULL) - frm = add_ie(frm, vap->iv_rsn_ie); - /* XXX else complain? */ - } + frm = ieee80211_add_rsn(frm, vap); frm = ieee80211_add_xrates(frm, &ni->ni_rates); if (capinfo & IEEE80211_CAPINFO_SPECTRUM_MGMT) { frm = ieee80211_add_powercapability(frm, ic->ic_curchan); frm = ieee80211_add_supportedchannels(frm, ic); } + + /* + * Check the channel - we may be using an 11n NIC with an + * 11n capable station, but we're configured to be an 11b + * channel. + */ if ((vap->iv_flags_ht & IEEE80211_FHT_HT) && + IEEE80211_IS_CHAN_HT(ni->ni_chan) && ni->ni_ies.htcap_ie != NULL && - ni->ni_ies.htcap_ie[0] == IEEE80211_ELEMID_HTCAP) + ni->ni_ies.htcap_ie[0] == IEEE80211_ELEMID_HTCAP) { frm = ieee80211_add_htcap(frm, ni); - if (vap->iv_flags & IEEE80211_F_WPA1) { - if (vap->iv_wpa_ie != NULL) - frm = add_ie(frm, vap->iv_wpa_ie); - /* XXX else complain */ } + frm = ieee80211_add_wpa(frm, vap); if ((ic->ic_flags & IEEE80211_F_WME) && ni->ni_ies.wme_ie != NULL) frm = ieee80211_add_wme_info(frm, &ic->ic_wme); + + /* + * Same deal - only send HT info if we're on an 11n + * capable channel. + */ if ((vap->iv_flags_ht & IEEE80211_FHT_HT) && + IEEE80211_IS_CHAN_HT(ni->ni_chan) && ni->ni_ies.htcap_ie != NULL && - ni->ni_ies.htcap_ie[0] == IEEE80211_ELEMID_VENDOR) + ni->ni_ies.htcap_ie[0] == IEEE80211_ELEMID_VENDOR) { frm = ieee80211_add_htcap_vendor(frm, ni); + } #ifdef IEEE80211_SUPPORT_SUPERG if (IEEE80211_ATH_CAP(vap, ni, IEEE80211_F_ATHEROS)) { frm = ieee80211_add_ath(frm, @@ -2169,7 +2562,8 @@ ieee80211_send_mgmt(struct ieee80211_node *ni, int type, int arg) case IEEE80211_FC0_SUBTYPE_DISASSOC: IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC, ni, - "send station disassociate (reason %d)", arg); + "send station disassociate (reason: %d (%s))", arg, + ieee80211_reason_to_string(arg)); m = ieee80211_getmgtframe(&frm, ic->ic_headroom + sizeof(struct ieee80211_frame), sizeof(uint16_t)); @@ -2255,6 +2649,7 @@ ieee80211_alloc_proberesp(struct ieee80211_node *bss, int legacy) + IEEE80211_COUNTRY_MAX_SIZE + 3 + sizeof(struct ieee80211_csa_ie) + + sizeof(struct ieee80211_quiet_ie) + 3 + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) + sizeof(struct ieee80211_ie_wpa) @@ -2321,14 +2716,17 @@ ieee80211_alloc_proberesp(struct ieee80211_node *bss, int legacy) if (ic->ic_flags & IEEE80211_F_CSAPENDING) frm = ieee80211_add_csa(frm, vap); } + if (vap->iv_flags & IEEE80211_F_DOTH) { + if (IEEE80211_IS_CHAN_DFS(ic->ic_bsschan) && + (vap->iv_flags_ext & IEEE80211_FEXT_DFS)) { + if (vap->iv_quiet) + frm = ieee80211_add_quiet(frm, vap); + } + } if (IEEE80211_IS_CHAN_ANYG(bss->ni_chan)) frm = ieee80211_add_erp(frm, ic); frm = ieee80211_add_xrates(frm, rs); - if (vap->iv_flags & IEEE80211_F_WPA2) { - if (vap->iv_rsn_ie != NULL) - frm = add_ie(frm, vap->iv_rsn_ie); - /* XXX else complain? */ - } + frm = ieee80211_add_rsn(frm, vap); /* * NB: legacy 11b clients do not get certain ie's. * The caller identifies such clients by passing @@ -2340,11 +2738,7 @@ ieee80211_alloc_proberesp(struct ieee80211_node *bss, int legacy) frm = ieee80211_add_htcap(frm, bss); frm = ieee80211_add_htinfo(frm, bss); } - if (vap->iv_flags & IEEE80211_F_WPA1) { - if (vap->iv_wpa_ie != NULL) - frm = add_ie(frm, vap->iv_wpa_ie); - /* XXX else complain? */ - } + frm = ieee80211_add_wpa(frm, vap); if (vap->iv_flags & IEEE80211_F_WME) frm = ieee80211_add_wme_param(frm, &ic->ic_wme); if (IEEE80211_IS_CHAN_HT(bss->ni_chan) && @@ -2383,8 +2777,8 @@ ieee80211_send_proberesp(struct ieee80211vap *vap, { struct ieee80211_node *bss = vap->iv_bss; struct ieee80211com *ic = vap->iv_ic; - struct ieee80211_frame *wh; struct mbuf *m; + int ret; if (vap->iv_state == IEEE80211_S_CAC) { IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, bss, @@ -2410,10 +2804,10 @@ ieee80211_send_proberesp(struct ieee80211vap *vap, return ENOMEM; } - M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT); + M_PREPEND(m, sizeof(struct ieee80211_frame), M_NOWAIT); KASSERT(m != NULL, ("no room for header")); - wh = mtod(m, struct ieee80211_frame *); + IEEE80211_TX_LOCK(ic); ieee80211_send_setup(bss, m, IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_RESP, IEEE80211_NONQOS_TID, vap->iv_myaddr, da, bss->ni_bssid); @@ -2428,7 +2822,9 @@ ieee80211_send_proberesp(struct ieee80211vap *vap, legacy ? " <legacy>" : ""); IEEE80211_NODE_STAT(bss, tx_mgmt); - return ic->ic_raw_xmit(bss, m, NULL); + ret = ieee80211_raw_output(vap, bss, m, NULL); + IEEE80211_TX_UNLOCK(ic); + return (ret); } /* @@ -2444,7 +2840,7 @@ ieee80211_alloc_rts(struct ieee80211com *ic, struct mbuf *m; /* XXX honor ic_headroom */ - m = m_gethdr(M_DONTWAIT, MT_DATA); + m = m_gethdr(M_NOWAIT, MT_DATA); if (m != NULL) { rts = mtod(m, struct ieee80211_frame_rts *); rts->i_fc[0] = IEEE80211_FC0_VERSION_0 | @@ -2470,7 +2866,7 @@ ieee80211_alloc_cts(struct ieee80211com *ic, struct mbuf *m; /* XXX honor ic_headroom */ - m = m_gethdr(M_DONTWAIT, MT_DATA); + m = m_gethdr(M_NOWAIT, MT_DATA); if (m != NULL) { cts = mtod(m, struct ieee80211_frame_cts *); cts->i_fc[0] = IEEE80211_FC0_VERSION_0 | @@ -2487,20 +2883,35 @@ ieee80211_alloc_cts(struct ieee80211com *ic, static void ieee80211_tx_mgt_timeout(void *arg) { - struct ieee80211_node *ni = arg; - struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211vap *vap = arg; + IEEE80211_LOCK(vap->iv_ic); if (vap->iv_state != IEEE80211_S_INIT && (vap->iv_ic->ic_flags & IEEE80211_F_SCAN) == 0) { /* * NB: it's safe to specify a timeout as the reason here; * it'll only be used in the right state. */ - ieee80211_new_state(vap, IEEE80211_S_SCAN, + ieee80211_new_state_locked(vap, IEEE80211_S_SCAN, IEEE80211_SCAN_FAIL_TIMEOUT); } + IEEE80211_UNLOCK(vap->iv_ic); } +/* + * This is the callback set on net80211-sourced transmitted + * authentication request frames. + * + * This does a couple of things: + * + * + If the frame transmitted was a success, it schedules a future + * event which will transition the interface to scan. + * If a state transition _then_ occurs before that event occurs, + * said state transition will cancel this callout. + * + * + If the frame transmit was a failure, it immediately schedules + * the transition back to scan. + */ static void ieee80211_tx_mgt_cb(struct ieee80211_node *ni, void *arg, int status) { @@ -2509,7 +2920,7 @@ ieee80211_tx_mgt_cb(struct ieee80211_node *ni, void *arg, int status) /* * Frame transmit completed; arrange timer callback. If - * transmit was successfuly we wait for response. Otherwise + * transmit was successfully we wait for response. Otherwise * we arrange an immediate callback instead of doing the * callback directly since we don't know what state the driver * is in (e.g. what locks it is holding). This work should @@ -2518,17 +2929,19 @@ ieee80211_tx_mgt_cb(struct ieee80211_node *ni, void *arg, int status) * * XXX what happens if !acked but response shows up before callback? */ - if (vap->iv_state == ostate) + if (vap->iv_state == ostate) { callout_reset(&vap->iv_mgtsend, status == 0 ? IEEE80211_TRANS_WAIT*hz : 0, - ieee80211_tx_mgt_timeout, ni); + ieee80211_tx_mgt_timeout, vap); + } } static void ieee80211_beacon_construct(struct mbuf *m, uint8_t *frm, - struct ieee80211_beacon_offsets *bo, struct ieee80211_node *ni) + struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211_beacon_offsets *bo = &vap->iv_bcn_off; struct ieee80211com *ic = ni->ni_ic; struct ieee80211_rateset *rs = &ni->ni_rates; uint16_t capinfo; @@ -2619,29 +3032,32 @@ ieee80211_beacon_construct(struct mbuf *m, uint8_t *frm, frm = ieee80211_add_powerconstraint(frm, vap); bo->bo_csa = frm; if (ic->ic_flags & IEEE80211_F_CSAPENDING) - frm = ieee80211_add_csa(frm, vap); + frm = ieee80211_add_csa(frm, vap); } else bo->bo_csa = frm; + + if (vap->iv_flags & IEEE80211_F_DOTH) { + bo->bo_quiet = frm; + if (IEEE80211_IS_CHAN_DFS(ic->ic_bsschan) && + (vap->iv_flags_ext & IEEE80211_FEXT_DFS)) { + if (vap->iv_quiet) + frm = ieee80211_add_quiet(frm,vap); + } + } else + bo->bo_quiet = frm; + if (IEEE80211_IS_CHAN_ANYG(ni->ni_chan)) { bo->bo_erp = frm; frm = ieee80211_add_erp(frm, ic); } frm = ieee80211_add_xrates(frm, rs); - if (vap->iv_flags & IEEE80211_F_WPA2) { - if (vap->iv_rsn_ie != NULL) - frm = add_ie(frm, vap->iv_rsn_ie); - /* XXX else complain */ - } + frm = ieee80211_add_rsn(frm, vap); if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) { frm = ieee80211_add_htcap(frm, ni); bo->bo_htinfo = frm; frm = ieee80211_add_htinfo(frm, ni); } - if (vap->iv_flags & IEEE80211_F_WPA1) { - if (vap->iv_wpa_ie != NULL) - frm = add_ie(frm, vap->iv_wpa_ie); - /* XXX else complain */ - } + frm = ieee80211_add_wpa(frm, vap); if (vap->iv_flags & IEEE80211_F_WME) { bo->bo_wme = frm; frm = ieee80211_add_wme_param(frm, &ic->ic_wme); @@ -2684,8 +3100,7 @@ ieee80211_beacon_construct(struct mbuf *m, uint8_t *frm, * Allocate a beacon frame and fillin the appropriate bits. */ struct mbuf * -ieee80211_beacon_alloc(struct ieee80211_node *ni, - struct ieee80211_beacon_offsets *bo) +ieee80211_beacon_alloc(struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; @@ -2735,7 +3150,8 @@ ieee80211_beacon_alloc(struct ieee80211_node *ni, + 2 + 4 + vap->iv_tim_len /* DTIM/IBSSPARMS */ + IEEE80211_COUNTRY_MAX_SIZE /* country */ + 2 + 1 /* power control */ - + sizeof(struct ieee80211_csa_ie) /* CSA */ + + sizeof(struct ieee80211_csa_ie) /* CSA */ + + sizeof(struct ieee80211_quiet_ie) /* Quiet */ + 2 + 1 /* ERP */ + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) + (vap->iv_caps & IEEE80211_C_WPA ? /* WPA 1+2 */ @@ -2766,9 +3182,9 @@ ieee80211_beacon_alloc(struct ieee80211_node *ni, vap->iv_stats.is_tx_nobuf++; return NULL; } - ieee80211_beacon_construct(m, frm, bo, ni); + ieee80211_beacon_construct(m, frm, ni); - M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT); + M_PREPEND(m, sizeof(struct ieee80211_frame), M_NOWAIT); KASSERT(m != NULL, ("no space for 802.11 header?")); wh = mtod(m, struct ieee80211_frame *); wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | @@ -2787,10 +3203,10 @@ ieee80211_beacon_alloc(struct ieee80211_node *ni, * Update the dynamic parts of a beacon frame based on the current state. */ int -ieee80211_beacon_update(struct ieee80211_node *ni, - struct ieee80211_beacon_offsets *bo, struct mbuf *m, int mcast) +ieee80211_beacon_update(struct ieee80211_node *ni, struct mbuf *m, int mcast) { struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211_beacon_offsets *bo = &vap->iv_bcn_off; struct ieee80211com *ic = ni->ni_ic; int len_changed = 0; uint16_t capinfo; @@ -2820,7 +3236,7 @@ ieee80211_beacon_update(struct ieee80211_node *ni, * clear IEEE80211_BEACON_CSA. */ ieee80211_beacon_construct(m, - mtod(m, uint8_t*) + sizeof(struct ieee80211_frame), bo, ni); + mtod(m, uint8_t*) + sizeof(struct ieee80211_frame), ni); /* XXX do WME aggressive mode processing? */ IEEE80211_UNLOCK(ic); @@ -2841,10 +3257,10 @@ ieee80211_beacon_update(struct ieee80211_node *ni, struct ieee80211_wme_state *wme = &ic->ic_wme; /* - * Check for agressive mode change. When there is + * Check for aggressive mode change. When there is * significant high priority traffic in the BSS * throttle back BE traffic by using conservative - * parameters. Otherwise BE uses agressive params + * parameters. Otherwise BE uses aggressive params * to optimize performance of legacy/non-QoS traffic. */ if (wme->wme_flags & WME_F_AGGRMODE) { @@ -2955,6 +3371,7 @@ ieee80211_beacon_update(struct ieee80211_node *ni, bo->bo_appie += adjust; bo->bo_wme += adjust; bo->bo_csa += adjust; + bo->bo_quiet += adjust; bo->bo_tim_len = timlen; /* update information element */ @@ -3008,6 +3425,7 @@ ieee80211_beacon_update(struct ieee80211_node *ni, #endif bo->bo_appie += sizeof(*csa); bo->bo_csa_trailer_len += sizeof(*csa); + bo->bo_quiet += sizeof(*csa); bo->bo_tim_trailer_len += sizeof(*csa); m->m_len += sizeof(*csa); m->m_pkthdr.len += sizeof(*csa); @@ -3018,6 +3436,11 @@ ieee80211_beacon_update(struct ieee80211_node *ni, vap->iv_csa_count++; /* NB: don't clear IEEE80211_BEACON_CSA */ } + if (IEEE80211_IS_CHAN_DFS(ic->ic_bsschan) && + (vap->iv_flags_ext & IEEE80211_FEXT_DFS) ){ + if (vap->iv_quiet) + ieee80211_add_quiet(bo->bo_quiet, vap); + } if (isset(bo->bo_flags, IEEE80211_BEACON_ERP)) { /* * ERP element needs updating. @@ -3060,3 +3483,80 @@ ieee80211_beacon_update(struct ieee80211_node *ni, return len_changed; } + +/* + * Do Ethernet-LLC encapsulation for each payload in a fast frame + * tunnel encapsulation. The frame is assumed to have an Ethernet + * header at the front that must be stripped before prepending the + * LLC followed by the Ethernet header passed in (with an Ethernet + * type that specifies the payload size). + */ +struct mbuf * +ieee80211_ff_encap1(struct ieee80211vap *vap, struct mbuf *m, + const struct ether_header *eh) +{ + struct llc *llc; + uint16_t payload; + + /* XXX optimize by combining m_adj+M_PREPEND */ + m_adj(m, sizeof(struct ether_header) - sizeof(struct llc)); + llc = mtod(m, struct llc *); + llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP; + llc->llc_control = LLC_UI; + llc->llc_snap.org_code[0] = 0; + llc->llc_snap.org_code[1] = 0; + llc->llc_snap.org_code[2] = 0; + llc->llc_snap.ether_type = eh->ether_type; + payload = m->m_pkthdr.len; /* NB: w/o Ethernet header */ + + M_PREPEND(m, sizeof(struct ether_header), M_NOWAIT); + if (m == NULL) { /* XXX cannot happen */ + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG, + "%s: no space for ether_header\n", __func__); + vap->iv_stats.is_tx_nobuf++; + return NULL; + } + ETHER_HEADER_COPY(mtod(m, void *), eh); + mtod(m, struct ether_header *)->ether_type = htons(payload); + return m; +} + +/* + * Complete an mbuf transmission. + * + * For now, this simply processes a completed frame after the + * driver has completed it's transmission and/or retransmission. + * It assumes the frame is an 802.11 encapsulated frame. + * + * Later on it will grow to become the exit path for a given frame + * from the driver and, depending upon how it's been encapsulated + * and already transmitted, it may end up doing A-MPDU retransmission, + * power save requeuing, etc. + * + * In order for the above to work, the driver entry point to this + * must not hold any driver locks. Thus, the driver needs to delay + * any actual mbuf completion until it can release said locks. + * + * This frees the mbuf and if the mbuf has a node reference, + * the node reference will be freed. + */ +void +ieee80211_tx_complete(struct ieee80211_node *ni, struct mbuf *m, int status) +{ + + if (ni != NULL) { + struct ifnet *ifp = ni->ni_vap->iv_ifp; + + if (status == 0) { + if_inc_counter(ifp, IFCOUNTER_OBYTES, m->m_pkthdr.len); + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); + if (m->m_flags & M_MCAST) + if_inc_counter(ifp, IFCOUNTER_OMCASTS, 1); + } else + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); + if (m->m_flags & M_TXCB) + ieee80211_process_callback(ni, m, status); + ieee80211_free_node(ni); + } + m_freem(m); +} |