PERFORCE change 137932 for review

Sam Leffler sam at FreeBSD.org
Mon Mar 17 18:24:41 UTC 2008


http://perforce.freebsd.org/chv.cgi?CH=137932

Change 137932 by sam at 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 = &reg->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, &reg->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, &reg->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 = &reg->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, &reg->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, &reg->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 */


More information about the p4-projects mailing list