Пример #1
0
static void
scan_dump(struct ieee80211_scan_state *ss)
{
	struct ieee80211vap *vap = ss->ss_vap;

	if_printf(vap->iv_ifp, "scan set ");
	ieee80211_scan_dump_channels(ss);
	printf(" dwell min %lums max %lums\n",
	    ticks_to_msecs(ss->ss_mindwell), ticks_to_msecs(ss->ss_maxdwell));
}
Пример #2
0
static void
hwmp_rootmode_cb(void *arg)
{
	struct ieee80211vap *vap = (struct ieee80211vap *)arg;
	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
	struct ieee80211_mesh_state *ms = vap->iv_mesh;
	struct ieee80211_meshpreq_ie preq;

	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, vap->iv_bss,
	    "%s", "send broadcast PREQ");

	preq.preq_flags = IEEE80211_MESHPREQ_FLAGS_AM;
	if (ms->ms_flags & IEEE80211_MESHFLAGS_PORTAL)
		preq.preq_flags |= IEEE80211_MESHPREQ_FLAGS_PR;
	if (hs->hs_rootmode == IEEE80211_HWMP_ROOTMODE_PROACTIVE)
		preq.preq_flags |= IEEE80211_MESHPREQ_FLAGS_PP;
	preq.preq_hopcount = 0;
	preq.preq_ttl = ms->ms_ttl;
	preq.preq_id = ++hs->hs_preqid;
	IEEE80211_ADDR_COPY(preq.preq_origaddr, vap->iv_myaddr);
	preq.preq_origseq = ++hs->hs_seq;
	preq.preq_lifetime = ticks_to_msecs(ieee80211_hwmp_roottimeout);
	preq.preq_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
	preq.preq_tcount = 1;
	IEEE80211_ADDR_COPY(PREQ_TADDR(0), broadcastaddr);
	PREQ_TFLAGS(0) = IEEE80211_MESHPREQ_TFLAGS_TO |
	    IEEE80211_MESHPREQ_TFLAGS_RF;
	PREQ_TSEQ(0) = 0;
	vap->iv_stats.is_hwmp_rootreqs++;
	hwmp_send_preq(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &preq);
	hwmp_rootmode_setup(vap);
}
Пример #3
0
/**
 * This function provides control to read/write various timing 
 * parameters
 *
 * @param 
 *  SYSCTL_HANDLER_ARGS Standard arguments of the Linux sysctl handler 
 *
 * @returns 
 *  0 on success, or corresponding negative error code on failure
 */
static int
ieee80211_sysctl_msecs_ticks(SYSCTL_HANDLER_ARGS)
{
	int msecs = ticks_to_msecs(*((int *)ctl->extra1));
	int val = 0;
	int ret;
	
	ctl->data   = &val;
        ctl->maxlen = sizeof(int);

	if (write) 
	{
		ret = IEEE80211_SYSCTL_PROC_DOINTVEC(ctl, write, filp, buffer,
		                                     lenp, ppos);
		if (ret == 0)
		{
			*((int *)ctl->extra1) = val;
		}
	}
	else
	{
		val = msecs;
		ret = IEEE80211_SYSCTL_PROC_DOINTVEC(ctl, write, filp, buffer,
		                                     lenp, ppos);
	}

	return ret;
}
Пример #4
0
/*
 * Timer check on whether the VAP has had any transmit activity.
 *
 * This may schedule a transition from _RUN -> _SLEEP if it's appropriate.
 */
