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); }
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); } } }
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 {