Index: projects/mesh11s/sys/net80211/ieee80211_hwmp.c =================================================================== --- projects/mesh11s/sys/net80211/ieee80211_hwmp.c (revision 193897) +++ projects/mesh11s/sys/net80211/ieee80211_hwmp.c (revision 193898) @@ -1,1014 +1,1011 @@ /*- * Copyright (c) 2009 The FreeBSD Foundation * All rights reserved. * * This software was developed by Rui Paulo under sponsorship from the * FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #ifdef __FreeBSD__ __FBSDID("$FreeBSD$"); #endif /* * IEEE 802.11s Hybrid Wireless Mesh Protocol, HWMP. * * Based on March 2009, D3.0 802.11s draft spec. */ #include "opt_inet.h" #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int ieee80211_hwmp_send_action(struct ieee80211_node *, const uint8_t [IEEE80211_ADDR_LEN], const uint8_t [IEEE80211_ADDR_LEN], uint8_t *, size_t); static uint8_t *hwmp_add_meshpreq(uint8_t *, const struct ieee80211_meshpreq_ie *); static uint8_t *hwmp_add_meshprep(uint8_t *, const struct ieee80211_meshprep_ie *); static uint8_t *hwmp_add_meshperr(uint8_t *, const struct ieee80211_meshperr_ie *); static uint8_t *hwmp_add_meshrann(uint8_t *, const struct ieee80211_meshrann_ie *); static void hwmp_recv_preq(struct ieee80211vap *, struct ieee80211_node *, const struct ieee80211_meshpreq_ie *); static inline int hwmp_send_preq(struct ieee80211_node *, const uint8_t [IEEE80211_ADDR_LEN], const uint8_t [IEEE80211_ADDR_LEN], struct ieee80211_meshpreq_ie *); static void hwmp_recv_prep(struct ieee80211vap *, struct ieee80211_node *, const struct ieee80211_meshprep_ie *); static inline int hwmp_send_prep(struct ieee80211_node *, const uint8_t [IEEE80211_ADDR_LEN], const uint8_t [IEEE80211_ADDR_LEN], struct ieee80211_meshprep_ie *); static void hwmp_recv_perr(struct ieee80211vap *, struct ieee80211_node *, const struct ieee80211_meshperr_ie *); static inline int hwmp_send_perr(struct ieee80211_node *, const uint8_t [IEEE80211_ADDR_LEN], const uint8_t [IEEE80211_ADDR_LEN], struct ieee80211_meshperr_ie *); static void hwmp_recv_rann(struct ieee80211vap *, struct ieee80211_node *, const struct ieee80211_meshrann_ie *); static inline int hwmp_send_rann(struct ieee80211_node *, const uint8_t [IEEE80211_ADDR_LEN], const uint8_t [IEEE80211_ADDR_LEN], struct ieee80211_meshrann_ie *); static int ieee80211_hwmp_maxhops = 31; #ifdef notyet static int ieee80211_hwmp_maxhopstime = 500; static int ieee80211_hwmp_maxprepretries = 3; static int ieee80211_hwmp_prepminint = 100; static int ieee80211_hwmp_perrminint = 100; #endif static int ieee80211_hwmp_roottimeout = 5000; static int ieee80211_hwmp_pathtimeout = 5000; #ifdef notyet static int ieee80211_hwmp_rootmode = 0; /* XXX move to vap */ static int ieee80211_hwmp_pathtoroottimeout = 5000; static int ieee80211_hmwp_rootint = 2000; static int ieee80211_hwmp_rannint = 1000; #endif static int ieee80211_hwmp_targetonly = 0; static int ieee80211_hwmp_replyforward = 1; #ifdef notyet static int ieee80211_hwmp_pathmaintenance = 2000; static int ieee80211_hwmp_confirmint = 2000; #endif /* * Target Address set in a Proactive PREQ. * XXX */ static const uint8_t proactiveaddr[IEEE80211_ADDR_LEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; static const uint8_t broadcastaddr[IEEE80211_ADDR_LEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; static const uint8_t invalidaddr[IEEE80211_ADDR_LEN] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; SYSCTL_NODE(_net_wlan, OID_AUTO, hwmp, CTLFLAG_RD, 0, "IEEE 802.11s HWMP parameters"); SYSCTL_INT(_net_wlan_hwmp, OID_AUTO, maxhops, CTLTYPE_INT | CTLFLAG_RW, &ieee80211_hwmp_maxhops, 0, "Maximum number of hops for paths"); SYSCTL_INT(_net_wlan_hwmp, OID_AUTO, targetonly, CTLTYPE_INT | CTLFLAG_RW, &ieee80211_hwmp_targetonly, 0, "Set TO bit on generated PREQs"); SYSCTL_INT(_net_wlan_hwmp, OID_AUTO, replyforward, CTLTYPE_INT | CTLFLAG_RW, &ieee80211_hwmp_replyforward, 0, "Set RF bit on generated PREQs"); #ifdef notyet SYSCTL_INT(_net_wlan_hwmp, OID_AUTO, rootmode, CTLTYPE_INT | CTLFLAG_RW, &ieee80211_hwmp_rootmode, 0, "0 = Not a Root, " "2 = Root with normal PREPs, 3 = Root with proactive PREPs, " "3 = Root with RANNs"); #endif extern int ieee80211_mesh_ttl; void ieee80211_hwmp_vattach(struct ieee80211vap *vap) { struct ieee80211_hwmp_state *hs; KASSERT(vap->iv_opmode == IEEE80211_M_MBSS, ("not a mesh vap, opmode %d", vap->iv_opmode)); hs = malloc(sizeof(struct ieee80211_hwmp_state), M_80211_VAP, M_NOWAIT | M_ZERO); if (hs == NULL) { printf("%s: couldn't alloc HWMP state\n", __func__); return; } TAILQ_INIT(&hs->hs_head); mtx_init(&hs->hs_lock, "HWMP", "802.11s HWMP", MTX_DEF); vap->iv_hwmp = hs; } void ieee80211_hwmp_vdetach(struct ieee80211vap *vap) { struct ieee80211_hwmp_state *hs = vap->iv_hwmp; /* XXX missing flush table */ mtx_destroy(&hs->hs_lock); free(vap->iv_hwmp, M_80211_VAP); } void ieee80211_hwmp_recv_action(struct ieee80211vap *vap, struct ieee80211_node *ni, struct mbuf *m0) { struct ieee80211_frame *wh; struct ieee80211_action *ia; uint8_t *frm, *efrm; struct ieee80211_meshpreq_ie *meshpreq = NULL; struct ieee80211_meshprep_ie *meshprep = NULL; struct ieee80211_meshperr_ie *meshperr = NULL; struct ieee80211_meshrann_ie *meshrann = NULL; wh = mtod(m0, struct ieee80211_frame *); ia = (struct ieee80211_action *) &wh[1]; frm = (uint8_t *)&wh[1] + sizeof(struct ieee80211_action); efrm = mtod(m0, uint8_t *) + m0->m_len; KASSERT(ia->ia_category == IEEE80211_ACTION_CAT_MESHPATH, ("wrong category")); while (efrm - frm > 1) { /*IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return);*/ switch (*frm) { case IEEE80211_ELEMID_MESHPREQ: meshpreq = (struct ieee80211_meshpreq_ie *) frm; meshpreq->preq_id = LE_READ_4(&meshpreq->preq_id); meshpreq->preq_origseq = LE_READ_4(&meshpreq->preq_origseq); meshpreq->preq_lifetime = LE_READ_4(&meshpreq->preq_lifetime); meshpreq->preq_metric = LE_READ_4(&meshpreq->preq_metric); break; case IEEE80211_ELEMID_MESHPREP: meshprep = (struct ieee80211_meshprep_ie *) frm; meshprep->prep_targetseq = LE_READ_4(&meshprep->prep_targetseq); meshprep->prep_lifetime = LE_READ_4(&meshprep->prep_lifetime); meshprep->prep_metric = LE_READ_4(&meshprep->prep_metric); meshprep->prep_origseq = LE_READ_4(&meshprep->prep_origseq); break; case IEEE80211_ELEMID_MESHPERR: meshperr = (struct ieee80211_meshperr_ie *) frm; break; case IEEE80211_ELEMID_MESHRANN: meshrann = (struct ieee80211_meshrann_ie *) frm; meshrann->rann_seq = LE_READ_4(&meshrann->rann_seq); meshrann->rann_metric = LE_READ_4(&meshrann->rann_metric); break; } frm += frm[1] + 2; } switch (ia->ia_action) { case IEEE80211_ACTION_MESHPATH_REQ: if (meshpreq == NULL) { IEEE80211_DISCARD(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, wh, NULL, "%s", "PREQ without IE"); vap->iv_stats.is_rx_mgtdiscard++; return; } hwmp_recv_preq(vap, ni, meshpreq); break; case IEEE80211_ACTION_MESHPATH_REP: if (meshprep == NULL) { IEEE80211_DISCARD(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, wh, NULL, "%s", "PREP without IE"); vap->iv_stats.is_rx_mgtdiscard++; return; } hwmp_recv_prep(vap, ni, meshprep); break; case IEEE80211_ACTION_MESHPATH_ERR: if (meshperr == NULL) { IEEE80211_DISCARD(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, wh, NULL, "%s", "PERR without IE"); vap->iv_stats.is_rx_mgtdiscard++; return; } hwmp_recv_perr(vap, ni, meshperr); break; case IEEE80211_ACTION_MESHPATH_RANN: if (meshrann == NULL) { IEEE80211_DISCARD(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, wh, NULL, "%s", "RANN without IE"); vap->iv_stats.is_rx_mgtdiscard++; return; } hwmp_recv_rann(vap, ni, meshrann); break; default: IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_MESH, ni->ni_macaddr, NULL, "unknown path action 0x%x", ia->ia_action); } } static int ieee80211_hwmp_send_action(struct ieee80211_node *ni, const uint8_t sa[IEEE80211_ADDR_LEN], const uint8_t da[IEEE80211_ADDR_LEN], uint8_t *ie, size_t len) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; struct ieee80211_bpf_params params; struct mbuf *m; uint8_t *frm; if (vap->iv_state == IEEE80211_S_CAC) { IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, ni, "block %s frame in CAC state", "probe request"); vap->iv_stats.is_tx_badstate++; return EIO; /* XXX */ } KASSERT(ni != NULL, ("null node")); /* * Hold a reference on the node so it doesn't go away until after * the xmit is complete all the way in the driver. On error we * will remove our reference. */ IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__, ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1); ieee80211_ref_node(ni); m = ieee80211_getmgtframe(&frm, ic->ic_headroom + sizeof(struct ieee80211_frame), sizeof(struct ieee80211_action) + len ); if (m == NULL) { ieee80211_free_node(ni); vap->iv_stats.is_tx_nobuf++; return ENOMEM; } *frm++ = IEEE80211_ACTION_CAT_MESHPATH; switch (*ie) { case IEEE80211_ELEMID_MESHPREQ: *frm++ = IEEE80211_ACTION_MESHPATH_REQ; frm = hwmp_add_meshpreq(frm, (struct ieee80211_meshpreq_ie *)ie); break; case IEEE80211_ELEMID_MESHPREP: *frm++ = IEEE80211_ACTION_MESHPATH_REP; frm = hwmp_add_meshprep(frm, (struct ieee80211_meshprep_ie *)ie); break; case IEEE80211_ELEMID_MESHPERR: *frm++ = IEEE80211_ACTION_MESHPATH_ERR; frm = hwmp_add_meshperr(frm, (struct ieee80211_meshperr_ie *)ie); break; case IEEE80211_ELEMID_MESHRANN: *frm++ = IEEE80211_ACTION_MESHPATH_RANN; frm = hwmp_add_meshrann(frm, (struct ieee80211_meshrann_ie *)ie); break; } m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT); if (m == NULL) { ieee80211_free_node(ni); vap->iv_stats.is_tx_nobuf++; return ENOMEM; } ieee80211_send_setup(ni, m, IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_ACTION, IEEE80211_NONQOS_TID, sa, da, sa); m->m_flags |= M_ENCAP; /* mark encapsulated */ IEEE80211_NODE_STAT(ni, tx_mgmt); memset(¶ms, 0, sizeof(params)); params.ibp_pri = WME_AC_VO; params.ibp_rate0 = ni->ni_txparms->mgmtrate; /* XXX: NB: we know all frames are unicast */ params.ibp_try0 = ni->ni_txparms->maxretry; params.ibp_power = ni->ni_txpower; return ic->ic_raw_xmit(ni, m, ¶ms); } #define ADDWORD(frm, v) do { \ frm[0] = (v) & 0xff; \ frm[1] = ((v) >> 8) & 0xff; \ frm[2] = ((v) >> 16) & 0xff; \ frm[3] = ((v) >> 24) & 0xff; \ frm += 4; \ } while (0) /* * Add a Mesh Path Request IE to a frame. */ static uint8_t * hwmp_add_meshpreq(uint8_t *frm, const struct ieee80211_meshpreq_ie *preq) { int i; *frm++ = IEEE80211_ELEMID_MESHPREQ; *frm++ = sizeof(struct ieee80211_meshpreq_ie) - 2 + (preq->preq_tcount - 1) * sizeof(*preq->preq_targets); *frm++ = preq->preq_flags; *frm++ = preq->preq_hopcount; *frm++ = preq->preq_ttl; ADDWORD(frm, preq->preq_id); IEEE80211_ADDR_COPY(frm, preq->preq_origaddr); frm += 6; ADDWORD(frm, preq->preq_origseq); ADDWORD(frm, preq->preq_lifetime); ADDWORD(frm, preq->preq_metric); *frm++ = preq->preq_tcount; for (i = 0; i < preq->preq_tcount; i++) { *frm++ = preq->preq_targets[i].target_flags; IEEE80211_ADDR_COPY(frm, preq->preq_targets[i].target_addr); frm += 6; ADDWORD(frm, preq->preq_targets[i].target_seq); } return frm; } /* * Add a Mesh Path Reply IE to a frame. */ static uint8_t * hwmp_add_meshprep(uint8_t *frm, const struct ieee80211_meshprep_ie *prep) { *frm++ = IEEE80211_ELEMID_MESHPREP; *frm++ = sizeof(struct ieee80211_meshprep_ie) - 2; *frm++ = prep->prep_flags; *frm++ = prep->prep_hopcount; *frm++ = prep->prep_ttl; IEEE80211_ADDR_COPY(frm, prep->prep_targetaddr); frm += 6; ADDWORD(frm, prep->prep_targetseq); ADDWORD(frm, prep->prep_lifetime); ADDWORD(frm, prep->prep_metric); IEEE80211_ADDR_COPY(frm, prep->prep_origaddr); frm += 6; ADDWORD(frm, prep->prep_origseq); return frm; } /* * Add a Mesh Path Error IE to a frame. */ static uint8_t * hwmp_add_meshperr(uint8_t *frm, const struct ieee80211_meshperr_ie *perr) { int i; *frm++ = IEEE80211_ELEMID_MESHPERR; *frm++ = sizeof(struct ieee80211_meshperr_ie) - 2 + (perr->perr_ndests - 1) * sizeof(*perr->perr_dests); *frm++ = perr->perr_mode; *frm++ = perr->perr_ndests; for (i = 0; i < perr->perr_ndests; i++) { IEEE80211_ADDR_COPY(frm, perr->perr_dests[i].dest_addr); frm += 6; ADDWORD(frm, perr->perr_dests[i].dest_seq); } return frm; } /* * Add a Root Annoucement IE to a frame. */ static uint8_t * hwmp_add_meshrann(uint8_t *frm, const struct ieee80211_meshrann_ie *rann) { *frm++ = IEEE80211_ELEMID_MESHRANN; *frm++ = sizeof(struct ieee80211_meshrann_ie) - 2; *frm++ = rann->rann_flags; *frm++ = rann->rann_hopcount; *frm++ = rann->rann_ttl; IEEE80211_ADDR_COPY(frm, rann->rann_addr); frm += 6; ADDWORD(frm, rann->rann_seq); ADDWORD(frm, rann->rann_metric); return frm; } #define PREQ_TFLAGS(n) preq->preq_targets[n].target_flags #define PREQ_TADDR(n) preq->preq_targets[n].target_addr #define PREQ_TSEQ(n) preq->preq_targets[n].target_seq static void hwmp_recv_preq(struct ieee80211vap *vap, struct ieee80211_node *ni, const struct ieee80211_meshpreq_ie *preq) { struct ieee80211_hwmp_state *hs = vap->iv_hwmp; struct ieee80211_hwmp_fi *fi = NULL; IEEE80211_NOTE(vap, IEEE80211_MSG_MESH, ni, "received PREQ, source %s", ether_sprintf(preq->preq_origaddr)); /* * 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)) && !(vap->iv_meshflags & IEEE80211_MFLAGS_FWRD)) { IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_MESH, preq->preq_origaddr, NULL, "%s", "not accepting PREQ"); return; } mtx_lock(&hs->hs_lock); TAILQ_FOREACH(fi, &hs->hs_head, fi_next) { if (IEEE80211_ADDR_EQ(PREQ_TADDR(0), fi->fi_dest)) break; } mtx_unlock(&hs->hs_lock); /* * Record the PREQ ID and the originator MAC address. */ if (fi == NULL) { fi = malloc(sizeof(struct ieee80211_hwmp_fi), M_80211_VAP, M_NOWAIT | M_ZERO); memset(fi, 0, sizeof(*fi)); IEEE80211_ADDR_COPY(fi->fi_dest, preq->preq_origaddr); fi->fi_seq = preq->preq_origseq; fi->fi_metric = preq->preq_metric; fi->fi_lifetime = preq->preq_lifetime; mtx_lock(&hs->hs_lock); TAILQ_INSERT_TAIL(&hs->hs_head, fi, fi_next); mtx_unlock(&hs->hs_lock); } fi->fi_preqid = preq->preq_id; /* * Check if the PREQ is addressed to us. * XXX: check if this is part of a proxy address. */ if (IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0))) { struct ieee80211_meshprep_ie prep; IEEE80211_NOTE(vap, IEEE80211_MSG_MESH, ni, "replying to %s", ether_sprintf(preq->preq_origaddr)); /* * Build and send a PREP frame. */ prep.prep_flags = 0; prep.prep_hopcount = 0; prep.prep_ttl = ieee80211_mesh_ttl; IEEE80211_ADDR_COPY(prep.prep_targetaddr, preq->preq_targets[0].target_addr); prep.prep_targetseq = preq->preq_origseq; prep.prep_lifetime = preq->preq_lifetime; prep.prep_metric = IEEE80211_MESHLMETRIC_INITIALVAL; IEEE80211_ADDR_COPY(prep.prep_origaddr, vap->iv_myaddr); prep.prep_origseq = hs->hs_seq++; /* XXX addr1 = next hop */ hwmp_send_prep(ni, vap->iv_myaddr, preq->preq_origaddr, &prep); /* * Build the reverse path, if we don't have it already. */ if (fi == NULL || memcmp(fi->fi_nexthop, invalidaddr, IEEE80211_ADDR_LEN)) { ieee80211_hwmp_discover(vap, fi->fi_dest); } return; } /* XXX missing. Check for AE bit and update proxy information */ /* * Intermediate reply for PREQs with 1 target. */ if (preq->preq_ttl > 1 && preq->preq_tcount == 1 && !(PREQ_TFLAGS(0) & IEEE80211_MESHPREQ_TFLAGS_TO)) { struct ieee80211_meshpreq_ie ppreq; /* propagated PREQ */ memcpy(&ppreq, preq, sizeof(ppreq)); /* * Can we do an intermediate path reply? */ if (fi != NULL) { struct ieee80211_meshprep_ie prep; IEEE80211_NOTE(vap, IEEE80211_MSG_MESH, ni, "replying and forwarding PREQ from %s", ether_sprintf(preq->preq_origaddr)); /* * Propagate the original PREQ. */ ppreq.preq_hopcount += 1; ppreq.preq_ttl -= 1; ppreq.preq_metric += ieee80211_airtime_calc(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); /* * Build and send an intermediate PREP. */ prep.prep_flags = 0; prep.prep_hopcount = 0; prep.prep_ttl = ieee80211_mesh_ttl; IEEE80211_ADDR_COPY(&prep.prep_targetaddr, preq->preq_origaddr); prep.prep_targetseq = fi->fi_seq; prep.prep_lifetime = preq->preq_lifetime; prep.prep_metric = fi->fi_metric; IEEE80211_ADDR_COPY(&prep.prep_origaddr, vap->iv_myaddr); prep.prep_origseq = hs->hs_seq++; hwmp_send_prep(ni, vap->iv_myaddr, broadcastaddr, &prep); } else { IEEE80211_NOTE(vap, IEEE80211_MSG_MESH, ni, "forwarding PREQ from %s", ether_sprintf(preq->preq_origaddr)); /* * We have no information about this path, * propagate the PREQ. */ ppreq.preq_hopcount += 1; ppreq.preq_ttl -= 1; ppreq.preq_metric += ieee80211_airtime_calc(ni); hwmp_send_preq(ni, vap->iv_myaddr, broadcastaddr, &ppreq); } /* * XXX: Update the percursor table */ return; } /* * XXX: Proactive PREQ: reply with a proactive PREP to the * root STA if requested. */ if (IEEE80211_ADDR_EQ(PREQ_TADDR(0), proactiveaddr) && (PREQ_TFLAGS(0) & ((IEEE80211_MESHPREQ_TFLAGS_TO|IEEE80211_MESHPREQ_TFLAGS_RF) == (IEEE80211_MESHPREQ_TFLAGS_TO|IEEE80211_MESHPREQ_TFLAGS_RF)))) { /* * Does the root want a proactive PREP ? */ if (preq->preq_flags & IEEE80211_MESHPREQ_FLAGS_PP) { } } } #undef PREQ_TFLAGS #undef PREQ_TADDR #undef PREQ_TSEQ static inline int hwmp_send_preq(struct ieee80211_node *ni, const uint8_t sa[IEEE80211_ADDR_LEN], const uint8_t da[IEEE80211_ADDR_LEN], struct ieee80211_meshpreq_ie *preq) { /* * mesh preq action frame format * [6] da * [6] sa * [6] addr3 = sa * [1] action * [1] category * [tlv] mesh path request */ preq->preq_ie = IEEE80211_ELEMID_MESHPREQ; return ieee80211_hwmp_send_action(ni, sa, da, (uint8_t *)preq, sizeof(struct ieee80211_meshpreq_ie)); } static void hwmp_recv_prep(struct ieee80211vap *vap, struct ieee80211_node *ni, const struct ieee80211_meshprep_ie *prep) { /* * Acceptance criteria: if the PREP was not generated by us and * forwarding is disabled, discard this PREP. */ if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, prep->prep_origaddr) && !(vap->iv_meshflags & IEEE80211_MFLAGS_FWRD)) return; /* * Update the Forwarding Information. */ /* * If it's NOT for us, propagate the PREP if TTL is * greater than 1. */ if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, prep->prep_targetaddr) && prep->prep_ttl > 1) { struct ieee80211_meshprep_ie pprep; /* propagated PREP */ memcpy(&pprep, prep, sizeof(pprep)); pprep.prep_hopcount += 1; pprep.prep_ttl -= 1; pprep.prep_metric += ieee80211_airtime_calc(ni); IEEE80211_ADDR_COPY(pprep.prep_origaddr, vap->iv_myaddr); hwmp_send_prep(ni, vap->iv_myaddr, broadcastaddr, &pprep); /* * XXX: update the precursor list. */ return; } /* * XXX: If it's for us and the AE bit is set, update the * proxy information table. */ /* * XXX: If it's NOT for us and the AE bit is set, * update the proxy information table. */ } static inline int hwmp_send_prep(struct ieee80211_node *ni, const uint8_t sa[IEEE80211_ADDR_LEN], const uint8_t da[IEEE80211_ADDR_LEN], struct ieee80211_meshprep_ie *prep) { /* * mesh prep action frame format * [6] da * [6] sa * [6] addr3 = sa * [1] action * [1] category * [tlv] mesh path reply */ prep->prep_ie = IEEE80211_ELEMID_MESHPREP; return ieee80211_hwmp_send_action(ni, sa, da, (uint8_t *)prep, sizeof(struct ieee80211_meshprep_ie)); } #define PERR_DADDR(n) perr->perr_dests[n].dest_addr #define PREQ_DSEQ(n) perr->perr_dests[n].dest_seq static void hwmp_recv_perr(struct ieee80211vap *vap, struct ieee80211_node *ni, const struct ieee80211_meshperr_ie *perr) { struct ieee80211_hwmp_state *hs = vap->iv_hwmp; struct ieee80211_hwmp_fi *fi = NULL; /* struct ieee80211_meshperr_ie pperr;*/ /* * Acceptance criteria: check if we received a PERR from a * neighbor and forwarding is 1. */ if (ni == vap->iv_bss || ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED || !(vap->iv_meshflags & IEEE80211_MFLAGS_FWRD)) return; mtx_lock(&hs->hs_lock); TAILQ_FOREACH(fi, &hs->hs_head, fi_next) { if (IEEE80211_ADDR_EQ(PERR_DADDR(0), fi->fi_dest)) break; } mtx_unlock(&hs->hs_lock); if (fi == NULL) return; /* * Propagate the PERR. */ } static inline int hwmp_send_perr(struct ieee80211_node *ni, const uint8_t sa[IEEE80211_ADDR_LEN], const uint8_t da[IEEE80211_ADDR_LEN], struct ieee80211_meshperr_ie *perr) { /* * mesh perr action frame format * [6] da * [6] sa * [6] addr3 = sa * [1] action * [1] category * [tlv] mesh path error */ return ieee80211_hwmp_send_action(ni, sa, da, (uint8_t *)perr, sizeof(struct ieee80211_meshperr_ie)); } static void hwmp_recv_rann(struct ieee80211vap *vap, struct ieee80211_node *ni, const struct ieee80211_meshrann_ie *rann) { struct ieee80211_hwmp_state *hs = vap->iv_hwmp; struct ieee80211_hwmp_fi *fi = NULL; /* * Acceptance criteria: check the HWMP sequence number * and the path metric is better than what we have. */ mtx_lock(&hs->hs_lock); TAILQ_FOREACH(fi, &hs->hs_head, fi_next) { if (IEEE80211_ADDR_EQ(rann->rann_addr, fi->fi_dest)) break; } mtx_unlock(&hs->hs_lock); if (fi == NULL) { struct ieee80211_meshpreq_ie preq; /* * Try to establish a path to this root mesh station. */ preq.preq_flags = 0; preq.preq_hopcount = 0; preq.preq_ttl = ieee80211_mesh_ttl; IEEE80211_ADDR_COPY(&preq.preq_origaddr, vap->iv_myaddr); preq.preq_origseq = hs->hs_seq++; preq.preq_targets[0].target_flags |= IEEE80211_MESHPREQ_TFLAGS_TO; IEEE80211_ADDR_COPY(preq.preq_targets[0].target_addr, rann->rann_addr); preq.preq_targets[0].target_seq = rann->rann_seq; preq.preq_lifetime = ieee80211_hwmp_roottimeout; preq.preq_metric = IEEE80211_MESHLMETRIC_INITIALVAL; hwmp_send_preq(ni, vap->iv_myaddr, broadcastaddr, &preq); /* * We have a path for this Root Mesh station, so * propagate the RANN if forwarding is enabled. */ } else if (rann->rann_ttl > 1 && (vap->iv_meshflags & IEEE80211_MFLAGS_FWRD)) { struct ieee80211_meshrann_ie prann; memcpy(&prann, rann, sizeof(prann)); prann.rann_hopcount += 1; prann.rann_ttl -= 1; prann.rann_metric += ieee80211_airtime_calc(ni); hwmp_send_rann(ni, vap->iv_myaddr, broadcastaddr, &prann); } } static inline int hwmp_send_rann(struct ieee80211_node *ni, const uint8_t sa[IEEE80211_ADDR_LEN], const uint8_t da[IEEE80211_ADDR_LEN], struct ieee80211_meshrann_ie *rann) { /* * mesh rann action frame format * [6] da * [6] sa * [6] addr3 = sa * [1] action * [1] category * [tlv] root annoucement */ return ieee80211_hwmp_send_action(ni, sa, da, (uint8_t *)rann, sizeof(struct ieee80211_meshrann_ie)); } #define PREQ_TFLAGS(n) preq.preq_targets[n].target_flags #define PREQ_TADDR(n) preq.preq_targets[n].target_addr #define PREQ_TSEQ(n) preq.preq_targets[n].target_seq struct ieee80211_node * ieee80211_hwmp_discover(struct ieee80211vap *vap, uint8_t dest[IEEE80211_ADDR_LEN]) { struct ieee80211_hwmp_state *hs = vap->iv_hwmp; struct ieee80211_hwmp_fi *fi = NULL; - static const uint8_t invalidaddr[] = { 0, 0, 0, 0, 0, 0 }; struct ieee80211_meshpreq_ie preq; int sendpreq = 0, unknowndst = 0; KASSERT(vap->iv_opmode == IEEE80211_M_MBSS, ("not a mesh vap, opmode %d", vap->iv_opmode)); if (IEEE80211_IS_MULTICAST(dest)) return ieee80211_find_txnode(vap, dest); IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, dest, "%s", "initiating path discovery"); mtx_lock(&hs->hs_lock); TAILQ_FOREACH(fi, &hs->hs_head, fi_next) { - if (IEEE80211_ADDR_EQ(fi->fi_dest, dest)) { - if (IEEE80211_ADDR_EQ(fi->fi_nexthop, invalidaddr)) { - /* XXX check preq retries */ - sendpreq = 1; - unknowndst = 1; - } + if (IEEE80211_ADDR_EQ(fi->fi_dest, dest) && + IEEE80211_ADDR_EQ(fi->fi_nexthop, invalidaddr)) { + /* XXX check preq retries */ + sendpreq = 1; + unknowndst = 1; break; } } mtx_unlock(&hs->hs_lock); if (fi == NULL) { fi = malloc(sizeof(struct ieee80211_hwmp_fi), M_80211_VAP, M_NOWAIT | M_ZERO); - memcpy(fi->fi_dest, dest, IEEE80211_ADDR_LEN); + IEEE80211_ADDR_COPY(fi->fi_dest, dest); fi->fi_seq = ++hs->hs_seq; fi->fi_metric = IEEE80211_MESHLMETRIC_INITIALVAL; fi->fi_lifetime = ieee80211_hwmp_pathtimeout; mtx_lock(&hs->hs_lock); TAILQ_INSERT_TAIL(&hs->hs_head, fi, fi_next); mtx_unlock(&hs->hs_lock); sendpreq = 1; unknowndst = 1; } if (sendpreq) { /* * Try to discover the path for this node. */ preq.preq_flags = 0; preq.preq_hopcount = 0; preq.preq_ttl = ieee80211_mesh_ttl; preq.preq_id = ++hs->hs_preqid; IEEE80211_ADDR_COPY(preq.preq_origaddr, vap->iv_myaddr); preq.preq_origseq = fi->fi_seq; preq.preq_lifetime = fi->fi_lifetime; preq.preq_metric = fi->fi_metric; preq.preq_tcount = 1; IEEE80211_ADDR_COPY(PREQ_TADDR(0), dest); if (ieee80211_hwmp_targetonly) PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_TO; if (ieee80211_hwmp_replyforward) PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_RF; if (unknowndst) { PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_USN; PREQ_TSEQ(0) = 0; } else PREQ_TSEQ(0) = fi->fi_seq; hwmp_send_preq(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &preq); } else return vap->iv_bss; //ieee80211_find_txnode(vap, fi->fi_nexthop); return vap->iv_bss; } #undef PREQ_TFLAGS #undef PREQ_TADDR #undef PREQ_TSEQ - static int hwmp_ioctl_get80211(struct ieee80211vap *vap, struct ieee80211req *ireq) { int error; size_t len, off; struct ieee80211_hwmp_state *hs; struct ieee80211_hwmp_fi *fi; uint8_t *p; - + if (vap->iv_opmode != IEEE80211_M_MBSS) return EINVAL; error = 0; switch (ireq->i_type) { case IEEE80211_IOC_HWMP_TABLE: hs = vap->iv_hwmp; len = 0; mtx_lock(&hs->hs_lock); TAILQ_FOREACH(fi, &hs->hs_head, fi_next) { len += sizeof(*fi); } mtx_unlock(&hs->hs_lock); if (len > ireq->i_len || ireq->i_len < sizeof(*fi)) return ENOMEM; ireq->i_len = len; p = malloc(len, M_TEMP, M_NOWAIT | M_ZERO); if (p == NULL) return ENOMEM; off = 0; mtx_lock(&hs->hs_lock); TAILQ_FOREACH(fi, &hs->hs_head, fi_next) { if (off >= len) break; memcpy(p + off, fi, sizeof(*fi)); off += sizeof(*fi); } mtx_unlock(&hs->hs_lock); error = copyout(p, (uint8_t *) ireq->i_data, ireq->i_len); free(p, M_TEMP); break; default: return ENOSYS; } return error; } IEEE80211_IOCTL_GET(hwmp, hwmp_ioctl_get80211); static int hwmp_ioctl_set80211(struct ieee80211vap *vap, struct ieee80211req *ireq) { int error; if (vap->iv_opmode != IEEE80211_M_MBSS) return EINVAL; error = 0; switch (ireq->i_type) { case IEEE80211_IOC_HWMP_TABLE: default: return ENOSYS; } return error; } IEEE80211_IOCTL_SET(hwmp, hwmp_ioctl_set80211);