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.)
This commit is contained in:
mycroft 2004-07-29 22:28:05 +00:00
parent 4c72b79518
commit 88a8480ea3
3 changed files with 67 additions and 74 deletions

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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 *);