void
ieee80211_sta_ps_timer_check(struct ieee80211vap *vap)
{
	struct ieee80211com *ic = vap->iv_ic;

	/* XXX lock assert */

	/* For no, only do this in STA mode */
	if (! (vap->iv_caps & IEEE80211_C_SWSLEEP))
		goto out;

	if (vap->iv_opmode != IEEE80211_M_STA)
		goto out;

	/* If we're not at run state, bail */
	if (vap->iv_state != IEEE80211_S_RUN)
		goto out;

	IEEE80211_DPRINTF(vap, IEEE80211_MSG_POWER,
	    "%s: lastdata=%llu, ticks=%llu\n",
	    __func__, (unsigned long long) ic->ic_lastdata,
	    (unsigned long long) ticks);

	/* If powersave is disabled on the VAP, don't bother */
	if (! (vap->iv_flags & IEEE80211_F_PMGTON))
		goto out;

	/* If we've done any data within our idle interval, bail */
	/* XXX hard-coded to one second for now, ew! */
	if (ieee80211_time_after(ic->ic_lastdata + 500, ticks))
		goto out;

	/*
	 * Signify we're going into power save and transition the
	 * node to powersave.
	 */
	if ((vap->iv_bss->ni_flags & IEEE80211_NODE_PWR_MGT) == 0)
		vap->iv_sta_ps(vap, 1);

	/*
	 * XXX The driver has to handle the fact that we're going
	 * to sleep but frames may still be transmitted;
	 * hopefully it and/or us will do the right thing and mark any
	 * transmitted frames with PWRMGT set to 1.
	 */
	ieee80211_new_state_locked(vap, IEEE80211_S_SLEEP, 0);

	IEEE80211_DPRINTF(vap, IEEE80211_MSG_POWER,
	    "%s: time delta=%d msec\n", __func__,
	    (int) ticks_to_msecs(ticks - ic->ic_lastdata));

out:
	return;
}
Пример #5
0
static int
amrr_sysctl_interval(SYSCTL_HANDLER_ARGS)
{
	struct ieee80211_amrr *amrr = arg1;
	int msecs = ticks_to_msecs(amrr->amrr_interval);
	int error;

	error = sysctl_handle_int(oidp, &msecs, 0, req);
	if (error || !req->newptr)
		return error;
	ieee80211_amrr_setinterval(amrr, msecs);
	return 0;
}
Пример #6
0
int
ieee80211_sysctl_msecs_ticks(SYSCTL_HANDLER_ARGS)
{
	int msecs = ticks_to_msecs(*(int *)arg1);
	int error, t;

	error = sysctl_handle_int(oidp, &msecs, 0, req);
	if (error || !req->newptr)
		return error;
	t = msecs_to_ticks(msecs);
	*(int *)arg1 = (t < 1) ? 1 : t;
	return 0;
}
Пример #7
0
static void
scan_curchan_task(void *arg, int pending)
{
	struct ieee80211_scan_state *ss = arg;
	struct scan_state *ss_priv = SCAN_PRIVATE(ss);
	struct ieee80211com *ic = ss->ss_ic;
	struct ieee80211_channel *chan;
	unsigned long maxdwell;
	int scandone;

	IEEE80211_LOCK(ic);
end:
	scandone = (ss->ss_next >= ss->ss_last) ||
	    (ss_priv->ss_iflags & ISCAN_CANCEL) != 0;

	IEEE80211_DPRINTF(ss->ss_vap, IEEE80211_MSG_SCAN,
	    "%s: loop start; scandone=%d\n",
	    __func__,
	    scandone);

	if (scandone || (ss->ss_flags & IEEE80211_SCAN_GOTPICK) ||
	    (ss_priv->ss_iflags & ISCAN_ABORT) ||
	     ieee80211_time_after(ticks + ss->ss_mindwell, ss_priv->ss_scanend)) {
		ss_priv->ss_iflags &= ~ISCAN_RUNNING;
		scan_end(ss, scandone);
		return;
	} else
		ss_priv->ss_iflags |= ISCAN_RUNNING;

	chan = ss->ss_chans[ss->ss_next++];

	/*
	 * Watch for truncation due to the scan end time.
	 */
	if (ieee80211_time_after(ticks + ss->ss_maxdwell, ss_priv->ss_scanend))
		maxdwell = ss_priv->ss_scanend - ticks;
	else
		maxdwell = ss->ss_maxdwell;

	IEEE80211_DPRINTF(ss->ss_vap, IEEE80211_MSG_SCAN,
	    "%s: chan %3d%c -> %3d%c [%s, dwell min %lums max %lums]\n",
	    __func__,
	    ieee80211_chan2ieee(ic, ic->ic_curchan),
	    ieee80211_channel_type_char(ic->ic_curchan),
	    ieee80211_chan2ieee(ic, chan),
	    ieee80211_channel_type_char(chan),
	    (ss->ss_flags & IEEE80211_SCAN_ACTIVE) &&
		(chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0 ?
		"active" : "passive",
	    ticks_to_msecs(ss->ss_mindwell), ticks_to_msecs(maxdwell));

	/*
	 * Potentially change channel and phy mode.
	 */
	ic->ic_curchan = chan;
	ic->ic_rt = ieee80211_get_ratetable(chan);
	IEEE80211_UNLOCK(ic);
	/*
	 * Perform the channel change and scan unlocked so the driver
	 * may sleep. Once set_channel returns the hardware has
	 * completed the channel change.
	 */
	ic->ic_set_channel(ic);
	ieee80211_radiotap_chan_change(ic);

	/*
	 * Scan curchan.  Drivers for "intelligent hardware"
	 * override ic_scan_curchan to tell the device to do
	 * the work.  Otherwise we manage the work ourselves;
	 * sending a probe request (as needed), and arming the
	 * timeout to switch channels after maxdwell ticks.
	 *
	 * scan_curchan should only pause for the time required to
	 * prepare/initiate the hardware for the scan (if at all).
	 */
	ic->ic_scan_curchan(ss, maxdwell);
	IEEE80211_LOCK(ic);

	/* XXX scan state can change! Re-validate scan state! */

	ss_priv->ss_chanmindwell = ticks + ss->ss_mindwell;
	/* clear mindwell lock and initial channel change flush */
	ss_priv->ss_iflags &= ~ISCAN_REP;

	if (ss_priv->ss_iflags & (ISCAN_CANCEL|ISCAN_ABORT)) {
		taskqueue_cancel_timeout(ic->ic_tq, &ss_priv->ss_scan_curchan,
		    NULL);
		goto end;
	}

	IEEE80211_DPRINTF(ss->ss_vap, IEEE80211_MSG_SCAN, "%s: waiting\n",
	    __func__);
	IEEE80211_UNLOCK(ic);
}
Пример #8
0
static void
scan_task(void *arg, int pending)
{
#define	ISCAN_REP	(ISCAN_MINDWELL | ISCAN_DISCARD)
	struct ieee80211_scan_state *ss = (struct ieee80211_scan_state *) arg;
	struct ieee80211vap *vap = ss->ss_vap;
	struct ieee80211com *ic = ss->ss_ic;
	struct ieee80211_channel *chan;
	unsigned long maxdwell, scanend;
	int scandone = 0;

	IEEE80211_LOCK(ic);
	if (vap == NULL || (ic->ic_flags & IEEE80211_F_SCAN) == 0 ||
	    (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_ABORT)) {
		/* Cancelled before we started */
		goto done;
	}

	if (ss->ss_next == ss->ss_last) {
		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
			"%s: no channels to scan\n", __func__);
		goto done;
	}

	if (vap->iv_opmode == IEEE80211_M_STA &&
	    vap->iv_state == IEEE80211_S_RUN) {
		if ((vap->iv_bss->ni_flags & IEEE80211_NODE_PWR_MGT) == 0) {
			/* Enable station power save mode */
			ieee80211_sta_pwrsave(vap, 1);
			/*
			 * Use an 1ms delay so the null data frame has a chance
			 * to go out.
			 * XXX Should use M_TXCB mechanism to eliminate this.
			 */
			cv_timedwait(&SCAN_PRIVATE(ss)->ss_scan_cv,
			    IEEE80211_LOCK_OBJ(ic), hz / 1000);
			if (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_ABORT)
				goto done;
		}
	}

	scanend = ticks + SCAN_PRIVATE(ss)->ss_duration;
	IEEE80211_UNLOCK(ic);
	ic->ic_scan_start(ic);		/* notify driver */
	IEEE80211_LOCK(ic);

	for (;;) {
		scandone = (ss->ss_next >= ss->ss_last) ||
		    (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) != 0;
		if (scandone || (ss->ss_flags & IEEE80211_SCAN_GOTPICK) ||
		    (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_ABORT) ||
		     time_after(ticks + ss->ss_mindwell, scanend))
			break;

		chan = ss->ss_chans[ss->ss_next++];

		/*
		 * Watch for truncation due to the scan end time.
		 */
		if (time_after(ticks + ss->ss_maxdwell, scanend))
			maxdwell = scanend - ticks;
		else
			maxdwell = ss->ss_maxdwell;

		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
		    "%s: chan %3d%c -> %3d%c [%s, dwell min %lums max %lums]\n",
		    __func__,
		    ieee80211_chan2ieee(ic, ic->ic_curchan),
		        channel_type(ic->ic_curchan),
		    ieee80211_chan2ieee(ic, chan), channel_type(chan),
		    (ss->ss_flags & IEEE80211_SCAN_ACTIVE) &&
			(chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0 ?
			"active" : "passive",
		    ticks_to_msecs(ss->ss_mindwell), ticks_to_msecs(maxdwell));

		/*
		 * Potentially change channel and phy mode.
		 */
		ic->ic_curchan = chan;
		ic->ic_rt = ieee80211_get_ratetable(chan);
		IEEE80211_UNLOCK(ic);
		/*
		 * Perform the channel change and scan unlocked so the driver
		 * may sleep. Once set_channel returns the hardware has
		 * completed the channel change.
		 */
		ic->ic_set_channel(ic);
		ieee80211_radiotap_chan_change(ic);

		/*
		 * Scan curchan.  Drivers for "intelligent hardware"
		 * override ic_scan_curchan to tell the device to do
		 * the work.  Otherwise we manage the work outselves;
		 * sending a probe request (as needed), and arming the
		 * timeout to switch channels after maxdwell ticks.
		 *
		 * scan_curchan should only pause for the time required to
		 * prepare/initiate the hardware for the scan (if at all), the
		 * below condvar is used to sleep for the channels dwell time
		 * and allows it to be signalled for abort.
		 */
		ic->ic_scan_curchan(ss, maxdwell);
		IEEE80211_LOCK(ic);

		SCAN_PRIVATE(ss)->ss_chanmindwell = ticks + ss->ss_mindwell;
		/* clear mindwell lock and initial channel change flush */
		SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_REP;

		if ((SCAN_PRIVATE(ss)->ss_iflags & (ISCAN_CANCEL|ISCAN_ABORT)))
			continue;

		/* Wait to be signalled to scan the next channel */
		cv_wait(&SCAN_PRIVATE(ss)->ss_scan_cv, IEEE80211_LOCK_OBJ(ic));
	}
	if (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_ABORT)
		goto done;

	IEEE80211_UNLOCK(ic);
	ic->ic_scan_end(ic);		/* notify driver */
	IEEE80211_LOCK(ic);

	/*
	 * Record scan complete time.  Note that we also do
	 * this when canceled so any background scan will
	 * not be restarted for a while.
	 */
	if (scandone)
		ic->ic_lastscan = ticks;
	/* return to the bss channel */
	if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
	    ic->ic_curchan != ic->ic_bsschan) {
		ieee80211_setupcurchan(ic, ic->ic_bsschan);
		IEEE80211_UNLOCK(ic);
		ic->ic_set_channel(ic);
		ieee80211_radiotap_chan_change(ic);
		IEEE80211_LOCK(ic);
	}
	/* clear internal flags and any indication of a pick */
	SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_REP;
	ss->ss_flags &= ~IEEE80211_SCAN_GOTPICK;

	/*
	 * If not canceled and scan completed, do post-processing.
	 * If the callback function returns 0, then it wants to
	 * continue/restart scanning.  Unfortunately we needed to
	 * notify the driver to end the scan above to avoid having
	 * rx frames alter the scan candidate list.
	 */
	if ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) == 0 &&
	    !ss->ss_ops->scan_end(ss, vap) &&
	    (ss->ss_flags & IEEE80211_SCAN_ONCE) == 0 &&
	    time_before(ticks + ss->ss_mindwell, scanend)) {
		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
		    "%s: done, restart "
		    "[ticks %u, dwell min %lu scanend %lu]\n",
		    __func__,
		    ticks, ss->ss_mindwell, scanend);
		ss->ss_next = 0;	/* reset to begining */
		if (ss->ss_flags & IEEE80211_SCAN_ACTIVE)
			vap->iv_stats.is_scan_active++;
		else
			vap->iv_stats.is_scan_passive++;

		ss->ss_ops->scan_restart(ss, vap);	/* XXX? */
		ieee80211_runtask(ic, &SCAN_PRIVATE(ss)->ss_scan_task);
		IEEE80211_UNLOCK(ic);
		return;
	}

	/* past here, scandone is ``true'' if not in bg mode */
	if ((ss->ss_flags & IEEE80211_SCAN_BGSCAN) == 0)
		scandone = 1;

	IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
	    "%s: %s, [ticks %u, dwell min %lu scanend %lu]\n",
	    __func__, scandone ? "done" : "stopped",
	    ticks, ss->ss_mindwell, scanend);

	/*
	 * Clear the SCAN bit first in case frames are
	 * pending on the station power save queue.  If
	 * we defer this then the dispatch of the frames
	 * may generate a request to cancel scanning.
	 */
