Index: projects/mesh11s/sys/net80211/ieee80211_ht.c =================================================================== --- projects/mesh11s/sys/net80211/ieee80211_ht.c (revision 191684) +++ projects/mesh11s/sys/net80211/ieee80211_ht.c (revision 191685) @@ -1,2484 +1,2540 @@ /*- * Copyright (c) 2007-2008 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. * * 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. */ #include #ifdef __FreeBSD__ __FBSDID("$FreeBSD$"); #endif /* * IEEE 802.11n protocol support. */ #include "opt_inet.h" #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include +#include /* define here, used throughout file */ #define MS(_v, _f) (((_v) & _f) >> _f##_S) #define SM(_v, _f) (((_v) << _f##_S) & _f) const struct ieee80211_mcs_rates ieee80211_htrates[16] = { { 13, 14, 27, 30 }, /* MCS 0 */ { 26, 29, 54, 60 }, /* MCS 1 */ { 39, 43, 81, 90 }, /* MCS 2 */ { 52, 58, 108, 120 }, /* MCS 3 */ { 78, 87, 162, 180 }, /* MCS 4 */ { 104, 116, 216, 240 }, /* MCS 5 */ { 117, 130, 243, 270 }, /* MCS 6 */ { 130, 144, 270, 300 }, /* MCS 7 */ { 26, 29, 54, 60 }, /* MCS 8 */ { 52, 58, 108, 120 }, /* MCS 9 */ { 78, 87, 162, 180 }, /* MCS 10 */ { 104, 116, 216, 240 }, /* MCS 11 */ { 156, 173, 324, 360 }, /* MCS 12 */ { 208, 231, 432, 480 }, /* MCS 13 */ { 234, 260, 486, 540 }, /* MCS 14 */ { 260, 289, 540, 600 } /* MCS 15 */ }; static const struct ieee80211_htrateset ieee80211_rateset_11n = { 16, { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } }; #ifdef IEEE80211_AMPDU_AGE /* XXX public for sysctl hookup */ int ieee80211_ampdu_age = -1; /* threshold for ampdu reorder q (ms) */ #endif int ieee80211_recv_bar_ena = 1; int ieee80211_addba_timeout = -1; /* timeout waiting for ADDBA response */ int ieee80211_addba_backoff = -1; /* backoff after max ADDBA requests */ int ieee80211_addba_maxtries = 3; /* max ADDBA requests before backoff */ int ieee80211_bar_timeout = -1; /* timeout waiting for BAR response */ int ieee80211_bar_maxtries = 50; /* max BAR requests before DELBA */ /* * Setup HT parameters that depends on the clock frequency. */ static void ieee80211_ht_setup(void) { #ifdef IEEE80211_AMPDU_AGE ieee80211_ampdu_age = msecs_to_ticks(500); #endif ieee80211_addba_timeout = msecs_to_ticks(250); ieee80211_addba_backoff = msecs_to_ticks(10*1000); ieee80211_bar_timeout = msecs_to_ticks(250); } SYSINIT(wlan_ht, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_ht_setup, NULL); static int ieee80211_ampdu_enable(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap); static int ieee80211_addba_request(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap, int dialogtoken, int baparamset, int batimeout); static int ieee80211_addba_response(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap, int code, int baparamset, int batimeout); static void ieee80211_addba_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap); static void ieee80211_aggr_recv_action(struct ieee80211_node *ni, const uint8_t *frm, const uint8_t *efrm); static void ieee80211_bar_response(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap, int status); static void ampdu_tx_stop(struct ieee80211_tx_ampdu *tap); static void bar_stop_timer(struct ieee80211_tx_ampdu *tap); static int ampdu_rx_start(struct ieee80211_node *, struct ieee80211_rx_ampdu *, int baparamset, int batimeout, int baseqctl); static void ampdu_rx_stop(struct ieee80211_node *, struct ieee80211_rx_ampdu *); void ieee80211_ht_attach(struct ieee80211com *ic) { /* setup default aggregation policy */ ic->ic_recv_action = ieee80211_aggr_recv_action; ic->ic_send_action = ieee80211_send_action; ic->ic_ampdu_enable = ieee80211_ampdu_enable; ic->ic_addba_request = ieee80211_addba_request; ic->ic_addba_response = ieee80211_addba_response; ic->ic_addba_stop = ieee80211_addba_stop; ic->ic_bar_response = ieee80211_bar_response; ic->ic_ampdu_rx_start = ampdu_rx_start; ic->ic_ampdu_rx_stop = ampdu_rx_stop; ic->ic_htprotmode = IEEE80211_PROT_RTSCTS; ic->ic_curhtprotmode = IEEE80211_HTINFO_OPMODE_PURE; } void ieee80211_ht_detach(struct ieee80211com *ic) { } void ieee80211_ht_vattach(struct ieee80211vap *vap) { /* driver can override defaults */ vap->iv_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_8K; vap->iv_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_NA; vap->iv_ampdu_limit = vap->iv_ampdu_rxmax; vap->iv_amsdu_limit = vap->iv_htcaps & IEEE80211_HTCAP_MAXAMSDU; /* tx aggregation traffic thresholds */ vap->iv_ampdu_mintraffic[WME_AC_BK] = 128; vap->iv_ampdu_mintraffic[WME_AC_BE] = 64; vap->iv_ampdu_mintraffic[WME_AC_VO] = 32; vap->iv_ampdu_mintraffic[WME_AC_VI] = 32; if (vap->iv_htcaps & IEEE80211_HTC_HT) { /* * Device is HT capable; enable all HT-related * facilities by default. * XXX these choices may be too aggressive. */ vap->iv_flags_ext |= IEEE80211_FEXT_HT | IEEE80211_FEXT_HTCOMPAT ; if (vap->iv_htcaps & IEEE80211_HTCAP_SHORTGI20) vap->iv_flags_ext |= IEEE80211_FEXT_SHORTGI20; /* XXX infer from channel list? */ if (vap->iv_htcaps & IEEE80211_HTCAP_CHWIDTH40) { vap->iv_flags_ext |= IEEE80211_FEXT_USEHT40; if (vap->iv_htcaps & IEEE80211_HTCAP_SHORTGI40) vap->iv_flags_ext |= IEEE80211_FEXT_SHORTGI40; } /* enable RIFS if capable */ if (vap->iv_htcaps & IEEE80211_HTC_RIFS) vap->iv_flags_ext |= IEEE80211_FEXT_RIFS; /* NB: A-MPDU and A-MSDU rx are mandated, these are tx only */ vap->iv_flags_ext |= IEEE80211_FEXT_AMPDU_RX; if (vap->iv_htcaps & IEEE80211_HTC_AMPDU) vap->iv_flags_ext |= IEEE80211_FEXT_AMPDU_TX; vap->iv_flags_ext |= IEEE80211_FEXT_AMSDU_RX; if (vap->iv_htcaps & IEEE80211_HTC_AMSDU) vap->iv_flags_ext |= IEEE80211_FEXT_AMSDU_TX; } /* NB: disable default legacy WDS, too many issues right now */ if (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY) vap->iv_flags_ext &= ~IEEE80211_FEXT_HT; } void ieee80211_ht_vdetach(struct ieee80211vap *vap) { } static void ht_announce(struct ieee80211com *ic, int mode, const struct ieee80211_htrateset *rs) { struct ifnet *ifp = ic->ic_ifp; int i, rate, mword; if_printf(ifp, "%s MCS: ", ieee80211_phymode_name[mode]); for (i = 0; i < rs->rs_nrates; i++) { mword = ieee80211_rate2media(ic, rs->rs_rates[i] | IEEE80211_RATE_MCS, mode); if (IFM_SUBTYPE(mword) != IFM_IEEE80211_MCS) continue; rate = ieee80211_htrates[rs->rs_rates[i]].ht40_rate_400ns; printf("%s%d%sMbps", (i != 0 ? " " : ""), rate / 2, ((rate & 0x1) != 0 ? ".5" : "")); } printf("\n"); } void ieee80211_ht_announce(struct ieee80211com *ic) { if (isset(ic->ic_modecaps, IEEE80211_MODE_11NA)) ht_announce(ic, IEEE80211_MODE_11NA, &ieee80211_rateset_11n); if (isset(ic->ic_modecaps, IEEE80211_MODE_11NG)) ht_announce(ic, IEEE80211_MODE_11NG, &ieee80211_rateset_11n); } const struct ieee80211_htrateset * ieee80211_get_suphtrates(struct ieee80211com *ic, const struct ieee80211_channel *c) { return &ieee80211_rateset_11n; } /* * Receive processing. */ /* * Decap the encapsulated A-MSDU frames and dispatch all but * the last for delivery. The last frame is returned for * delivery via the normal path. */ struct mbuf * ieee80211_decap_amsdu(struct ieee80211_node *ni, struct mbuf *m) { struct ieee80211vap *vap = ni->ni_vap; int framelen; struct mbuf *n; /* discard 802.3 header inserted by ieee80211_decap */ m_adj(m, sizeof(struct ether_header)); vap->iv_stats.is_amsdu_decap++; for (;;) { /* * Decap the first frame, bust it apart from the * remainder and deliver. We leave the last frame * delivery to the caller (for consistency with other * code paths, could also do it here). */ m = ieee80211_decap1(m, &framelen); if (m == NULL) { IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, ni->ni_macaddr, "a-msdu", "%s", "decap failed"); vap->iv_stats.is_amsdu_tooshort++; return NULL; } if (m->m_pkthdr.len == framelen) break; n = m_split(m, framelen, M_NOWAIT); if (n == NULL) { IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, ni->ni_macaddr, "a-msdu", "%s", "unable to split encapsulated frames"); vap->iv_stats.is_amsdu_split++; m_freem(m); /* NB: must reclaim */ return NULL; } vap->iv_deliver_data(vap, ni, m); /* * Remove frame contents; each intermediate frame * is required to be aligned to a 4-byte boundary. */ m = n; m_adj(m, roundup2(framelen, 4) - framelen); /* padding */ } return m; /* last delivered by caller */ } /* * Purge all frames in the A-MPDU re-order queue. */ static void ampdu_rx_purge(struct ieee80211_rx_ampdu *rap) { struct mbuf *m; int i; for (i = 0; i < rap->rxa_wnd; i++) { m = rap->rxa_m[i]; if (m != NULL) { rap->rxa_m[i] = NULL; rap->rxa_qbytes -= m->m_pkthdr.len; m_freem(m); if (--rap->rxa_qframes == 0) break; } } KASSERT(rap->rxa_qbytes == 0 && rap->rxa_qframes == 0, ("lost %u data, %u frames on ampdu rx q", rap->rxa_qbytes, rap->rxa_qframes)); } /* * Start A-MPDU rx/re-order processing for the specified TID. */ static int ampdu_rx_start(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap, int baparamset, int batimeout, int baseqctl) { int bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ); if (rap->rxa_flags & IEEE80211_AGGR_RUNNING) { /* * AMPDU previously setup and not terminated with a DELBA, * flush the reorder q's in case anything remains. */ ampdu_rx_purge(rap); } memset(rap, 0, sizeof(*rap)); rap->rxa_wnd = (bufsiz == 0) ? IEEE80211_AGGR_BAWMAX : min(bufsiz, IEEE80211_AGGR_BAWMAX); rap->rxa_start = MS(baseqctl, IEEE80211_BASEQ_START); rap->rxa_flags |= IEEE80211_AGGR_RUNNING | IEEE80211_AGGR_XCHGPEND; return 0; } /* * Stop A-MPDU rx processing for the specified TID. */ static void ampdu_rx_stop(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap) { ampdu_rx_purge(rap); rap->rxa_flags &= ~(IEEE80211_AGGR_RUNNING | IEEE80211_AGGR_XCHGPEND); } /* * Dispatch a frame from the A-MPDU reorder queue. The * frame is fed back into ieee80211_input marked with an * M_AMPDU_MPDU flag so it doesn't come back to us (it also * permits ieee80211_input to optimize re-processing). */ static __inline void ampdu_dispatch(struct ieee80211_node *ni, struct mbuf *m) { m->m_flags |= M_AMPDU_MPDU; /* bypass normal processing */ /* NB: rssi, noise, and rstamp are ignored w/ M_AMPDU_MPDU set */ (void) ieee80211_input(ni, m, 0, 0, 0); } /* * Dispatch as many frames as possible from the re-order queue. * Frames will always be "at the front"; we process all frames * up to the first empty slot in the window. On completion we * cleanup state if there are still pending frames in the current * BA window. We assume the frame at slot 0 is already handled * by the caller; we always start at slot 1. */ static void ampdu_rx_dispatch(struct ieee80211_rx_ampdu *rap, struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct mbuf *m; int i; /* flush run of frames */ for (i = 1; i < rap->rxa_wnd; i++) { m = rap->rxa_m[i]; if (m == NULL) break; rap->rxa_m[i] = NULL; rap->rxa_qbytes -= m->m_pkthdr.len; rap->rxa_qframes--; ampdu_dispatch(ni, m); } /* * If frames remain, copy the mbuf pointers down so * they correspond to the offsets in the new window. */ if (rap->rxa_qframes != 0) { int n = rap->rxa_qframes, j; for (j = i+1; j < rap->rxa_wnd; j++) { if (rap->rxa_m[j] != NULL) { rap->rxa_m[j-i] = rap->rxa_m[j]; rap->rxa_m[j] = NULL; if (--n == 0) break; } } KASSERT(n == 0, ("lost %d frames", n)); vap->iv_stats.is_ampdu_rx_copy += rap->rxa_qframes; } /* * Adjust the start of the BA window to * reflect the frames just dispatched. */ rap->rxa_start = IEEE80211_SEQ_ADD(rap->rxa_start, i); vap->iv_stats.is_ampdu_rx_oor += i; } #ifdef IEEE80211_AMPDU_AGE /* * Dispatch all frames in the A-MPDU re-order queue. */ static void ampdu_rx_flush(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap) { struct ieee80211vap *vap = ni->ni_vap; struct mbuf *m; int i; for (i = 0; i < rap->rxa_wnd; i++) { m = rap->rxa_m[i]; if (m == NULL) continue; rap->rxa_m[i] = NULL; rap->rxa_qbytes -= m->m_pkthdr.len; rap->rxa_qframes--; vap->iv_stats.is_ampdu_rx_oor++; ampdu_dispatch(ni, m); if (rap->rxa_qframes == 0) break; } } #endif /* IEEE80211_AMPDU_AGE */ /* * Dispatch all frames in the A-MPDU re-order queue * preceding the specified sequence number. This logic * handles window moves due to a received MSDU or BAR. */ static void ampdu_rx_flush_upto(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap, ieee80211_seq winstart) { struct ieee80211vap *vap = ni->ni_vap; struct mbuf *m; ieee80211_seq seqno; int i; /* * Flush any complete MSDU's with a sequence number lower * than winstart. Gaps may exist. Note that we may actually * dispatch frames past winstart if a run continues; this is * an optimization that avoids having to do a separate pass * to dispatch frames after moving the BA window start. */ seqno = rap->rxa_start; for (i = 0; i < rap->rxa_wnd; i++) { m = rap->rxa_m[i]; if (m != NULL) { rap->rxa_m[i] = NULL; rap->rxa_qbytes -= m->m_pkthdr.len; rap->rxa_qframes--; vap->iv_stats.is_ampdu_rx_oor++; ampdu_dispatch(ni, m); } else { if (!IEEE80211_SEQ_BA_BEFORE(seqno, winstart)) break; } seqno = IEEE80211_SEQ_INC(seqno); } /* * If frames remain, copy the mbuf pointers down so * they correspond to the offsets in the new window. */ if (rap->rxa_qframes != 0) { int n = rap->rxa_qframes, j; /* NB: this loop assumes i > 0 and/or rxa_m[0] is NULL */ KASSERT(rap->rxa_m[0] == NULL, ("%s: BA window slot 0 occupied", __func__)); for (j = i+1; j < rap->rxa_wnd; j++) { if (rap->rxa_m[j] != NULL) { rap->rxa_m[j-i] = rap->rxa_m[j]; rap->rxa_m[j] = NULL; if (--n == 0) break; } } KASSERT(n == 0, ("%s: lost %d frames, qframes %d off %d " "BA win <%d:%d> winstart %d", __func__, n, rap->rxa_qframes, i, rap->rxa_start, IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1), winstart)); vap->iv_stats.is_ampdu_rx_copy += rap->rxa_qframes; } /* * Move the start of the BA window; we use the * sequence number of the last MSDU that was * passed up the stack+1 or winstart if stopped on * a gap in the reorder buffer. */ rap->rxa_start = seqno; } /* * Process a received QoS data frame for an HT station. Handle * A-MPDU reordering: if this frame is received out of order * and falls within the BA window hold onto it. Otherwise if * this frame completes a run, flush any pending frames. We * return 1 if the frame is consumed. A 0 is returned if * the frame should be processed normally by the caller. */ int ieee80211_ampdu_reorder(struct ieee80211_node *ni, struct mbuf *m) { #define IEEE80211_FC0_QOSDATA \ (IEEE80211_FC0_TYPE_DATA|IEEE80211_FC0_SUBTYPE_QOS|IEEE80211_FC0_VERSION_0) #define PROCESS 0 /* caller should process frame */ #define CONSUMED 1 /* frame consumed, caller does nothing */ struct ieee80211vap *vap = ni->ni_vap; struct ieee80211_qosframe *wh; struct ieee80211_rx_ampdu *rap; ieee80211_seq rxseq; uint8_t tid; int off; KASSERT((m->m_flags & (M_AMPDU | M_AMPDU_MPDU)) == M_AMPDU, ("!a-mpdu or already re-ordered, flags 0x%x", m->m_flags)); KASSERT(ni->ni_flags & IEEE80211_NODE_HT, ("not an HT sta")); /* NB: m_len known to be sufficient */ wh = mtod(m, struct ieee80211_qosframe *); if (wh->i_fc[0] != IEEE80211_FC0_QOSDATA) { /* * Not QoS data, shouldn't get here but just * return it to the caller for processing. */ return PROCESS; } if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS) tid = ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0]; else tid = wh->i_qos[0]; tid &= IEEE80211_QOS_TID; rap = &ni->ni_rx_ampdu[tid]; if ((rap->rxa_flags & IEEE80211_AGGR_XCHGPEND) == 0) { /* * No ADDBA request yet, don't touch. */ return PROCESS; } rxseq = le16toh(*(uint16_t *)wh->i_seq); if ((rxseq & IEEE80211_SEQ_FRAG_MASK) != 0) { /* * Fragments are not allowed; toss. */ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, ni->ni_macaddr, "A-MPDU", "fragment, rxseq 0x%x tid %u%s", rxseq, tid, wh->i_fc[1] & IEEE80211_FC1_RETRY ? " (retransmit)" : ""); vap->iv_stats.is_ampdu_rx_drop++; IEEE80211_NODE_STAT(ni, rx_drop); m_freem(m); return CONSUMED; } rxseq >>= IEEE80211_SEQ_SEQ_SHIFT; rap->rxa_nframes++; again: if (rxseq == rap->rxa_start) { /* * First frame in window. */ if (rap->rxa_qframes != 0) { /* * Dispatch as many packets as we can. */ KASSERT(rap->rxa_m[0] == NULL, ("unexpected dup")); ampdu_dispatch(ni, m); ampdu_rx_dispatch(rap, ni); return CONSUMED; } else { /* * In order; advance window and notify * caller to dispatch directly. */ rap->rxa_start = IEEE80211_SEQ_INC(rxseq); return PROCESS; } } /* * Frame is out of order; store if in the BA window. */ /* calculate offset in BA window */ off = IEEE80211_SEQ_SUB(rxseq, rap->rxa_start); if (off < rap->rxa_wnd) { /* * Common case (hopefully): in the BA window. * Sec 9.10.7.6 a) (D2.04 p.118 line 47) */ #ifdef IEEE80211_AMPDU_AGE /* * Check for frames sitting too long in the reorder queue. * This should only ever happen if frames are not delivered * without the sender otherwise notifying us (e.g. with a * BAR to move the window). Typically this happens because * of vendor bugs that cause the sequence number to jump. * When this happens we get a gap in the reorder queue that * leaves frame sitting on the queue until they get pushed * out due to window moves. When the vendor does not send * BAR this move only happens due to explicit packet sends * * NB: we only track the time of the oldest frame in the * reorder q; this means that if we flush we might push * frames that still "new"; if this happens then subsequent * frames will result in BA window moves which cost something * but is still better than a big throughput dip. */ if (rap->rxa_qframes != 0) { /* XXX honor batimeout? */ if (ticks - rap->rxa_age > ieee80211_ampdu_age) { /* * Too long since we received the first * frame; flush the reorder buffer. */ if (rap->rxa_qframes != 0) { vap->iv_stats.is_ampdu_rx_age += rap->rxa_qframes; ampdu_rx_flush(ni, rap); } rap->rxa_start = IEEE80211_SEQ_INC(rxseq); return PROCESS; } } else { /* * First frame, start aging timer. */ rap->rxa_age = ticks; } #endif /* IEEE80211_AMPDU_AGE */ /* save packet */ if (rap->rxa_m[off] == NULL) { rap->rxa_m[off] = m; rap->rxa_qframes++; rap->rxa_qbytes += m->m_pkthdr.len; vap->iv_stats.is_ampdu_rx_reorder++; } else { IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, ni->ni_macaddr, "a-mpdu duplicate", "seqno %u tid %u BA win <%u:%u>", rxseq, tid, rap->rxa_start, IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1)); vap->iv_stats.is_rx_dup++; IEEE80211_NODE_STAT(ni, rx_dup); m_freem(m); } return CONSUMED; } if (off < IEEE80211_SEQ_BA_RANGE) { /* * Outside the BA window, but within range; * flush the reorder q and move the window. * Sec 9.10.7.6 b) (D2.04 p.118 line 60) */ IEEE80211_NOTE(vap, IEEE80211_MSG_11N, ni, "move BA win <%u:%u> (%u frames) rxseq %u tid %u", rap->rxa_start, IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1), rap->rxa_qframes, rxseq, tid); vap->iv_stats.is_ampdu_rx_move++; /* * The spec says to flush frames up to but not including: * WinStart_B = rxseq - rap->rxa_wnd + 1 * Then insert the frame or notify the caller to process * it immediately. We can safely do this by just starting * over again because we know the frame will now be within * the BA window. */ /* NB: rxa_wnd known to be >0 */ ampdu_rx_flush_upto(ni, rap, IEEE80211_SEQ_SUB(rxseq, rap->rxa_wnd-1)); goto again; } else { /* * Outside the BA window and out of range; toss. * Sec 9.10.7.6 c) (D2.04 p.119 line 16) */ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, ni->ni_macaddr, "MPDU", "BA win <%u:%u> (%u frames) rxseq %u tid %u%s", rap->rxa_start, IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1), rap->rxa_qframes, rxseq, tid, wh->i_fc[1] & IEEE80211_FC1_RETRY ? " (retransmit)" : ""); vap->iv_stats.is_ampdu_rx_drop++; IEEE80211_NODE_STAT(ni, rx_drop); m_freem(m); return CONSUMED; } #undef CONSUMED #undef PROCESS #undef IEEE80211_FC0_QOSDATA } /* * Process a BAR ctl frame. Dispatch all frames up to * the sequence number of the frame. If this frame is * out of range it's discarded. */ void ieee80211_recv_bar(struct ieee80211_node *ni, struct mbuf *m0) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211_frame_bar *wh; struct ieee80211_rx_ampdu *rap; ieee80211_seq rxseq; int tid, off; if (!ieee80211_recv_bar_ena) { #if 0 IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_11N, ni->ni_macaddr, "BAR", "%s", "processing disabled"); #endif vap->iv_stats.is_ampdu_bar_bad++; return; } wh = mtod(m0, struct ieee80211_frame_bar *); /* XXX check basic BAR */ tid = MS(le16toh(wh->i_ctl), IEEE80211_BAR_TID); rap = &ni->ni_rx_ampdu[tid]; if ((rap->rxa_flags & IEEE80211_AGGR_XCHGPEND) == 0) { /* * No ADDBA request yet, don't touch. */ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, ni->ni_macaddr, "BAR", "no BA stream, tid %u", tid); vap->iv_stats.is_ampdu_bar_bad++; return; } vap->iv_stats.is_ampdu_bar_rx++; rxseq = le16toh(wh->i_seq) >> IEEE80211_SEQ_SEQ_SHIFT; if (rxseq == rap->rxa_start) return; /* calculate offset in BA window */ off = IEEE80211_SEQ_SUB(rxseq, rap->rxa_start); if (off < IEEE80211_SEQ_BA_RANGE) { /* * Flush the reorder q up to rxseq and move the window. * Sec 9.10.7.6 a) (D2.04 p.119 line 22) */ IEEE80211_NOTE(vap, IEEE80211_MSG_11N, ni, "BAR moves BA win <%u:%u> (%u frames) rxseq %u tid %u", rap->rxa_start, IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1), rap->rxa_qframes, rxseq, tid); vap->iv_stats.is_ampdu_bar_move++; ampdu_rx_flush_upto(ni, rap, rxseq); if (off >= rap->rxa_wnd) { /* * BAR specifies a window start to the right of BA * window; we must move it explicitly since * ampdu_rx_flush_upto will not. */ rap->rxa_start = rxseq; } } else { /* * Out of range; toss. * Sec 9.10.7.6 b) (D2.04 p.119 line 41) */ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, ni->ni_macaddr, "BAR", "BA win <%u:%u> (%u frames) rxseq %u tid %u%s", rap->rxa_start, IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1), rap->rxa_qframes, rxseq, tid, wh->i_fc[1] & IEEE80211_FC1_RETRY ? " (retransmit)" : ""); vap->iv_stats.is_ampdu_bar_oow++; IEEE80211_NODE_STAT(ni, rx_drop); } } /* * Setup HT-specific state in a node. Called only * when HT use is negotiated so we don't do extra * work for temporary and/or legacy sta's. */ void ieee80211_ht_node_init(struct ieee80211_node *ni) { struct ieee80211_tx_ampdu *tap; int ac; if (ni->ni_flags & IEEE80211_NODE_HT) { /* * Clean AMPDU state on re-associate. This handles the case * where a station leaves w/o notifying us and then returns * before node is reaped for inactivity. */ ieee80211_ht_node_cleanup(ni); } for (ac = 0; ac < WME_NUM_AC; ac++) { tap = &ni->ni_tx_ampdu[ac]; tap->txa_ac = ac; tap->txa_ni = ni; /* NB: further initialization deferred */ } ni->ni_flags |= IEEE80211_NODE_HT | IEEE80211_NODE_AMPDU; } /* * Cleanup HT-specific state in a node. Called only * when HT use has been marked. */ void ieee80211_ht_node_cleanup(struct ieee80211_node *ni) { struct ieee80211com *ic = ni->ni_ic; int i; KASSERT(ni->ni_flags & IEEE80211_NODE_HT, ("not an HT node")); /* XXX optimize this */ for (i = 0; i < WME_NUM_AC; i++) { struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[i]; if (tap->txa_flags & IEEE80211_AGGR_SETUP) ampdu_tx_stop(tap); } for (i = 0; i < WME_NUM_TID; i++) ic->ic_ampdu_rx_stop(ni, &ni->ni_rx_ampdu[i]); ni->ni_htcap = 0; ni->ni_flags &= ~IEEE80211_NODE_HT_ALL; } /* * Age out HT resources for a station. */ void ieee80211_ht_node_age(struct ieee80211_node *ni) { #ifdef IEEE80211_AMPDU_AGE struct ieee80211vap *vap = ni->ni_vap; uint8_t tid; #endif KASSERT(ni->ni_flags & IEEE80211_NODE_HT, ("not an HT sta")); #ifdef IEEE80211_AMPDU_AGE for (tid = 0; tid < WME_NUM_TID; tid++) { struct ieee80211_rx_ampdu *rap; rap = &ni->ni_rx_ampdu[tid]; if ((rap->rxa_flags & IEEE80211_AGGR_XCHGPEND) == 0) continue; if (rap->rxa_qframes == 0) continue; /* * Check for frames sitting too long in the reorder queue. * See above for more details on what's happening here. */ /* XXX honor batimeout? */ if (ticks - rap->rxa_age > ieee80211_ampdu_age) { /* * Too long since we received the first * frame; flush the reorder buffer. */ vap->iv_stats.is_ampdu_rx_age += rap->rxa_qframes; ampdu_rx_flush(ni, rap); } } #endif /* IEEE80211_AMPDU_AGE */ } static struct ieee80211_channel * findhtchan(struct ieee80211com *ic, struct ieee80211_channel *c, int htflags) { return ieee80211_find_channel(ic, c->ic_freq, (c->ic_flags &~ IEEE80211_CHAN_HT) | htflags); } /* * Adjust a channel to be HT/non-HT according to the vap's configuration. */ struct ieee80211_channel * ieee80211_ht_adjust_channel(struct ieee80211com *ic, struct ieee80211_channel *chan, int flags) { struct ieee80211_channel *c; if (flags & IEEE80211_FEXT_HT) { /* promote to HT if possible */ if (flags & IEEE80211_FEXT_USEHT40) { if (!IEEE80211_IS_CHAN_HT40(chan)) { /* NB: arbitrarily pick ht40+ over ht40- */ c = findhtchan(ic, chan, IEEE80211_CHAN_HT40U); if (c == NULL) c = findhtchan(ic, chan, IEEE80211_CHAN_HT40D); if (c == NULL) c = findhtchan(ic, chan, IEEE80211_CHAN_HT20); if (c != NULL) chan = c; } } else if (!IEEE80211_IS_CHAN_HT20(chan)) { c = findhtchan(ic, chan, IEEE80211_CHAN_HT20); if (c != NULL) chan = c; } } else if (IEEE80211_IS_CHAN_HT(chan)) { /* demote to legacy, HT use is disabled */ c = ieee80211_find_channel(ic, chan->ic_freq, chan->ic_flags &~ IEEE80211_CHAN_HT); if (c != NULL) chan = c; } return chan; } /* * Setup HT-specific state for a legacy WDS peer. */ void ieee80211_ht_wds_init(struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211_tx_ampdu *tap; int ac; KASSERT(vap->iv_flags_ext & IEEE80211_FEXT_HT, ("no HT requested")); /* XXX check scan cache in case peer has an ap and we have info */ /* * If setup with a legacy channel; locate an HT channel. * Otherwise if the inherited channel (from a companion * AP) is suitable use it so we use the same location * for the extension channel). */ ni->ni_chan = ieee80211_ht_adjust_channel(ni->ni_ic, ni->ni_chan, ieee80211_htchanflags(ni->ni_chan)); ni->ni_htcap = 0; if (vap->iv_flags_ext & IEEE80211_FEXT_SHORTGI20) ni->ni_htcap |= IEEE80211_HTCAP_SHORTGI20; if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) { ni->ni_htcap |= IEEE80211_HTCAP_CHWIDTH40; ni->ni_chw = 40; if (IEEE80211_IS_CHAN_HT40U(ni->ni_chan)) ni->ni_ht2ndchan = IEEE80211_HTINFO_2NDCHAN_ABOVE; else if (IEEE80211_IS_CHAN_HT40D(ni->ni_chan)) ni->ni_ht2ndchan = IEEE80211_HTINFO_2NDCHAN_BELOW; if (vap->iv_flags_ext & IEEE80211_FEXT_SHORTGI40) ni->ni_htcap |= IEEE80211_HTCAP_SHORTGI40; } else { ni->ni_chw = 20; ni->ni_ht2ndchan = IEEE80211_HTINFO_2NDCHAN_NONE; } ni->ni_htctlchan = ni->ni_chan->ic_ieee; if (vap->iv_flags_ext & IEEE80211_FEXT_RIFS) ni->ni_flags |= IEEE80211_NODE_RIFS; /* XXX does it make sense to enable SMPS? */ ni->ni_htopmode = 0; /* XXX need protection state */ ni->ni_htstbc = 0; /* XXX need info */ for (ac = 0; ac < WME_NUM_AC; ac++) { tap = &ni->ni_tx_ampdu[ac]; tap->txa_ac = ac; } /* NB: AMPDU tx/rx governed by IEEE80211_FEXT_AMPDU_{TX,RX} */ ni->ni_flags |= IEEE80211_NODE_HT | IEEE80211_NODE_AMPDU; } /* * Notify hostap vaps of a change in the HTINFO ie. */ static void htinfo_notify(struct ieee80211com *ic) { struct ieee80211vap *vap; int first = 1; IEEE80211_LOCK_ASSERT(ic); TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { if (vap->iv_opmode != IEEE80211_M_HOSTAP) continue; if (vap->iv_state != IEEE80211_S_RUN || !IEEE80211_IS_CHAN_HT(vap->iv_bss->ni_chan)) continue; if (first) { IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, vap->iv_bss, "HT bss occupancy change: %d sta, %d ht, " "%d ht40%s, HT protmode now 0x%x" , ic->ic_sta_assoc , ic->ic_ht_sta_assoc , ic->ic_ht40_sta_assoc , (ic->ic_flags_ext & IEEE80211_FEXT_NONHT_PR) ? ", non-HT sta present" : "" , ic->ic_curhtprotmode); first = 0; } ieee80211_beacon_notify(vap, IEEE80211_BEACON_HTINFO); } } /* * Calculate HT protection mode from current * state and handle updates. */ static void htinfo_update(struct ieee80211com *ic) { uint8_t protmode; if (ic->ic_sta_assoc != ic->ic_ht_sta_assoc) { protmode = IEEE80211_HTINFO_OPMODE_MIXED | IEEE80211_HTINFO_NONHT_PRESENT; } else if (ic->ic_flags_ext & IEEE80211_FEXT_NONHT_PR) { protmode = IEEE80211_HTINFO_OPMODE_PROTOPT | IEEE80211_HTINFO_NONHT_PRESENT; } else if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && IEEE80211_IS_CHAN_HT40(ic->ic_bsschan) && ic->ic_sta_assoc != ic->ic_ht40_sta_assoc) { protmode = IEEE80211_HTINFO_OPMODE_HT20PR; } else { protmode = IEEE80211_HTINFO_OPMODE_PURE; } if (protmode != ic->ic_curhtprotmode) { ic->ic_curhtprotmode = protmode; htinfo_notify(ic); } } /* * Handle an HT station joining a BSS. */ void ieee80211_ht_node_join(struct ieee80211_node *ni) { struct ieee80211com *ic = ni->ni_ic; IEEE80211_LOCK_ASSERT(ic); if (ni->ni_flags & IEEE80211_NODE_HT) { ic->ic_ht_sta_assoc++; if (ni->ni_chw == 40) ic->ic_ht40_sta_assoc++; } htinfo_update(ic); } /* * Handle an HT station leaving a BSS. */ void ieee80211_ht_node_leave(struct ieee80211_node *ni) { struct ieee80211com *ic = ni->ni_ic; IEEE80211_LOCK_ASSERT(ic); if (ni->ni_flags & IEEE80211_NODE_HT) { ic->ic_ht_sta_assoc--; if (ni->ni_chw == 40) ic->ic_ht40_sta_assoc--; } htinfo_update(ic); } /* * Public version of htinfo_update; used for processing * beacon frames from overlapping bss. * * Caller can specify either IEEE80211_HTINFO_OPMODE_MIXED * (on receipt of a beacon that advertises MIXED) or * IEEE80211_HTINFO_OPMODE_PROTOPT (on receipt of a beacon * from an overlapping legacy bss). We treat MIXED with * a higher precedence than PROTOPT (i.e. we will not change * change PROTOPT -> MIXED; only MIXED -> PROTOPT). This * corresponds to how we handle things in htinfo_update. */ void ieee80211_htprot_update(struct ieee80211com *ic, int protmode) { #define OPMODE(x) SM(x, IEEE80211_HTINFO_OPMODE) IEEE80211_LOCK(ic); /* track non-HT station presence */ KASSERT(protmode & IEEE80211_HTINFO_NONHT_PRESENT, ("protmode 0x%x", protmode)); ic->ic_flags_ext |= IEEE80211_FEXT_NONHT_PR; ic->ic_lastnonht = ticks; if (protmode != ic->ic_curhtprotmode && (OPMODE(ic->ic_curhtprotmode) != IEEE80211_HTINFO_OPMODE_MIXED || OPMODE(protmode) == IEEE80211_HTINFO_OPMODE_PROTOPT)) { /* push beacon update */ ic->ic_curhtprotmode = protmode; htinfo_notify(ic); } IEEE80211_UNLOCK(ic); #undef OPMODE } /* * Time out presence of an overlapping bss with non-HT * stations. When operating in hostap mode we listen for * beacons from other stations and if we identify a non-HT * station is present we update the opmode field of the * HTINFO ie. To identify when all non-HT stations are * gone we time out this condition. */ void ieee80211_ht_timeout(struct ieee80211com *ic) { IEEE80211_LOCK_ASSERT(ic); if ((ic->ic_flags_ext & IEEE80211_FEXT_NONHT_PR) && time_after(ticks, ic->ic_lastnonht + IEEE80211_NONHT_PRESENT_AGE)) { #if 0 IEEE80211_NOTE(vap, IEEE80211_MSG_11N, ni, "%s", "time out non-HT STA present on channel"); #endif ic->ic_flags_ext &= ~IEEE80211_FEXT_NONHT_PR; htinfo_update(ic); } } /* unalligned little endian access */ #define LE_READ_2(p) \ ((uint16_t) \ ((((const uint8_t *)(p))[0] ) | \ (((const uint8_t *)(p))[1] << 8))) /* * Process an 802.11n HT capabilities ie. */ void ieee80211_parse_htcap(struct ieee80211_node *ni, const uint8_t *ie) { if (ie[0] == IEEE80211_ELEMID_VENDOR) { /* * Station used Vendor OUI ie to associate; * mark the node so when we respond we'll use * the Vendor OUI's and not the standard ie's. */ ni->ni_flags |= IEEE80211_NODE_HTCOMPAT; ie += 4; } else ni->ni_flags &= ~IEEE80211_NODE_HTCOMPAT; ni->ni_htcap = LE_READ_2(ie + __offsetof(struct ieee80211_ie_htcap, hc_cap)); ni->ni_htparam = ie[__offsetof(struct ieee80211_ie_htcap, hc_param)]; } static void htinfo_parse(struct ieee80211_node *ni, const struct ieee80211_ie_htinfo *htinfo) { uint16_t w; ni->ni_htctlchan = htinfo->hi_ctrlchannel; ni->ni_ht2ndchan = SM(htinfo->hi_byte1, IEEE80211_HTINFO_2NDCHAN); w = LE_READ_2(&htinfo->hi_byte2); ni->ni_htopmode = SM(w, IEEE80211_HTINFO_OPMODE); w = LE_READ_2(&htinfo->hi_byte45); ni->ni_htstbc = SM(w, IEEE80211_HTINFO_BASIC_STBCMCS); } /* * Parse an 802.11n HT info ie and save useful information * to the node state. Note this does not effect any state * changes such as for channel width change. */ void ieee80211_parse_htinfo(struct ieee80211_node *ni, const uint8_t *ie) { if (ie[0] == IEEE80211_ELEMID_VENDOR) ie += 4; htinfo_parse(ni, (const struct ieee80211_ie_htinfo *) ie); } /* * Handle 11n channel switch. Use the received HT ie's to * identify the right channel to use. If we cannot locate it * in the channel table then fallback to legacy operation. * Note that we use this information to identify the node's * channel only; the caller is responsible for insuring any * required channel change is done (e.g. in sta mode when * parsing the contents of a beacon frame). */ static void htinfo_update_chw(struct ieee80211_node *ni, int htflags) { struct ieee80211com *ic = ni->ni_ic; struct ieee80211_channel *c; int chanflags; chanflags = (ni->ni_chan->ic_flags &~ IEEE80211_CHAN_HT) | htflags; if (chanflags != ni->ni_chan->ic_flags) { /* XXX not right for ht40- */ c = ieee80211_find_channel(ic, ni->ni_chan->ic_freq, chanflags); if (c == NULL && (htflags & IEEE80211_CHAN_HT40)) { /* * No HT40 channel entry in our table; fall back * to HT20 operation. This should not happen. */ c = findhtchan(ic, ni->ni_chan, IEEE80211_CHAN_HT20); #if 0 IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, ni, "no HT40 channel (freq %u), falling back to HT20", ni->ni_chan->ic_freq); #endif /* XXX stat */ } if (c != NULL && c != ni->ni_chan) { IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, ni, "switch station to HT%d channel %u/0x%x", IEEE80211_IS_CHAN_HT40(c) ? 40 : 20, c->ic_freq, c->ic_flags); ni->ni_chan = c; } /* NB: caller responsible for forcing any channel change */ } /* update node's tx channel width */ ni->ni_chw = IEEE80211_IS_CHAN_HT40(ni->ni_chan)? 40 : 20; } /* * Update 11n MIMO PS state according to received htcap. */ static __inline int htcap_update_mimo_ps(struct ieee80211_node *ni) { uint16_t oflags = ni->ni_flags; switch (ni->ni_htcap & IEEE80211_HTCAP_SMPS) { case IEEE80211_HTCAP_SMPS_DYNAMIC: ni->ni_flags |= IEEE80211_NODE_MIMO_PS; ni->ni_flags |= IEEE80211_NODE_MIMO_RTS; break; case IEEE80211_HTCAP_SMPS_ENA: ni->ni_flags |= IEEE80211_NODE_MIMO_PS; ni->ni_flags &= ~IEEE80211_NODE_MIMO_RTS; break; case IEEE80211_HTCAP_SMPS_OFF: default: /* disable on rx of reserved value */ ni->ni_flags &= ~IEEE80211_NODE_MIMO_PS; ni->ni_flags &= ~IEEE80211_NODE_MIMO_RTS; break; } return (oflags ^ ni->ni_flags); } /* * Update short GI state according to received htcap * and local settings. */ static __inline void htcap_update_shortgi(struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; ni->ni_flags &= ~(IEEE80211_NODE_SGI20|IEEE80211_NODE_SGI40); if ((ni->ni_htcap & IEEE80211_HTCAP_SHORTGI20) && (vap->iv_flags_ext & IEEE80211_FEXT_SHORTGI20)) ni->ni_flags |= IEEE80211_NODE_SGI20; if ((ni->ni_htcap & IEEE80211_HTCAP_SHORTGI40) && (vap->iv_flags_ext & IEEE80211_FEXT_SHORTGI40)) ni->ni_flags |= IEEE80211_NODE_SGI40; } /* * Parse and update HT-related state extracted from * the HT cap and info ie's. */ void ieee80211_ht_updateparams(struct ieee80211_node *ni, const uint8_t *htcapie, const uint8_t *htinfoie) { struct ieee80211vap *vap = ni->ni_vap; const struct ieee80211_ie_htinfo *htinfo; int htflags; ieee80211_parse_htcap(ni, htcapie); if (vap->iv_htcaps & IEEE80211_HTCAP_SMPS) htcap_update_mimo_ps(ni); htcap_update_shortgi(ni); if (htinfoie[0] == IEEE80211_ELEMID_VENDOR) htinfoie += 4; htinfo = (const struct ieee80211_ie_htinfo *) htinfoie; htinfo_parse(ni, htinfo); htflags = (vap->iv_flags_ext & IEEE80211_FEXT_HT) ? IEEE80211_CHAN_HT20 : 0; /* NB: honor operating mode constraint */ if ((htinfo->hi_byte1 & IEEE80211_HTINFO_TXWIDTH_2040) && (vap->iv_flags_ext & IEEE80211_FEXT_USEHT40)) { if (ni->ni_ht2ndchan == IEEE80211_HTINFO_2NDCHAN_ABOVE) htflags = IEEE80211_CHAN_HT40U; else if (ni->ni_ht2ndchan == IEEE80211_HTINFO_2NDCHAN_BELOW) htflags = IEEE80211_CHAN_HT40D; } htinfo_update_chw(ni, htflags); if ((htinfo->hi_byte1 & IEEE80211_HTINFO_RIFSMODE_PERM) && (vap->iv_flags_ext & IEEE80211_FEXT_RIFS)) ni->ni_flags |= IEEE80211_NODE_RIFS; else ni->ni_flags &= ~IEEE80211_NODE_RIFS; } /* * Parse and update HT-related state extracted from the HT cap ie * for a station joining an HT BSS. */ void ieee80211_ht_updatehtcap(struct ieee80211_node *ni, const uint8_t *htcapie) { struct ieee80211vap *vap = ni->ni_vap; int htflags; ieee80211_parse_htcap(ni, htcapie); if (vap->iv_htcaps & IEEE80211_HTCAP_SMPS) htcap_update_mimo_ps(ni); htcap_update_shortgi(ni); /* NB: honor operating mode constraint */ /* XXX 40 MHZ intolerant */ htflags = (vap->iv_flags_ext & IEEE80211_FEXT_HT) ? IEEE80211_CHAN_HT20 : 0; if ((ni->ni_htcap & IEEE80211_HTCAP_CHWIDTH40) && (vap->iv_flags_ext & IEEE80211_FEXT_USEHT40)) { if (IEEE80211_IS_CHAN_HT40U(vap->iv_bss->ni_chan)) htflags = IEEE80211_CHAN_HT40U; else if (IEEE80211_IS_CHAN_HT40D(vap->iv_bss->ni_chan)) htflags = IEEE80211_CHAN_HT40D; } htinfo_update_chw(ni, htflags); } /* * Install received HT rate set by parsing the HT cap ie. */ int ieee80211_setup_htrates(struct ieee80211_node *ni, const uint8_t *ie, int flags) { struct ieee80211vap *vap = ni->ni_vap; const struct ieee80211_ie_htcap *htcap; struct ieee80211_htrateset *rs; int i; rs = &ni->ni_htrates; memset(rs, 0, sizeof(*rs)); if (ie != NULL) { if (ie[0] == IEEE80211_ELEMID_VENDOR) ie += 4; htcap = (const struct ieee80211_ie_htcap *) ie; for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++) { if (isclr(htcap->hc_mcsset, i)) continue; if (rs->rs_nrates == IEEE80211_HTRATE_MAXSIZE) { IEEE80211_NOTE(vap, IEEE80211_MSG_XRATE | IEEE80211_MSG_11N, ni, "WARNING, HT rate set too large; only " "using %u rates", IEEE80211_HTRATE_MAXSIZE); vap->iv_stats.is_rx_rstoobig++; break; } rs->rs_rates[rs->rs_nrates++] = i; } } return ieee80211_fix_rate(ni, (struct ieee80211_rateset *) rs, flags); } /* * Mark rates in a node's HT rate set as basic according * to the information in the supplied HT info ie. */ void ieee80211_setup_basic_htrates(struct ieee80211_node *ni, const uint8_t *ie) { const struct ieee80211_ie_htinfo *htinfo; struct ieee80211_htrateset *rs; int i, j; if (ie[0] == IEEE80211_ELEMID_VENDOR) ie += 4; htinfo = (const struct ieee80211_ie_htinfo *) ie; rs = &ni->ni_htrates; if (rs->rs_nrates == 0) { IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_XRATE | IEEE80211_MSG_11N, ni, "%s", "WARNING, empty HT rate set"); return; } for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++) { if (isclr(htinfo->hi_basicmcsset, i)) continue; for (j = 0; j < rs->rs_nrates; j++) if ((rs->rs_rates[j] & IEEE80211_RATE_VAL) == i) rs->rs_rates[j] |= IEEE80211_RATE_BASIC; } } static void ampdu_tx_setup(struct ieee80211_tx_ampdu *tap) { callout_init(&tap->txa_timer, CALLOUT_MPSAFE); tap->txa_flags |= IEEE80211_AGGR_SETUP; } static void ampdu_tx_stop(struct ieee80211_tx_ampdu *tap) { struct ieee80211_node *ni = tap->txa_ni; struct ieee80211com *ic = ni->ni_ic; KASSERT(tap->txa_flags & IEEE80211_AGGR_SETUP, ("txa_flags 0x%x ac %d", tap->txa_flags, tap->txa_ac)); /* * Stop BA stream if setup so driver has a chance * to reclaim any resources it might have allocated. */ ic->ic_addba_stop(ni, tap); /* * Stop any pending BAR transmit. */ bar_stop_timer(tap); tap->txa_lastsample = 0; tap->txa_avgpps = 0; /* NB: clearing NAK means we may re-send ADDBA */ tap->txa_flags &= ~(IEEE80211_AGGR_SETUP | IEEE80211_AGGR_NAK); } static void addba_timeout(void *arg) { struct ieee80211_tx_ampdu *tap = arg; /* XXX ? */ tap->txa_flags &= ~IEEE80211_AGGR_XCHGPEND; tap->txa_attempts++; } static void addba_start_timeout(struct ieee80211_tx_ampdu *tap) { /* XXX use CALLOUT_PENDING instead? */ callout_reset(&tap->txa_timer, ieee80211_addba_timeout, addba_timeout, tap); tap->txa_flags |= IEEE80211_AGGR_XCHGPEND; tap->txa_nextrequest = ticks + ieee80211_addba_timeout; } static void addba_stop_timeout(struct ieee80211_tx_ampdu *tap) { /* XXX use CALLOUT_PENDING instead? */ if (tap->txa_flags & IEEE80211_AGGR_XCHGPEND) { callout_stop(&tap->txa_timer); tap->txa_flags &= ~IEEE80211_AGGR_XCHGPEND; } } /* * Default method for requesting A-MPDU tx aggregation. * We setup the specified state block and start a timer * to wait for an ADDBA response frame. */ static int ieee80211_addba_request(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap, int dialogtoken, int baparamset, int batimeout) { int bufsiz; /* XXX locking */ tap->txa_token = dialogtoken; tap->txa_flags |= IEEE80211_AGGR_IMMEDIATE; bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ); tap->txa_wnd = (bufsiz == 0) ? IEEE80211_AGGR_BAWMAX : min(bufsiz, IEEE80211_AGGR_BAWMAX); addba_start_timeout(tap); return 1; } /* * Default method for processing an A-MPDU tx aggregation * response. We shutdown any pending timer and update the * state block according to the reply. */ static int ieee80211_addba_response(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap, int status, int baparamset, int batimeout) { int bufsiz, tid; /* XXX locking */ addba_stop_timeout(tap); if (status == IEEE80211_STATUS_SUCCESS) { bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ); /* XXX override our request? */ tap->txa_wnd = (bufsiz == 0) ? IEEE80211_AGGR_BAWMAX : min(bufsiz, IEEE80211_AGGR_BAWMAX); /* XXX AC/TID */ tid = MS(baparamset, IEEE80211_BAPS_TID); tap->txa_flags |= IEEE80211_AGGR_RUNNING; tap->txa_attempts = 0; } else { /* mark tid so we don't try again */ tap->txa_flags |= IEEE80211_AGGR_NAK; } return 1; } /* * Default method for stopping A-MPDU tx aggregation. * Any timer is cleared and we drain any pending frames. */ static void ieee80211_addba_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap) { /* XXX locking */ addba_stop_timeout(tap); if (tap->txa_flags & IEEE80211_AGGR_RUNNING) { /* XXX clear aggregation queue */ tap->txa_flags &= ~IEEE80211_AGGR_RUNNING; } tap->txa_attempts = 0; } /* * Process a received action frame using the default aggregation * policy. We intercept ADDBA-related frames and use them to * update our aggregation state. All other frames are passed up * for processing by ieee80211_recv_action. */ static void ieee80211_aggr_recv_action(struct ieee80211_node *ni, const uint8_t *frm, const uint8_t *efrm) { struct ieee80211com *ic = ni->ni_ic; struct ieee80211vap *vap = ni->ni_vap; const struct ieee80211_action *ia; struct ieee80211_rx_ampdu *rap; struct ieee80211_tx_ampdu *tap; uint8_t dialogtoken, policy; uint16_t baparamset, batimeout, baseqctl, code; uint16_t args[4]; int tid, ac, bufsiz; ia = (const struct ieee80211_action *) frm; switch (ia->ia_category) { case IEEE80211_ACTION_CAT_BA: switch (ia->ia_action) { case IEEE80211_ACTION_BA_ADDBA_REQUEST: dialogtoken = frm[2]; baparamset = LE_READ_2(frm+3); batimeout = LE_READ_2(frm+5); baseqctl = LE_READ_2(frm+7); tid = MS(baparamset, IEEE80211_BAPS_TID); IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, "recv ADDBA request: dialogtoken %u " "baparamset 0x%x (tid %d bufsiz %d) batimeout %d " "baseqctl %d:%d", dialogtoken, baparamset, tid, MS(baparamset, IEEE80211_BAPS_BUFSIZ), batimeout, MS(baseqctl, IEEE80211_BASEQ_START), MS(baseqctl, IEEE80211_BASEQ_FRAG)); rap = &ni->ni_rx_ampdu[tid]; /* Send ADDBA response */ args[0] = dialogtoken; /* * NB: We ack only if the sta associated with HT and * the ap is configured to do AMPDU rx (the latter * violates the 11n spec and is mostly for testing). */ if ((ni->ni_flags & IEEE80211_NODE_AMPDU_RX) && (vap->iv_flags_ext & IEEE80211_FEXT_AMPDU_RX)) { /* XXX handle ampdu_rx_start failure */ ic->ic_ampdu_rx_start(ni, rap, baparamset, batimeout, baseqctl); args[1] = IEEE80211_STATUS_SUCCESS; } else { IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, "reject ADDBA request: %s", ni->ni_flags & IEEE80211_NODE_AMPDU_RX ? "administratively disabled" : "not negotiated for station"); vap->iv_stats.is_addba_reject++; args[1] = IEEE80211_STATUS_UNSPECIFIED; } /* XXX honor rap flags? */ args[2] = IEEE80211_BAPS_POLICY_IMMEDIATE | SM(tid, IEEE80211_BAPS_TID) | SM(rap->rxa_wnd, IEEE80211_BAPS_BUFSIZ) ; args[3] = 0; ic->ic_send_action(ni, IEEE80211_ACTION_CAT_BA, IEEE80211_ACTION_BA_ADDBA_RESPONSE, args); return; case IEEE80211_ACTION_BA_ADDBA_RESPONSE: dialogtoken = frm[2]; code = LE_READ_2(frm+3); baparamset = LE_READ_2(frm+5); tid = MS(baparamset, IEEE80211_BAPS_TID); bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ); policy = MS(baparamset, IEEE80211_BAPS_POLICY); batimeout = LE_READ_2(frm+7); ac = TID_TO_WME_AC(tid); tap = &ni->ni_tx_ampdu[ac]; if ((tap->txa_flags & IEEE80211_AGGR_XCHGPEND) == 0) { IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni->ni_macaddr, "ADDBA response", "no pending ADDBA, tid %d dialogtoken %u " "code %d", tid, dialogtoken, code); vap->iv_stats.is_addba_norequest++; return; } if (dialogtoken != tap->txa_token) { IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni->ni_macaddr, "ADDBA response", "dialogtoken mismatch: waiting for %d, " "received %d, tid %d code %d", tap->txa_token, dialogtoken, tid, code); vap->iv_stats.is_addba_badtoken++; return; } /* NB: assumes IEEE80211_AGGR_IMMEDIATE is 1 */ if (policy != (tap->txa_flags & IEEE80211_AGGR_IMMEDIATE)) { IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni->ni_macaddr, "ADDBA response", "policy mismatch: expecting %s, " "received %s, tid %d code %d", tap->txa_flags & IEEE80211_AGGR_IMMEDIATE, policy, tid, code); vap->iv_stats.is_addba_badpolicy++; return; } #if 0 /* XXX we take MIN in ieee80211_addba_response */ if (bufsiz > IEEE80211_AGGR_BAWMAX) { IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni->ni_macaddr, "ADDBA response", "BA window too large: max %d, " "received %d, tid %d code %d", bufsiz, IEEE80211_AGGR_BAWMAX, tid, code); vap->iv_stats.is_addba_badbawinsize++; return; } #endif IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, "recv ADDBA response: dialogtoken %u code %d " "baparamset 0x%x (tid %d bufsiz %d) batimeout %d", dialogtoken, code, baparamset, tid, bufsiz, batimeout); ic->ic_addba_response(ni, tap, code, baparamset, batimeout); return; case IEEE80211_ACTION_BA_DELBA: baparamset = LE_READ_2(frm+2); code = LE_READ_2(frm+4); tid = MS(baparamset, IEEE80211_DELBAPS_TID); IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, "recv DELBA: baparamset 0x%x (tid %d initiator %d) " "code %d", baparamset, tid, MS(baparamset, IEEE80211_DELBAPS_INIT), code); if ((baparamset & IEEE80211_DELBAPS_INIT) == 0) { ac = TID_TO_WME_AC(tid); tap = &ni->ni_tx_ampdu[ac]; ic->ic_addba_stop(ni, tap); } else { rap = &ni->ni_rx_ampdu[tid]; ic->ic_ampdu_rx_stop(ni, rap); } return; } break; } ieee80211_recv_action(ni, frm, efrm); } /* * Process a received 802.11n action frame. * Aggregation-related frames are assumed to be handled * already; we handle any other frames we can, otherwise * complain about being unsupported (with debugging). */ void ieee80211_recv_action(struct ieee80211_node *ni, const uint8_t *frm, const uint8_t *efrm) { struct ieee80211vap *vap = ni->ni_vap; const struct ieee80211_action *ia; int chw; ia = (const struct ieee80211_action *) frm; switch (ia->ia_category) { case IEEE80211_ACTION_CAT_BA: IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, "%s: BA action %d not implemented", __func__, ia->ia_action); vap->iv_stats.is_rx_mgtdiscard++; break; case IEEE80211_ACTION_CAT_HT: switch (ia->ia_action) { case IEEE80211_ACTION_HT_TXCHWIDTH: chw = frm[2] == IEEE80211_A_HT_TXCHWIDTH_2040 ? 40 : 20; IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, "%s: HT txchwidth, width %d%s", __func__, chw, ni->ni_chw != chw ? "*" : ""); if (chw != ni->ni_chw) { ni->ni_chw = chw; /* XXX notify on change */ } break; case IEEE80211_ACTION_HT_MIMOPWRSAVE: { const struct ieee80211_action_ht_mimopowersave *mps = (const struct ieee80211_action_ht_mimopowersave *) ia; /* XXX check iv_htcaps */ if (mps->am_control & IEEE80211_A_HT_MIMOPWRSAVE_ENA) ni->ni_flags |= IEEE80211_NODE_MIMO_PS; else ni->ni_flags &= ~IEEE80211_NODE_MIMO_PS; if (mps->am_control & IEEE80211_A_HT_MIMOPWRSAVE_MODE) ni->ni_flags |= IEEE80211_NODE_MIMO_RTS; else ni->ni_flags &= ~IEEE80211_NODE_MIMO_RTS; /* XXX notify on change */ IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, "%s: HT MIMO PS (%s%s)", __func__, (ni->ni_flags & IEEE80211_NODE_MIMO_PS) ? "on" : "off", (ni->ni_flags & IEEE80211_NODE_MIMO_RTS) ? "+rts" : "" ); break; } default: IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, "%s: HT action %d not implemented", __func__, ia->ia_action); vap->iv_stats.is_rx_mgtdiscard++; break; } break; default: IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, "%s: category %d not implemented", __func__, ia->ia_category); vap->iv_stats.is_rx_mgtdiscard++; break; } } /* * Transmit processing. */ /* * Check if A-MPDU should be requested/enabled for a stream. * We require a traffic rate above a per-AC threshold and we * also handle backoff from previous failed attempts. * * Drivers may override this method to bring in information * such as link state conditions in making the decision. */ static int ieee80211_ampdu_enable(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap) { struct ieee80211vap *vap = ni->ni_vap; if (tap->txa_avgpps < vap->iv_ampdu_mintraffic[tap->txa_ac]) return 0; /* XXX check rssi? */ if (tap->txa_attempts >= ieee80211_addba_maxtries && ticks < tap->txa_nextrequest) { /* * Don't retry too often; txa_nextrequest is set * to the minimum interval we'll retry after * ieee80211_addba_maxtries failed attempts are made. */ return 0; } IEEE80211_NOTE(vap, IEEE80211_MSG_11N, ni, "enable AMPDU on %s, avgpps %d pkts %d", ieee80211_wme_acnames[tap->txa_ac], tap->txa_avgpps, tap->txa_pkts); return 1; } /* * Request A-MPDU tx aggregation. Setup local state and * issue an ADDBA request. BA use will only happen after * the other end replies with ADDBA response. */ int ieee80211_ampdu_request(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap) { struct ieee80211com *ic = ni->ni_ic; uint16_t args[4]; int tid, dialogtoken; static int tokens = 0; /* XXX */ /* XXX locking */ if ((tap->txa_flags & IEEE80211_AGGR_SETUP) == 0) { /* do deferred setup of state */ ampdu_tx_setup(tap); } /* XXX hack for not doing proper locking */ tap->txa_flags &= ~IEEE80211_AGGR_NAK; dialogtoken = (tokens+1) % 63; /* XXX */ tid = WME_AC_TO_TID(tap->txa_ac); tap->txa_start = ni->ni_txseqs[tid]; args[0] = dialogtoken; args[1] = IEEE80211_BAPS_POLICY_IMMEDIATE | SM(tid, IEEE80211_BAPS_TID) | SM(IEEE80211_AGGR_BAWMAX, IEEE80211_BAPS_BUFSIZ) ; args[2] = 0; /* batimeout */ /* NB: do first so there's no race against reply */ if (!ic->ic_addba_request(ni, tap, dialogtoken, args[1], args[2])) { /* unable to setup state, don't make request */ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N, ni, "%s: could not setup BA stream for AC %d", __func__, tap->txa_ac); /* defer next try so we don't slam the driver with requests */ tap->txa_attempts = ieee80211_addba_maxtries; /* NB: check in case driver wants to override */ if (tap->txa_nextrequest <= ticks) tap->txa_nextrequest = ticks + ieee80211_addba_backoff; return 0; } tokens = dialogtoken; /* allocate token */ /* NB: after calling ic_addba_request so driver can set txa_start */ args[3] = SM(tap->txa_start, IEEE80211_BASEQ_START) | SM(0, IEEE80211_BASEQ_FRAG) ; return ic->ic_send_action(ni, IEEE80211_ACTION_CAT_BA, IEEE80211_ACTION_BA_ADDBA_REQUEST, args); } /* * Terminate an AMPDU tx stream. State is reclaimed * and the peer notified with a DelBA Action frame. */ void ieee80211_ampdu_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap, int reason) { struct ieee80211com *ic = ni->ni_ic; struct ieee80211vap *vap = ni->ni_vap; uint16_t args[4]; /* XXX locking */ tap->txa_flags &= ~IEEE80211_AGGR_BARPEND; if (IEEE80211_AMPDU_RUNNING(tap)) { IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, "%s: stop BA stream for AC %d (reason %d)", __func__, tap->txa_ac, reason); vap->iv_stats.is_ampdu_stop++; ic->ic_addba_stop(ni, tap); args[0] = WME_AC_TO_TID(tap->txa_ac); args[1] = IEEE80211_DELBAPS_INIT; args[2] = reason; /* XXX reason code */ ieee80211_send_action(ni, IEEE80211_ACTION_CAT_BA, IEEE80211_ACTION_BA_DELBA, args); } else { IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, "%s: BA stream for AC %d not running (reason %d)", __func__, tap->txa_ac, reason); vap->iv_stats.is_ampdu_stop_failed++; } } static void bar_timeout(void *arg) { struct ieee80211_tx_ampdu *tap = arg; struct ieee80211_node *ni = tap->txa_ni; KASSERT((tap->txa_flags & IEEE80211_AGGR_XCHGPEND) == 0, ("bar/addba collision, flags 0x%x", tap->txa_flags)); IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N, ni, "%s: tid %u flags 0x%x attempts %d", __func__, tap->txa_ac, tap->txa_flags, tap->txa_attempts); /* guard against race with bar_tx_complete */ if ((tap->txa_flags & IEEE80211_AGGR_BARPEND) == 0) return; /* XXX ? */ if (tap->txa_attempts >= ieee80211_bar_maxtries) ieee80211_ampdu_stop(ni, tap, IEEE80211_REASON_TIMEOUT); else ieee80211_send_bar(ni, tap, tap->txa_seqpending); } static void bar_start_timer(struct ieee80211_tx_ampdu *tap) { callout_reset(&tap->txa_timer, ieee80211_bar_timeout, bar_timeout, tap); } static void bar_stop_timer(struct ieee80211_tx_ampdu *tap) { callout_stop(&tap->txa_timer); } static void bar_tx_complete(struct ieee80211_node *ni, void *arg, int status) { struct ieee80211_tx_ampdu *tap = arg; IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N, ni, "%s: tid %u flags 0x%x pending %d status %d", __func__, tap->txa_ac, tap->txa_flags, callout_pending(&tap->txa_timer), status); /* XXX locking */ if ((tap->txa_flags & IEEE80211_AGGR_BARPEND) && callout_pending(&tap->txa_timer)) { struct ieee80211com *ic = ni->ni_ic; if (status) /* ACK'd */ bar_stop_timer(tap); ic->ic_bar_response(ni, tap, status); /* NB: just let timer expire so we pace requests */ } } static void ieee80211_bar_response(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap, int status) { if (status != 0) { /* got ACK */ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N, ni, "BAR moves BA win <%u:%u> (%u frames) txseq %u tid %u", tap->txa_start, IEEE80211_SEQ_ADD(tap->txa_start, tap->txa_wnd-1), tap->txa_qframes, tap->txa_seqpending, WME_AC_TO_TID(tap->txa_ac)); /* NB: timer already stopped in bar_tx_complete */ tap->txa_start = tap->txa_seqpending; tap->txa_flags &= ~IEEE80211_AGGR_BARPEND; } } /* * Transmit a BAR frame to the specified node. The * BAR contents are drawn from the supplied aggregation * state associated with the node. * * NB: we only handle immediate ACK w/ compressed bitmap. */ int ieee80211_send_bar(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap, ieee80211_seq seq) { #define senderr(_x, _v) do { vap->iv_stats._v++; ret = _x; goto bad; } while (0) struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; struct ieee80211_frame_bar *bar; struct mbuf *m; uint16_t barctl, barseqctl; uint8_t *frm; int tid, ret; if ((tap->txa_flags & IEEE80211_AGGR_RUNNING) == 0) { /* no ADDBA response, should not happen */ /* XXX stat+msg */ return EINVAL; } /* XXX locking */ bar_stop_timer(tap); ieee80211_ref_node(ni); m = ieee80211_getmgtframe(&frm, ic->ic_headroom, sizeof(*bar)); if (m == NULL) senderr(ENOMEM, is_tx_nobuf); if (!ieee80211_add_callback(m, bar_tx_complete, tap)) { m_freem(m); senderr(ENOMEM, is_tx_nobuf); /* XXX */ /* NOTREACHED */ } bar = mtod(m, struct ieee80211_frame_bar *); bar->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_BAR; bar->i_fc[1] = 0; IEEE80211_ADDR_COPY(bar->i_ra, ni->ni_macaddr); IEEE80211_ADDR_COPY(bar->i_ta, vap->iv_myaddr); tid = WME_AC_TO_TID(tap->txa_ac); barctl = (tap->txa_flags & IEEE80211_AGGR_IMMEDIATE ? 0 : IEEE80211_BAR_NOACK) | IEEE80211_BAR_COMP | SM(tid, IEEE80211_BAR_TID) ; barseqctl = SM(seq, IEEE80211_BAR_SEQ_START); /* NB: known to have proper alignment */ bar->i_ctl = htole16(barctl); bar->i_seq = htole16(barseqctl); m->m_pkthdr.len = m->m_len = sizeof(struct ieee80211_frame_bar); M_WME_SETAC(m, WME_AC_VO); IEEE80211_NODE_STAT(ni, tx_mgmt); /* XXX tx_ctl? */ /* XXX locking */ /* init/bump attempts counter */ if ((tap->txa_flags & IEEE80211_AGGR_BARPEND) == 0) tap->txa_attempts = 1; else tap->txa_attempts++; tap->txa_seqpending = seq; tap->txa_flags |= IEEE80211_AGGR_BARPEND; IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_11N, ni, "send BAR: tid %u ctl 0x%x start %u (attempt %d)", tid, barctl, seq, tap->txa_attempts); ret = ic->ic_raw_xmit(ni, m, NULL); if (ret != 0) { /* xmit failed, clear state flag */ tap->txa_flags &= ~IEEE80211_AGGR_BARPEND; goto bad; } /* XXX hack against tx complete happening before timer is started */ if (tap->txa_flags & IEEE80211_AGGR_BARPEND) bar_start_timer(tap); return 0; bad: ieee80211_free_node(ni); return ret; #undef senderr } /* * Send an action management frame. The arguments are stuff * into a frame without inspection; the caller is assumed to * prepare them carefully (e.g. based on the aggregation state). */ int ieee80211_send_action(struct ieee80211_node *ni, int category, int action, uint16_t args[4]) { #define senderr(_x, _v) do { vap->iv_stats._v++; ret = _x; goto bad; } while (0) #define ADDSHORT(frm, v) do { \ frm[0] = (v) & 0xff; \ frm[1] = (v) >> 8; \ frm += 2; \ } while (0) struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; struct ieee80211_bpf_params params; struct mbuf *m; uint8_t *frm; uint16_t baparamset; - int ret; + int ret, addsize; KASSERT(ni != NULL, ("null node")); /* * Hold a reference on the node so it doesn't go away until after * the xmit is complete all the way in the driver. On error we * will remove our reference. */ IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__, ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1); ieee80211_ref_node(ni); + addsize = 0; + switch (category) { + case IEEE80211_ACTION_CAT_BA: + case IEEE80211_ACTION_CAT_HT: + addsize += sizeof(struct ieee80211_action_ba_addbaresponse); + break; + case IEEE80211_ACTION_CAT_MESHPEERING: + addsize += sizeof(uint16_t); /* capabilities */ + addsize += 2 + vap->iv_meshidlen; /* Mesh ID */ + addsize += sizeof(struct ieee80211_meshconf_ie); + /* On Open frames, the peer link ID is not sent */ + if (action == IEEE80211_ACTION_MESHPEERING_OPEN) + addsize += sizeof(struct ieee80211_meshpeer_ie) - 2; + else + addsize += sizeof(struct ieee80211_meshpeer_ie); + break; + } m = ieee80211_getmgtframe(&frm, ic->ic_headroom + sizeof(struct ieee80211_frame), sizeof(uint16_t) /* action+category */ /* XXX may action payload */ - + sizeof(struct ieee80211_action_ba_addbaresponse) + + addsize + ); if (m == NULL) senderr(ENOMEM, is_tx_nobuf); *frm++ = category; *frm++ = action; switch (category) { case IEEE80211_ACTION_CAT_BA: switch (action) { case IEEE80211_ACTION_BA_ADDBA_REQUEST: IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, "send ADDBA request: dialogtoken %d " "baparamset 0x%x (tid %d) batimeout 0x%x baseqctl 0x%x", args[0], args[1], MS(args[1], IEEE80211_BAPS_TID), args[2], args[3]); *frm++ = args[0]; /* dialog token */ ADDSHORT(frm, args[1]); /* baparamset */ ADDSHORT(frm, args[2]); /* batimeout */ ADDSHORT(frm, args[3]); /* baseqctl */ break; case IEEE80211_ACTION_BA_ADDBA_RESPONSE: IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, "send ADDBA response: dialogtoken %d status %d " "baparamset 0x%x (tid %d) batimeout %d", args[0], args[1], args[2], MS(args[2], IEEE80211_BAPS_TID), args[3]); *frm++ = args[0]; /* dialog token */ ADDSHORT(frm, args[1]); /* statuscode */ ADDSHORT(frm, args[2]); /* baparamset */ ADDSHORT(frm, args[3]); /* batimeout */ break; case IEEE80211_ACTION_BA_DELBA: /* XXX */ baparamset = SM(args[0], IEEE80211_DELBAPS_TID) | args[1] ; ADDSHORT(frm, baparamset); ADDSHORT(frm, args[2]); /* reason code */ IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, "send DELBA action: tid %d, initiator %d reason %d", args[0], args[1], args[2]); break; default: goto badaction; } break; case IEEE80211_ACTION_CAT_HT: switch (action) { case IEEE80211_ACTION_HT_TXCHWIDTH: IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, "send HT txchwidth: width %d", IEEE80211_IS_CHAN_HT40(ni->ni_chan) ? 40 : 20 ); *frm++ = IEEE80211_IS_CHAN_HT40(ni->ni_chan) ? IEEE80211_A_HT_TXCHWIDTH_2040 : IEEE80211_A_HT_TXCHWIDTH_20; break; default: goto badaction; + } + break; + case IEEE80211_ACTION_CAT_MESHPEERING: + + switch (action) { + case IEEE80211_ACTION_MESHPEERING_OPEN: + IEEE80211_NOTE(vap, + IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, ni, + "send PEER OPEN action: lid %x", args[0]); + *frm++ = 0; /* capabilites */ + *frm++ = 0; + frm = ieee80211_add_meshid(frm, vap); + frm = ieee80211_add_meshconf(frm, vap); + *frm++ = IEEE80211_ELEMID_MESHPEER; + *frm++ = 3; /* len */ + *frm++ = IEEE80211_MESH_PEER_LINK_OPEN; /* subtype */ + ADDSHORT(frm, args[0]); /* local ID */ + break; + case IEEE80211_ACTION_MESHPEERING_CONFIRM: + IEEE80211_NOTE(vap, + IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, ni, + "send PEER CONFIRM action: lid %x, pid %x", + args[0], args[1]); + *frm++ = 0; /* capabilites */ + *frm++ = 0; + *frm++ = 0; /* status code */ + *frm++ = 0; + *frm++ = 0; /* AID */ + *frm++ = 0; + frm = ieee80211_add_meshid(frm, vap); + frm = ieee80211_add_meshconf(frm, vap); + *frm++ = IEEE80211_ELEMID_MESHPEER; + *frm++ = 5; /* len */ + *frm++ = IEEE80211_MESH_PEER_LINK_CONFIRM; /* subtype */ + ADDSHORT(frm, args[0]); + ADDSHORT(frm, args[1]); + break; } break; default: badaction: IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, "%s: unsupported category %d action %d", __func__, category, action); senderr(EINVAL, is_tx_unknownmgt); /* NOTREACHED */ } m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); memset(¶ms, 0, sizeof(params)); params.ibp_pri = WME_AC_VO; params.ibp_rate0 = ni->ni_txparms->mgmtrate; /* NB: we know all frames are unicast */ params.ibp_try0 = ni->ni_txparms->maxretry; params.ibp_power = ni->ni_txpower; return ieee80211_mgmt_output(ni, m, IEEE80211_FC0_SUBTYPE_ACTION, ¶ms); bad: ieee80211_free_node(ni); if (m != NULL) m_freem(m); return ret; #undef ADDSHORT #undef senderr } /* * Construct the MCS bit mask for inclusion * in an HT information element. */ static void ieee80211_set_htrates(uint8_t *frm, const struct ieee80211_htrateset *rs) { int i; for (i = 0; i < rs->rs_nrates; i++) { int r = rs->rs_rates[i] & IEEE80211_RATE_VAL; if (r < IEEE80211_HTRATE_MAXSIZE) { /* XXX? */ /* NB: this assumes a particular implementation */ setbit(frm, r); } } } /* * Add body of an HTCAP information element. */ static uint8_t * ieee80211_add_htcap_body(uint8_t *frm, struct ieee80211_node *ni) { #define ADDSHORT(frm, v) do { \ frm[0] = (v) & 0xff; \ frm[1] = (v) >> 8; \ frm += 2; \ } while (0) struct ieee80211vap *vap = ni->ni_vap; uint16_t caps; int rxmax, density; /* HT capabilities */ caps = vap->iv_htcaps & 0xffff; /* * Note channel width depends on whether we are operating as * a sta or not. When operating as a sta we are generating * a request based on our desired configuration. Otherwise * we are operational and the channel attributes identify * how we've been setup (which might be different if a fixed * channel is specified). */ if (vap->iv_opmode == IEEE80211_M_STA) { /* override 20/40 use based on config */ if (vap->iv_flags_ext & IEEE80211_FEXT_USEHT40) caps |= IEEE80211_HTCAP_CHWIDTH40; else caps &= ~IEEE80211_HTCAP_CHWIDTH40; /* use advertised setting (XXX locally constraint) */ rxmax = MS(ni->ni_htparam, IEEE80211_HTCAP_MAXRXAMPDU); density = MS(ni->ni_htparam, IEEE80211_HTCAP_MPDUDENSITY); } else { /* override 20/40 use based on current channel */ if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) caps |= IEEE80211_HTCAP_CHWIDTH40; else caps &= ~IEEE80211_HTCAP_CHWIDTH40; rxmax = vap->iv_ampdu_rxmax; density = vap->iv_ampdu_density; } /* adjust short GI based on channel and config */ if ((vap->iv_flags_ext & IEEE80211_FEXT_SHORTGI20) == 0) caps &= ~IEEE80211_HTCAP_SHORTGI20; if ((vap->iv_flags_ext & IEEE80211_FEXT_SHORTGI40) == 0 || (caps & IEEE80211_HTCAP_CHWIDTH40) == 0) caps &= ~IEEE80211_HTCAP_SHORTGI40; ADDSHORT(frm, caps); /* HT parameters */ *frm = SM(rxmax, IEEE80211_HTCAP_MAXRXAMPDU) | SM(density, IEEE80211_HTCAP_MPDUDENSITY) ; frm++; /* pre-zero remainder of ie */ memset(frm, 0, sizeof(struct ieee80211_ie_htcap) - __offsetof(struct ieee80211_ie_htcap, hc_mcsset)); /* supported MCS set */ /* * XXX it would better to get the rate set from ni_htrates * so we can restrict it but for sta mode ni_htrates isn't * setup when we're called to form an AssocReq frame so for * now we're restricted to the default HT rate set. */ ieee80211_set_htrates(frm, &ieee80211_rateset_11n); frm += sizeof(struct ieee80211_ie_htcap) - __offsetof(struct ieee80211_ie_htcap, hc_mcsset); return frm; #undef ADDSHORT } /* * Add 802.11n HT capabilities information element */ uint8_t * ieee80211_add_htcap(uint8_t *frm, struct ieee80211_node *ni) { frm[0] = IEEE80211_ELEMID_HTCAP; frm[1] = sizeof(struct ieee80211_ie_htcap) - 2; return ieee80211_add_htcap_body(frm + 2, ni); } /* * Add Broadcom OUI wrapped standard HTCAP ie; this is * used for compatibility w/ pre-draft implementations. */ uint8_t * ieee80211_add_htcap_vendor(uint8_t *frm, struct ieee80211_node *ni) { frm[0] = IEEE80211_ELEMID_VENDOR; frm[1] = 4 + sizeof(struct ieee80211_ie_htcap) - 2; frm[2] = (BCM_OUI >> 0) & 0xff; frm[3] = (BCM_OUI >> 8) & 0xff; frm[4] = (BCM_OUI >> 16) & 0xff; frm[5] = BCM_OUI_HTCAP; return ieee80211_add_htcap_body(frm + 6, ni); } /* * Construct the MCS bit mask of basic rates * for inclusion in an HT information element. */ static void ieee80211_set_basic_htrates(uint8_t *frm, const struct ieee80211_htrateset *rs) { int i; for (i = 0; i < rs->rs_nrates; i++) { int r = rs->rs_rates[i] & IEEE80211_RATE_VAL; if ((rs->rs_rates[i] & IEEE80211_RATE_BASIC) && r < IEEE80211_HTRATE_MAXSIZE) { /* NB: this assumes a particular implementation */ setbit(frm, r); } } } /* * Update the HTINFO ie for a beacon frame. */ void ieee80211_ht_update_beacon(struct ieee80211vap *vap, struct ieee80211_beacon_offsets *bo) { #define PROTMODE (IEEE80211_HTINFO_OPMODE|IEEE80211_HTINFO_NONHT_PRESENT) const struct ieee80211_channel *bsschan = vap->iv_bss->ni_chan; struct ieee80211com *ic = vap->iv_ic; struct ieee80211_ie_htinfo *ht = (struct ieee80211_ie_htinfo *) bo->bo_htinfo; /* XXX only update on channel change */ ht->hi_ctrlchannel = ieee80211_chan2ieee(ic, bsschan); if (vap->iv_flags_ext & IEEE80211_FEXT_RIFS) ht->hi_byte1 = IEEE80211_HTINFO_RIFSMODE_PERM; else ht->hi_byte1 = IEEE80211_HTINFO_RIFSMODE_PROH; if (IEEE80211_IS_CHAN_HT40U(bsschan)) ht->hi_byte1 |= IEEE80211_HTINFO_2NDCHAN_ABOVE; else if (IEEE80211_IS_CHAN_HT40D(bsschan)) ht->hi_byte1 |= IEEE80211_HTINFO_2NDCHAN_BELOW; else ht->hi_byte1 |= IEEE80211_HTINFO_2NDCHAN_NONE; if (IEEE80211_IS_CHAN_HT40(bsschan)) ht->hi_byte1 |= IEEE80211_HTINFO_TXWIDTH_2040; /* protection mode */ ht->hi_byte2 = (ht->hi_byte2 &~ PROTMODE) | ic->ic_curhtprotmode; /* XXX propagate to vendor ie's */ #undef PROTMODE } /* * Add body of an HTINFO information element. * * NB: We don't use struct ieee80211_ie_htinfo because we can * be called to fillin both a standard ie and a compat ie that * has a vendor OUI at the front. */ static uint8_t * ieee80211_add_htinfo_body(uint8_t *frm, struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; /* pre-zero remainder of ie */ memset(frm, 0, sizeof(struct ieee80211_ie_htinfo) - 2); /* primary/control channel center */ *frm++ = ieee80211_chan2ieee(ic, ni->ni_chan); if (vap->iv_flags_ext & IEEE80211_FEXT_RIFS) frm[0] = IEEE80211_HTINFO_RIFSMODE_PERM; else frm[0] = IEEE80211_HTINFO_RIFSMODE_PROH; if (IEEE80211_IS_CHAN_HT40U(ni->ni_chan)) frm[0] |= IEEE80211_HTINFO_2NDCHAN_ABOVE; else if (IEEE80211_IS_CHAN_HT40D(ni->ni_chan)) frm[0] |= IEEE80211_HTINFO_2NDCHAN_BELOW; else frm[0] |= IEEE80211_HTINFO_2NDCHAN_NONE; if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) frm[0] |= IEEE80211_HTINFO_TXWIDTH_2040; frm[1] = ic->ic_curhtprotmode; frm += 5; /* basic MCS set */ ieee80211_set_basic_htrates(frm, &ni->ni_htrates); frm += sizeof(struct ieee80211_ie_htinfo) - __offsetof(struct ieee80211_ie_htinfo, hi_basicmcsset); return frm; } /* * Add 802.11n HT information information element. */ uint8_t * ieee80211_add_htinfo(uint8_t *frm, struct ieee80211_node *ni) { frm[0] = IEEE80211_ELEMID_HTINFO; frm[1] = sizeof(struct ieee80211_ie_htinfo) - 2; return ieee80211_add_htinfo_body(frm + 2, ni); } /* * Add Broadcom OUI wrapped standard HTINFO ie; this is * used for compatibility w/ pre-draft implementations. */ uint8_t * ieee80211_add_htinfo_vendor(uint8_t *frm, struct ieee80211_node *ni) { frm[0] = IEEE80211_ELEMID_VENDOR; frm[1] = 4 + sizeof(struct ieee80211_ie_htinfo) - 2; frm[2] = (BCM_OUI >> 0) & 0xff; frm[3] = (BCM_OUI >> 8) & 0xff; frm[4] = (BCM_OUI >> 16) & 0xff; frm[5] = BCM_OUI_HTINFO; return ieee80211_add_htinfo_body(frm + 6, ni); } Index: projects/mesh11s/sys/net80211/ieee80211_mesh.c =================================================================== --- projects/mesh11s/sys/net80211/ieee80211_mesh.c (revision 191684) +++ projects/mesh11s/sys/net80211/ieee80211_mesh.c (revision 191685) @@ -1,648 +1,780 @@ /*- * Copyright (c) 2007-2009 Sam Leffler, Errno Consulting * Copyright (c) 2009 The FreeBSD Foundation * All rights reserved. * * This software was developed by Rui Paulo under sponsorship from the * FreeBSD Foundation. * * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. */ #include #ifdef __FreeBSD__ __FBSDID("$FreeBSD$"); #endif /* * IEEE 802.11s Mesh Point (MBSS) support. */ #include "opt_inet.h" #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void mesh_vattach(struct ieee80211vap *); static int mesh_newstate(struct ieee80211vap *, enum ieee80211_state, int); static int mesh_input(struct ieee80211_node *, struct mbuf *, int, int, uint32_t); static void mesh_recv_mgmt(struct ieee80211_node *, struct mbuf *, int, int, int, uint32_t); +static void mesh_recv_action(struct ieee80211_node *, struct mbuf *); void ieee80211_mesh_attach(struct ieee80211com *ic) { ic->ic_vattach[IEEE80211_M_MBSS] = mesh_vattach; } void ieee80211_mesh_detach(struct ieee80211com *ic) { } static void mesh_vdetach(struct ieee80211vap *vap) { } static void mesh_vattach(struct ieee80211vap *vap) { vap->iv_newstate = mesh_newstate; vap->iv_input = mesh_input; vap->iv_opdetach = mesh_vdetach; vap->iv_recv_mgmt = mesh_recv_mgmt; } /* * IEEE80211_M_MBSS vap state machine handler. */ static int mesh_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { struct ieee80211com *ic = vap->iv_ic; struct ieee80211_node *ni; enum ieee80211_state ostate; IEEE80211_LOCK_ASSERT(ic); ostate = vap->iv_state; IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n", __func__, ieee80211_state_name[ostate], ieee80211_state_name[nstate], arg); vap->iv_state = nstate; /* state transition */ if (ostate != IEEE80211_S_SCAN) ieee80211_cancel_scan(vap); /* background scan */ ni = vap->iv_bss; /* NB: no reference held */ switch (nstate) { case IEEE80211_S_INIT: if (ostate == IEEE80211_S_SCAN) ieee80211_cancel_scan(vap); if (ostate != IEEE80211_S_INIT) { /* NB: optimize INIT -> INIT case */ ieee80211_reset_bss(vap); } break; case IEEE80211_S_SCAN: switch (ostate) { case IEEE80211_S_INIT: if (vap->iv_des_chan != IEEE80211_CHAN_ANYC && !IEEE80211_IS_CHAN_RADAR(vap->iv_des_chan)) { /* * Already have a channel; bypass the * scan and startup immediately. */ ieee80211_create_mbss(vap, vap->iv_des_chan); break; } /* * Initiate a scan. We can come here as a result * of an IEEE80211_IOC_SCAN_REQ too in which case * the vap will be marked with IEEE80211_FEXT_SCANREQ * and the scan request parameters will be present * in iv_scanreq. Otherwise we do the default. */ if (vap->iv_flags_ext & IEEE80211_FEXT_SCANREQ) { ieee80211_check_scan(vap, vap->iv_scanreq_flags, vap->iv_scanreq_duration, vap->iv_scanreq_mindwell, vap->iv_scanreq_maxdwell, vap->iv_scanreq_nssid, vap->iv_scanreq_ssid); vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANREQ; } else ieee80211_check_scan_current(vap); break; default: break; } case IEEE80211_S_AUTH: case IEEE80211_S_ASSOC: case IEEE80211_S_CAC: case IEEE80211_S_RUN: case IEEE80211_S_CSA: case IEEE80211_S_SLEEP: default: break; } return 0; } /* * 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 int doprint(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 1; } return 1; } static int mesh_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int noise, uint32_t rstamp) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; struct ifnet *ifp = vap->iv_ifp; struct ieee80211_frame *wh; int need_tap; uint8_t dir, type, subtype; KASSERT(ni != NULL, ("null node")); ni->ni_inact = ni->ni_inact_reload; need_tap = 1; /* mbuf need to be tapped. */ type = -1; /* undefined */ if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) { IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, ni->ni_macaddr, NULL, "too short (1): len %u", m->m_pkthdr.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 = mtod(m, struct ieee80211_frame *); 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 (type) { case IEEE80211_FC0_TYPE_DATA: case IEEE80211_FC0_TYPE_MGT: vap->iv_stats.is_rx_mgmt++; IEEE80211_NODE_STAT(ni, rx_mgmt); if (dir != IEEE80211_FC1_DIR_NODS) { IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh, "data", "incorrect dir 0x%x", dir); vap->iv_stats.is_rx_wrongdir++; goto err; } if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) { IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, ni->ni_macaddr, "mgt", "too short: len %u", m->m_pkthdr.len); vap->iv_stats.is_rx_tooshort++; goto out; } #ifdef IEEE80211_DEBUG if ((ieee80211_msg_debug(vap) && doprint(vap, subtype)) || ieee80211_msg_dumppkts(vap)) { if_printf(ifp, "received %s from %s rssi %d\n", ieee80211_mgt_subtype_name[subtype >> IEEE80211_FC0_SUBTYPE_SHIFT], ether_sprintf(wh->i_addr2), rssi); } #endif if (wh->i_fc[1] & IEEE80211_FC1_WEP) { IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh, NULL, "%s", "WEP set but not permitted"); vap->iv_stats.is_rx_mgtdiscard++; /* XXX */ goto out; } if (bpf_peers_present(vap->iv_rawbpf)) bpf_mtap(vap->iv_rawbpf, m); vap->iv_recv_mgmt(ni, m, subtype, rssi, noise, rstamp); m_freem(m); return IEEE80211_FC0_TYPE_MGT; case IEEE80211_FC0_TYPE_CTL: vap->iv_stats.is_rx_ctl++; IEEE80211_NODE_STAT(ni, rx_ctrl); goto out; default: IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, wh, "bad", "frame type 0x%x", type); /* should not come here */ break; } err: ifp->if_ierrors++; out: if (m != NULL) { if (bpf_peers_present(vap->iv_rawbpf) && need_tap) bpf_mtap(vap->iv_rawbpf, m); m_freem(m); } return type; } static int is11bclient(const uint8_t *rates, const uint8_t *xrates) { static const uint32_t brates = (1<<2*1)|(1<<2*2)|(1<<11)|(1<<2*11); int i; /* NB: the 11b clients we care about will not have xrates */ if (xrates != NULL || rates == NULL) return 0; for (i = 0; i < rates[1]; i++) { int r = rates[2+i] & IEEE80211_RATE_VAL; if (r > 2*11 || ((1<ni_vap; struct ieee80211com *ic = ni->ni_ic; struct ieee80211_frame *wh; uint8_t *frm, *efrm; wh = mtod(m0, struct ieee80211_frame *); frm = (uint8_t *)&wh[1]; efrm = mtod(m0, uint8_t *) + m0->m_len; switch (subtype) { case IEEE80211_FC0_SUBTYPE_PROBE_RESP: case IEEE80211_FC0_SUBTYPE_BEACON: { struct ieee80211_scanparams scan; - /* * We process beacon/probe response * frames to discover neighbors. */ if (ieee80211_parse_beacon(ni, m0, &scan) != 0) return; /* * Count frame now that we know it's to be processed. */ if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) { vap->iv_stats.is_rx_beacon++; /* XXX remove */ IEEE80211_NODE_STAT(ni, rx_beacons); } else IEEE80211_NODE_STAT(ni, rx_proberesp); /* * If scanning, just pass information to the scan module. */ if (ic->ic_flags & IEEE80211_F_SCAN) { if (ic->ic_flags_ext & IEEE80211_FEXT_PROBECHAN) { /* * Actively scanning a channel marked passive; * send a probe request now that we know there * is 802.11 traffic present. * * XXX check if the beacon we recv'd gives * us what we need and suppress the probe req */ ieee80211_probe_curchan(vap, 1); ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN; } ieee80211_add_scan(vap, &scan, wh, subtype, rssi, noise, rstamp); return; } + /* The rest of this code assumes we setup and running */ + if (vap->iv_state != IEEE80211_S_RUN) + return; /* + * Ignore non-mesh STAs and STAs for other mesh networks. + */ + if (scan.meshid && + memcmp(scan.meshid+2, vap->iv_meshid, vap->iv_meshidlen) != 0) { + IEEE80211_NOTE(vap, IEEE80211_MSG_MESH, ni, + "beacon not for our mesh (%s)", scan.meshid+2); + return; + } + + /* + * More validation: make sure we are talking to a Mesh node. + */ + if ((scan.capinfo & (IEEE80211_CAPINFO_ESS|IEEE80211_CAPINFO_IBSS)) == 0 && + !IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr) && + IEEE80211_ADDR_EQ(wh->i_addr3, zerobssid)) { + /* + * Create a new entry in the neighbor table. + */ + ni = ieee80211_add_neighbor(vap, wh, &scan); + } else { + /* + * Record tsf for potential resync. + */ + memcpy(ni->ni_tstamp.data, scan.tstamp, + sizeof(ni->ni_tstamp)); + } + if (ni != NULL) { + IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi); + ni->ni_noise = noise; + ni->ni_rstamp = rstamp; + } + + /* * If it's a beacon for our mesh and we haven't already * peered with this node, send him a mgmt frame with * peer link IE. */ /* XXX */ break; } case IEEE80211_FC0_SUBTYPE_PROBE_REQ: { uint8_t *ssid, *meshid, *rates, *xrates; uint8_t *sfrm; if (vap->iv_state != IEEE80211_S_RUN) { IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh, NULL, "wrong state %s", ieee80211_state_name[vap->iv_state]); vap->iv_stats.is_rx_mgtdiscard++; return; } if (IEEE80211_IS_MULTICAST(wh->i_addr2)) { /* frame must be directed */ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh, NULL, "%s", "not unicast"); vap->iv_stats.is_rx_mgtdiscard++; /* XXX stat */ return; } /* * prreq frame format * [tlv] ssid * [tlv] supported rates * [tlv] extended supported rates * [tlv] Mesh ID */ ssid = meshid = rates = xrates = NULL; sfrm = frm; while (efrm - frm > 1) { IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return); switch (*frm) { case IEEE80211_ELEMID_SSID: ssid = frm; break; case IEEE80211_ELEMID_RATES: rates = frm; break; case IEEE80211_ELEMID_XRATES: xrates = frm; break; case IEEE80211_ELEMID_MESHID: meshid = frm; break; } frm += frm[2] + 2; } IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN, return); IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE, return); if (xrates != NULL) IEEE80211_VERIFY_ELEMENT(xrates, IEEE80211_RATE_MAXSIZE - rates[1], return); if (meshid != NULL) IEEE80211_VERIFY_ELEMENT(meshid, IEEE80211_MESHID_LEN, return); IEEE80211_VERIFY_SSID(vap->iv_bss, ssid, return); /* XXX add verify meshid ? */ /* XXX find a better class or define it's own */ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_INPUT, wh->i_addr2, "%s", "recv probe req"); /* * Some legacy 11b clients cannot hack a complete * probe response frame. When the request includes * only a bare-bones rate set, communicate this to * the transmit side. */ ieee80211_send_proberesp(vap, wh->i_addr2, is11bclient(rates, xrates) ? IEEE80211_SEND_LEGACY_11B : 0); break; } case IEEE80211_FC0_SUBTYPE_ACTION: - if (vap->iv_state == IEEE80211_S_RUN) { - if (ieee80211_parse_action(ni, m0) == 0) - ic->ic_recv_action(ni, frm, efrm); - } else + if (vap->iv_state != IEEE80211_S_RUN) { vap->iv_stats.is_rx_mgtdiscard++; + break; + } + /* XXX parse_action is a bit useless now */ + if (ieee80211_parse_action(ni, m0) == 0) { + mesh_recv_action(ni, m0); + ic->ic_recv_action(ni, frm, efrm); + } break; case IEEE80211_FC0_SUBTYPE_AUTH: case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: case IEEE80211_FC0_SUBTYPE_DEAUTH: case IEEE80211_FC0_SUBTYPE_DISASSOC: IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh, NULL, "%s", "not handled"); vap->iv_stats.is_rx_mgtdiscard++; return; default: IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, wh, "mgt", "subtype 0x%x not handled", subtype); vap->iv_stats.is_rx_badsubtype++; break; } } +static void +mesh_recv_action(struct ieee80211_node *ni, struct mbuf *m0) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211_action *ia; + struct ieee80211_frame *wh; + struct ieee80211_meshid_ie *meshid; + struct ieee80211_meshconf_ie *meshconf; + struct ieee80211_meshpeer_ie *meshpeer; + uint8_t *frm, *efrm; + uint16_t args[4]; + + wh = mtod(m0, struct ieee80211_frame *); + ia = (struct ieee80211_action *) &wh[1]; + frm = (uint8_t *)&wh[1]; + frm += sizeof(ia); + efrm = mtod(m0, uint8_t *) + m0->m_len; + + /* XXX explain frame format */ + meshid = NULL; + meshpeer = NULL; + meshconf = NULL; + while (efrm - frm > 1) { + IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return); + switch (*frm) { + case IEEE80211_ELEMID_MESHID: + meshid = (struct ieee80211_meshid_ie *) frm; + break; + case IEEE80211_ELEMID_MESHCONF: + meshconf = (struct ieee80211_meshconf_ie *) frm; + break; + case IEEE80211_ELEMID_MESHPEER: + meshpeer = (struct ieee80211_meshpeer_ie *) frm; + break; + } + frm += frm[1] + 2; + } + /* + * Check if we agree on Mesh ID and Configuration. + * XXX: TBD + */ + if (!meshid || !meshconf || !meshpeer) { + IEEE80211_DISCARD(vap, + IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, + wh, NULL, "%s", "action frame not for our mesh"); + vap->iv_stats.is_rx_mgtdiscard++; + return; + } + switch (ia->ia_category) { + case IEEE80211_ACTION_CAT_MESHPEERING: + switch (ia->ia_action) { + case IEEE80211_ACTION_MESHPEERING_OPEN: + IEEE80211_NOTE(vap, + IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, ni, + "%s", "recv peering open"); + ni->ni_peerstate = IEEE80211_NODE_MESH_OPENRCV; + ni->ni_llid = meshpeer->peer_llinkid; + ni->ni_plid = 0xf4ef; + args[0] = ni->ni_plid; + ieee80211_send_action(ni, + IEEE80211_ACTION_CAT_MESHPEERING, + IEEE80211_ACTION_MESHPEERING_OPEN, args); + break; + case IEEE80211_ACTION_MESHPEERING_CONFIRM: + IEEE80211_NOTE(vap, + IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, ni, + "%s", "recv peering confirm"); + if (ni->ni_peerstate != IEEE80211_NODE_MESH_OPNSENT && + ni->ni_peerstate != IEEE80211_NODE_MESH_OPENRCV) { + IEEE80211_DISCARD(vap, + IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, + wh, NULL, "received confirm in invalid " + "state %d", ni->ni_peerstate); + vap->iv_stats.is_rx_mgtdiscard++; + return; + } + ni->ni_peerstate = IEEE80211_NODE_MESH_CONFIRMRECV; + args[0] = 0xf4ef; + args[1] = ni->ni_llid; + ieee80211_send_action(ni, + IEEE80211_ACTION_CAT_MESHPEERING, + IEEE80211_ACTION_MESHPEERING_CONFIRM, args); + } + break; + default: + IEEE80211_DISCARD(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, + wh, NULL, "%s", "not handled"); + vap->iv_stats.is_rx_mgtdiscard++; + } +} + /* * Parse a MESH ID ie on station join. */ void ieee80211_parse_meshid(struct ieee80211_node *ni, const uint8_t *ie) { struct ieee80211vap *vap = ni->ni_vap; if (vap->iv_caps & IEEE80211_C_MBSS) { /*const struct ieee80211_meshid_ie *meshid = (const struct ieee80211_meshid_ie *)ie;*/ /* * Mesh STAs are QoS stations, so QoS is not optional. */ ni->ni_flags |= IEEE80211_NODE_QOS; } } /* * Add a MESH ID element to a frame. */ uint8_t * ieee80211_add_meshid(uint8_t *frm, struct ieee80211vap *vap) { KASSERT(vap->iv_opmode == IEEE80211_M_MBSS, ("not a mbss vap")); *frm++ = IEEE80211_ELEMID_MESHID; *frm++ = vap->iv_meshidlen; memcpy(frm, vap->iv_meshid, vap->iv_meshidlen); return frm + vap->iv_meshidlen; } /* * Add a Mesh Configuration element to a frame. * For now just use HWMP routing, Airtime link metric, Null Congestion * Signaling, Null Sync Protocol and Null Authentication. */ uint8_t * ieee80211_add_meshconf(uint8_t *frm, struct ieee80211vap *vap) { static const struct ieee80211_meshconf_ie ie = { .conf_ie = IEEE80211_ELEMID_MESHCONF, .conf_len = sizeof(struct ieee80211_meshconf_ie) - 2, .conf_ver = IEEE80211_MESHCONF_VERSION, .conf_pselid = IEEE80211_MESHCONF_HWMP, .conf_pmetid = IEEE80211_MESHCONF_AIRTIME, .conf_ccid = IEEE80211_MESHCONF_CCSIG, .conf_syncid = { 0, 0, 0, 0 }, /* XXX */ .conf_authid = { 0x80, 0, 0, 0 }, /* XXX */ .conf_form = 0, /* XXX */ .conf_cap = 1, /* XXX */ }; KASSERT(vap->iv_opmode == IEEE80211_M_MBSS, ("not a mbss vap")); memcpy(frm, &ie, sizeof(ie)); return frm + sizeof(ie); } void ieee80211_create_mbss(struct ieee80211vap *vap, struct ieee80211_channel *chan) { struct ieee80211com *ic = vap->iv_ic; struct ieee80211_node *ni; IEEE80211_DPRINTF(vap, IEEE80211_MSG_MESH, "%s: creating mbss on channel %u\n", __func__, ieee80211_chan2ieee(ic, chan)); ni = ieee80211_alloc_node(&ic->ic_sta, vap, vap->iv_myaddr); if (ni == NULL) { /* XXX recovery? */ return; } IEEE80211_ADDR_COPY(ni->ni_bssid, vap->iv_myaddr); ni->ni_meshidlen = vap->iv_meshidlen; memcpy(ni->ni_meshid, vap->iv_meshid, ni->ni_meshidlen); ni->ni_intval = ic->ic_bintval; /* * Fix the channel and related attributes. */ /* clear DFS CAC state on previous channel */ if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && ic->ic_bsschan->ic_freq != chan->ic_freq && IEEE80211_IS_CHAN_CACDONE(ic->ic_bsschan)) ieee80211_dfs_cac_clear(ic, ic->ic_bsschan); ic->ic_bsschan = chan; ieee80211_node_set_chan(ni, chan); ic->ic_curmode = ieee80211_chan2mode(chan); /* * Do mode-specific setup. */ if (IEEE80211_IS_CHAN_FULL(chan)) { if (IEEE80211_IS_CHAN_ANYG(chan)) { /* * Use a mixed 11b/11g basic rate set. */ ieee80211_setbasicrates(&ni->ni_rates, IEEE80211_MODE_11G); if (vap->iv_flags & IEEE80211_F_PUREG) { /* * Also mark OFDM rates basic so 11b * stations do not join (WiFi compliance). */ ieee80211_addbasicrates(&ni->ni_rates, IEEE80211_MODE_11A); } } else if (IEEE80211_IS_CHAN_B(chan)) { /* * Force pure 11b rate set. */ ieee80211_setbasicrates(&ni->ni_rates, IEEE80211_MODE_11B); } } ieee80211_ref_node(ni); vap->iv_bss = ni; ieee80211_setcurchan(ic, ni->ni_chan); /* * Set the erp state (mostly the slot time) to deal with * the auto-select case; this should be redundant if the * mode is locked. */ ieee80211_reset_erp(ic); ieee80211_wme_initparams(vap); ieee80211_new_state(vap, IEEE80211_S_RUN, -1); } static int mesh_ioctl_get80211(struct ieee80211vap *vap, struct ieee80211req *ireq) { int error; uint8_t tmpmeshid[IEEE80211_NWID_LEN]; error = 0; switch (ireq->i_type) { case IEEE80211_IOC_MESH_ID: if (vap->iv_opmode != IEEE80211_M_MBSS) return EINVAL; ireq->i_len = vap->iv_meshidlen; memcpy(tmpmeshid, vap->iv_meshid, ireq->i_len); error = copyout(tmpmeshid, ireq->i_data, ireq->i_len); break; default: return ENOSYS; } return error; } IEEE80211_IOCTL_GET(mesh, mesh_ioctl_get80211); static int mesh_ioctl_set80211(struct ieee80211vap *vap, struct ieee80211req *ireq) { int error; uint8_t tmpmeshid[IEEE80211_NWID_LEN]; error = 0; switch (ireq->i_type) { case IEEE80211_IOC_MESH_ID: if (ireq->i_val != 0 || ireq->i_len > IEEE80211_NWID_LEN) return EINVAL; error = copyin(ireq->i_data, tmpmeshid, ireq->i_len); if (error) break; memset(vap->iv_meshid, 0, IEEE80211_NWID_LEN); vap->iv_meshidlen = ireq->i_len; memcpy(vap->iv_meshid, tmpmeshid, ireq->i_len); break; default: return ENOSYS; } return error; } IEEE80211_IOCTL_SET(mesh, mesh_ioctl_set80211); Index: projects/mesh11s/sys/net80211/ieee80211_mesh.h =================================================================== --- projects/mesh11s/sys/net80211/ieee80211_mesh.h (revision 191684) +++ projects/mesh11s/sys/net80211/ieee80211_mesh.h (revision 191685) @@ -1,261 +1,275 @@ /*- * Copyright (c) 2009 The FreeBSD Foundation * All rights reserved. * * This software was developed by Rui Paulo under sponsorship from the * FreeBSD Foundation. * * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. * * $FreeBSD$ */ #ifndef _NET80211_IEEE80211_MESH_H_ #define _NET80211_IEEE80211_MESH_H_ /* * 802.11s Information Elements. */ /* Mesh Configuration */ struct ieee80211_meshconf_ie { uint8_t conf_ie; /* IEEE80211_ELEMID_MESHCONF */ uint8_t conf_len; uint8_t conf_ver; uint8_t conf_pselid[4]; /* Active Path Sel. Proto. ID */ uint8_t conf_pmetid[4]; /* APS Metric Identifier */ uint8_t conf_ccid[4]; /* Congestion Control Mode ID */ uint8_t conf_syncid[4]; /* Sync. Protocol ID */ uint8_t conf_authid[4]; /* Auth. Protocol ID */ uint8_t conf_form; /* Formation Information */ uint8_t conf_cap; } __packed; #define IEEE80211_MESHCONF_VERSION 1 /* Null Protocol */ #define IEEE80211_MESHCONF_NULL_OUI 0x00, 0x0f, 0xac #define IEEE80211_MESHCONF_NULL_VALUE 0xff #define IEEE80211_MESHCONF_NULL { IEEE80211_MESHCONF_NULL_OUI, IEEE80211_MESHCONF_NULL_VALUE } /* Hybrid Wireless Mesh Protocol */ #define IEEE80211_MESHCONF_HWMP_OUI 0x00, 0x0f, 0xac #define IEEE80211_MESHCONF_HWMP_VALUE 0xff /* XXX Linux */ #define IEEE80211_MESHCONF_HWMP { IEEE80211_MESHCONF_HWMP_OUI, IEEE80211_MESHCONF_HWMP_VALUE } /* Airtime Link Metric */ #define IEEE80211_MESHCONF_AIRTIME_OUI 0x00, 0x0f, 0xac #define IEEE80211_MESHCONF_AIRTIME_VALUE 0xff /* XXX Linux */ #define IEEE80211_MESHCONF_AIRTIME { IEEE80211_MESHCONF_AIRTIME_OUI, IEEE80211_MESHCONF_AIRTIME_VALUE } /* Congestion Control Signaling */ #define IEEE80211_MESHCONF_CCSIG_OUI 0x00, 0x0f, 0xac #define IEEE80211_MESHCONF_CCSIG_VALUE 0xff /* XXX Linux */ #define IEEE80211_MESHCONF_CCSIG { IEEE80211_MESHCONF_CCSIG_OUI, IEEE80211_MESHCONF_CCSIG_VALUE } /* Neighbour Offset */ #define IEEE80211_MESHCONF_NEIGHOFF_OUI 0x00, 0x0f, 0xac #define IEEE80211_MESHCONF_NEIGHOFF_VALUE 0x00 #define IEEE80211_MESHCONF_NEIGHOFF { IEEE80211_MESHCONF_NEIGHOFF_OUI, IEEE80211_MESHCONF_NEIGHOFF_VALUE } /* Simultaneous Authenticaction of Equals */ #define IEEE80211_MESHCONF_SAE_OUI 0x00, 0x0f, 0xac #define IEEE80211_MESHCONF_SAE_VALUE 0x01 #define IEEE80211_MESHCONF_SAE { IEEE80211_MESHCONF_SAE_OUI, IEEE80211_MESHCONF_SAE_VALUE } #define IEEE80211_MESHCONF_FORM_MP (1 << 1) /* connected to portal */ #define IEEE80211_MESHCONF_FORM_NEIGH (1 << 4) /* no of neighbours */ /* XXX ... */ /* Mesh Identifier */ struct ieee80211_meshid_ie { uint8_t id_ie; /* IEEE80211_ELEMID_MESHID */ uint8_t id_len; } __packed; #define IEEE80211_MESHID_LEN IEEE80211_NWID_LEN /* Link Metric Report */ struct ieee80211_meshlink_ie { uint8_t link_ie; /* IEEE80211_ELEMID_MESHLINK */ uint8_t link_len; /* XXXRP: missing variable metric */ } __packed; /* Congestion Notification */ struct ieee80211_meshcngst_ie { uint8_t cngst_ie; /* IEEE80211_ELEMID_MESHCNGST */ uint8_t cngst_len; uint16_t cngst_timer[4]; /* Expiration Timers: AC_BK, AC_BE, AC_VI, AC_VO */ } __packed; /* Peer Link Management */ struct ieee80211_meshpeer_ie { uint8_t peer_ie; /* IEEE80211_ELEMID_MESHPEER */ uint8_t peer_len; uint8_t peer_subtype; uint16_t peer_llinkid; /* Local Link ID */ uint16_t peer_linkid; /* Peer Link ID */ uint16_t peer_rcode; } __packed; enum { IEEE80211_MESH_PEER_LINK_OPEN = 0, IEEE80211_MESH_PEER_LINK_CONFIRM = 1, IEEE80211_MESH_PEER_LINK_CLOSE = 2, /* values 3-255 are reserved */ }; /* Mesh Channel Switch Annoucement */ struct ieee80211_meshcsa_ie { uint8_t csa_ie; /* IEEE80211_ELEMID_MESHCSA */ uint8_t csa_len; uint8_t csa_mode; uint8_t csa_newclass; /* New Regulatory Class */ uint8_t csa_newchan; uint8_t csa_precvalue; /* Precedence Value */ uint8_t csa_count; } __packed; /* Mesh TIM */ /* Equal to the non Mesh version */ /* Mesh Awake Window */ struct ieee80211_meshawakew_ie { uint8_t awakew_ie; /* IEEE80211_ELEMID_MESHAWAKEW */ uint8_t awakew_len; uint8_t awakew_windowlen; /* in TUs */ } __packed; /* Mesh Beacon Timing */ struct ieee80211_meshbeacont_ie { uint8_t beacont_ie; /* IEEE80211_ELEMID_MESHBEACONT */ uint8_t beacont_len; struct { uint8_t mp_aid; /* Least Octet of AID */ uint16_t mp_btime; /* Beacon Time */ uint16_t mp_bint; /* Beacon Interval */ } mp[1] __packed; /* NB: variable size */ } __packed; /* MDAOP Setup Request */ struct ieee80211_meshdaopsreq_ie { uint8_t daopsreq_ie; /* IEEE80211_ELEMID_MESHDAOPSREQ */ uint8_t daopsreq_len; uint8_t daopsreq_setid; uint32_t daposreq_reserv; /* MDAOP Reservation */ } __packed; /* MDAOP Setup Reply */ struct ieee80211_meshdaopsrep_ie { uint8_t daopsrep_ie; /* IEEE80211_ELEMID_MESHDAOPSREPLY */ uint8_t daopsrep_len; uint8_t daopsrep_setid; uint8_t daopsrep_code; /* Reply Code */ uint8_t daopsrep_reserv; /* MDAOP Reservation */ } __packed; enum { IEEE80211_MESH_DAOP_REPLY_ACCEPT = 0, IEEE80211_MESH_DAOP_REPLY_RESERV_CONFLICT = 1, IEEE80211_MESH_DAOP_REPLY_MAF_LIMIT = 2, /* everything else is reserved */ }; /* MDAOP Advertisements */ /* TBD */ /* MDAOP Set Teardown */ struct ieee80211_meshdaopst_ie { uint8_t daopsrep_ie; /* IEEE80211_ELEMID_MESHDAOPSREP */ uint8_t daopsrep_len; uint8_t daopsrep_setid; uint8_t daopsrep_setowner[IEEE80211_ADDR_LEN]; } __packed; /* Portal (MP) Annoucement */ struct ieee80211_meshpann_ie { uint8_t pann_ie; /* IEEE80211_ELEMID_MESHPANN */ uint8_t pann_len; uint8_t pann_flags; uint8_t pann_hopcount; uint8_t pann_ttl; uint8_t pann_addr[IEEE80211_ADDR_LEN]; uint8_t pann_seq; /* PANN Sequence Number */ } __packed; /* Root (MP) Annoucement */ struct ieee80211_meshrann_ie { uint8_t rann_ie; /* IEEE80211_ELEMID_MESHRANN */ uint8_t rann_len; uint8_t rann_flags; uint8_t rann_hopcount; uint8_t rann_ttl; uint8_t rann_addr[IEEE80211_ADDR_LEN]; uint32_t rann_seq; /* HWMP Sequence Number */ uint32_t rann_metric; } __packed; /* Mesh Path Request */ struct ieee80211_meshpreq_ie { uint8_t preq_ie; /* IEEE80211_ELEMID_MESHPREQ */ uint8_t preq_len; /* XXXRP: TBD */ } __packed; /* Mesh Path Reply */ struct ieee80211_meshprep_ie { uint8_t prep_ie; /* IEEE80211_ELEMID_MESHPREP */ uint8_t prep_len; /* XXXRP: TBD */ } __packed; /* Mesh Path Error */ struct ieee80211_meshperr_ie { uint8_t perr_ie; /* IEEE80211_ELEMID_MESHPERR */ uint8_t perr_len; uint8_t perr_mode; uint8_t perr_ndests; /* Number of Destinations */ struct { uint8_t dest_addr[IEEE80211_ADDR_LEN]; uint32_t dest_seq; } dests[1] __packed; /* NB: variable size */ } __packed; /* Mesh Proxy Update */ struct ieee80211_meshpu_ie { uint8_t pu_ie; /* IEEE80211_ELEMID_MESHPU */ uint8_t pu_len; /* XXXRP: TBD */ } __packed; /* Mesh Proxy Update Confirmation */ struct ieee80211_meshpuc_ie { uint8_t puc_ie; /* IEEE80211_ELEMID_MESHPUC */ uint8_t puc_len; uint8_t puc_flags; uint8_t puc_seq; /* PU Sequence Number */ uint8_t puc_daddr[IEEE80211_ADDR_LEN]; } __packed; +/* + * 802.11s Action Frames + */ +#define IEEE80211_ACTION_CAT_MESHPEERING 30 /* XXX Linux */ +#define IEEE80211_ACTION_CAT_MESHLINK 13 +#define IEEE80211_ACTION_CAT_PATHSEL 14 +#define IEEE80211_ACTION_CAT_INTERWORK 15 +#define IEEE80211_ACTION_CAT_RESOURCE 16 +#define IEEE80211_ACTION_CAT_PROXY 17 + +#define IEEE80211_ACTION_MESHPEERING_OPEN 0 +#define IEEE80211_ACTION_MESHPEERING_CONFIRM 1 +#define IEEE80211_ACTION_MESHPEERING_CLOSE 2 + void ieee80211_mesh_attach(struct ieee80211com *); void ieee80211_mesh_detach(struct ieee80211com *); void ieee80211_parse_meshid(struct ieee80211_node *, const uint8_t *); uint8_t * ieee80211_add_meshid(uint8_t *, struct ieee80211vap *); uint8_t * ieee80211_add_meshconf(uint8_t *, struct ieee80211vap *); void ieee80211_create_mbss(struct ieee80211vap *, struct ieee80211_channel *); #endif /* !_NET80211_IEEE80211_MESH_H_ */ Index: projects/mesh11s/sys/net80211/ieee80211_node.h =================================================================== --- projects/mesh11s/sys/net80211/ieee80211_node.h (revision 191684) +++ projects/mesh11s/sys/net80211/ieee80211_node.h (revision 191685) @@ -1,428 +1,439 @@ /*- * Copyright (c) 2001 Atsushi Onoe * Copyright (c) 2002-2009 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. * * 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. * * $FreeBSD$ */ #ifndef _NET80211_IEEE80211_NODE_H_ #define _NET80211_IEEE80211_NODE_H_ #include /* for ieee80211_nodestats */ #include /* for aggregation state */ #include /* for mesh defines */ /* * Each ieee80211com instance has a single timer that fires every * IEEE80211_INACT_WAIT seconds to handle "inactivity processing". * This is used to do node inactivity processing when operating - * as an AP or in adhoc mode. For inactivity processing each node + * as an AP, adhoc or mesh mode. For inactivity processing each node * has a timeout set in it's ni_inact field that is decremented * on each timeout and the node is reclaimed when the counter goes * to zero. We use different inactivity timeout values depending * on whether the node is associated and authorized (either by * 802.1x or open/shared key authentication) or associated but yet * to be authorized. The latter timeout is shorter to more aggressively * reclaim nodes that leave part way through the 802.1x exchange. */ #define IEEE80211_INACT_WAIT 15 /* inactivity interval (secs) */ #define IEEE80211_INACT_INIT (30/IEEE80211_INACT_WAIT) /* initial */ #define IEEE80211_INACT_AUTH (180/IEEE80211_INACT_WAIT) /* associated but not authorized */ #define IEEE80211_INACT_RUN (300/IEEE80211_INACT_WAIT) /* authorized */ #define IEEE80211_INACT_PROBE (30/IEEE80211_INACT_WAIT) /* probe */ #define IEEE80211_INACT_SCAN (300/IEEE80211_INACT_WAIT) /* scanned */ #define IEEE80211_TRANS_WAIT 2 /* mgt frame tx timer (secs) */ /* threshold for aging overlapping non-ERP bss */ #define IEEE80211_NONERP_PRESENT_AGE msecs_to_ticks(60*1000) #define IEEE80211_NODE_HASHSIZE 32 /* simple hash is enough for variation of macaddr */ #define IEEE80211_NODE_HASH(addr) \ (((const uint8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % \ IEEE80211_NODE_HASHSIZE) struct ieee80211_node_table; struct ieee80211com; struct ieee80211vap; /* * Information element ``blob''. We use this structure * to capture management frame payloads that need to be * retained. Information elements within the payload that * we need to consult have references recorded. */ struct ieee80211_ies { /* the following are either NULL or point within data */ uint8_t *wpa_ie; /* captured WPA ie */ uint8_t *rsn_ie; /* captured RSN ie */ uint8_t *wme_ie; /* captured WME ie */ uint8_t *ath_ie; /* captured Atheros ie */ uint8_t *htcap_ie; /* captured HTCAP ie */ uint8_t *htinfo_ie; /* captured HTINFO ie */ uint8_t *tdma_ie; /* captured TDMA ie */ uint8_t *meshid_ie; /* captured MESH ID ie */ /* NB: these must be the last members of this structure */ uint8_t *data; /* frame data > 802.11 header */ int len; /* data size in bytes */ }; /* * Node specific information. Note that drivers are expected * to derive from this structure to add device-specific per-node * state. This is done by overriding the ic_node_* methods in * the ieee80211com structure. */ struct ieee80211_node { struct ieee80211vap *ni_vap; /* associated vap */ struct ieee80211com *ni_ic; /* copy from vap to save deref*/ struct ieee80211_node_table *ni_table; /* NB: may be NULL */ TAILQ_ENTRY(ieee80211_node) ni_list; /* list of all nodes */ LIST_ENTRY(ieee80211_node) ni_hash; /* hash collision list */ u_int ni_refcnt; /* count of held references */ u_int ni_scangen; /* gen# for timeout scan */ u_int ni_flags; #define IEEE80211_NODE_AUTH 0x000001 /* authorized for data */ #define IEEE80211_NODE_QOS 0x000002 /* QoS enabled */ #define IEEE80211_NODE_ERP 0x000004 /* ERP enabled */ /* NB: this must have the same value as IEEE80211_FC1_PWR_MGT */ #define IEEE80211_NODE_PWR_MGT 0x000010 /* power save mode enabled */ #define IEEE80211_NODE_AREF 0x000020 /* authentication ref held */ #define IEEE80211_NODE_HT 0x000040 /* HT enabled */ #define IEEE80211_NODE_HTCOMPAT 0x000080 /* HT setup w/ vendor OUI's */ #define IEEE80211_NODE_WPS 0x000100 /* WPS association */ #define IEEE80211_NODE_TSN 0x000200 /* TSN association */ #define IEEE80211_NODE_AMPDU_RX 0x000400 /* AMPDU rx enabled */ #define IEEE80211_NODE_AMPDU_TX 0x000800 /* AMPDU tx enabled */ #define IEEE80211_NODE_MIMO_PS 0x001000 /* MIMO power save enabled */ #define IEEE80211_NODE_MIMO_RTS 0x002000 /* send RTS in MIMO PS */ #define IEEE80211_NODE_RIFS 0x004000 /* RIFS enabled */ #define IEEE80211_NODE_SGI20 0x008000 /* Short GI in HT20 enabled */ #define IEEE80211_NODE_SGI40 0x010000 /* Short GI in HT40 enabled */ #define IEEE80211_NODE_ASSOCID 0x020000 /* xmit requires associd */ uint16_t ni_associd; /* association ID */ uint16_t ni_vlan; /* vlan tag */ uint16_t ni_txpower; /* current transmit power */ uint8_t ni_authmode; /* authentication algorithm */ uint8_t ni_ath_flags; /* Atheros feature flags */ /* NB: These must have the same values as IEEE80211_ATHC_* */ #define IEEE80211_NODE_TURBOP 0x0001 /* Turbo prime enable */ #define IEEE80211_NODE_COMP 0x0002 /* Compresssion enable */ #define IEEE80211_NODE_FF 0x0004 /* Fast Frame capable */ #define IEEE80211_NODE_XR 0x0008 /* Atheros WME enable */ #define IEEE80211_NODE_AR 0x0010 /* AR capable */ #define IEEE80211_NODE_BOOST 0x0080 /* Dynamic Turbo boosted */ uint16_t ni_ath_defkeyix;/* Atheros def key index */ const struct ieee80211_txparam *ni_txparms; uint32_t ni_jointime; /* time of join (secs) */ uint32_t *ni_challenge; /* shared-key challenge */ struct ieee80211_ies ni_ies; /* captured ie's */ /* tx seq per-tid */ uint16_t ni_txseqs[IEEE80211_TID_SIZE]; /* rx seq previous per-tid*/ uint16_t ni_rxseqs[IEEE80211_TID_SIZE]; uint32_t ni_rxfragstamp; /* time stamp of last rx frag */ struct mbuf *ni_rxfrag[3]; /* rx frag reassembly */ struct ieee80211_key ni_ucastkey; /* unicast key */ /* hardware */ uint32_t ni_rstamp; /* recv timestamp */ uint32_t ni_avgrssi; /* recv ssi state */ int8_t ni_noise; /* noise floor */ /* header */ uint8_t ni_macaddr[IEEE80211_ADDR_LEN]; uint8_t ni_bssid[IEEE80211_ADDR_LEN]; /* beacon, probe response */ union { uint8_t data[8]; u_int64_t tsf; } ni_tstamp; /* from last rcv'd beacon */ uint16_t ni_intval; /* beacon interval */ uint16_t ni_capinfo; /* capabilities */ uint8_t ni_esslen; uint8_t ni_essid[IEEE80211_NWID_LEN]; uint8_t ni_meshidlen; uint8_t ni_meshid[IEEE80211_MESHID_LEN]; struct ieee80211_rateset ni_rates; /* negotiated rate set */ struct ieee80211_channel *ni_chan; uint16_t ni_fhdwell; /* FH only */ uint8_t ni_fhindex; /* FH only */ uint16_t ni_erp; /* ERP from beacon/probe resp */ uint16_t ni_timoff; /* byte offset to TIM ie */ uint8_t ni_dtim_period; /* DTIM period */ uint8_t ni_dtim_count; /* DTIM count for last bcn */ + + /* mesh */ +#define IEEE80211_NODE_MESH_IDLE 0 +#define IEEE80211_NODE_MESH_OPNSENT 1 /* Peer Open Frame Received */ +#define IEEE80211_NODE_MESH_OPENRCV 2 /* Peer Open Frame Sent */ +#define IEEE80211_NODE_MESH_CONFIRMRECV 3 /* Peer Confirm Frame Recvived */ +#define IEEE80211_NODE_MESH_ESTABLISHED 4 /* Peer Link Established */ +#define IEEE80211_NODE_MESH_HOLDING 5 /* Peer Link Closing */ + int8_t ni_peerstate; /* Mesh Peering state */ + uint16_t ni_llid; /* local link ID */ + uint16_t ni_plid; /* peer link ID */ /* 11n state */ uint16_t ni_htcap; /* HT capabilities */ uint8_t ni_htparam; /* HT params */ uint8_t ni_htctlchan; /* HT control channel */ uint8_t ni_ht2ndchan; /* HT 2nd channel */ uint8_t ni_htopmode; /* HT operating mode */ uint8_t ni_htstbc; /* HT */ uint8_t ni_chw; /* negotiated channel width */ struct ieee80211_htrateset ni_htrates; /* negotiated ht rate set */ struct ieee80211_tx_ampdu ni_tx_ampdu[WME_NUM_AC]; struct ieee80211_rx_ampdu ni_rx_ampdu[WME_NUM_TID]; /* others */ short ni_inact; /* inactivity mark count */ short ni_inact_reload;/* inactivity reload value */ int ni_txrate; /* legacy rate/MCS */ struct ieee80211_psq ni_psq; /* power save queue */ struct ieee80211_nodestats ni_stats; /* per-node statistics */ struct ieee80211vap *ni_wdsvap; /* associated WDS vap */ /* XXX move to vap? */ struct ifqueue ni_wdsq; /* wds pending queue */ }; MALLOC_DECLARE(M_80211_NODE); MALLOC_DECLARE(M_80211_NODE_IE); #define IEEE80211_NODE_ATH (IEEE80211_NODE_FF | IEEE80211_NODE_TURBOP) #define IEEE80211_NODE_AMPDU \ (IEEE80211_NODE_AMPDU_RX | IEEE80211_NODE_AMPDU_TX) #define IEEE80211_NODE_HT_ALL \ (IEEE80211_NODE_HT | IEEE80211_NODE_HTCOMPAT | \ IEEE80211_NODE_AMPDU | IEEE80211_NODE_MIMO_PS | \ IEEE80211_NODE_MIMO_RTS | IEEE80211_NODE_RIFS | \ IEEE80211_NODE_SGI20 | IEEE80211_NODE_SGI40) #define IEEE80211_NODE_BITS \ "\20\1AUTH\2QOS\3ERP\5PWR_MGT\6AREF\7HT\10HTCOMPAT\11WPS\12TSN" \ "\13AMPDU_RX\14AMPDU_TX\15MIMO_PS\16MIMO_RTS\17RIFS\20SGI20\21SGI40" \ "\22ASSOCID" #define IEEE80211_NODE_AID(ni) IEEE80211_AID(ni->ni_associd) #define IEEE80211_NODE_STAT(ni,stat) (ni->ni_stats.ns_##stat++) #define IEEE80211_NODE_STAT_ADD(ni,stat,v) (ni->ni_stats.ns_##stat += v) #define IEEE80211_NODE_STAT_SET(ni,stat,v) (ni->ni_stats.ns_##stat = v) /* * Filtered rssi calculation support. The receive rssi is maintained * as an average over the last 10 frames received using a low pass filter * (all frames for now, possibly need to be more selective). Calculations * are designed such that a good compiler can optimize them. The avg * rssi state should be initialized to IEEE80211_RSSI_DUMMY_MARKER and * each sample incorporated with IEEE80211_RSSI_LPF. Use IEEE80211_RSSI_GET * to extract the current value. * * Note that we assume rssi data are in the range [-127..127] and we * discard values <-20. This is consistent with assumptions throughout * net80211 that signal strength data are in .5 dBm units relative to * the current noise floor (linear, not log). */ #define IEEE80211_RSSI_LPF_LEN 10 #define IEEE80211_RSSI_DUMMY_MARKER 127 /* NB: pow2 to optimize out * and / */ #define IEEE80211_RSSI_EP_MULTIPLIER (1<<7) #define IEEE80211_RSSI_IN(x) ((x) * IEEE80211_RSSI_EP_MULTIPLIER) #define _IEEE80211_RSSI_LPF(x, y, len) \ (((x) != IEEE80211_RSSI_DUMMY_MARKER) ? (((x) * ((len) - 1) + (y)) / (len)) : (y)) #define IEEE80211_RSSI_LPF(x, y) do { \ if ((y) >= -20) { \ x = _IEEE80211_RSSI_LPF((x), IEEE80211_RSSI_IN((y)), \ IEEE80211_RSSI_LPF_LEN); \ } \ } while (0) #define IEEE80211_RSSI_EP_RND(x, mul) \ ((((x) % (mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul)) #define IEEE80211_RSSI_GET(x) \ IEEE80211_RSSI_EP_RND(x, IEEE80211_RSSI_EP_MULTIPLIER) static __inline struct ieee80211_node * ieee80211_ref_node(struct ieee80211_node *ni) { ieee80211_node_incref(ni); return ni; } static __inline void ieee80211_unref_node(struct ieee80211_node **ni) { ieee80211_node_decref(*ni); *ni = NULL; /* guard against use */ } struct ieee80211com; void ieee80211_node_attach(struct ieee80211com *); void ieee80211_node_lateattach(struct ieee80211com *); void ieee80211_node_detach(struct ieee80211com *); void ieee80211_node_vattach(struct ieee80211vap *); void ieee80211_node_latevattach(struct ieee80211vap *); void ieee80211_node_vdetach(struct ieee80211vap *); static __inline int ieee80211_node_is_authorized(const struct ieee80211_node *ni) { return (ni->ni_flags & IEEE80211_NODE_AUTH); } void ieee80211_node_authorize(struct ieee80211_node *); void ieee80211_node_unauthorize(struct ieee80211_node *); void ieee80211_node_set_chan(struct ieee80211_node *, struct ieee80211_channel *); void ieee80211_create_ibss(struct ieee80211vap*, struct ieee80211_channel *); void ieee80211_reset_bss(struct ieee80211vap *); void ieee80211_sync_curchan(struct ieee80211com *); void ieee80211_setcurchan(struct ieee80211com *, struct ieee80211_channel *); int ieee80211_ibss_merge(struct ieee80211_node *); struct ieee80211_scan_entry; int ieee80211_sta_join(struct ieee80211vap *, struct ieee80211_channel *, const struct ieee80211_scan_entry *); void ieee80211_sta_leave(struct ieee80211_node *); void ieee80211_node_deauth(struct ieee80211_node *, int); int ieee80211_ies_init(struct ieee80211_ies *, const uint8_t *, int); void ieee80211_ies_cleanup(struct ieee80211_ies *); void ieee80211_ies_expand(struct ieee80211_ies *); #define ieee80211_ies_setie(_ies, _ie, _off) do { \ (_ies)._ie = (_ies).data + (_off); \ } while (0) /* * Table of ieee80211_node instances. Each ieee80211com * has one that holds association stations (when operating * as an ap) or neighbors (in ibss mode). * * XXX embed this in ieee80211com instead of indirect? */ struct ieee80211_node_table { struct ieee80211com *nt_ic; /* back reference */ ieee80211_node_lock_t nt_nodelock; /* on node table */ TAILQ_HEAD(, ieee80211_node) nt_node; /* information of all nodes */ LIST_HEAD(, ieee80211_node) nt_hash[IEEE80211_NODE_HASHSIZE]; struct ieee80211_node **nt_keyixmap; /* key ix -> node map */ int nt_keyixmax; /* keyixmap size */ const char *nt_name; /* table name for debug msgs */ ieee80211_scan_lock_t nt_scanlock; /* on nt_scangen */ u_int nt_scangen; /* gen# for iterators */ int nt_inact_init; /* initial node inact setting */ }; struct ieee80211_node *ieee80211_alloc_node(struct ieee80211_node_table *, struct ieee80211vap *, const uint8_t macaddr[IEEE80211_ADDR_LEN]); struct ieee80211_node *ieee80211_tmp_node(struct ieee80211vap *, const uint8_t macaddr[IEEE80211_ADDR_LEN]); struct ieee80211_node *ieee80211_dup_bss(struct ieee80211vap *, const uint8_t macaddr[IEEE80211_ADDR_LEN]); struct ieee80211_node *ieee80211_node_create_wds(struct ieee80211vap *, const uint8_t bssid[IEEE80211_ADDR_LEN], struct ieee80211_channel *); #ifdef IEEE80211_DEBUG_REFCNT void ieee80211_free_node_debug(struct ieee80211_node *, const char *func, int line); struct ieee80211_node *ieee80211_find_node_locked_debug( struct ieee80211_node_table *, const uint8_t macaddr[IEEE80211_ADDR_LEN], const char *func, int line); struct ieee80211_node *ieee80211_find_node_debug(struct ieee80211_node_table *, const uint8_t macaddr[IEEE80211_ADDR_LEN], const char *func, int line); struct ieee80211_node *ieee80211_find_vap_node_locked_debug( struct ieee80211_node_table *, const struct ieee80211vap *vap, const uint8_t macaddr[IEEE80211_ADDR_LEN], const char *func, int line); struct ieee80211_node *ieee80211_find_vap_node_debug( struct ieee80211_node_table *, const struct ieee80211vap *vap, const uint8_t macaddr[IEEE80211_ADDR_LEN], const char *func, int line); struct ieee80211_node * ieee80211_find_rxnode_debug(struct ieee80211com *, const struct ieee80211_frame_min *, const char *func, int line); struct ieee80211_node * ieee80211_find_rxnode_withkey_debug( struct ieee80211com *, const struct ieee80211_frame_min *, uint16_t keyix, const char *func, int line); struct ieee80211_node *ieee80211_find_txnode_debug(struct ieee80211vap *, const uint8_t *, const char *func, int line); #define ieee80211_free_node(ni) \ ieee80211_free_node_debug(ni, __func__, __LINE__) #define ieee80211_find_node_locked(nt, mac) \ ieee80211_find_node_locked_debug(nt, mac, __func__, __LINE__) #define ieee80211_find_node(nt, mac) \ ieee80211_find_node_debug(nt, mac, __func__, __LINE__) #define ieee80211_find_vap_node_locked(nt, vap, mac) \ ieee80211_find_vap_node_locked_debug(nt, vap, mac, __func__, __LINE__) #define ieee80211_find_vap_node(nt, vap, mac) \ ieee80211_find_vap_node_debug(nt, vap, mac, __func__, __LINE__) #define ieee80211_find_rxnode(ic, wh) \ ieee80211_find_rxnode_debug(ic, wh, __func__, __LINE__) #define ieee80211_find_rxnode_withkey(ic, wh, keyix) \ ieee80211_find_rxnode_withkey_debug(ic, wh, keyix, __func__, __LINE__) #define ieee80211_find_txnode(vap, mac) \ ieee80211_find_txnode_debug(vap, mac, __func__, __LINE__) #else void ieee80211_free_node(struct ieee80211_node *); struct ieee80211_node *ieee80211_find_node_locked(struct ieee80211_node_table *, const uint8_t macaddr[IEEE80211_ADDR_LEN]); struct ieee80211_node *ieee80211_find_node(struct ieee80211_node_table *, const uint8_t macaddr[IEEE80211_ADDR_LEN]); struct ieee80211_node *ieee80211_find_vap_node_locked( struct ieee80211_node_table *, const struct ieee80211vap *, const uint8_t macaddr[IEEE80211_ADDR_LEN]); struct ieee80211_node *ieee80211_find_vap_node( struct ieee80211_node_table *, const struct ieee80211vap *, const uint8_t macaddr[IEEE80211_ADDR_LEN]); struct ieee80211_node * ieee80211_find_rxnode(struct ieee80211com *, const struct ieee80211_frame_min *); struct ieee80211_node * ieee80211_find_rxnode_withkey(struct ieee80211com *, const struct ieee80211_frame_min *, uint16_t keyix); struct ieee80211_node *ieee80211_find_txnode(struct ieee80211vap *, const uint8_t macaddr[IEEE80211_ADDR_LEN]); #endif int ieee80211_node_delucastkey(struct ieee80211_node *); void ieee80211_node_timeout(void *arg); typedef void ieee80211_iter_func(void *, struct ieee80211_node *); void ieee80211_iterate_nodes(struct ieee80211_node_table *, ieee80211_iter_func *, void *); void ieee80211_notify_erp(struct ieee80211com *); void ieee80211_dump_node(struct ieee80211_node_table *, struct ieee80211_node *); void ieee80211_dump_nodes(struct ieee80211_node_table *); struct ieee80211_node *ieee80211_fakeup_adhoc_node(struct ieee80211vap *, const uint8_t macaddr[IEEE80211_ADDR_LEN]); struct ieee80211_scanparams; void ieee80211_init_neighbor(struct ieee80211_node *, const struct ieee80211_frame *, const struct ieee80211_scanparams *); struct ieee80211_node *ieee80211_add_neighbor(struct ieee80211vap *, const struct ieee80211_frame *, const struct ieee80211_scanparams *); void ieee80211_node_join(struct ieee80211_node *,int); void ieee80211_node_leave(struct ieee80211_node *); int8_t ieee80211_getrssi(struct ieee80211vap *); void ieee80211_getsignal(struct ieee80211vap *, int8_t *, int8_t *); #endif /* _NET80211_IEEE80211_NODE_H_ */