IEEE80211_DPRINTF(("%s: capability mismatch %x for %s\n", __func__, capinfo, ether_sprintf(wh->i_addr2))); IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap); ni->ni_associd = 0;
IEEE80211_DPRINTF(("%s: rate mismatch for %s\n", __func__, ether_sprintf(wh->i_addr2)));
ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0,	struct ieee80211_node *ni,	int subtype, int rssi, u_int32_t rstamp){	struct ifnet *ifp = &ic->ic_if;	struct ieee80211_frame *wh;	u_int8_t *frm, *efrm;	u_int8_t *ssid, *rates, *xrates;	int reassoc, resp, newassoc, allocbs;	wh = mtod(m0, struct ieee80211_frame *);	frm = (u_int8_t *)&wh[1];	efrm = mtod(m0, u_int8_t *) + m0->m_len;	switch (subtype) {	case IEEE80211_FC0_SUBTYPE_PROBE_RESP:	case IEEE80211_FC0_SUBTYPE_BEACON: {		u_int8_t *tstamp, *bintval, *capinfo, *country;		u_int8_t chan, bchan, fhindex, erp;		u_int16_t fhdwell;		int isprobe;		if (ic->ic_opmode != IEEE80211_M_IBSS &&		    ic->ic_state != IEEE80211_S_SCAN) {			/* XXX: may be useful for background scan */			return;		}		isprobe = (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP);		/*		 * beacon/probe response frame format		 *	[8] time stamp		 *	[2] beacon interval		 *	[2] capability information		 *	[tlv] ssid		 *	[tlv] supported rates		 *	[tlv] country information		 *	[tlv] parameter set (FH/DS)		 *	[tlv] erp information		 *	[tlv] extended supported rates		 */		IEEE80211_VERIFY_LENGTH(efrm - frm, 12);		tstamp  = frm;	frm += 8;		bintval = frm;	frm += 2;		capinfo = frm;	frm += 2;		ssid = rates = xrates = country = NULL;		bchan = ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan);		chan = bchan;		fhdwell = 0;		fhindex = 0;		erp = 0;		while (frm < efrm) {			switch (*frm) {			case IEEE80211_ELEMID_SSID:				ssid = frm;				break;			case IEEE80211_ELEMID_RATES:				rates = frm;				break;			case IEEE80211_ELEMID_COUNTRY:				country = frm;				break;			case IEEE80211_ELEMID_FHPARMS:				if (ic->ic_phytype == IEEE80211_T_FH) {					fhdwell = (frm[3] << 8) | frm[2];					chan = IEEE80211_FH_CHAN(frm[4], frm[5]);					fhindex = frm[6];				}				break;			case IEEE80211_ELEMID_DSPARMS:				/*				 * XXX hack this since depending on phytype				 * is problematic for multi-mode devices.				 */				if (ic->ic_phytype != IEEE80211_T_FH)					chan = frm[2];				break;			case IEEE80211_ELEMID_TIM:				break;			case IEEE80211_ELEMID_IBSSPARMS:				break;			case IEEE80211_ELEMID_XRATES:				xrates = frm;				break;			case IEEE80211_ELEMID_ERP:				if (frm[1] != 1) {					IEEE80211_DPRINTF(("%s: invalid ERP "						"element; length %u, expecting "						"1\n", __func__, frm[1]));					ic->ic_stats.is_rx_elem_toobig++;					break;				}				erp = frm[2];				break;			default:				IEEE80211_DPRINTF2(("%s: element id %u/len %u "					"ignored\n", __func__, *frm, frm[1]));				ic->ic_stats.is_rx_elem_unknown++;				break;			}			frm += frm[1] + 2;		}		IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE);		IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN);		if (#if IEEE80211_CHAN_MAX < 255		    chan > IEEE80211_CHAN_MAX ||#endif		    isclr(ic->ic_chan_active, chan)) {			IEEE80211_DPRINTF(("%s: ignore %s with invalid channel "				"%u\n", __func__,				isprobe ? "probe response" : "beacon",				chan));			ic->ic_stats.is_rx_badchan++;			return;		}		if (!(ic->ic_caps & IEEE80211_C_SCANALL) && 		    (chan != bchan && ic->ic_phytype != IEEE80211_T_FH)) {			/*			 * Frame was received on a channel different from the			 * one indicated in the DS params element id;			 * silently discard it.			 *			 * NB: this can happen due to signal leakage.			 *     But we should take it for FH phy because			 *     the rssi value should be correct even for			 *     different hop pattern in FH.			 */			IEEE80211_DPRINTF(("%s: ignore %s on channel %u marked "				"for channel %u\n", __func__,				isprobe ? "probe response" : "beacon",				bchan, chan));			ic->ic_stats.is_rx_chanmismatch++;			return;		}		/*		 * Use mac and channel for lookup so we collect all		 * potential AP's when scanning.  Otherwise we may		 * see the same AP on multiple channels and will only		 * record the last one.  We could filter APs here based		 * on rssi, etc. but leave that to the end of the scan		 * so we can keep the selection criteria in one spot.		 * This may result in a bloat of the scanned AP list but		 * it shouldn't be too much.		 */		ni = ieee80211_lookup_node(ic, wh->i_addr2,				&ic->ic_channels[chan]);#ifdef IEEE80211_DEBUG		if (ieee80211_debug &&		    (ni == NULL || ic->ic_state == IEEE80211_S_SCAN)) {			printf("%s: %s%s on chan %u (bss chan %u) ",			    __func__, (ni == NULL ? "new " : ""),			    isprobe ? "probe response" : "beacon",			    chan, bchan);			ieee80211_print_essid(ssid + 2, ssid[1]);			printf(" from %s\n", ether_sprintf(wh->i_addr2));			printf("%s: caps 0x%x bintval %u erp 0x%x\n",				__func__, letoh16(*(u_int16_t *)capinfo),				letoh16(*(u_int16_t *)bintval), erp);			if (country) {				int i;				printf("%s: country info", __func__);				for (i = 0; i < country[1]; i++)					printf(" %02x", country[i+2]);				printf("\n");			}		}#endif		if (ni == NULL) {			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 || ni->ni_esslen == 0)) {			/*			 * Update ESSID at probe response to adopt hidden AP by			 * Lucent/Cisco, which announces null ESSID in beacon.			 */			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;		memcpy(ni->ni_tstamp, tstamp, sizeof(ni->ni_tstamp));		ni->ni_intval = letoh16(*(u_int16_t *)bintval);		ni->ni_capinfo = letoh16(*(u_int16_t *)capinfo);		/* XXX validate channel # */		ni->ni_chan = &ic->ic_channels[chan];		ni->ni_fhdwell = fhdwell;		ni->ni_fhindex = fhindex;		ni->ni_erp = erp;		/* NB: must be after ni_chan is setup */		ieee80211_setup_rates(ic, ni, rates, xrates, IEEE80211_F_DOSORT);		/*		 * When scanning we record results (nodes) with a zero		 * refcnt.  Otherwise we want to hold the reference for		 * ibss neighbors so the nodes don't get released prematurely.		 * 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) {			/*			 * Fake an association so the driver can setup it's			 * private state.  The rate set has been setup above;			 * there is no handshake as in ap/station operation.			 */			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);		}		break;	}	case IEEE80211_FC0_SUBTYPE_PROBE_REQ: {		u_int8_t rate;		if (ic->ic_opmode == IEEE80211_M_STA)			return;		if (ic->ic_state != IEEE80211_S_RUN)			return;		/*		 * prreq frame format		 *	[tlv] ssid		 *	[tlv] supported rates		 *	[tlv] extended supported rates		 */		ssid = rates = xrates = NULL;		while (frm < efrm) {			switch (*frm) {			case IEEE80211_ELEMID_SSID:				ssid = frm;				break;			case IEEE80211_ELEMID_RATES:				rates = frm;				break;			case IEEE80211_ELEMID_XRATES:				xrates = frm;				break;			}			frm += frm[1] + 2;		}		IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE);		IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN);		if (ssid[1] != 0 &&		    (ssid[1] != ic->ic_bss->ni_esslen ||		    memcmp(ssid + 2, ic->ic_bss->ni_essid, ic->ic_bss->ni_esslen) != 0)) {#ifdef IEEE80211_DEBUG			if (ieee80211_debug) {				printf("%s: ssid mismatch ", __func__);				ieee80211_print_essid(ssid + 2, ssid[1]);				printf(" from %s\n", ether_sprintf(wh->i_addr2));			}#endif			ic->ic_stats.is_rx_ssidmismatch++;			return;		}		if (ni == ic->ic_bss) {			ni = ieee80211_dup_bss(ic, wh->i_addr2);			if (ni == NULL)				return;			IEEE80211_DPRINTF(("%s: new req from %s\n",				__func__, ether_sprintf(wh->i_addr2)));			allocbs = 1;		} else			allocbs = 0;		ni->ni_rssi = rssi;		ni->ni_rstamp = rstamp;		rate = ieee80211_setup_rates(ic, ni, rates, xrates,				IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE				| IEEE80211_F_DONEGO | IEEE80211_F_DODEL);		if (rate & IEEE80211_RATE_BASIC) {			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);		}		if (allocbs)			ieee80211_free_node(ic, ni);		break;	}	case IEEE80211_FC0_SUBTYPE_AUTH: {		u_int16_t algo, seq, status;		/*		 * auth frame format		 *	[2] algorithm		 *	[2] sequence		 *	[2] status		 *	[tlv*] challenge		 */		IEEE80211_VERIFY_LENGTH(efrm - frm, 6);		algo   = letoh16(*(u_int16_t *)frm);		seq    = letoh16(*(u_int16_t *)(frm + 2));		status = letoh16(*(u_int16_t *)(frm + 4));		IEEE80211_DPRINTF(("%s: auth %d seq %d from %s\n",		    __func__, algo, seq, ether_sprintf(wh->i_addr2)));		if (algo == IEEE80211_AUTH_ALG_SHARED)			ieee80211_auth_shared(ic, wh, frm + 6, efrm, ni, rssi,			    rstamp, seq, status);		else if (algo == IEEE80211_AUTH_ALG_OPEN)			ieee80211_auth_open(ic, wh, ni, rssi, rstamp, seq,			    status);		else {			IEEE80211_DPRINTF(("%s: unsupported authentication "				"algorithm %d from %s\n",				__func__, algo, ether_sprintf(wh->i_addr2)));			ic->ic_stats.is_rx_auth_unsupported++;			return;		}		break;	}	case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:	case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: {		u_int16_t capinfo, bintval;		if (ic->ic_opmode != IEEE80211_M_HOSTAP ||		    (ic->ic_state != IEEE80211_S_RUN))			return;		if (subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) {			reassoc = 1;			resp = IEEE80211_FC0_SUBTYPE_REASSOC_RESP;		} else {			reassoc = 0;			resp = IEEE80211_FC0_SUBTYPE_ASSOC_RESP;		}		/*		 * asreq frame format		 *	[2] capability information		 *	[2] listen interval		 *	[6*] current AP address (reassoc only)		 *	[tlv] ssid		 *	[tlv] supported rates		 *	[tlv] extended supported rates		 */		IEEE80211_VERIFY_LENGTH(efrm - frm, (reassoc ? 10 : 4));		if (!IEEE80211_ADDR_EQ(wh->i_addr3, ic->ic_bss->ni_bssid)) {			IEEE80211_DPRINTF(("%s: ignore other bss from %s\n",				__func__, ether_sprintf(wh->i_addr2)));			ic->ic_stats.is_rx_assoc_bss++;			return;		}		capinfo = letoh16(*(u_int16_t *)frm);	frm += 2;		bintval = letoh16(*(u_int16_t *)frm);	frm += 2;		if (reassoc)			frm += 6;	/* ignore current AP info */		ssid = rates = xrates = NULL;		while (frm < efrm) {			switch (*frm) {			case IEEE80211_ELEMID_SSID:				ssid = frm;				break;			case IEEE80211_ELEMID_RATES:				rates = frm;				break;			case IEEE80211_ELEMID_XRATES:				xrates = frm;				break;			}			frm += frm[1] + 2;		}		IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE);		IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN);		if (ssid[1] != ic->ic_bss->ni_esslen ||		    memcmp(ssid + 2, ic->ic_bss->ni_essid, ssid[1]) != 0) {#ifdef IEEE80211_DEBUG			if (ieee80211_debug) {				printf("%s: ssid mismatch ", __func__);				ieee80211_print_essid(ssid + 2, ssid[1]);				printf(" from %s\n", ether_sprintf(wh->i_addr2));			}#endif			ic->ic_stats.is_rx_ssidmismatch++;			return;		}		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) {				IEEE80211_SEND_MGMT(ic, ni,				    IEEE80211_FC0_SUBTYPE_DEAUTH,				    IEEE80211_REASON_ASSOC_NOT_AUTHED);				ieee80211_free_node(ic, ni);			}			ic->ic_stats.is_rx_assoc_notauth++;			return;		}		/* discard challenge after association */		if (ni->ni_challenge != NULL) {			FREE(ni->ni_challenge, M_DEVBUF);			ni->ni_challenge = NULL;		}		/* XXX per-node cipher suite */		/* XXX some stations use the privacy bit for handling APs		       that suport both encrypted and unencrypted traffic */		if ((capinfo & IEEE80211_CAPINFO_ESS) == 0 ||		    (capinfo & IEEE80211_CAPINFO_PRIVACY) !=		    ((ic->ic_flags & IEEE80211_F_WEPON) ?		     IEEE80211_CAPINFO_PRIVACY : 0)) {			IEEE80211_DPRINTF(("%s: capability mismatch %x for %s\n",				__func__, capinfo, ether_sprintf(wh->i_addr2)));			IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap);			ni->ni_associd = 0;			IEEE80211_SEND_MGMT(ic, ni, resp,				IEEE80211_STATUS_CAPINFO);			ic->ic_stats.is_rx_assoc_capmismatch++;			return;		}		ieee80211_setup_rates(ic, ni, rates, xrates,				IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE |				IEEE80211_F_DONEGO | IEEE80211_F_DODEL);		if (ni->ni_rates.rs_nrates == 0) {			IEEE80211_DPRINTF(("%s: rate mismatch for %s\n",				__func__, ether_sprintf(wh->i_addr2)));			IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap);			ni->ni_associd = 0;			IEEE80211_SEND_MGMT(ic, ni, resp,				IEEE80211_STATUS_BASIC_RATE);			ic->ic_stats.is_rx_assoc_norate++;			return;		}		ni->ni_rssi = rssi;		ni->ni_rstamp = rstamp;		ni->ni_intval = bintval;		ni->ni_capinfo = capinfo;		ni->ni_chan = ic->ic_bss->ni_chan;		ni->ni_fhdwell = ic->ic_bss->ni_fhdwell;		ni->ni_fhindex = ic->ic_bss->ni_fhindex;		if (ni->ni_associd == 0) {			u_int16_t aid;			/*			 * It would be clever to search the bitmap			 * more efficiently, but this will do for now.			 */			for (aid = 1; aid < ic->ic_max_aid; aid++) {				if (!IEEE80211_AID_ISSET(aid,				    ic->ic_aid_bitmap))					break;			}			if (ic->ic_bss->ni_associd >= ic->ic_max_aid) {				IEEE80211_SEND_MGMT(ic, ni, resp,				    IEEE80211_REASON_ASSOC_TOOMANY);				return;			} else {				ni->ni_associd = aid | 0xc000;				IEEE80211_AID_SET(ni->ni_associd,				    ic->ic_aid_bitmap);				newassoc = 1;			}		} else			newassoc = 0;		/* XXX for 11g must turn off short slot time if long	           slot time sta associates */		IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_SUCCESS);		if (ifp->if_flags & IFF_DEBUG)			if_printf(ifp, "station %s %s associated at aid %d\n",			    (newassoc ? "newly" : "already"),			    ether_sprintf(ni->ni_macaddr),			    ni->ni_associd & ~0xc000);		/* give driver a chance to setup state like ni_txrate */		if (ic->ic_newassoc)			(*ic->ic_newassoc)(ic, ni, newassoc);		break;	}	case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:	case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: {		u_int16_t status;		if (ic->ic_opmode != IEEE80211_M_STA ||		    ic->ic_state != IEEE80211_S_ASSOC)			return;		/*		 * asresp frame format		 *	[2] capability information		 *	[2] status		 *	[2] association ID		 *	[tlv] supported rates		 *	[tlv] extended supported rates		 */		IEEE80211_VERIFY_LENGTH(efrm - frm, 6);		ni = ic->ic_bss;		ni->ni_capinfo = letoh16(*(u_int16_t *)frm);		frm += 2;		status = letoh16(*(u_int16_t *)frm);		frm += 2;		if (status != 0) {			if (ifp->if_flags & IFF_DEBUG)				if_printf(ifp,				    "association failed (reason %d) for %s\n",				    status, ether_sprintf(wh->i_addr3));			if (ni != ic->ic_bss)				ni->ni_fails++;			ic->ic_stats.is_rx_auth_fail++;			return;		}		ni->ni_associd = letoh16(*(u_int16_t *)frm);		frm += 2;		rates = xrates = NULL;		while (frm < efrm) {			switch (*frm) {			case IEEE80211_ELEMID_RATES:				rates = frm;				break;			case IEEE80211_ELEMID_XRATES:				xrates = frm;				break;			}			frm += frm[1] + 2;		}		IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE);		ieee80211_setup_rates(ic, ni, rates, xrates,				IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE |				IEEE80211_F_DONEGO | IEEE80211_F_DODEL);		if (ni->ni_rates.rs_nrates != 0)			ieee80211_new_state(ic, IEEE80211_S_RUN,				wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);		break;	}	case IEEE80211_FC0_SUBTYPE_DEAUTH: {		u_int16_t reason;		/*		 * deauth frame format		 *	[2] reason		 */		IEEE80211_VERIFY_LENGTH(efrm - frm, 2);		reason = letoh16(*(u_int16_t *)frm);		ic->ic_stats.is_rx_deauth++;		switch (ic->ic_opmode) {		case IEEE80211_M_STA:			ieee80211_new_state(ic, IEEE80211_S_AUTH,			    wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);			break;		case IEEE80211_M_HOSTAP:			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);				/* node will be free'd on return */				ieee80211_unref_node(&ni);			}			break;		default:			break;		}		break;	}	case IEEE80211_FC0_SUBTYPE_DISASSOC: {		u_int16_t reason;		/*		 * disassoc frame format		 *	[2] reason		 */		IEEE80211_VERIFY_LENGTH(efrm - frm, 2);		reason = letoh16(*(u_int16_t *)frm);		ic->ic_stats.is_rx_disassoc++;		switch (ic->ic_opmode) {		case IEEE80211_M_STA:			ieee80211_new_state(ic, IEEE80211_S_ASSOC,			    wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);			break;		case IEEE80211_M_HOSTAP:			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);				IEEE80211_AID_CLR(ni->ni_associd,				    ic->ic_aid_bitmap);				ni->ni_associd = 0;				/* XXX node reclaimed how? */			}			break;		default:			break;		}		break;	}	default:		IEEE80211_DPRINTF(("%s: mgmt frame with subtype 0x%x not "			"handled\n", __func__, subtype));		ic->ic_stats.is_rx_badsubtype++;		break;	}}