/* * 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)); }
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); } }