From 88a8480ea336abefb0cf67eb583cbb50a7c0eeab Mon Sep 17 00:00:00 2001 From: mycroft Date: Thu, 29 Jul 2004 22:28:05 +0000 Subject: [PATCH] Implement passive scanning for APs in station and host-AP mode: ieee80211_input(): * Do not discard management frames in station mode just because they have the wrong BSSID. * Do not discard beacons in station and host-AP mode when not scanning. * Some minor rearrangement. Update node statistics even if the packet is rejected. ieee80211_recv_mgmt(): * Accept probe responses and beacons in station and host-AP even when not scanning. * Do not immediately free the node created by a beacon or probe response. XXX Should I check BSSIDs more carefully in ieee80211_recv_mgmt() -- specifically for ASSOC_RESP, REASSOC_RESP, AUTH, DEAUTH and DISASSOC? Fix a problem with APs that advertise multiple SSIDs: Change the footprint of ieee80211_find_node_with_channel() to take a SSID as well, and, if not empty, compared it with the existing nodes. This causes us to allocate multiple nodes for the same AP. Without this we were only leaving one SSID in the node table, which might not be the desired one, and so the interface would fail to fully initialize. (Reported by he@ with a Cisco 350 AP.) --- sys/net80211/ieee80211_input.c | 123 +++++++++++++++------------------ sys/net80211/ieee80211_node.c | 12 ++-- sys/net80211/ieee80211_node.h | 6 +- 3 files changed, 67 insertions(+), 74 deletions(-) diff --git a/sys/net80211/ieee80211_input.c b/sys/net80211/ieee80211_input.c index d63368b9e58e..50801d0fb2bd 100644 --- a/sys/net80211/ieee80211_input.c +++ b/sys/net80211/ieee80211_input.c @@ -1,4 +1,4 @@ -/* $NetBSD: ieee80211_input.c,v 1.32 2004/07/28 08:12:49 dyoung Exp $ */ +/* $NetBSD: ieee80211_input.c,v 1.33 2004/07/29 22:28:05 mycroft Exp $ */ /*- * Copyright (c) 2001 Atsushi Onoe * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting @@ -35,7 +35,7 @@ #ifdef __FreeBSD__ __FBSDID("$FreeBSD: src/sys/net80211/ieee80211_input.c,v 1.20 2004/04/02 23:35:24 sam Exp $"); #else -__KERNEL_RCSID(0, "$NetBSD: ieee80211_input.c,v 1.32 2004/07/28 08:12:49 dyoung Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ieee80211_input.c,v 1.33 2004/07/29 22:28:05 mycroft Exp $"); #endif #include "opt_inet.h" @@ -135,7 +135,6 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni, struct mbuf *m1; int len; u_int8_t dir, type, subtype; - u_int8_t *bssid; u_int16_t rxseq; ALTQ_DECL(struct altq_pktattr pktattr;) @@ -181,43 +180,6 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni, goto out; } if (ic->ic_state != IEEE80211_S_SCAN) { - switch (ic->ic_opmode) { - case IEEE80211_M_STA: - if (!IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_bssid)) { - /* not interested in */ - IEEE80211_DPRINTF(ic, IEEE80211_MSG_INPUT, - ("%s: discard frame from " - "bss %s\n", __func__, - ether_sprintf(wh->i_addr2))); - ic->ic_stats.is_rx_wrongbss++; - goto out; - } - break; - case IEEE80211_M_IBSS: - case IEEE80211_M_AHDEMO: - case IEEE80211_M_HOSTAP: - if (dir == IEEE80211_FC1_DIR_NODS) - bssid = wh->i_addr3; - else - bssid = wh->i_addr1; - if (!IEEE80211_ADDR_EQ(bssid, ic->ic_bss->ni_bssid) && - !IEEE80211_ADDR_EQ(bssid, ifp->if_broadcastaddr) && - (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == - IEEE80211_FC0_TYPE_DATA) { - /* not interested in */ - IEEE80211_DPRINTF(ic, IEEE80211_MSG_INPUT, - ("%s: discard data frame from bss %s\n", - __func__, ether_sprintf(bssid))); - ic->ic_stats.is_rx_wrongbss++; - goto out; - } - break; - case IEEE80211_M_MONITOR: - goto out; - default: - /* XXX catch bad values */ - break; - } ni->ni_rssi = rssi; ni->ni_rstamp = rstamp; rxseq = ni->ni_rxseq; @@ -231,6 +193,8 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni, goto out; } ni->ni_inact = 0; + if (ic->ic_opmode == IEEE80211_M_MONITOR) + goto out; } if (ic->ic_set_tim != NULL && @@ -273,6 +237,15 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni, ic->ic_stats.is_rx_wrongdir++; goto out; } + if (ic->ic_state != IEEE80211_S_SCAN && + !IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_bssid)) { + /* Source address is not our BSS. */ + IEEE80211_DPRINTF(ic, IEEE80211_MSG_INPUT, + ("%s: discard frame from SA %s\n", + __func__, ether_sprintf(wh->i_addr2))); + ic->ic_stats.is_rx_wrongbss++; + goto out; + } if ((ifp->if_flags & IFF_SIMPLEX) && IEEE80211_IS_MULTICAST(wh->i_addr1) && IEEE80211_ADDR_EQ(wh->i_addr3, ic->ic_myaddr)) { @@ -292,12 +265,32 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni, ic->ic_stats.is_rx_wrongdir++; goto out; } + if (ic->ic_state != IEEE80211_S_SCAN && + !IEEE80211_ADDR_EQ(wh->i_addr3, ic->ic_bss->ni_bssid) && + !IEEE80211_ADDR_EQ(wh->i_addr3, ifp->if_broadcastaddr)) { + /* Destination is not our BSS or broadcast. */ + IEEE80211_DPRINTF(ic, IEEE80211_MSG_INPUT, + ("%s: discard data frame to DA %s\n", + __func__, ether_sprintf(wh->i_addr3))); + ic->ic_stats.is_rx_wrongbss++; + goto out; + } break; case IEEE80211_M_HOSTAP: if (dir != IEEE80211_FC1_DIR_TODS) { ic->ic_stats.is_rx_wrongdir++; goto out; } + if (ic->ic_state != IEEE80211_S_SCAN && + !IEEE80211_ADDR_EQ(wh->i_addr1, ic->ic_bss->ni_bssid) && + !IEEE80211_ADDR_EQ(wh->i_addr1, ifp->if_broadcastaddr)) { + /* BSS is not us or broadcast. */ + IEEE80211_DPRINTF(ic, IEEE80211_MSG_INPUT, + ("%s: discard data frame to BSS %s\n", + __func__, ether_sprintf(wh->i_addr1))); + ic->ic_stats.is_rx_wrongbss++; + goto out; + } /* check if source STA is associated */ if (ni == ic->ic_bss) { IEEE80211_DPRINTF(ic, IEEE80211_MSG_INPUT, @@ -421,12 +414,6 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni, ic->ic_stats.is_rx_mgtdiscard++; goto out; } - } else { - if (ic->ic_opmode != IEEE80211_M_IBSS && - subtype == IEEE80211_FC0_SUBTYPE_BEACON) { - ic->ic_stats.is_rx_mgtdiscard++; - goto out; - } } #ifdef IEEE80211_DEBUG @@ -969,12 +956,24 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, u_int8_t chan, bchan, fhindex, erp; u_int16_t fhdwell; - if (ic->ic_opmode != IEEE80211_M_IBSS && + /* + * We process beacon/probe response frames for: + * o station mode: to collect state + * updates such as 802.11g slot time and for passive + * scanning of APs + * o adhoc mode: to discover neighbors + * o hostap mode: for passive scanning of neighbor APs + * o when scanning + * Frames otherwise received are discarded. + */ + if (ic->ic_opmode != IEEE80211_M_STA && + ic->ic_opmode != IEEE80211_M_IBSS && + ic->ic_opmode != IEEE80211_M_HOSTAP && ic->ic_state != IEEE80211_S_SCAN) { /* XXX: may be useful for background scan */ + ic->ic_stats.is_rx_mgtdiscard++; return; } - /* * beacon/probe response frame format * [8] time stamp @@ -1096,8 +1095,8 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, * This may result in a bloat of the scanned AP list but * it shouldn't be too much. */ - ni = ieee80211_find_node_with_channel(ic, wh->i_addr2, - &ic->ic_channels[chan]); + ni = ieee80211_find_node_for_beacon(ic, wh->i_addr2, + &ic->ic_channels[chan], ssid); #ifdef IEEE80211_DEBUG if (ieee80211_debug && (ni == NULL || ic->ic_state == IEEE80211_S_SCAN)) { @@ -1123,12 +1122,10 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, ni = ieee80211_alloc_node(ic, wh->i_addr2); if (ni == NULL) return; - ni->ni_esslen = ssid[1]; - memset(ni->ni_essid, 0, sizeof(ni->ni_essid)); - memcpy(ni->ni_essid, ssid + 2, ssid[1]); allocbs = 1; - } else if (ssid[1] != 0 && - (ISPROBE(subtype) || ni->ni_esslen == 0)) { + } else + allocbs = 0; + if (ssid[1] != 0 && ni->ni_esslen == 0) { /* * Update ESSID at probe response to adopt hidden AP by * Lucent/Cisco, which announces null ESSID in beacon. @@ -1136,9 +1133,7 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, ni->ni_esslen = ssid[1]; memset(ni->ni_essid, 0, sizeof(ni->ni_essid)); memcpy(ni->ni_essid, ssid + 2, ssid[1]); - allocbs = 0; - } else - allocbs = 0; + } IEEE80211_ADDR_COPY(ni->ni_bssid, wh->i_addr3); ni->ni_rssi = rssi; ni->ni_rstamp = rstamp; @@ -1159,10 +1154,8 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, * Anything else can be discarded (XXX and should be handled * above so we don't do so much work). */ - if (ic->ic_state == IEEE80211_S_SCAN) - ieee80211_unref_node(&ni); /* NB: do not free */ - else if (ic->ic_opmode == IEEE80211_M_IBSS && - allocbs && ISPROBE(subtype)) { + if (ic->ic_opmode == IEEE80211_M_IBSS && allocbs && + ISPROBE(subtype)) { /* * Fake an association so the driver can setup it's * private state. The rate set has been setup above; @@ -1171,10 +1164,8 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, if (ic->ic_newassoc) (*ic->ic_newassoc)(ic, ni, 1); /* NB: hold reference */ - } else { - /* XXX optimize to avoid work done above */ - ieee80211_free_node(ic, ni); - } + } else + ieee80211_unref_node(&ni); /* NB: do not free */ break; } diff --git a/sys/net80211/ieee80211_node.c b/sys/net80211/ieee80211_node.c index 481fbc140829..0ca16da7d87e 100644 --- a/sys/net80211/ieee80211_node.c +++ b/sys/net80211/ieee80211_node.c @@ -1,4 +1,4 @@ -/* $NetBSD: ieee80211_node.c,v 1.29 2004/07/28 08:11:03 dyoung Exp $ */ +/* $NetBSD: ieee80211_node.c,v 1.30 2004/07/29 22:28:05 mycroft Exp $ */ /*- * Copyright (c) 2001 Atsushi Onoe * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting @@ -35,7 +35,7 @@ #ifdef __FreeBSD__ __FBSDID("$FreeBSD: src/sys/net80211/ieee80211_node.c,v 1.22 2004/04/05 04:15:55 sam Exp $"); #else -__KERNEL_RCSID(0, "$NetBSD: ieee80211_node.c,v 1.29 2004/07/28 08:11:03 dyoung Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ieee80211_node.c,v 1.30 2004/07/29 22:28:05 mycroft Exp $"); #endif #include "opt_inet.h" @@ -758,8 +758,8 @@ ieee80211_find_rxnode(struct ieee80211com *ic, struct ieee80211_frame *wh) * Like find but search based on the channel too. */ struct ieee80211_node * -ieee80211_find_node_with_channel(struct ieee80211com *ic, u_int8_t *macaddr, - struct ieee80211_channel *chan) +ieee80211_find_node_for_beacon(struct ieee80211com *ic, u_int8_t *macaddr, + struct ieee80211_channel *chan, char *ssid) { struct ieee80211_node *ni; int hash; @@ -768,7 +768,9 @@ ieee80211_find_node_with_channel(struct ieee80211com *ic, u_int8_t *macaddr, IEEE80211_NODE_LOCK(ic); LIST_FOREACH(ni, &ic->ic_hash[hash], ni_hash) { if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr) && - ni->ni_chan == chan) { + ni->ni_chan == chan && + (ssid[1] == 0 || (ssid[1] == ni->ni_esslen && + !memcmp(ssid + 2, ni->ni_essid, ssid[1])))) { ieee80211_node_incref(ni);/* mark referenced */ break; } diff --git a/sys/net80211/ieee80211_node.h b/sys/net80211/ieee80211_node.h index 412a43eef330..032b9e5e5ada 100644 --- a/sys/net80211/ieee80211_node.h +++ b/sys/net80211/ieee80211_node.h @@ -1,4 +1,4 @@ -/* $NetBSD: ieee80211_node.h,v 1.12 2004/07/23 10:15:13 mycroft Exp $ */ +/* $NetBSD: ieee80211_node.h,v 1.13 2004/07/29 22:28:05 mycroft Exp $ */ /*- * Copyright (c) 2001 Atsushi Onoe * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting @@ -184,9 +184,9 @@ extern struct ieee80211_node *ieee80211_find_rxnode(struct ieee80211com *, struct ieee80211_frame *); extern struct ieee80211_node *ieee80211_find_txnode(struct ieee80211com *, u_int8_t *); -extern struct ieee80211_node *ieee80211_find_node_with_channel( +extern struct ieee80211_node *ieee80211_find_node_for_beacon( struct ieee80211com *, u_int8_t *macaddr, - struct ieee80211_channel *); + struct ieee80211_channel *, char *ssid); extern void ieee80211_free_node(struct ieee80211com *, struct ieee80211_node *); extern void ieee80211_free_allnodes(struct ieee80211com *);