/*
 * Drain/reclaim all frames from an ageq.
 */
void
ieee80211_ageq_drain(struct ieee80211_ageq *aq)
{
	ieee80211_ageq_mfree(ieee80211_ageq_remove(aq, NULL));
}
/*
 * Drain/reclaim frames associated with a specific node from an ageq.
 */
void
ieee80211_ageq_drain_node(struct ieee80211_ageq *aq,
	struct ieee80211_node *ni)
{
	ieee80211_ageq_mfree(ieee80211_ageq_remove(aq, ni));
}
Esempio n. 3
0
static void
hwmp_recv_prep(struct ieee80211vap *vap, struct ieee80211_node *ni,
    const struct ieee80211_frame *wh, const struct ieee80211_meshprep_ie *prep)
{
	struct ieee80211_mesh_state *ms = vap->iv_mesh;
	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
	struct ieee80211_mesh_route *rt = NULL;
	struct ieee80211_hwmp_route *hr;
	struct ieee80211com *ic = vap->iv_ic;
	struct ifnet *ifp = vap->iv_ifp;
	struct mbuf *m, *next;
#ifdef IEEE80211_DEBUG
	char ethstr[ETHER_ADDRSTRLEN + 1];
#endif

	/*
	 * Acceptance criteria: if the corresponding PREQ was not generated
	 * by us and forwarding is disabled, discard this PREP.
	 */
	if (ni == vap->iv_bss ||
	    ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED)
		return;
	if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, prep->prep_origaddr) &&
	    !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD))
		return;

	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
	    "received PREP from %s", kether_ntoa(prep->prep_targetaddr, ethstr));

	rt = ieee80211_mesh_rt_find(vap, prep->prep_targetaddr);
	if (rt == NULL) {
		/*
		 * If we have no entry this could be a reply to a root PREQ.
		 */
		if (hs->hs_rootmode != IEEE80211_HWMP_ROOTMODE_DISABLED) {
			rt = ieee80211_mesh_rt_add(vap, prep->prep_targetaddr);
			if (rt == NULL) {
				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP,
				    ni, "unable to add PREP path to %s",
				    kether_ntoa(prep->prep_targetaddr, ethstr));
				vap->iv_stats.is_mesh_rtaddfailed++;
				return;
			}
			IEEE80211_ADDR_COPY(rt->rt_nexthop, wh->i_addr2);
			rt->rt_nhops = prep->prep_hopcount;
			rt->rt_lifetime = prep->prep_lifetime;
			rt->rt_metric = prep->prep_metric;
			rt->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID;
			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
			    "add root path to %s nhops %d metric %d (PREP)",
			    kether_ntoa(prep->prep_targetaddr, ethstr),
			    rt->rt_nhops, rt->rt_metric);
			return;
		} 
		return;
	}
	/*
	 * Sequence number validation.
	 */
	hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
	if (HWMP_SEQ_LEQ(prep->prep_targetseq, hr->hr_seq)) {
		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
		    "discard PREP from %s, old seq no %u <= %u",
		    kether_ntoa(prep->prep_targetaddr, ethstr),
		    prep->prep_targetseq, hr->hr_seq);
		return;
	}
	hr->hr_seq = prep->prep_targetseq;
	/*
	 * If it's NOT for us, propagate the PREP.
	 */
	if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, prep->prep_origaddr) &&
	    prep->prep_ttl > 1 && prep->prep_hopcount < hs->hs_maxhops) {
		struct ieee80211_meshprep_ie pprep; /* propagated PREP */

		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
		    "propagate PREP from %s",
		    kether_ntoa(prep->prep_targetaddr, ethstr));

		memcpy(&pprep, prep, sizeof(pprep));
		pprep.prep_hopcount += 1;
		pprep.prep_ttl -= 1;
		pprep.prep_metric += ms->ms_pmetric->mpm_metric(ni);
		IEEE80211_ADDR_COPY(pprep.prep_targetaddr, vap->iv_myaddr);
		hwmp_send_prep(ni, vap->iv_myaddr, broadcastaddr, &pprep);
	}
	hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
	if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY) {
		/* NB: never clobber a proxy entry */;
		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
		    "discard PREP for %s, route is marked PROXY",
		    kether_ntoa(prep->prep_targetaddr, ethstr));
		vap->iv_stats.is_hwmp_proxy++;
	} else if (prep->prep_origseq == hr->hr_origseq) {
		/*
		 * Check if we already have a path to this node.
		 * If we do, check if this path reply contains a
		 * better route.
		 */
		if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0 ||
		    (prep->prep_hopcount < rt->rt_nhops ||
		     prep->prep_metric < rt->rt_metric)) {
			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
			    "%s path to %s, hopcount %d:%d metric %d:%d",
			    rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID ?
				"prefer" : "update",
			    kether_ntoa(prep->prep_origaddr, ethstr),
			    rt->rt_nhops, prep->prep_hopcount,
			    rt->rt_metric, prep->prep_metric);
			IEEE80211_ADDR_COPY(rt->rt_nexthop, wh->i_addr2);
			rt->rt_nhops = prep->prep_hopcount;
			rt->rt_lifetime = prep->prep_lifetime;
			rt->rt_metric = prep->prep_metric;
			rt->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID;
		} else {
			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
			    "ignore PREP for %s, hopcount %d:%d metric %d:%d",
			    kether_ntoa(prep->prep_targetaddr, ethstr),
			    rt->rt_nhops, prep->prep_hopcount,
			    rt->rt_metric, prep->prep_metric);
		}
	} else {
		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
		    "discard PREP for %s, wrong seqno %u != %u",
		    kether_ntoa(prep->prep_targetaddr, ethstr), prep->prep_origseq,
		    hr->hr_seq);
		vap->iv_stats.is_hwmp_wrongseq++;
	} 
	/*
	 * Check for frames queued awaiting path discovery.
	 * XXX probably can tell exactly and avoid remove call
	 * NB: hash may have false matches, if so they will get
	 *     stuck back on the stageq because there won't be
	 *     a path.
	 */
	m = ieee80211_ageq_remove(&ic->ic_stageq, 
	    (struct ieee80211_node *)(uintptr_t)
		ieee80211_mac_hash(ic, rt->rt_dest));
	for (; m != NULL; m = next) {
		next = m->m_nextpkt;
		m->m_nextpkt = NULL;
		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
		    "flush queued frame %p len %d", m, m->m_pkthdr.len);
		ieee80211_handoff(ifp, m);
	}
}