From owner-p4-projects@FreeBSD.ORG Mon Mar 17 18:24:40 2008 Return-Path: Delivered-To: p4-projects@freebsd.org Received: by hub.freebsd.org (Postfix, from userid 32767) id A1DF61065674; Mon, 17 Mar 2008 18:24:40 +0000 (UTC) Delivered-To: perforce@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 5FEF3106566B for ; Mon, 17 Mar 2008 18:24:40 +0000 (UTC) (envelope-from sam@freebsd.org) Received: from repoman.freebsd.org (repoman.freebsd.org [IPv6:2001:4f8:fff6::29]) by mx1.freebsd.org (Postfix) with ESMTP id 375548FC25 for ; Mon, 17 Mar 2008 18:24:40 +0000 (UTC) (envelope-from sam@freebsd.org) Received: from repoman.freebsd.org (localhost [127.0.0.1]) by repoman.freebsd.org (8.14.1/8.14.1) with ESMTP id m2HIOeLL052027 for ; Mon, 17 Mar 2008 18:24:40 GMT (envelope-from sam@freebsd.org) Received: (from perforce@localhost) by repoman.freebsd.org (8.14.1/8.14.1/Submit) id m2HIOegO052025 for perforce@freebsd.org; Mon, 17 Mar 2008 18:24:40 GMT (envelope-from sam@freebsd.org) Date: Mon, 17 Mar 2008 18:24:40 GMT Message-Id: <200803171824.m2HIOegO052025@repoman.freebsd.org> X-Authentication-Warning: repoman.freebsd.org: perforce set sender to sam@freebsd.org using -f From: Sam Leffler To: Perforce Change Reviews Cc: Subject: PERFORCE change 137932 for review X-BeenThere: p4-projects@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: p4 projects tree changes List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 17 Mar 2008 18:24:41 -0000 http://perforce.freebsd.org/chv.cgi?CH=137932 Change 137932 by sam@sam_ebb on 2008/03/17 18:24:22 Fix multiple issues with country ie handling: o remove bogus band count from ieee80211_country_ie struct definition; it was added as a bandaid but quietly caused regdomain's that didn't fit to have their country ie truncated (with some interesting ramifications for certain vendor drivers) o add IEEE80211_COUNTRY_MAX_BANDS and IEEE80211_COUNTRY_MAX_SIZE definitions to codify country ie bounds o calculate the country ie once and cache the result in the com for all vaps to use; we invalidate the cached value on channel change and regdomain setting (could optimize channel change but not worthwhile) o move core regdomain setting code out of the ioctl area and into the regdomain code Note this grows the max size of the probe response and beacon frames allocated as we allocate based on the max size of the country ie. Not clear it's worth optimizing as there are already enough ie's that these frames don't fit in an inline mbuf. Reported by: Chris Zimmermann Affected files ... .. //depot/projects/vap/sys/net80211/ieee80211.h#8 edit .. //depot/projects/vap/sys/net80211/ieee80211_ioctl.c#45 edit .. //depot/projects/vap/sys/net80211/ieee80211_output.c#40 edit .. //depot/projects/vap/sys/net80211/ieee80211_regdomain.c#10 edit .. //depot/projects/vap/sys/net80211/ieee80211_regdomain.h#6 edit .. //depot/projects/vap/sys/net80211/ieee80211_var.h#33 edit Differences ... ==== //depot/projects/vap/sys/net80211/ieee80211.h#8 (text+ko) ==== @@ -696,9 +696,13 @@ uint8_t schan; /* starting channel */ uint8_t nchan; /* number channels */ uint8_t maxtxpwr; /* tx power cap */ - } __packed band[10]; /* sub bands */ + } __packed band[1]; /* sub bands (NB: var size) */ } __packed; +#define IEEE80211_COUNTRY_MAX_BANDS 84 /* max possible bands */ +#define IEEE80211_COUNTRY_MAX_SIZE \ + (sizeof(struct ieee80211_country_ie) + 3*(IEEE80211_COUNTRY_MAX_BANDS-1)) + /* * 802.11h Channel Switch Announcement (CSA). */ ==== //depot/projects/vap/sys/net80211/ieee80211_ioctl.c#45 (text+ko) ==== @@ -1944,27 +1944,12 @@ return setcurchan(vap, c); } -static int -allvapsdown(struct ieee80211com *ic) -{ - struct ieee80211vap *vap; - - IEEE80211_LOCK_ASSERT(ic); - TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) - if (vap->iv_state != IEEE80211_S_INIT) - return 0; - return 1; -} - static __noinline int ieee80211_ioctl_setregdomain(struct ieee80211vap *vap, const struct ieee80211req *ireq) { - struct ieee80211com *ic = vap->iv_ic; struct ieee80211_regdomain_req *reg; - struct ieee80211_channel *c; - int desfreq = 0, desflags = 0; /* XXX silence gcc complaint */ - int error, i; + int error; if (ireq->i_len != sizeof(struct ieee80211_regdomain_req)) return EINVAL; @@ -1973,93 +1958,11 @@ if (reg == NULL) return ENOMEM; error = copyin(ireq->i_data, reg, sizeof(*reg)); - if (error != 0) - goto bad; - if (reg->rd.location != 'I' && reg->rd.location != 'O' && - reg->rd.location != ' ') - goto invalid; - if (reg->rd.isocc[0] == '\0' || reg->rd.isocc[1] == '\0') - goto invalid; - if (reg->chaninfo.ic_nchans >= IEEE80211_CHAN_MAX) - goto invalid; - /* - * Calculate freq<->IEEE mapping and default max tx power - * for channels not setup. The driver can override these - * setting to reflect device properties/requirements. - */ - for (i = 0; i < reg->chaninfo.ic_nchans; i++) { - c = ®->chaninfo.ic_chans[i]; - if (c->ic_freq == 0 || c->ic_flags == 0) - goto invalid; - if (c->ic_maxregpower == 0) - goto invalid; - if (c->ic_ieee == 0) - c->ic_ieee = ieee80211_mhz2ieee(c->ic_freq,c->ic_flags); - if (IEEE80211_IS_CHAN_HT40(c) && c->ic_extieee == 0) - c->ic_extieee = ieee80211_mhz2ieee(c->ic_freq + - (IEEE80211_IS_CHAN_HT40U(c) ? 20 : -20), - c->ic_flags); - if (c->ic_maxpower == 0) - c->ic_maxpower = 2*c->ic_maxregpower; - } - IEEE80211_LOCK(ic); - error = ic->ic_setregdomain(ic, ®->rd, - reg->chaninfo.ic_nchans, reg->chaninfo.ic_chans); - if (error != 0) { - IEEE80211_UNLOCK(ic); - goto bad; - } - /* XXX bandaid; a running vap will likely crash */ - if (!allvapsdown(ic)) { - error = EBUSY; - IEEE80211_UNLOCK(ic); - goto bad; - } - /* - * Commit: copy in new channel table and reset media state. - * On return the state machines will be clocked so all vaps - * will reset their state. - * - * XXX ic_bsschan is marked undefined, must have vap's in - * INIT state or we blow up forcing stations off - */ - /* - * Save any desired channel for restore below. Note this - * needs to be done for all vaps but for now we only do - * the one where the ioctl is issued. - */ - if (vap->iv_des_chan != IEEE80211_CHAN_ANYC) { - desfreq = vap->iv_des_chan->ic_freq; - desflags = vap->iv_des_chan->ic_flags; - } - /* regdomain parameters */ - memcpy(&ic->ic_regdomain, ®->rd, sizeof(reg->rd)); - /* channel table */ - memcpy(ic->ic_channels, reg->chaninfo.ic_chans, - reg->chaninfo.ic_nchans * sizeof(struct ieee80211_channel)); - ic->ic_nchans = reg->chaninfo.ic_nchans; - memset(&ic->ic_channels[ic->ic_nchans], 0, - (IEEE80211_CHAN_MAX - ic->ic_nchans) * - sizeof(struct ieee80211_channel)); - ieee80211_media_init(ic, ic->ic_media.ifm_change, - ic->ic_media.ifm_status); + if (error == 0) + error = ieee80211_setregdomain(vap, reg); + FREE(reg, M_TEMP); - ieee80211_scan_flush(vap); - ieee80211_dfs_reset(ic); - if (vap->iv_des_chan != IEEE80211_CHAN_ANYC) { - /* NB: may be NULL if not present in new channel list */ - vap->iv_des_chan = ieee80211_find_channel(ic, desfreq, desflags); - } - IEEE80211_UNLOCK(ic); - - error = ENETRESET; - /* fall thru... */ -bad: - FREE(reg, M_TEMP); - return error; -invalid: - error = EINVAL; - goto bad; + return (error == 0 ? ENETRESET : error); } static int ==== //depot/projects/vap/sys/net80211/ieee80211_output.c#40 (text+ko) ==== @@ -1579,6 +1579,30 @@ } /* + * Add an 11h country information element to a frame. + */ +static uint8_t * +ieee80211_add_countryie(uint8_t *frm, struct ieee80211com *ic) +{ + + if (ic->ic_countryie == NULL || + ic->ic_countryie_chan != ic->ic_bsschan) { + /* + * Handle lazy construction of ie. This is done on + * first use and after a channel change that requires + * re-calculation. + */ + if (ic->ic_countryie != NULL) + free(ic->ic_countryie, M_80211_NODE_IE); + ic->ic_countryie = ieee80211_alloc_countryie(ic); + if (ic->ic_countryie == NULL) + return frm; + ic->ic_countryie_chan = ic->ic_bsschan; + } + return add_appie(frm, ic->ic_countryie); +} + +/* * Send a probe request frame with the specified ssid * and any optional information element data. */ @@ -2083,7 +2107,7 @@ + 2 + IEEE80211_NWID_LEN + 2 + IEEE80211_RATE_SIZE + 7 /* max(7,3) */ - + sizeof(struct ieee80211_country_ie) + + IEEE80211_COUNTRY_MAX_SIZE + sizeof(struct ieee80211_ie_wpa) + 3 + sizeof(struct ieee80211_csa_ie) @@ -2519,7 +2543,7 @@ + 2 + 1 /* DS parameters */ + 2 + 6 /* CF parameters */ + 2 + 4 + vap->iv_tim_len /* DTIM/IBSSPARMS */ - + sizeof(struct ieee80211_country_ie) /* country */ + + IEEE80211_COUNTRY_MAX_SIZE /* country */ + 2 + 1 /* power control */ + sizeof(struct ieee80211_csa_ie) /* CSA */ + 2 + 1 /* ERP */ ==== //depot/projects/vap/sys/net80211/ieee80211_regdomain.c#10 (text+ko) ==== @@ -68,6 +68,10 @@ void ieee80211_regdomain_detach(struct ieee80211com *ic) { + if (ic->ic_countryie != NULL) { + free(ic->ic_countryie, M_80211_NODE_IE); + ic->ic_countryie = NULL; + } } void @@ -189,10 +193,10 @@ } /* - * Add Country Information IE. + * Allocate and construct a Country Information IE. */ -uint8_t * -ieee80211_add_countryie(uint8_t *frm, struct ieee80211com *ic) +struct ieee80211_appie * +ieee80211_alloc_countryie(struct ieee80211com *ic) { #define CHAN_UNINTERESTING \ (IEEE80211_CHAN_TURBO | IEEE80211_CHAN_STURBO | \ @@ -212,11 +216,21 @@ CHAN_UNINTERESTING | IEEE80211_CHAN_2GHZ, /* MODE_11NA */ CHAN_UNINTERESTING | IEEE80211_CHAN_5GHZ, /* MODE_11NG */ }; - struct ieee80211_country_ie *ie = (struct ieee80211_country_ie *)frm; const struct ieee80211_regdomain *rd = &ic->ic_regdomain; - uint8_t nextchan, chans[IEEE80211_CHAN_BYTES]; + uint8_t nextchan, chans[IEEE80211_CHAN_BYTES], *frm; + struct ieee80211_appie *aie; + struct ieee80211_country_ie *ie; int i, skip, nruns; + aie = malloc(IEEE80211_COUNTRY_MAX_SIZE, M_80211_NODE_IE, + M_NOWAIT | M_ZERO); + if (aie == NULL) { + if_printf(ic->ic_ifp, + "%s: unable to allocate memory for country ie\n", __func__); + /* XXX stat */ + return NULL; + } + ie = (struct ieee80211_country_ie *) aie->ie_data; ie->ie = IEEE80211_ELEMID_COUNTRY; if (rd->isocc[0] == '\0') { if_printf(ic->ic_ifp, "no ISO country string for cc %d; " @@ -252,8 +266,11 @@ setbit(chans, c->ic_ieee); if (c->ic_ieee != nextchan || c->ic_maxregpower != frm[-1]) { /* new run */ - if (nruns == 10) { /* XXX max runs */ - /* XXX add msg but can't now 'cuz we're called too often */ + if (nruns == IEEE80211_COUNTRY_MAX_BANDS) { + if_printf(ic->ic_ifp, "%s: country ie too big, " + "runs > max %d, truncating\n", + __func__, IEEE80211_COUNTRY_MAX_BANDS); + /* XXX stat? fail? */ break; } frm[0] = c->ic_ieee; /* starting channel # */ @@ -272,6 +289,115 @@ ie->len++; *frm++ = 0; } - return frm; + aie->ie_len = frm - aie->ie_data; + + return aie; #undef CHAN_UNINTERESTING } + +static int +allvapsdown(struct ieee80211com *ic) +{ + struct ieee80211vap *vap; + + IEEE80211_LOCK_ASSERT(ic); + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) + if (vap->iv_state != IEEE80211_S_INIT) + return 0; + return 1; +} + +int +ieee80211_setregdomain(struct ieee80211vap *vap, + struct ieee80211_regdomain_req *reg) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_channel *c; + int desfreq = 0, desflags = 0; /* XXX silence gcc complaint */ + int error, i; + + if (reg->rd.location != 'I' && reg->rd.location != 'O' && + reg->rd.location != ' ') + return EINVAL; + if (reg->rd.isocc[0] == '\0' || reg->rd.isocc[1] == '\0') + return EINVAL; + if (reg->chaninfo.ic_nchans >= IEEE80211_CHAN_MAX) + return EINVAL; + /* + * Calculate freq<->IEEE mapping and default max tx power + * for channels not setup. The driver can override these + * setting to reflect device properties/requirements. + */ + for (i = 0; i < reg->chaninfo.ic_nchans; i++) { + c = ®->chaninfo.ic_chans[i]; + if (c->ic_freq == 0 || c->ic_flags == 0) + return EINVAL; + if (c->ic_maxregpower == 0) + return EINVAL; + if (c->ic_ieee == 0) + c->ic_ieee = ieee80211_mhz2ieee(c->ic_freq,c->ic_flags); + if (IEEE80211_IS_CHAN_HT40(c) && c->ic_extieee == 0) + c->ic_extieee = ieee80211_mhz2ieee(c->ic_freq + + (IEEE80211_IS_CHAN_HT40U(c) ? 20 : -20), + c->ic_flags); + if (c->ic_maxpower == 0) + c->ic_maxpower = 2*c->ic_maxregpower; + } + IEEE80211_LOCK(ic); + error = ic->ic_setregdomain(ic, ®->rd, + reg->chaninfo.ic_nchans, reg->chaninfo.ic_chans); + if (error != 0) { + IEEE80211_UNLOCK(ic); + return error; + } + /* XXX bandaid; a running vap will likely crash */ + if (!allvapsdown(ic)) { + IEEE80211_UNLOCK(ic); + return EBUSY; + } + /* + * Commit: copy in new channel table and reset media state. + * On return the state machines will be clocked so all vaps + * will reset their state. + * + * XXX ic_bsschan is marked undefined, must have vap's in + * INIT state or we blow up forcing stations off + */ + /* + * Save any desired channel for restore below. Note this + * needs to be done for all vaps but for now we only do + * the one where the ioctl is issued. + */ + if (vap->iv_des_chan != IEEE80211_CHAN_ANYC) { + desfreq = vap->iv_des_chan->ic_freq; + desflags = vap->iv_des_chan->ic_flags; + } + /* regdomain parameters */ + memcpy(&ic->ic_regdomain, ®->rd, sizeof(reg->rd)); + /* channel table */ + memcpy(ic->ic_channels, reg->chaninfo.ic_chans, + reg->chaninfo.ic_nchans * sizeof(struct ieee80211_channel)); + ic->ic_nchans = reg->chaninfo.ic_nchans; + memset(&ic->ic_channels[ic->ic_nchans], 0, + (IEEE80211_CHAN_MAX - ic->ic_nchans) * + sizeof(struct ieee80211_channel)); + ieee80211_media_init(ic, ic->ic_media.ifm_change, + ic->ic_media.ifm_status); + + /* + * Invalidate channel-related state. + */ + if (ic->ic_countryie != NULL) { + free(ic->ic_countryie, M_80211_NODE_IE); + ic->ic_countryie = NULL; + } + ieee80211_scan_flush(vap); + ieee80211_dfs_reset(ic); + if (vap->iv_des_chan != IEEE80211_CHAN_ANYC) { + /* NB: may be NULL if not present in new channel list */ + vap->iv_des_chan = ieee80211_find_channel(ic, desfreq, desflags); + } + IEEE80211_UNLOCK(ic); + + return 0; +} ==== //depot/projects/vap/sys/net80211/ieee80211_regdomain.h#6 (text+ko) ==== @@ -240,6 +240,10 @@ int ieee80211_init_channels(struct ieee80211com *, const struct ieee80211_regdomain *, const uint8_t bands[]); void ieee80211_sort_channels(struct ieee80211_channel chans[], int nchans); -uint8_t *ieee80211_add_countryie(uint8_t *, struct ieee80211com *); +struct ieee80211_appie; +struct ieee80211_appie *ieee80211_alloc_countryie(struct ieee80211com *); +struct ieee80211_regdomain_req; +int ieee80211_setregdomain(struct ieee80211vap *, + struct ieee80211_regdomain_req *); #endif /* defined(__KERNEL__) || defined(_KERNEL) */ #endif /* _NET80211_IEEE80211_REGDOMAIN_H_ */ ==== //depot/projects/vap/sys/net80211/ieee80211_var.h#33 (text+ko) ==== @@ -100,6 +100,11 @@ struct ieee80211vap; typedef void (*ieee80211vap_attach)(struct ieee80211vap *); +struct ieee80211_appie { + uint16_t ie_len; /* size of ie_data */ + uint8_t ie_data[]; /* user-specified IE's */ +}; + struct ieee80211com { struct ifnet *ic_ifp; /* associated device */ ieee80211_com_lock_t ic_comlock; /* state update lock */ @@ -158,6 +163,8 @@ struct ieee80211_channel *ic_bsschan; /* bss channel */ struct ieee80211_channel *ic_prevchan; /* previous channel */ struct ieee80211_regdomain ic_regdomain;/* regulatory data */ + struct ieee80211_appie *ic_countryie; /* calculated country ie */ + struct ieee80211_channel *ic_countryie_chan; /* 802.11h/DFS state */ struct ieee80211_channel *ic_csa_newchan;/* channel for doing CSA */ @@ -268,11 +275,6 @@ struct ieee80211_aclator; -struct ieee80211_appie { - uint16_t ie_len; /* size of ie_data */ - uint8_t ie_data[]; /* user-specified IE's */ -}; - struct ieee80211vap { struct ifmedia iv_media; /* interface media config */ struct ifnet *iv_ifp; /* associated device */