done:
	ic->ic_flags &= ~IEEE80211_F_SCAN;
	/*
	 * Drop out of power save mode when a scan has
	 * completed.  If this scan was prematurely terminated
	 * because it is a background scan then don't notify
	 * the ap; we'll either return to scanning after we
	 * receive the beacon frame or we'll drop out of power
	 * save mode because the beacon indicates we have frames
	 * waiting for us.
	 */
	if (scandone) {
		ieee80211_sta_pwrsave(vap, 0);
		if (ss->ss_next >= ss->ss_last) {
			ieee80211_notify_scan_done(vap);
			ic->ic_flags_ext &= ~IEEE80211_FEXT_BGSCAN;
		}
	}
	SCAN_PRIVATE(ss)->ss_iflags &= ~(ISCAN_CANCEL|ISCAN_ABORT);
	ss->ss_flags &= ~(IEEE80211_SCAN_ONCE | IEEE80211_SCAN_PICK1ST);
	IEEE80211_UNLOCK(ic);
#undef ISCAN_REP
}
Пример #9
0
static struct ieee80211_node *
hwmp_discover(struct ieee80211vap *vap,
    const uint8_t dest[IEEE80211_ADDR_LEN], struct mbuf *m)
{
	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
	struct ieee80211_mesh_state *ms = vap->iv_mesh;
	struct ieee80211_mesh_route *rt = NULL;
	struct ieee80211_hwmp_route *hr;
	struct ieee80211_meshpreq_ie preq;
	struct ieee80211_node *ni;
	int sendpreq = 0;
#ifdef IEEE80211_DEBUG
	char ethstr[ETHER_ADDRSTRLEN + 1];
#endif

	KASSERT(vap->iv_opmode == IEEE80211_M_MBSS,
	    ("not a mesh vap, opmode %d", vap->iv_opmode));

	KASSERT(!IEEE80211_ADDR_EQ(vap->iv_myaddr, dest),
	    ("%s: discovering self!", __func__));

	ni = NULL;
	if (!IEEE80211_IS_MULTICAST(dest)) {
		rt = ieee80211_mesh_rt_find(vap, dest);
		if (rt == NULL) {
			rt = ieee80211_mesh_rt_add(vap, dest);
			if (rt == NULL) {
				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP,
				    ni, "unable to add discovery path to %s",
				    kether_ntoa(dest, ethstr));
				vap->iv_stats.is_mesh_rtaddfailed++;
				goto done;
			}
		}
		hr = IEEE80211_MESH_ROUTE_PRIV(rt,
		    struct ieee80211_hwmp_route);
		if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0) {
			if (hr->hr_origseq == 0)
				hr->hr_origseq = ++hs->hs_seq;
			rt->rt_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
			rt->rt_lifetime =
			    ticks_to_msecs(ieee80211_hwmp_pathtimeout);
			/* XXX check preq retries */
			sendpreq = 1;
			if (m != NULL) {
				IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP,
				    dest, "%s",
				    "start path discovery (src <none>)");
			} else {
				IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP,
				    dest,
				    "start path discovery (src %s)",
				    kether_ntoa(
					    mtod(m, struct ether_header *)->ether_shost,
					    ethstr));
			}
			/*
			 * Try to discover the path for this node.
			 */
			preq.preq_flags = 0;
			preq.preq_hopcount = 0;
			preq.preq_ttl = ms->ms_ttl;
			preq.preq_id = ++hs->hs_preqid;
			IEEE80211_ADDR_COPY(preq.preq_origaddr, vap->iv_myaddr);
			preq.preq_origseq = hr->hr_origseq;
			preq.preq_lifetime = rt->rt_lifetime;
			preq.preq_metric = rt->rt_metric;
			preq.preq_tcount = 1;
			IEEE80211_ADDR_COPY(PREQ_TADDR(0), dest);
			PREQ_TFLAGS(0) = 0;
			if (ieee80211_hwmp_targetonly)
				PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_TO;
			if (ieee80211_hwmp_replyforward)
				PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_RF;
			PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_USN;
			PREQ_TSEQ(0) = 0;
			/* XXX check return value */
			hwmp_send_preq(vap->iv_bss, vap->iv_myaddr,
			    broadcastaddr, &preq);
		}
		if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID)
			ni = ieee80211_find_txnode(vap, rt->rt_nexthop);
	} else {