summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorsam <sam@FreeBSD.org>2003-08-19 22:17:04 +0000
committersam <sam@FreeBSD.org>2003-08-19 22:17:04 +0000
commit1c1194250d59edd49cfbc3438c6e16a732403a44 (patch)
tree83efb1b5bb46b4bb6dbad6bd4c5de0b0559f8398 /sys
parentd321d4f4dfa7cf729da088c2648aed6dcea60cc2 (diff)
downloadFreeBSD-src-1c1194250d59edd49cfbc3438c6e16a732403a44.zip
FreeBSD-src-1c1194250d59edd49cfbc3438c6e16a732403a44.tar.gz
MFp4 changes to fix locking issues and correct reference
count handling of station entries in hostap mode: Input path: o driver is now expected to find the node associated with the sender of a received frame; use ic_bss if none is located o driver passes the (referenced) node into ieee80211_input for use within the wlan module and is responsible for cleaning up on return o the antenna state is no longer passed up with each frame; this is now considered driver-private state and drivers are responsible for keeping it in the driver-private part of a node Output path: Revamp output path for management frames to eliminate redundant locking that causes problems and to correct reference counting bogosity that occurs when stations are timed out due to inactivity (in AP mode). On output the refcnt'd node is stashed in the pkthdr's recvif field (yech) and retrieved by the driver. This eliminates an unref/ref scenario and related node table unlock/lock due to the driver looking up the node. This is particularly important when stations are timed out as this causes a lock order reversal that can result in a deadlock. As a byproduct we also reduce the overhead for sending management frames (minimal). Additional fallout from this is a change to ieee80211_encap to return a refcn't node for tieing to the outbound frame. Node refcnts are not reclaimed until after a frame is completely processed (e.g. in the tx interrupt handler). This is especially important for timed out stations as this deref will be the final one causing the node entry to be reclaimed. Additional semi-related changes: o replace m_copym use with m_copypacket (optimization) o add assert to verify ic_bss is never free'd during normal operation o add comments explaining calling conventions by drivers for frames going in each direction o remove extraneous code that "cannot be executed" (e.g. because pointers may never be null)
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/ath/if_ath.c107
-rw-r--r--sys/dev/ath/if_athvar.h4
-rw-r--r--sys/dev/wi/if_wi.c73
-rw-r--r--sys/net80211/ieee80211_input.c128
-rw-r--r--sys/net80211/ieee80211_node.c33
-rw-r--r--sys/net80211/ieee80211_node.h3
-rw-r--r--sys/net80211/ieee80211_output.c141
-rw-r--r--sys/net80211/ieee80211_proto.h12
-rw-r--r--sys/net80211/ieee80211_var.h3
9 files changed, 310 insertions, 194 deletions
diff --git a/sys/dev/ath/if_ath.c b/sys/dev/ath/if_ath.c
index ba84f09..fdb0091 100644
--- a/sys/dev/ath/if_ath.c
+++ b/sys/dev/ath/if_ath.c
@@ -691,7 +691,7 @@ ath_start(struct ifnet *ifp)
/*
* Encapsulate the packet in prep for transmission.
*/
- m = ieee80211_encap(ifp, m);
+ m = ieee80211_encap(ifp, m, &ni);
if (m == NULL) {
DPRINTF(("ath_start: encapsulation failure\n"));
sc->sc_stats.ast_tx_encap++;
@@ -701,6 +701,18 @@ ath_start(struct ifnet *ifp)
if (ic->ic_flags & IEEE80211_F_WEPON)
wh->i_fc[1] |= IEEE80211_FC1_WEP;
} else {
+ /*
+ * Hack! The referenced node pointer is in the
+ * rcvif field of the packet header. This is
+ * placed there by ieee80211_mgmt_output because
+ * we need to hold the reference with the frame
+ * and there's no other way (other than packet
+ * tags which we consider too expensive to use)
+ * to pass it along.
+ */
+ ni = (struct ieee80211_node *) m->m_pkthdr.rcvif;
+ m->m_pkthdr.rcvif = NULL;
+
wh = mtod(m, struct ieee80211_frame *);
if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) ==
IEEE80211_FC0_SUBTYPE_PROBE_RESP) {
@@ -720,26 +732,6 @@ ath_start(struct ifnet *ifp)
if (ic->ic_rawbpf)
bpf_mtap(ic->ic_rawbpf, m);
- if (ic->ic_opmode != IEEE80211_M_STA) {
- ni = ieee80211_find_node(ic, wh->i_addr1);
- if (ni == NULL) {
- /*
- * When not in station mode the destination
- * address should always be in the node table
- * unless this is a multicast/broadcast frame.
- */
- if (!IEEE80211_IS_MULTICAST(wh->i_addr1) &&
- (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
- IEEE80211_FC0_TYPE_DATA) {
- m_freem(m);
- sc->sc_stats.ast_tx_nonode++;
- goto bad;
- }
- ni = ic->ic_bss;
- }
- } else
- ni = ic->ic_bss;
-
/*
* TODO:
* The duration field of 802.11 header should be filled.
@@ -759,6 +751,8 @@ ath_start(struct ifnet *ifp)
TAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list);
mtx_unlock(&sc->sc_txbuflock);
ifp->if_oerrors++;
+ if (ni && ni != ic->ic_bss)
+ ieee80211_free_node(ic, ni);
continue;
}
@@ -1112,12 +1106,13 @@ ath_beacon_proc(void *arg, int pending)
struct ath_hal *ah = sc->sc_ah;
DPRINTF2(("%s: pending %u\n", __func__, pending));
- if (ic->ic_opmode == IEEE80211_M_STA || bf == NULL || bf->bf_m == NULL) {
+ if (ic->ic_opmode == IEEE80211_M_STA ||
+ bf == NULL || bf->bf_m == NULL) {
DPRINTF(("%s: ic_flags=%x bf=%p bf_m=%p\n",
__func__, ic->ic_flags, bf, bf ? bf->bf_m : NULL));
return;
}
- /* update beacon to reflect PS poll state */
+ /* TODO: update beacon to reflect PS poll state */
if (!ath_hal_stoptxdma(ah, sc->sc_bhalq)) {
DPRINTF(("%s: beacon queue %u did not stop?",
__func__, sc->sc_bhalq));
@@ -1371,7 +1366,7 @@ ath_node_alloc(struct ieee80211com *ic)
{
struct ath_node *an =
malloc(sizeof(struct ath_node), M_DEVBUF, M_NOWAIT | M_ZERO);
- return an ? &an->st_node : NULL;
+ return an ? &an->an_node : NULL;
}
static void
@@ -1461,6 +1456,7 @@ ath_rx_proc(void *arg, int npending)
struct ath_desc *ds;
struct mbuf *m;
struct ieee80211_frame *wh, whbuf;
+ struct ieee80211_node *ni;
int len;
u_int phyerr;
HAL_STATUS status;
@@ -1505,6 +1501,7 @@ ath_rx_proc(void *arg, int npending)
len = ds->ds_rxstat.rs_datalen;
if (len < sizeof(struct ieee80211_frame)) {
DPRINTF(("ath_rx_proc: short packet %d\n", len));
+ sc->sc_stats.ast_rx_tooshort++;
goto rx_next;
}
@@ -1516,7 +1513,7 @@ ath_rx_proc(void *arg, int npending)
IEEE80211_FC0_TYPE_CTL &&
ic->ic_opmode != IEEE80211_M_MONITOR) {
/*
- * Ignore control frame received in promisc mode.
+ * Discard control frame when not in monitor mode.
*/
DPRINTF(("ath_rx_proc: control frame\n"));
sc->sc_stats.ast_rx_ctl++;
@@ -1533,6 +1530,7 @@ ath_rx_proc(void *arg, int npending)
IEEE80211_RATE_VAL,
ds->ds_rxstat.rs_rssi);
}
+
m_adj(m, -IEEE80211_CRC_LEN);
if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
/*
@@ -1548,10 +1546,35 @@ ath_rx_proc(void *arg, int npending)
*/
m_adj(m, -IEEE80211_WEP_CRCLEN);
}
- ieee80211_input(ifp, m,
- ds->ds_rxstat.rs_rssi,
- ds->ds_rxstat.rs_tstamp,
- ds->ds_rxstat.rs_antenna);
+
+ /*
+ * Locate the node for sender, track state, and
+ * then pass this node (referenced) up to the 802.11
+ * layer for its use. We are required to pass
+ * something so we fall back to ic_bss when this frame
+ * is from an unknown sender.
+ */
+ if (ic->ic_opmode != IEEE80211_M_STA) {
+ ni = ieee80211_find_node(ic, wh->i_addr2);
+ if (ni == NULL)
+ ni = ieee80211_ref_node(ic->ic_bss);
+ } else
+ ni = ieee80211_ref_node(ic->ic_bss);
+ ATH_NODE(ni)->an_rx_antenna = ds->ds_rxstat.rs_antenna;
+ /*
+ * Send frame up for processing.
+ */
+ ieee80211_input(ifp, m, ni,
+ ds->ds_rxstat.rs_rssi, ds->ds_rxstat.rs_tstamp);
+ /*
+ * The frame may have caused the node to be marked for
+ * reclamation (e.g. in response to a DEAUTH message)
+ * so use free_node here instead of unref_node.
+ */
+ if (ni == ic->ic_bss)
+ ieee80211_unref_node(&ni);
+ else
+ ieee80211_free_node(ic, ni);
rx_next:
TAILQ_INSERT_TAIL(&sc->sc_rxbuf, bf, bf_list);
} while (ath_rxbuf_init(sc, bf) == 0);
@@ -1681,7 +1704,7 @@ ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf
DPRINTF2(("ath_tx_start: m %p len %u\n", m0, pktlen));
bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREWRITE);
bf->bf_m = m0;
- bf->bf_node = ni;
+ bf->bf_node = ni; /* NB: held reference */
/* setup descriptors */
ds = bf->bf_desc;
@@ -1792,7 +1815,7 @@ ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf
if (an->an_tx_antenna)
antenna = an->an_tx_antenna;
else
- antenna = ni->ni_rantenna;
+ antenna = an->an_rx_antenna;
/*
* Formulate first tx descriptor with tx controls.
@@ -1865,7 +1888,8 @@ ath_tx_proc(void *arg, int npending)
struct ath_softc *sc = arg;
struct ath_hal *ah = sc->sc_ah;
struct ath_buf *bf;
- struct ifnet *ifp = &sc->sc_ic.ic_if;
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = &ic->ic_if;
struct ath_desc *ds;
struct ieee80211_node *ni;
struct ath_node *an;
@@ -1920,6 +1944,15 @@ ath_tx_proc(void *arg, int npending)
sc->sc_stats.ast_tx_longretry += lr;
if (sr + lr)
an->an_tx_retr++;
+ /*
+ * Reclaim reference to node.
+ *
+ * NB: the node may be reclaimed here if, for example
+ * this is a DEAUTH message that was sent and the
+ * node was timed out due to inactivity.
+ */
+ if (ni != ic->ic_bss)
+ ieee80211_free_node(ic, ni);
}
bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap,
BUS_DMASYNC_POSTWRITE);
@@ -2093,11 +2126,6 @@ ath_chan_set(struct ath_softc *sc, struct ieee80211_channel *chan)
}
/*
- * Re-enable interrupts.
- */
- ath_hal_intrset(ah, sc->sc_imask);
-
- /*
* Change channels and update the h/w rate map
* if we're switching; e.g. 11a to 11b/g.
*/
@@ -2105,6 +2133,11 @@ ath_chan_set(struct ath_softc *sc, struct ieee80211_channel *chan)
mode = ieee80211_chan2mode(ic, chan);
if (mode != sc->sc_curmode)
ath_setcurmode(sc, mode);
+
+ /*
+ * Re-enable interrupts.
+ */
+ ath_hal_intrset(ah, sc->sc_imask);
}
return 0;
}
diff --git a/sys/dev/ath/if_athvar.h b/sys/dev/ath/if_athvar.h
index dac204a..378a0ba 100644
--- a/sys/dev/ath/if_athvar.h
+++ b/sys/dev/ath/if_athvar.h
@@ -55,13 +55,15 @@
/* driver-specific node */
struct ath_node {
- struct ieee80211_node st_node; /* base class */
+ struct ieee80211_node an_node; /* base class */
u_int an_tx_ok; /* tx ok pkt */
u_int an_tx_err; /* tx !ok pkt */
u_int an_tx_retr; /* tx retry count */
int an_tx_upper; /* tx upper rate req cnt */
u_int an_tx_antenna; /* antenna for last good frame */
+ u_int an_rx_antenna; /* antenna for last rcvd frame */
};
+#define ATH_NODE(_n) ((struct ath_node *)(_n))
struct ath_buf {
TAILQ_ENTRY(ath_buf) bf_list;
diff --git a/sys/dev/wi/if_wi.c b/sys/dev/wi/if_wi.c
index 91e68b1..2fd33d5 100644
--- a/sys/dev/wi/if_wi.c
+++ b/sys/dev/wi/if_wi.c
@@ -805,10 +805,11 @@ wi_start(struct ifnet *ifp)
{
struct wi_softc *sc = ifp->if_softc;
struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211_node *ni;
struct ieee80211_frame *wh;
struct mbuf *m0;
struct wi_frame frmhdr;
- int cur, fid, off;
+ int cur, fid, off, error;
WI_LOCK_DECL();
WI_LOCK(sc);
@@ -832,6 +833,18 @@ wi_start(struct ifnet *ifp)
break;
}
IF_DEQUEUE(&ic->ic_mgtq, m0);
+ /*
+ * Hack! The referenced node pointer is in the
+ * rcvif field of the packet header. This is
+ * placed there by ieee80211_mgmt_output because
+ * we need to hold the reference with the frame
+ * and there's no other way (other than packet
+ * tags which we consider too expensive to use)
+ * to pass it along.
+ */
+ ni = (struct ieee80211_node *) m0->m_pkthdr.rcvif;
+ m0->m_pkthdr.rcvif = NULL;
+
m_copydata(m0, 4, ETHER_ADDR_LEN * 2,
(caddr_t)&frmhdr.wi_ehdr);
frmhdr.wi_ehdr.ether_type = 0;
@@ -854,26 +867,12 @@ wi_start(struct ifnet *ifp)
BPF_MTAP(ifp, m0);
#endif
- if ((m0 = ieee80211_encap(ifp, m0)) == NULL) {
+ m0 = ieee80211_encap(ifp, m0, &ni);
+ if (m0 == NULL) {
ifp->if_oerrors++;
continue;
}
wh = mtod(m0, struct ieee80211_frame *);
- if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
- !IEEE80211_IS_MULTICAST(wh->i_addr1) &&
- (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
- IEEE80211_FC0_TYPE_DATA) {
- struct ieee80211_node *ni =
- ieee80211_find_node(ic, wh->i_addr1);
- int err = (ni == NULL || ni->ni_associd == 0);
- if (ni != NULL)
- ieee80211_unref_node(&ni);
- if (err) {
- m_freem(m0);
- ifp->if_oerrors++;
- continue;
- }
- }
if (ic->ic_flags & IEEE80211_F_WEPON)
wh->i_fc[1] |= IEEE80211_FC1_WEP;
@@ -887,6 +886,8 @@ wi_start(struct ifnet *ifp)
(wh->i_fc[1] & IEEE80211_FC1_WEP)) {
if ((m0 = ieee80211_wep_crypt(ifp, m0, 1)) == NULL) {
ifp->if_oerrors++;
+ if (ni && ni != ic->ic_bss)
+ ieee80211_free_node(ic, ni);
continue;
}
frmhdr.wi_tx_ctl |= htole16(WI_TXCNTL_NOCRYPT);
@@ -915,13 +916,15 @@ wi_start(struct ifnet *ifp)
wi_dump_pkt(&frmhdr, NULL, -1);
fid = sc->sc_txd[cur].d_fid;
off = sizeof(frmhdr);
- if (wi_write_bap(sc, fid, 0, &frmhdr, sizeof(frmhdr)) != 0 ||
- wi_mwrite_bap(sc, fid, off, m0, m0->m_pkthdr.len) != 0) {
+ error = wi_write_bap(sc, fid, 0, &frmhdr, sizeof(frmhdr)) != 0
+ || wi_mwrite_bap(sc, fid, off, m0, m0->m_pkthdr.len) != 0;
+ m_freem(m0);
+ if (ni && ni != ic->ic_bss)
+ ieee80211_free_node(ic, ni);
+ if (error) {
ifp->if_oerrors++;
- m_freem(m0);
continue;
}
- m_freem(m0);
sc->sc_txd[cur].d_len = off;
if (sc->sc_txcur == cur) {
if (wi_cmd(sc, WI_CMD_TX | WI_RECLAIM, fid, 0, 0)) {
@@ -1355,6 +1358,7 @@ wi_rx_intr(struct wi_softc *sc)
struct wi_frame frmhdr;
struct mbuf *m;
struct ieee80211_frame *wh;
+ struct ieee80211_node *ni;
int fid, len, off, rssi;
u_int8_t dir;
u_int16_t status;
@@ -1471,7 +1475,32 @@ wi_rx_intr(struct wi_softc *sc)
if (ic->ic_opmode == IEEE80211_M_IBSS && dir == IEEE80211_FC1_DIR_NODS)
wi_sync_bssid(sc, wh->i_addr3);
- ieee80211_input(ifp, m, rssi, rstamp, 0);
+ /*
+ * Locate the node for sender, track state, and
+ * then pass this node (referenced) up to the 802.11
+ * layer for its use. We are required to pass
+ * something so we fallback to ic_bss when this frame
+ * is from an unknown sender.
+ */
+ if (ic->ic_opmode != IEEE80211_M_STA) {
+ ni = ieee80211_find_node(ic, wh->i_addr2);
+ if (ni == NULL)
+ ni = ieee80211_ref_node(ic->ic_bss);
+ } else
+ ni = ieee80211_ref_node(ic->ic_bss);
+ /*
+ * Send frame up for processing.
+ */
+ ieee80211_input(ifp, m, ni, rssi, rstamp);
+ /*
+ * The frame may have caused the node to be marked for
+ * reclamation (e.g. in response to a DEAUTH message)
+ * so use free_node here instead of unref_node.
+ */
+ if (ni == ic->ic_bss)
+ ieee80211_unref_node(&ni);
+ else
+ ieee80211_free_node(ic, ni);
}
static void
diff --git a/sys/net80211/ieee80211_input.c b/sys/net80211/ieee80211_input.c
index 542d455..003b425 100644
--- a/sys/net80211/ieee80211_input.c
+++ b/sys/net80211/ieee80211_input.c
@@ -66,12 +66,21 @@ __FBSDID("$FreeBSD$");
#include <netinet/if_ether.h>
#endif
+/*
+ * Process a received frame. The node associated with the sender
+ * should be supplied. If nothing was found in the node table then
+ * the caller is assumed to supply a reference to ic_bss instead.
+ * The RSSI and a timestamp are also supplied. The RSSI data is used
+ * during AP scanning to select a AP to associate with; it can have
+ * any units so long as values have consistent units and higher values
+ * mean ``better signal''. The receive timestamp is currently not used
+ * by the 802.11 layer.
+ */
void
-ieee80211_input(struct ifnet *ifp, struct mbuf *m,
- int rssi, u_int32_t rstamp, u_int rantenna)
+ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni,
+ int rssi, u_int32_t rstamp)
{
struct ieee80211com *ic = (void *)ifp;
- struct ieee80211_node *ni = NULL;
struct ieee80211_frame *wh;
struct ether_header *eh;
struct mbuf *m1;
@@ -80,6 +89,8 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m,
u_int8_t *bssid;
u_int16_t rxseq;
+ KASSERT(ni != NULL, ("null node"));
+
/* trim CRC here for WEP can find its own CRC at the end of packet. */
if (m->m_flags & M_HASFCS) {
m_adj(m, -IEEE80211_CRC_LEN);
@@ -92,6 +103,7 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m,
if (ifp->if_flags & IFF_DEBUG)
if_printf(ifp, "receive packet with wrong version: %x\n",
wh->i_fc[0]);
+ ieee80211_unref_node(&ni);
goto err;
}
@@ -100,13 +112,11 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m,
if (ic->ic_state != IEEE80211_S_SCAN) {
switch (ic->ic_opmode) {
case IEEE80211_M_STA:
- ni = ieee80211_ref_node(ic->ic_bss);
if (!IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_bssid)) {
IEEE80211_DPRINTF2(("%s: discard frame from "
"bss %s\n", __func__,
ether_sprintf(wh->i_addr2)));
/* not interested in */
- ieee80211_unref_node(&ni);
goto out;
}
break;
@@ -124,19 +134,6 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m,
__func__, ether_sprintf(wh->i_addr3)));
goto out;
}
- ni = ieee80211_find_node(ic, wh->i_addr2);
- if (ni == NULL) {
- IEEE80211_DPRINTF2(("%s: warning, unknown src "
- "%s\n", __func__,
- ether_sprintf(wh->i_addr2)));
- /*
- * NB: Node allocation is handled in the
- * management handling routines. Just fake
- * up a reference to the hosts's node to do
- * the stuff below.
- */
- ni = ieee80211_ref_node(ic->ic_bss);
- }
break;
case IEEE80211_M_MONITOR:
/* NB: this should collect everything */
@@ -147,7 +144,6 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m,
}
ni->ni_rssi = rssi;
ni->ni_rstamp = rstamp;
- ni->ni_rantenna = rantenna;
rxseq = ni->ni_rxseq;
ni->ni_rxseq =
le16toh(*(u_int16_t *)wh->i_seq) >> IEEE80211_SEQ_SEQ_SHIFT;
@@ -155,11 +151,9 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m,
if ((wh->i_fc[1] & IEEE80211_FC1_RETRY) &&
rxseq == ni->ni_rxseq) {
/* duplicate, silently discarded */
- ieee80211_unref_node(&ni);
goto out;
}
ni->ni_inact = 0;
- ieee80211_unref_node(&ni);
}
switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) {
@@ -189,11 +183,11 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m,
if (dir != IEEE80211_FC1_DIR_TODS)
goto out;
/* check if source STA is associated */
- ni = ieee80211_find_node(ic, wh->i_addr2);
- if (ni == NULL) {
+ if (ni == ic->ic_bss) {
IEEE80211_DPRINTF(("%s: data from unknown src "
"%s\n", __func__,
ether_sprintf(wh->i_addr2)));
+ /* NB: caller deals with reference */
ni = ieee80211_dup_bss(ic, wh->i_addr2);
if (ni != NULL) {
IEEE80211_SEND_MGMT(ic, ni,
@@ -213,7 +207,6 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m,
ieee80211_unref_node(&ni);
goto err;
}
- ieee80211_unref_node(&ni);
break;
case IEEE80211_M_MONITOR:
break;
@@ -240,7 +233,7 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m,
if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
eh = mtod(m, struct ether_header *);
if (ETHER_IS_MULTICAST(eh->ether_dhost)) {
- m1 = m_copym(m, 0, M_COPYALL, M_DONTWAIT);
+ m1 = m_copypacket(m, M_DONTWAIT);
if (m1 == NULL)
ifp->if_oerrors++;
else
@@ -318,7 +311,7 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m,
}
if (ic->ic_rawbpf)
bpf_mtap(ic->ic_rawbpf, m);
- (*ic->ic_recv_mgmt)(ic, m, subtype, rssi, rstamp, rantenna);
+ (*ic->ic_recv_mgmt)(ic, m, ni, subtype, rssi, rstamp);
m_freem(m);
return;
@@ -499,13 +492,13 @@ ieee80211_setup_rates(struct ieee80211com *ic, struct ieee80211_node *ni,
} while (0)
void
-ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, int subtype,
- int rssi, u_int32_t rstamp, u_int rantenna)
+ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0,
+ struct ieee80211_node *ni,
+ int subtype, int rssi, u_int32_t rstamp)
{
#define ISPROBE(_st) ((_st) == IEEE80211_FC0_SUBTYPE_PROBE_RESP)
struct ifnet *ifp = &ic->ic_if;
struct ieee80211_frame *wh;
- struct ieee80211_node *ni;
u_int8_t *frm, *efrm;
u_int8_t *ssid, *rates, *xrates;
int reassoc, resp, newassoc, allocbs;
@@ -672,7 +665,6 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, int subtype,
IEEE80211_ADDR_COPY(ni->ni_bssid, wh->i_addr3);
ni->ni_rssi = rssi;
ni->ni_rstamp = rstamp;
- ni->ni_rantenna = rantenna;
memcpy(ni->ni_tstamp, tstamp, sizeof(ni->ni_tstamp));
ni->ni_intval = le16toh(*(u_int16_t *)bintval);
ni->ni_capinfo = le16toh(*(u_int16_t *)capinfo);
@@ -731,8 +723,7 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, int subtype,
return;
}
- ni = ieee80211_find_node(ic, wh->i_addr2);
- if (ni == NULL) {
+ if (ni == ic->ic_bss) {
ni = ieee80211_dup_bss(ic, wh->i_addr2);
if (ni == NULL)
return;
@@ -743,7 +734,6 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, int subtype,
allocbs = 0;
ni->ni_rssi = rssi;
ni->ni_rstamp = rstamp;
- ni->ni_rantenna = rantenna;
rate = ieee80211_setup_rates(ic, ni, rates, xrates,
IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE
| IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
@@ -751,13 +741,16 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, int subtype,
IEEE80211_DPRINTF(("%s: rate negotiation failed: %s\n",
__func__,ether_sprintf(wh->i_addr2)));
} else {
- IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_PROBE_RESP,
- 0);
+ IEEE80211_SEND_MGMT(ic, ni,
+ IEEE80211_FC0_SUBTYPE_PROBE_RESP, 0);
+ }
+ if (allocbs) {
+ /* XXX just use free? */
+ if (ic->ic_opmode == IEEE80211_M_HOSTAP)
+ ieee80211_free_node(ic, ni);
+ else
+ ieee80211_unref_node(&ni);
}
- if (allocbs && ic->ic_opmode == IEEE80211_M_HOSTAP)
- ieee80211_free_node(ic, ni);
- else
- ieee80211_unref_node(&ni);
break;
}
@@ -795,25 +788,23 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, int subtype,
case IEEE80211_M_HOSTAP:
if (ic->ic_state != IEEE80211_S_RUN || seq != 1)
return;
- allocbs = 0;
- ni = ieee80211_find_node(ic, wh->i_addr2);
- if (ni == NULL) {
+ if (ni == ic->ic_bss) {
ni = ieee80211_alloc_node(ic, wh->i_addr2);
if (ni == NULL)
return;
IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_bss->ni_bssid);
ni->ni_rssi = rssi;
ni->ni_rstamp = rstamp;
- ni->ni_rantenna = rantenna;
ni->ni_chan = ic->ic_bss->ni_chan;
allocbs = 1;
- }
- IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_AUTH, 2);
+ } else
+ allocbs = 0;
+ IEEE80211_SEND_MGMT(ic, ni,
+ IEEE80211_FC0_SUBTYPE_AUTH, 2);
if (ifp->if_flags & IFF_DEBUG)
if_printf(ifp, "station %s %s authenticated\n",
(allocbs ? "newly" : "already"),
ether_sprintf(ni->ni_macaddr));
- ieee80211_unref_node(&ni);
break;
case IEEE80211_M_STA:
@@ -824,11 +815,8 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, int subtype,
"authentication failed (reason %d) for %s\n",
status,
ether_sprintf(wh->i_addr3));
- ni = ieee80211_find_node(ic, wh->i_addr2);
- if (ni != NULL) {
+ if (ni != ic->ic_bss)
ni->ni_fails++;
- ieee80211_unref_node(&ni);
- }
return;
}
ieee80211_new_state(ic, IEEE80211_S_ASSOC,
@@ -902,16 +890,16 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, int subtype,
#endif
return;
}
- ni = ieee80211_find_node(ic, wh->i_addr2);
- if (ni == NULL) {
+ if (ni == ic->ic_bss) {
IEEE80211_DPRINTF(("%s: not authenticated for %s\n",
__func__, ether_sprintf(wh->i_addr2)));
ni = ieee80211_dup_bss(ic, wh->i_addr2);
- if (ni == NULL)
- return;
- IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
- IEEE80211_REASON_ASSOC_NOT_AUTHED);
- ieee80211_free_node(ic, ni);
+ if (ni != NULL) {
+ IEEE80211_SEND_MGMT(ic, ni,
+ IEEE80211_FC0_SUBTYPE_DEAUTH,
+ IEEE80211_REASON_ASSOC_NOT_AUTHED);
+ ieee80211_free_node(ic, ni);
+ }
return;
}
/* XXX per-node cipher suite */
@@ -924,8 +912,8 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, int subtype,
IEEE80211_DPRINTF(("%s: capability mismatch %x for %s\n",
__func__, capinfo, ether_sprintf(wh->i_addr2)));
ni->ni_associd = 0;
- IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_CAPINFO);
- ieee80211_unref_node(&ni);
+ IEEE80211_SEND_MGMT(ic, ni, resp,
+ IEEE80211_STATUS_CAPINFO);
return;
}
ieee80211_setup_rates(ic, ni, rates, xrates,
@@ -935,13 +923,12 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, int subtype,
IEEE80211_DPRINTF(("%s: rate unmatch for %s\n",
__func__, ether_sprintf(wh->i_addr2)));
ni->ni_associd = 0;
- IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_BASIC_RATE);
- ieee80211_unref_node(&ni);
+ IEEE80211_SEND_MGMT(ic, ni, resp,
+ IEEE80211_STATUS_BASIC_RATE);
return;
}
ni->ni_rssi = rssi;
ni->ni_rstamp = rstamp;
- ni->ni_rantenna = rantenna;
ni->ni_intval = bintval;
ni->ni_capinfo = capinfo;
ni->ni_chan = ic->ic_bss->ni_chan;
@@ -964,7 +951,6 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, int subtype,
/* give driver a chance to setup state like ni_txrate */
if (ic->ic_newassoc)
(*ic->ic_newassoc)(ic, ni, newassoc);
- ieee80211_unref_node(&ni);
break;
}
@@ -994,11 +980,8 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, int subtype,
if (status != 0) {
if_printf(ifp, "association failed (reason %d) for %s\n",
status, ether_sprintf(wh->i_addr3));
- ni = ieee80211_find_node(ic, wh->i_addr2);
- if (ni != NULL) {
+ if (ni != ic->ic_bss)
ni->ni_fails++;
- ieee80211_unref_node(&ni);
- }
return;
}
ni->ni_associd = le16toh(*(u_int16_t *)frm);
@@ -1041,13 +1024,13 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, int subtype,
wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
break;
case IEEE80211_M_HOSTAP:
- ni = ieee80211_find_node(ic, wh->i_addr2);
- if (ni != NULL) {
+ if (ni != ic->ic_bss) {
if (ifp->if_flags & IFF_DEBUG)
if_printf(ifp, "station %s deauthenticated"
" by peer (reason %d)\n",
ether_sprintf(ni->ni_macaddr), reason);
- ieee80211_free_node(ic, ni);
+ /* node will be free'd on return */
+ ieee80211_unref_node(&ni);
}
break;
default:
@@ -1070,14 +1053,13 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, int subtype,
wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
break;
case IEEE80211_M_HOSTAP:
- ni = ieee80211_find_node(ic, wh->i_addr2);
- if (ni != NULL) {
+ if (ni != ic->ic_bss) {
if (ifp->if_flags & IFF_DEBUG)
if_printf(ifp, "station %s disassociated"
" by peer (reason %d)\n",
ether_sprintf(ni->ni_macaddr), reason);
ni->ni_associd = 0;
- ieee80211_unref_node(&ni);
+ /* XXX node reclaimed how? */
}
break;
default:
diff --git a/sys/net80211/ieee80211_node.c b/sys/net80211/ieee80211_node.c
index dd2541e..5ea3466 100644
--- a/sys/net80211/ieee80211_node.c
+++ b/sys/net80211/ieee80211_node.c
@@ -216,7 +216,6 @@ ieee80211_create_ibss(struct ieee80211com* ic, struct ieee80211_channel *chan)
memcpy(ni->ni_essid, ic->ic_des_essid, ni->ni_esslen);
ni->ni_rssi = 0;
ni->ni_rstamp = 0;
- ni->ni_rantenna = 0;
memset(ni->ni_tstamp, 0, sizeof(ni->ni_tstamp));
ni->ni_intval = ic->ic_lintval;
ni->ni_capinfo = IEEE80211_CAPINFO_IBSS;
@@ -289,7 +288,7 @@ ieee80211_end_scan(struct ifnet *ifp)
}
selbs = NULL;
if (ifp->if_flags & IFF_DEBUG)
- if_printf(ifp, "\tmacaddr bssid chan rssi rate ant flag wep essid\n");
+ if_printf(ifp, "\tmacaddr bssid chan rssi rate flag wep essid\n");
for (; ni != NULL; ni = nextbs) {
ieee80211_ref_node(ni);
nextbs = TAILQ_NEXT(ni, ni_list);
@@ -344,7 +343,6 @@ ieee80211_end_scan(struct ifnet *ifp)
printf(" %+4d", ni->ni_rssi);
printf(" %2dM%c", (rate & IEEE80211_RATE_VAL) / 2,
fail & 0x08 ? '!' : ' ');
- printf(" %3d", ni->ni_rantenna);
printf(" %4s%c",
(ni->ni_capinfo & IEEE80211_CAPINFO_ESS) ? "ess" :
(ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) ? "ibss" :
@@ -497,6 +495,8 @@ ieee80211_lookup_node(struct ieee80211com *ic,
static void
_ieee80211_free_node(struct ieee80211com *ic, struct ieee80211_node *ni)
{
+ KASSERT(ni != ic->ic_bss, ("freeing bss node"));
+
TAILQ_REMOVE(&ic->ic_node, ni, ni_list);
LIST_REMOVE(ni, ni_hash);
if (TAILQ_EMPTY(&ic->ic_node))
@@ -507,6 +507,8 @@ _ieee80211_free_node(struct ieee80211com *ic, struct ieee80211_node *ni)
void
ieee80211_free_node(struct ieee80211com *ic, struct ieee80211_node *ni)
{
+ KASSERT(ni != ic->ic_bss, ("freeing ic_bss"));
+
/* XXX need equivalent of atomic_dec_and_test */
atomic_subtract_int(&ni->ni_refcnt, 1);
if (atomic_cmpset_int(&ni->ni_refcnt, 0, 1)) {
@@ -534,21 +536,22 @@ ieee80211_timeout_nodes(struct ieee80211com *ic)
mtx_lock(&ic->ic_nodelock);
for (ni = TAILQ_FIRST(&ic->ic_node); ni != NULL;) {
- if (++ni->ni_inact <= IEEE80211_INACT_MAX) {
- ni = TAILQ_NEXT(ni, ni_list);
- continue;
- }
- /* NB: don't honor reference count */
- IEEE80211_DPRINTF(("station %s timed out "
+ if (++ni->ni_inact > IEEE80211_INACT_MAX) {
+ IEEE80211_DPRINTF(("station %s timed out "
"due to inactivity (%u secs)\n",
ether_sprintf(ni->ni_macaddr),
ni->ni_inact));
- nextbs = TAILQ_NEXT(ni, ni_list);
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_DEAUTH,
- IEEE80211_REASON_AUTH_EXPIRE);
- _ieee80211_free_node(ic, ni);
- ni = nextbs;
+ nextbs = TAILQ_NEXT(ni, ni_list);
+ /*
+ * Send a deauthenticate frame.
+ */
+ IEEE80211_SEND_MGMT(ic, ni,
+ IEEE80211_FC0_SUBTYPE_DEAUTH,
+ IEEE80211_REASON_AUTH_EXPIRE);
+ ieee80211_free_node(ic, ni);
+ ni = nextbs;
+ } else
+ ni = TAILQ_NEXT(ni, ni_list);
}
if (!TAILQ_EMPTY(&ic->ic_node))
ic->ic_inact_timer = IEEE80211_INACT_WAIT;
diff --git a/sys/net80211/ieee80211_node.h b/sys/net80211/ieee80211_node.h
index d4d62de..de300fa 100644
--- a/sys/net80211/ieee80211_node.h
+++ b/sys/net80211/ieee80211_node.h
@@ -64,9 +64,8 @@ struct ieee80211_node {
u_int ni_refcnt;
/* hardware */
- u_int8_t ni_rssi; /* recv ssi */
u_int32_t ni_rstamp; /* recv timestamp */
- u_int8_t ni_rantenna; /* recv antenna */
+ u_int8_t ni_rssi; /* recv ssi */
/* header */
u_int8_t ni_macaddr[IEEE80211_ADDR_LEN];
diff --git a/sys/net80211/ieee80211_output.c b/sys/net80211/ieee80211_output.c
index 9b79d65..1fa87f2 100644
--- a/sys/net80211/ieee80211_output.c
+++ b/sys/net80211/ieee80211_output.c
@@ -66,20 +66,41 @@ __FBSDID("$FreeBSD$");
#include <netinet/if_ether.h>
#endif
-int
+/*
+ * Send a management frame to the specified node. The node pointer
+ * must have a reference as the pointer will be passed to the driver
+ * and potentially held for a long time. If the frame is successfully
+ * dispatched to the driver, then it is responsible for freeing the
+ * reference (and potentially free'ing up any associated storage).
+ */
+static int
ieee80211_mgmt_output(struct ifnet *ifp, struct ieee80211_node *ni,
struct mbuf *m, int type)
{
struct ieee80211com *ic = (void *)ifp;
struct ieee80211_frame *wh;
- /* XXX this probably shouldn't be permitted */
- KASSERT(ni != NULL, ("%s: null node", __func__));
+ KASSERT(ni != NULL, ("null node"));
ni->ni_inact = 0;
+ /*
+ * Yech, hack alert! We want to pass the node down to the
+ * driver's start routine. If we don't do so then the start
+ * routine must immediately look it up again and that can
+ * cause a lock order reversal if, for example, this frame
+ * is being sent because the station is being timedout and
+ * the frame being sent is a DEAUTH message. We could stick
+ * this in an m_tag and tack that on to the mbuf. However
+ * that's rather expensive to do for every frame so instead
+ * we stuff it in the rcvif field since outbound frames do
+ * not (presently) use this.
+ */
M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT);
if (m == NULL)
return ENOMEM;
+ KASSERT(m->m_pkthdr.rcvif == NULL, ("rcvif not null"));
+ m->m_pkthdr.rcvif = (void *)ni;
+
wh = mtod(m, struct ieee80211_frame *);
wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | type;
wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
@@ -106,31 +127,57 @@ ieee80211_mgmt_output(struct ifnet *ifp, struct ieee80211_node *ni,
ether_sprintf(ni->ni_macaddr),
ieee80211_chan2ieee(ic, ni->ni_chan));
}
+
IF_ENQUEUE(&ic->ic_mgtq, m);
ifp->if_timer = 1;
(*ifp->if_start)(ifp);
return 0;
}
+/*
+ * Encapsulate an outbound data frame. The mbuf chain is updated and
+ * a reference to the destination node is returned. If an error is
+ * encountered NULL is returned and the node reference will also be NULL.
+ *
+ * NB: The caller is responsible for free'ing a returned node reference.
+ * The convention is ic_bss is not reference counted; the caller must
+ * maintain that.
+ */
struct mbuf *
-ieee80211_encap(struct ifnet *ifp, struct mbuf *m)
+ieee80211_encap(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node **pni)
{
struct ieee80211com *ic = (void *)ifp;
struct ether_header eh;
struct ieee80211_frame *wh;
+ struct ieee80211_node *ni = NULL;
struct llc *llc;
- struct ieee80211_node *ni;
if (m->m_len < sizeof(struct ether_header)) {
m = m_pullup(m, sizeof(struct ether_header));
- if (m == NULL)
- return NULL;
+ if (m == NULL) {
+ /* XXX statistic */
+ goto bad;
+ }
}
memcpy(&eh, mtod(m, caddr_t), sizeof(struct ether_header));
- ni = ieee80211_find_node(ic, eh.ether_dhost);
- if (ni == NULL) /*ic_opmode?? XXX*/
- ni = ieee80211_ref_node(ic->ic_bss);
+ if (ic->ic_opmode != IEEE80211_M_STA) {
+ ni = ieee80211_find_node(ic, eh.ether_dhost);
+ if (ni == NULL) {
+ /*
+ * When not in station mode the
+ * destination address should always be
+ * in the node table unless this is a
+ * multicast/broadcast frame.
+ */
+ if (!IEEE80211_IS_MULTICAST(eh.ether_dhost)) {
+ /* ic->ic_stats.st_tx_nonode++; XXX statistic */
+ goto bad;
+ }
+ ni = ic->ic_bss;
+ }
+ } else
+ ni = ic->ic_bss;
ni->ni_inact = 0;
m_adj(m, sizeof(struct ether_header) - sizeof(struct llc));
@@ -142,10 +189,8 @@ ieee80211_encap(struct ifnet *ifp, struct mbuf *m)
llc->llc_snap.org_code[2] = 0;
llc->llc_snap.ether_type = eh.ether_type;
M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT);
- if (m == NULL) {
- ieee80211_unref_node(&ni);
- return NULL;
- }
+ if (m == NULL)
+ goto bad;
wh = mtod(m, struct ieee80211_frame *);
wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_DATA;
*(u_int16_t *)wh->i_dur = 0;
@@ -173,11 +218,17 @@ ieee80211_encap(struct ifnet *ifp, struct mbuf *m)
IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_shost);
break;
case IEEE80211_M_MONITOR:
- m_freem(m), m = NULL;
- break;
+ goto bad;
}
- ieee80211_unref_node(&ni);
+ *pni = ni;
return m;
+bad:
+ if (m != NULL)
+ m_freem(m);
+ if (ni && ni != ic->ic_bss)
+ ieee80211_free_node(ic, ni);
+ *pni = NULL;
+ return NULL;
}
/*
@@ -240,10 +291,16 @@ ieee80211_getmbuf(int flags, int type, u_int pktlen)
return m;
}
+/*
+ * Send a management frame. The node is for the destination (or ic_bss
+ * when in station mode). Nodes other than ic_bss have their reference
+ * count bumped to reflect our use for an indeterminant time.
+ */
int
ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
int type, int arg)
{
+#define senderr(_x) do { ret = _x; goto bad; } while (0)
struct ifnet *ifp = &ic->ic_if;
struct mbuf *m;
u_int8_t *frm;
@@ -251,6 +308,15 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
u_int16_t capinfo;
int ret, timer;
+ 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.
+ */
+ if (ni != ic->ic_bss)
+ ieee80211_ref_node(ni);
timer = 0;
switch (type) {
case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
@@ -265,7 +331,7 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
+ 2 + IEEE80211_RATE_SIZE
+ 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE));
if (m == NULL)
- return ENOMEM;
+ senderr(ENOMEM);
m->m_data += sizeof(struct ieee80211_frame);
frm = mtod(m, u_int8_t *);
frm = ieee80211_add_ssid(frm, ic->ic_des_essid, ic->ic_des_esslen);
@@ -295,7 +361,7 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
+ 6
+ 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE));
if (m == NULL)
- return ENOMEM;
+ senderr(ENOMEM);
m->m_data += sizeof(struct ieee80211_frame);
frm = mtod(m, u_int8_t *);
@@ -336,7 +402,7 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
case IEEE80211_FC0_SUBTYPE_AUTH:
MGETHDR(m, M_DONTWAIT, MT_DATA);
if (m == NULL)
- return ENOMEM;
+ senderr(ENOMEM);
MH_ALIGN(m, 2 * 3);
m->m_pkthdr.len = m->m_len = 6;
frm = mtod(m, u_int8_t *);
@@ -354,7 +420,7 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
ether_sprintf(ni->ni_macaddr), arg);
MGETHDR(m, M_DONTWAIT, MT_DATA);
if (m == NULL)
- return ENOMEM;
+ senderr(ENOMEM);
MH_ALIGN(m, 2);
m->m_pkthdr.len = m->m_len = 2;
*mtod(m, u_int16_t *) = htole16(arg); /* reason */
@@ -379,7 +445,7 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
+ 2 + IEEE80211_RATE_SIZE
+ 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE));
if (m == NULL)
- return ENOMEM;
+ senderr(ENOMEM);
m->m_data += sizeof(struct ieee80211_frame);
frm = mtod(m, u_int8_t *);
@@ -430,7 +496,7 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
+ 2 + IEEE80211_RATE_SIZE
+ 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE));
if (m == NULL)
- return ENOMEM;
+ senderr(ENOMEM);
m->m_data += sizeof(struct ieee80211_frame);
frm = mtod(m, u_int8_t *);
@@ -443,19 +509,12 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
*(u_int16_t *)frm = htole16(arg); /* status */
frm += 2;
- if (arg == IEEE80211_STATUS_SUCCESS && ni != NULL)
+ if (arg == IEEE80211_STATUS_SUCCESS)
*(u_int16_t *)frm = htole16(ni->ni_associd);
- else
- *(u_int16_t *)frm = htole16(0);
frm += 2;
- if (ni != NULL) {
- frm = ieee80211_add_rates(frm, &ni->ni_rates);
- frm = ieee80211_add_xrates(frm, &ni->ni_rates);
- } else {
- frm = ieee80211_add_rates(frm, &ic->ic_bss->ni_rates);
- frm = ieee80211_add_xrates(frm, &ic->ic_bss->ni_rates);
- }
+ frm = ieee80211_add_rates(frm, &ni->ni_rates);
+ frm = ieee80211_add_xrates(frm, &ni->ni_rates);
m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
break;
@@ -465,7 +524,7 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
ether_sprintf(ni->ni_macaddr), arg);
MGETHDR(m, M_DONTWAIT, MT_DATA);
if (m == NULL)
- return ENOMEM;
+ senderr(ENOMEM);
MH_ALIGN(m, 2);
m->m_pkthdr.len = m->m_len = 2;
*mtod(m, u_int16_t *) = htole16(arg); /* reason */
@@ -474,11 +533,19 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
default:
IEEE80211_DPRINTF(("%s: invalid mgmt frame type %u\n",
__func__, type));
- return EINVAL;
+ senderr(EINVAL);
+ /* NOTREACHED */
}
ret = ieee80211_mgmt_output(ifp, ni, m, type);
- if (ret == 0 && timer)
- ic->ic_mgt_timer = timer;
+ if (ret == 0) {
+ if (timer)
+ ic->ic_mgt_timer = timer;
+ } else {
+bad:
+ if (ni != ic->ic_bss) /* remove ref we added */
+ ieee80211_free_node(ic, ni);
+ }
return ret;
+#undef senderr
}
diff --git a/sys/net80211/ieee80211_proto.h b/sys/net80211/ieee80211_proto.h
index 81627ff..8c85003 100644
--- a/sys/net80211/ieee80211_proto.h
+++ b/sys/net80211/ieee80211_proto.h
@@ -55,15 +55,15 @@ extern const char *ieee80211_mgt_subtype_name[];
extern void ieee80211_proto_attach(struct ifnet *);
extern void ieee80211_proto_detach(struct ifnet *);
+struct ieee80211_node;
extern void ieee80211_input(struct ifnet *, struct mbuf *,
- int, u_int32_t, u_int);
-extern void ieee80211_recv_mgmt(struct ieee80211com *, struct mbuf *, int,
- int, u_int32_t, u_int);
+ struct ieee80211_node *, int, u_int32_t);
+extern void ieee80211_recv_mgmt(struct ieee80211com *, struct mbuf *,
+ struct ieee80211_node *, int, int, u_int32_t);
extern int ieee80211_send_mgmt(struct ieee80211com *, struct ieee80211_node *,
int, int);
-extern int ieee80211_mgmt_output(struct ifnet *, struct ieee80211_node *,
- struct mbuf *, int);
-extern struct mbuf *ieee80211_encap(struct ifnet *, struct mbuf *);
+extern struct mbuf *ieee80211_encap(struct ifnet *, struct mbuf *,
+ struct ieee80211_node **);
extern struct mbuf *ieee80211_decap(struct ifnet *, struct mbuf *);
extern u_int8_t *ieee80211_add_rates(u_int8_t *frm,
const struct ieee80211_rateset *);
diff --git a/sys/net80211/ieee80211_var.h b/sys/net80211/ieee80211_var.h
index af47773..187ab79 100644
--- a/sys/net80211/ieee80211_var.h
+++ b/sys/net80211/ieee80211_var.h
@@ -135,7 +135,8 @@ struct ieee80211_channel {
struct ieee80211com {
struct arpcom ic_ac;
void (*ic_recv_mgmt)(struct ieee80211com *,
- struct mbuf *, int, int, u_int32_t, u_int);
+ struct mbuf *, struct ieee80211_node *,
+ int, int, u_int32_t);
int (*ic_send_mgmt)(struct ieee80211com *,
struct ieee80211_node *, int, int);
int (*ic_newstate)(struct ieee80211com *,
OpenPOWER on IntegriCloud