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);
}
Exemple #2
0
static void
hwmp_recv_preq(struct ieee80211vap *vap, struct ieee80211_node *ni,
    const struct ieee80211_frame *wh, const struct ieee80211_meshpreq_ie *preq)
{
	struct ieee80211_mesh_state *ms = vap->iv_mesh;
	struct ieee80211_mesh_route *rt = NULL;
	struct ieee80211_mesh_route *rtorig = NULL;
	struct ieee80211_hwmp_route *hrorig;
	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
	struct ieee80211_meshprep_ie prep;
#ifdef IEEE80211_DEBUG
	char ethstr[ETHER_ADDRSTRLEN + 1];
#endif

	if (ni == vap->iv_bss ||
	    ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED)
		return;
	/*
	 * Ignore PREQs from us. Could happen because someone forward it
	 * back to us.
	 */
	if (IEEE80211_ADDR_EQ(vap->iv_myaddr, preq->preq_origaddr))
		return;

	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
	    "received PREQ, source %s", kether_ntoa(preq->preq_origaddr, ethstr));

	/*
	 * Acceptance criteria: if the PREQ is not for us and
	 * forwarding is disabled, discard this PREQ.
	 */
	if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0)) &&
	    !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) {
		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_HWMP,
		    preq->preq_origaddr, NULL, "%s", "not accepting PREQ");
		return;
	}
	rtorig = ieee80211_mesh_rt_find(vap, preq->preq_origaddr);
	if (rtorig == NULL)
		rtorig = ieee80211_mesh_rt_add(vap, preq->preq_origaddr);
	if (rtorig == NULL) {
		/* XXX stat */
		return;
	}
	hrorig = IEEE80211_MESH_ROUTE_PRIV(rtorig, struct ieee80211_hwmp_route);
	/*
	 * Sequence number validation.
	 */
	if (HWMP_SEQ_LEQ(preq->preq_id, hrorig->hr_preqid) &&
	    HWMP_SEQ_LEQ(preq->preq_origseq, hrorig->hr_seq)) {
		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
		    "discard PREQ from %s, old seq no %u <= %u",
		    kether_ntoa(preq->preq_origaddr, ethstr),
		    preq->preq_origseq, hrorig->hr_seq);
		return;
	}
	hrorig->hr_preqid = preq->preq_id;
	hrorig->hr_seq = preq->preq_origseq;

	/*
	 * Check if the PREQ is addressed to us.
	 */
	if (IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0))) {
		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
		    "reply to %s", kether_ntoa(preq->preq_origaddr, ethstr));
		/*
		 * Build and send a PREP frame.
		 */
		prep.prep_flags = 0;
		prep.prep_hopcount = 0;
		prep.prep_ttl = ms->ms_ttl;
		IEEE80211_ADDR_COPY(prep.prep_targetaddr, vap->iv_myaddr);
		prep.prep_targetseq = ++hs->hs_seq;
		prep.prep_lifetime = preq->preq_lifetime;
		prep.prep_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
		IEEE80211_ADDR_COPY(prep.prep_origaddr, preq->preq_origaddr);
		prep.prep_origseq = preq->preq_origseq;
		hwmp_send_prep(ni, vap->iv_myaddr, wh->i_addr2, &prep);
		/*
		 * Build the reverse path, if we don't have it already.
		 */
		rt = ieee80211_mesh_rt_find(vap, preq->preq_origaddr);
		if (rt == NULL)
			hwmp_discover(vap, preq->preq_origaddr, NULL);
		else if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0)
			hwmp_discover(vap, rt->rt_dest, NULL);
		return;
	}
	/*
	 * Proactive PREQ: reply with a proactive PREP to the
	 * root STA if requested.
	 */
	if (IEEE80211_ADDR_EQ(PREQ_TADDR(0), broadcastaddr) &&
	    (PREQ_TFLAGS(0) &
	    ((IEEE80211_MESHPREQ_TFLAGS_TO|IEEE80211_MESHPREQ_TFLAGS_RF) ==
	    (IEEE80211_MESHPREQ_TFLAGS_TO|IEEE80211_MESHPREQ_TFLAGS_RF)))) {
		uint8_t rootmac[IEEE80211_ADDR_LEN];

		IEEE80211_ADDR_COPY(rootmac, preq->preq_origaddr);
		rt = ieee80211_mesh_rt_find(vap, rootmac);
		if (rt == NULL) {
			rt = ieee80211_mesh_rt_add(vap, rootmac);
			if (rt == NULL) {
				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
				    "unable to add root mesh path to %s",
				    kether_ntoa(rootmac, ethstr));
				vap->iv_stats.is_mesh_rtaddfailed++;
				return;
			}
		}
		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
		    "root mesh station @ %s", kether_ntoa(rootmac, ethstr));

		/*
		 * Reply with a PREP if we don't have a path to the root
		 * or if the root sent us a proactive PREQ.
		 */
		if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0 ||
		    (preq->preq_flags & IEEE80211_MESHPREQ_FLAGS_PP)) {
			prep.prep_flags = 0;
			prep.prep_hopcount = 0;
			prep.prep_ttl = ms->ms_ttl;
			IEEE80211_ADDR_COPY(prep.prep_origaddr, rootmac);
			prep.prep_origseq = preq->preq_origseq;
			prep.prep_lifetime = preq->preq_lifetime;
			prep.prep_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
			IEEE80211_ADDR_COPY(prep.prep_targetaddr,
			    vap->iv_myaddr);
			prep.prep_targetseq = ++hs->hs_seq;
			hwmp_send_prep(vap->iv_bss, vap->iv_myaddr,
			    broadcastaddr, &prep);
		}
		hwmp_discover(vap, rootmac, NULL);
		return;
	}
	rt = ieee80211_mesh_rt_find(vap, PREQ_TADDR(0));

	/*
	 * Forwarding and Intermediate reply for PREQs with 1 target.
	 */
	if (preq->preq_tcount == 1) {
		struct ieee80211_meshpreq_ie ppreq; /* propagated PREQ */

		memcpy(&ppreq, preq, sizeof(ppreq));
		/*
		 * We have a valid route to this node.
		 */
		if (rt != NULL &&
		    (rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID)) {
			if (preq->preq_ttl > 1 &&
			    preq->preq_hopcount < hs->hs_maxhops) {
				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
				    "forward PREQ from %s",
				    kether_ntoa(preq->preq_origaddr, ethstr));
				/*
				 * Propagate the original PREQ.
				 */
				ppreq.preq_hopcount += 1;
				ppreq.preq_ttl -= 1;
				ppreq.preq_metric +=
				    ms->ms_pmetric->mpm_metric(ni);
				/*
				 * Set TO and unset RF bits because we are going
				 * to send a PREP next.
				 */
				ppreq.preq_targets[0].target_flags |=
				    IEEE80211_MESHPREQ_TFLAGS_TO;
				ppreq.preq_targets[0].target_flags &=
				    ~IEEE80211_MESHPREQ_TFLAGS_RF;
				hwmp_send_preq(ni, vap->iv_myaddr,
				    broadcastaddr, &ppreq);
			}
			/*
			 * Check if we can send an intermediate Path Reply,
			 * i.e., Target Only bit is not set.
			 */
	    		if (!(PREQ_TFLAGS(0) & IEEE80211_MESHPREQ_TFLAGS_TO)) {
				struct ieee80211_meshprep_ie prep;

				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
				    "intermediate reply for PREQ from %s",
				    kether_ntoa(preq->preq_origaddr, ethstr));
				prep.prep_flags = 0;
				prep.prep_hopcount = rt->rt_nhops + 1;
				prep.prep_ttl = ms->ms_ttl;
				IEEE80211_ADDR_COPY(&prep.prep_targetaddr,
				    PREQ_TADDR(0));
				prep.prep_targetseq = hrorig->hr_seq;
				prep.prep_lifetime = preq->preq_lifetime;
				prep.prep_metric = rt->rt_metric +
				    ms->ms_pmetric->mpm_metric(ni);
				IEEE80211_ADDR_COPY(&prep.prep_origaddr,
				    preq->preq_origaddr);
				prep.prep_origseq = hrorig->hr_seq;
				hwmp_send_prep(ni, vap->iv_myaddr,
				    broadcastaddr, &prep);
			}
		/*
		 * We have no information about this path,
		 * propagate the PREQ.
		 */
		} else if (preq->preq_ttl > 1 &&
		    preq->preq_hopcount < hs->hs_maxhops) {
			if (rt == NULL) {
				rt = ieee80211_mesh_rt_add(vap, PREQ_TADDR(0));
				if (rt == NULL) {
					IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP,
					    ni, "unable to add PREQ path to %s",
					    kether_ntoa(PREQ_TADDR(0), ethstr));
					vap->iv_stats.is_mesh_rtaddfailed++;
					return;
				}
			}
			rt->rt_metric = preq->preq_metric;
			rt->rt_lifetime = preq->preq_lifetime;
			hrorig = IEEE80211_MESH_ROUTE_PRIV(rt,
			    struct ieee80211_hwmp_route);
			hrorig->hr_seq = preq->preq_origseq;
			hrorig->hr_preqid = preq->preq_id;

			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
			    "forward PREQ from %s",
			    kether_ntoa(preq->preq_origaddr, ethstr));
			ppreq.preq_hopcount += 1;
			ppreq.preq_ttl -= 1;
			ppreq.preq_metric += ms->ms_pmetric->mpm_metric(ni);
			hwmp_send_preq(ni, vap->iv_myaddr, broadcastaddr,
			    &ppreq);
		}
	}

}
Exemple #3
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 {