Index: projects/mesh11s/sys/net80211/ieee80211_hwmp.c =================================================================== --- projects/mesh11s/sys/net80211/ieee80211_hwmp.c (revision 193045) +++ projects/mesh11s/sys/net80211/ieee80211_hwmp.c (revision 193046) @@ -1,634 +1,709 @@ /*- * 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 /* * HWMP Forwarding Information table. + * XXX: this should be in vap */ struct ieee80211_hwmp_fi { - TAILQ_ENTRY(ieee80211_hwmp_fi) fi_list; - uint8_t fi_target[IEEE80211_ADDR_LEN]; + TAILQ_ENTRY(ieee80211_hwmp_fi) fi_next; + uint8_t fi_dest[IEEE80211_ADDR_LEN]; ieee80211_seq fi_seq; /* HWMP sequence number */ - uint8_t fi_mnxthop[IEEE80211_ADDR_LEN]; + uint8_t fi_nexthop[IEEE80211_ADDR_LEN]; uint32_t fi_metric; /* Path Metric */ uint32_t fi_nhops; /* Number of Hops */ - /* XXX percursor list */ + uint8_t fi_prevhop[IEEE80211_ADDR_LEN]; uint32_t fi_lifetime; }; TAILQ_HEAD(, ieee80211_hwmp_fi) ieee80211_hwmp_ft; static int ieee80211_hwmp_send_action(struct ieee80211_node *, const uint8_t [IEEE80211_ADDR_LEN], const uint8_t [IEEE80211_ADDR_LEN], const uint8_t *, size_t); static void hwmp_recv_preq(struct ieee80211vap *, struct ieee80211_node *, const struct ieee80211_meshpreq_ie *); static int hwmp_send_preq(struct ieee80211_node *, const uint8_t [IEEE80211_ADDR_LEN], const uint8_t [IEEE80211_ADDR_LEN], - const struct ieee80211_meshpreq_ie *); + struct ieee80211_meshpreq_ie *); static void hwmp_recv_prep(struct ieee80211vap *, struct ieee80211_node *, const struct ieee80211_meshprep_ie *); static int hwmp_send_prep(struct ieee80211_node *, const uint8_t [IEEE80211_ADDR_LEN], const uint8_t [IEEE80211_ADDR_LEN], - const struct ieee80211_meshprep_ie *); + struct ieee80211_meshprep_ie *); static void hwmp_recv_perr(struct ieee80211vap *, struct ieee80211_node *, const struct ieee80211_meshperr_ie *); static int hwmp_send_perr(struct ieee80211_node *, const uint8_t [IEEE80211_ADDR_LEN], const uint8_t [IEEE80211_ADDR_LEN], - const struct ieee80211_meshperr_ie *); + struct ieee80211_meshperr_ie *); static void hwmp_recv_rann(struct ieee80211vap *, struct ieee80211_node *, - const struct ieee80211_meshrann_ie *); + const struct ieee80211_meshrann_ie *); #ifdef notyet static int hwmp_send_rann(struct ieee80211_node *, const uint8_t [IEEE80211_ADDR_LEN], const uint8_t [IEEE80211_ADDR_LEN], - const struct ieee80211_meshrann_ie *); + struct ieee80211_meshrann_ie *); #endif 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; static int ieee80211_hwmp_roottimeout = 5000; static int ieee80211_hwmp_pathtimeout = 5000; static int ieee80211_hwmp_rootmode = 0; static int ieee80211_hwmp_pathtoroottimeout = 5000; static int ieee80211_hmwp_rootint = 2000; static int ieee80211_hwmp_rannint = 1000; #endif static int ieee80211_hwmp_targetonly = 1;/* reply to PREQs automatically */ static int ieee80211_hwmp_replyforward = 1; /* propagate PREQs */ #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 }; 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, "TBD"); SYSCTL_INT(_net_wlan_hwmp, OID_AUTO, replyforward, CTLTYPE_INT | CTLFLAG_RW, &ieee80211_hwmp_replyforward, 0, "TBD"); #ifdef notyet SYSCTL_INT(_net_wlan_hwmp, OID_AUTO, rootmode, CTLTYPE_INT | CTLFLAG_RW, - &ieee80211_hwmp_rootmode, 0, "Root Mesh Point Node"); + &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; extern int ieee80211_mesh_forwarding; 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 addr1[IEEE80211_ADDR_LEN], const uint8_t addr2[IEEE80211_ADDR_LEN], const 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; break; case IEEE80211_ELEMID_MESHPREP: *frm++ = IEEE80211_ACTION_MESHPATH_REP; frm = ieee80211_add_meshprep(frm, (struct ieee80211_meshprep_ie *)&ie); break; case IEEE80211_ELEMID_MESHPERR: *frm++ = IEEE80211_ACTION_MESHPATH_ERR; break; case IEEE80211_ELEMID_MESHRANN: *frm++ = IEEE80211_ACTION_MESHPATH_RANN; 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, addr1, addr2, addr2); 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 Reply IE to a frame. */ uint8_t * ieee80211_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; } #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_fi *fi; + /* * 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)) && !ieee80211_mesh_forwarding) return; + fi = NULL; + /*HWMP_LOCK();*/ + TAILQ_FOREACH(fi, &ieee80211_hwmp_ft, fi_next) { + if (IEEE80211_ADDR_EQ(PREQ_TADDR(0), fi->fi_dest)) + break; + } + /*HWMP_UNLOCK();*/ + /* * Step 1. Record the PREQ ID and the originator MAC address. */ /* * Step 2. */ /* * Step 3. 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; /* * 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); /* XXX */ prep.prep_origseq = 1; /* XXX addr1 = next hop */ hwmp_send_prep(ni, preq->preq_origaddr, vap->iv_myaddr, &prep); return; } /* XXX: Step 4. Check for AE bit and update proxy information */ /* - * Step 5. Intermediate reply: check if we have this path on our - * table and the TO bit is unset. + * Step 5. Intermediate reply for PREQs with 1 target. */ - if (!(PREQ_TFLAGS(0) & IEEE80211_MESHPREQ_TFLAGS_TO)) { + 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; + + /* + * 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); + /* XXX */ + prep.prep_origseq = 1; + hwmp_send_prep(ni, vap->iv_myaddr, broadcastaddr, + &prep); + } else { + /* + * 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: Step 6. Update the percursor table */ + return; } + /* * XXX: Step 8. 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 addr1[IEEE80211_ADDR_LEN], const uint8_t addr2[IEEE80211_ADDR_LEN], - const struct ieee80211_meshpreq_ie *preq) + struct ieee80211_meshpreq_ie *preq) { /* * mesh preq action frame format * [6] addr1 * [6] addr2 * [6] addr3 = addr2 * [1] action * [1] category * [tlv] mesh path request */ + /* XXX target count > 1 */ + preq->preq_ie = IEEE80211_ELEMID_MESHPREQ; + preq->preq_len = sizeof(struct ieee80211_meshpreq_ie) - 2; + return ieee80211_hwmp_send_action(ni, addr1, addr2, (uint8_t *)&preq, sizeof(*preq)); } static void hwmp_recv_prep(struct ieee80211vap *vap, struct ieee80211_node *ni, const struct ieee80211_meshprep_ie *prep) { - union ieee80211_send_action_args vargs; /* * 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) && !ieee80211_mesh_forwarding) return; /* * Step 1: Update the Forwarding Information. */ + /* * Step 2: 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(vap, ni); + pprep.prep_metric += ieee80211_airtime_calc(ni); IEEE80211_ADDR_COPY(pprep.prep_origaddr, vap->iv_myaddr); pprep.prep_origseq = 1; /* XXX */ - - vargs.ptrarg = &pprep; - ieee80211_send_action(ni, IEEE80211_ACTION_CAT_MESHPATH, - IEEE80211_ACTION_MESHPATH_REQ, vargs); - + hwmp_send_prep(ni, vap->iv_myaddr, broadcastaddr, + &pprep); /* * XXX: Step 5: update the precursor list. */ return; } /* * XXX: Step 3: If it's for us and the AE bit is set, update the * proxy information table. */ /* * XXX: Step 4: 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 addr1[IEEE80211_ADDR_LEN], const uint8_t addr2[IEEE80211_ADDR_LEN], - const struct ieee80211_meshprep_ie *prep) + struct ieee80211_meshprep_ie *prep) { /* * mesh prep action frame format * [6] addr1 * [6] addr2 * [6] addr3 = addr2 * [1] action * [1] category * [tlv] mesh path reply */ + prep->prep_ie = IEEE80211_ELEMID_MESHPREP; + prep->prep_len = sizeof(struct ieee80211_meshprep_ie) - 2; + return ieee80211_hwmp_send_action(ni, addr1, addr2, (uint8_t *)&prep, sizeof(*prep)); } static void hwmp_recv_perr(struct ieee80211vap *vap, struct ieee80211_node *ni, const struct ieee80211_meshperr_ie *perr) { } static inline int hwmp_send_perr(struct ieee80211_node *ni, const uint8_t addr1[IEEE80211_ADDR_LEN], const uint8_t addr2[IEEE80211_ADDR_LEN], - const struct ieee80211_meshperr_ie *perr) + struct ieee80211_meshperr_ie *perr) { /* * mesh perr action frame format * [6] addr1 * [6] addr2 * [6] addr3 = addr2 * [1] action * [1] category * [tlv] mesh path error */ return ieee80211_hwmp_send_action(ni, addr1, addr2, (uint8_t *)&perr, sizeof(*perr)); } static void hwmp_recv_rann(struct ieee80211vap *vap, struct ieee80211_node *ni, const struct ieee80211_meshrann_ie *rann) { } #ifdef notyet static int hwmp_send_rann(struct ieee80211_node *ni, const uint8_t addr1[IEEE80211_ADDR_LEN], const uint8_t addr2[IEEE80211_ADDR_LEN], - const struct ieee80211_meshrann_ie *rann) + struct ieee80211_meshrann_ie *rann) { /* * mesh rann action frame format * [6] addr1 * [6] addr2 * [6] addr3 = addr2 * [1] action * [1] category * [tlv] root annoucement */ return ieee80211_hwmp_send_action(ni, addr1, addr2, (uint8_t *)&rann, sizeof(*rann)); } #endif static int hwmp_ioctl_get80211(struct ieee80211vap *vap, struct ieee80211req *ireq) { int error; error = 0; switch (ireq->i_type) { case IEEE80211_IOC_HWMP_TABLE: if (vap->iv_opmode != IEEE80211_M_MBSS) return EINVAL; 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; error = 0; switch (ireq->i_type) { case IEEE80211_IOC_HWMP_TABLE: if (vap->iv_opmode != IEEE80211_M_MBSS) return EINVAL; default: return ENOSYS; } return error; } IEEE80211_IOCTL_SET(hwmp, hwmp_ioctl_set80211); Index: projects/mesh11s/sys/net80211/ieee80211_mesh.c =================================================================== --- projects/mesh11s/sys/net80211/ieee80211_mesh.c (revision 193045) +++ projects/mesh11s/sys/net80211/ieee80211_mesh.c (revision 193046) @@ -1,1316 +1,1316 @@ /*- * Copyright (c) 2007-2009 Sam Leffler, Errno Consulting * 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 Mesh Point (MBSS) support. */ #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 #include static void mesh_vattach(struct ieee80211vap *); static int mesh_newstate(struct ieee80211vap *, enum ieee80211_state, int); static int mesh_input(struct ieee80211_node *, struct mbuf *, int, int); static void mesh_recv_mgmt(struct ieee80211_node *, struct mbuf *, int, int, int); static void mesh_recv_action(struct ieee80211_node *, struct mbuf *); static inline void mesh_peer_timeout_setup(struct ieee80211_node *); static void mesh_peer_timeout_backoff(struct ieee80211_node *); static void mesh_peer_timeout_cb(void *); static inline void mesh_peer_timeout_stop(struct ieee80211_node *); static int mesh_verify_meshid(struct ieee80211vap *, struct ieee80211_meshid_ie *); static int mesh_verify_meshconf(struct ieee80211vap *, struct ieee80211_meshconf_ie *); /* timeout values in miliseconds */ static const int ieee80211_mesh_retrytimeout = 40; #define RETRY_TIMEOUT msecs_to_ticks(ieee80211_mesh_retrytimeout) static const int ieee80211_mesh_holdingtimeout = 40; #define HOLDING_TIMEOUT msecs_to_ticks(ieee80211_mesh_holdingtimeout) static const int ieee80211_mesh_confirmtimeout = 40; #define CONFIRM_TIMEOUT msecs_to_ticks(ieee80211_mesh_confirmtimeout) static const int ieee80211_mesh_maxretries = 2; int ieee80211_mesh_ttl = 31; int ieee80211_mesh_forwarding = 1; SYSCTL_NODE(_net_wlan, OID_AUTO, mesh, CTLFLAG_RD, 0, "IEEE 802.11s parameters"); SYSCTL_INT(_net_wlan_mesh, OID_AUTO, ttl, CTLTYPE_INT | CTLFLAG_RW, &ieee80211_mesh_ttl, 0, "TTL for mesh packets"); SYSCTL_INT(_net_wlan_mesh, OID_AUTO, forwarding, CTLTYPE_INT | CTLFLAG_RW, &ieee80211_mesh_forwarding, 0, "Forward mesh packets"); static const char *nodemeshstates[] = { "IDLE", "OPEN SENT", "OPEN RECEIVED", "CONFIRM RECEIVED", "ESTABLISHED", "HOLDING" }; void ieee80211_mesh_attach(struct ieee80211com *ic) { ic->ic_vattach[IEEE80211_M_MBSS] = mesh_vattach; } void ieee80211_mesh_detach(struct ieee80211com *ic) { } static void mesh_vdetach(struct ieee80211vap *vap) { } static void mesh_vattach(struct ieee80211vap *vap) { vap->iv_newstate = mesh_newstate; vap->iv_input = mesh_input; vap->iv_opdetach = mesh_vdetach; vap->iv_recv_mgmt = mesh_recv_mgmt; } /* * IEEE80211_M_MBSS vap state machine handler. */ static int mesh_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { struct ieee80211com *ic = vap->iv_ic; struct ieee80211_node *ni; enum ieee80211_state ostate; IEEE80211_LOCK_ASSERT(ic); ostate = vap->iv_state; IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n", __func__, ieee80211_state_name[ostate], ieee80211_state_name[nstate], arg); vap->iv_state = nstate; /* state transition */ if (ostate != IEEE80211_S_SCAN) ieee80211_cancel_scan(vap); /* background scan */ ni = vap->iv_bss; /* NB: no reference held */ switch (nstate) { case IEEE80211_S_INIT: if (ostate == IEEE80211_S_SCAN) ieee80211_cancel_scan(vap); if (ostate != IEEE80211_S_INIT) { /* NB: optimize INIT -> INIT case */ ieee80211_reset_bss(vap); } break; case IEEE80211_S_SCAN: switch (ostate) { case IEEE80211_S_INIT: if (vap->iv_des_chan != IEEE80211_CHAN_ANYC && !IEEE80211_IS_CHAN_RADAR(vap->iv_des_chan)) { /* * Already have a channel; bypass the * scan and startup immediately. */ ieee80211_create_mbss(vap, vap->iv_des_chan); break; } /* * Initiate a scan. We can come here as a result * of an IEEE80211_IOC_SCAN_REQ too in which case * the vap will be marked with IEEE80211_FEXT_SCANREQ * and the scan request parameters will be present * in iv_scanreq. Otherwise we do the default. */ if (vap->iv_flags_ext & IEEE80211_FEXT_SCANREQ) { ieee80211_check_scan(vap, vap->iv_scanreq_flags, vap->iv_scanreq_duration, vap->iv_scanreq_mindwell, vap->iv_scanreq_maxdwell, vap->iv_scanreq_nssid, vap->iv_scanreq_ssid); vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANREQ; } else ieee80211_check_scan_current(vap); break; default: break; } case IEEE80211_S_AUTH: case IEEE80211_S_ASSOC: case IEEE80211_S_CAC: case IEEE80211_S_RUN: case IEEE80211_S_CSA: case IEEE80211_S_SLEEP: default: break; } return 0; } /* * Decide if a received management frame should be * printed when debugging is enabled. This filters some * of the less interesting frames that come frequently * (e.g. beacons). */ static inline int doprint(struct ieee80211vap *vap, int subtype) { switch (subtype) { case IEEE80211_FC0_SUBTYPE_BEACON: return (vap->iv_ic->ic_flags & IEEE80211_F_SCAN); case IEEE80211_FC0_SUBTYPE_PROBE_REQ: return 1; } return 1; } static int mesh_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int noise) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; struct ifnet *ifp = vap->iv_ifp; struct ieee80211_frame *wh; int hdrspace, need_tap; uint8_t dir, type, subtype, qos; KASSERT(ni != NULL, ("null node")); ni->ni_inact = ni->ni_inact_reload; need_tap = 1; /* mbuf need to be tapped. */ type = -1; /* undefined */ if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) { IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, ni->ni_macaddr, NULL, "too short (1): len %u", m->m_pkthdr.len); vap->iv_stats.is_rx_tooshort++; goto out; } /* * Bit of a cheat here, we use a pointer for a 3-address * frame format but don't reference fields past outside * ieee80211_frame_min w/o first validating the data is * present. */ wh = mtod(m, struct ieee80211_frame *); if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) != IEEE80211_FC0_VERSION_0) { IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, ni->ni_macaddr, NULL, "wrong version %x", wh->i_fc[0]); vap->iv_stats.is_rx_badversion++; goto err; } dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK; type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) { } switch (type) { case IEEE80211_FC0_TYPE_DATA: if (ni == vap->iv_bss) goto out; IEEE80211_NOTE(vap, IEEE80211_MSG_MESH, ni, "%s", "received data frame"); if (ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED) { IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_MESH, ni->ni_macaddr, NULL, "peer link not yet established (%s)", nodemeshstates[ni->ni_mlstate]); } hdrspace = ieee80211_hdrspace(ic, wh) + sizeof(struct ieee80211_meshcntl); if (m->m_len < hdrspace && (m = m_pullup(m, hdrspace)) == NULL) { IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, ni->ni_macaddr, NULL, "data too short: expecting %u", hdrspace); vap->iv_stats.is_rx_tooshort++; goto out; /* XXX */ } if (dir != IEEE80211_FC1_DIR_DSTODS) { IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh, "data", "incorrect dir 0x%x", dir); vap->iv_stats.is_rx_wrongdir++; goto err; } /* * Save QoS bits for use below--before we strip the header. */ if (subtype == IEEE80211_FC0_SUBTYPE_QOS) { qos = (dir == IEEE80211_FC1_DIR_DSTODS) ? ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0] : ((struct ieee80211_qosframe *)wh)->i_qos[0]; } else qos = 0; /* * Next up, any fragmentation. */ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { m = ieee80211_defrag(ni, m, hdrspace); if (m == NULL) { /* Fragment dropped or frame not complete yet */ goto out; } } wh = NULL; /* no longer valid, catch any uses */ if (bpf_peers_present(vap->iv_rawbpf)) bpf_mtap(vap->iv_rawbpf, m); need_tap = 0; /* * Finally, strip the 802.11 header. */ m = ieee80211_decap(vap, m, hdrspace); if (m == NULL) { /* XXX mask bit to check for both */ /* don't count Null data frames as errors */ if (subtype == IEEE80211_FC0_SUBTYPE_NODATA || subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL) goto out; IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, ni->ni_macaddr, "data", "%s", "decap error"); vap->iv_stats.is_rx_decap++; IEEE80211_NODE_STAT(ni, rx_decap); goto err; } /* XXX require HT? */ if (qos & IEEE80211_QOS_AMSDU) { m = ieee80211_decap_amsdu(ni, m); if (m == NULL) return IEEE80211_FC0_TYPE_DATA; } else { #ifdef IEEE80211_SUPPORT_SUPERG m = ieee80211_decap_fastframe(vap, ni, m); if (m == NULL) return IEEE80211_FC0_TYPE_DATA; #endif } /* XXX SuperG/HT */ ieee80211_deliver_data(vap, ni, m); return type; case IEEE80211_FC0_TYPE_MGT: vap->iv_stats.is_rx_mgmt++; IEEE80211_NODE_STAT(ni, rx_mgmt); if (dir != IEEE80211_FC1_DIR_NODS) { IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh, "mgt", "incorrect dir 0x%x", dir); vap->iv_stats.is_rx_wrongdir++; goto err; } if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) { IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, ni->ni_macaddr, "mgt", "too short: len %u", m->m_pkthdr.len); vap->iv_stats.is_rx_tooshort++; goto out; } #ifdef IEEE80211_DEBUG if ((ieee80211_msg_debug(vap) && doprint(vap, subtype)) || ieee80211_msg_dumppkts(vap)) { if_printf(ifp, "received %s from %s rssi %d\n", ieee80211_mgt_subtype_name[subtype >> IEEE80211_FC0_SUBTYPE_SHIFT], ether_sprintf(wh->i_addr2), rssi); } #endif if (wh->i_fc[1] & IEEE80211_FC1_WEP) { IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh, NULL, "%s", "WEP set but not permitted"); vap->iv_stats.is_rx_mgtdiscard++; /* XXX */ goto out; } if (bpf_peers_present(vap->iv_rawbpf)) bpf_mtap(vap->iv_rawbpf, m); vap->iv_recv_mgmt(ni, m, subtype, rssi, noise); goto out; case IEEE80211_FC0_TYPE_CTL: vap->iv_stats.is_rx_ctl++; IEEE80211_NODE_STAT(ni, rx_ctrl); goto out; default: IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, wh, "bad", "frame type 0x%x", type); /* should not come here */ break; } err: ifp->if_ierrors++; out: if (m != NULL) { if (bpf_peers_present(vap->iv_rawbpf) && need_tap) bpf_mtap(vap->iv_rawbpf, m); m_freem(m); } return type; } static int is11bclient(const uint8_t *rates, const uint8_t *xrates) { static const uint32_t brates = (1<<2*1)|(1<<2*2)|(1<<11)|(1<<2*11); int i; /* NB: the 11b clients we care about will not have xrates */ if (xrates != NULL || rates == NULL) return 0; for (i = 0; i < rates[1]; i++) { int r = rates[2+i] & IEEE80211_RATE_VAL; if (r > 2*11 || ((1<ni_vap; struct ieee80211com *ic = ni->ni_ic; struct ieee80211_frame *wh; uint8_t *frm, *efrm; wh = mtod(m0, struct ieee80211_frame *); frm = (uint8_t *)&wh[1]; efrm = mtod(m0, uint8_t *) + m0->m_len; switch (subtype) { case IEEE80211_FC0_SUBTYPE_PROBE_RESP: case IEEE80211_FC0_SUBTYPE_BEACON: { struct ieee80211_scanparams scan; /* * We process beacon/probe response * frames to discover neighbors. */ if (ieee80211_parse_beacon(ni, m0, &scan) != 0) return; /* * Count frame now that we know it's to be processed. */ if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) { vap->iv_stats.is_rx_beacon++; /* XXX remove */ IEEE80211_NODE_STAT(ni, rx_beacons); } else IEEE80211_NODE_STAT(ni, rx_proberesp); /* * If scanning, just pass information to the scan module. */ if (ic->ic_flags & IEEE80211_F_SCAN) { if (ic->ic_flags_ext & IEEE80211_FEXT_PROBECHAN) { /* * Actively scanning a channel marked passive; * send a probe request now that we know there * is 802.11 traffic present. * * XXX check if the beacon we recv'd gives * us what we need and suppress the probe req */ ieee80211_probe_curchan(vap, 1); ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN; } ieee80211_add_scan(vap, &scan, wh, subtype, rssi, noise); return; } /* The rest of this code assumes we setup and running */ if (vap->iv_state != IEEE80211_S_RUN) return; /* * Ignore non-mesh STAs and STAs for other mesh networks. */ if (scan.meshid && memcmp(scan.meshid+2, vap->iv_meshid, vap->iv_meshidlen) != 0) { IEEE80211_NOTE(vap, IEEE80211_MSG_MESH, ni, "beacon not for our mesh (%s)", scan.meshid+2); return; } /* * More validation: make sure we are talking to a Mesh node. */ if ((scan.capinfo & (IEEE80211_CAPINFO_ESS|IEEE80211_CAPINFO_IBSS)) == 0 && !IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr) && IEEE80211_ADDR_EQ(wh->i_addr3, zerobssid)) { union ieee80211_send_action_args vargs; /* * Create a new entry in the neighbor table. */ ni = ieee80211_add_neighbor(vap, wh, &scan); /* * Mesh nodes must be QoS capable. */ ni->ni_flags |= IEEE80211_NODE_QOS; /* * Try to peer with this node. */ get_random_bytes(&ni->ni_mlpid, 2); ni->ni_mlstate = IEEE80211_NODE_MESH_OPENSNT; IEEE80211_NOTE(vap, IEEE80211_MSG_MESH, ni, "peer link: switching to state %s", nodemeshstates[ni->ni_mlstate]); vargs.fixedarg[0] = ni->ni_mlpid; ieee80211_send_action(ni, IEEE80211_ACTION_CAT_MESHPEERING, IEEE80211_ACTION_MESHPEERING_OPEN, vargs); ni->ni_mlrcnt = 0; mesh_peer_timeout_setup(ni); } if (ni != NULL) { IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi); ni->ni_noise = noise; } break; } case IEEE80211_FC0_SUBTYPE_PROBE_REQ: { uint8_t *ssid, *meshid, *rates, *xrates; uint8_t *sfrm; if (vap->iv_state != IEEE80211_S_RUN) { IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh, NULL, "wrong state %s", ieee80211_state_name[vap->iv_state]); vap->iv_stats.is_rx_mgtdiscard++; return; } if (IEEE80211_IS_MULTICAST(wh->i_addr2)) { /* frame must be directed */ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh, NULL, "%s", "not unicast"); vap->iv_stats.is_rx_mgtdiscard++; /* XXX stat */ return; } /* * prreq frame format * [tlv] ssid * [tlv] supported rates * [tlv] extended supported rates * [tlv] mesh id */ ssid = meshid = rates = xrates = NULL; sfrm = frm; while (efrm - frm > 1) { IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return); switch (*frm) { case IEEE80211_ELEMID_SSID: ssid = frm; break; case IEEE80211_ELEMID_RATES: rates = frm; break; case IEEE80211_ELEMID_XRATES: xrates = frm; break; case IEEE80211_ELEMID_MESHID: meshid = frm; break; } frm += frm[2] + 2; } IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN, return); IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE, return); if (xrates != NULL) IEEE80211_VERIFY_ELEMENT(xrates, IEEE80211_RATE_MAXSIZE - rates[1], return); if (meshid != NULL) IEEE80211_VERIFY_ELEMENT(meshid, IEEE80211_MESHID_LEN, return); IEEE80211_VERIFY_SSID(vap->iv_bss, ssid, return); /* XXX add verify meshid ? */ /* XXX find a better class or define it's own */ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_INPUT, wh->i_addr2, "%s", "recv probe req"); /* * Some legacy 11b clients cannot hack a complete * probe response frame. When the request includes * only a bare-bones rate set, communicate this to * the transmit side. */ ieee80211_send_proberesp(vap, wh->i_addr2, is11bclient(rates, xrates) ? IEEE80211_SEND_LEGACY_11B : 0); break; } case IEEE80211_FC0_SUBTYPE_ACTION: if (vap->iv_state != IEEE80211_S_RUN) { vap->iv_stats.is_rx_mgtdiscard++; break; } /* XXX parse_action is a bit useless now */ if (ieee80211_parse_action(ni, m0) == 0) { mesh_recv_action(ni, m0); ic->ic_recv_action(ni, frm, efrm); } break; case IEEE80211_FC0_SUBTYPE_AUTH: case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: case IEEE80211_FC0_SUBTYPE_DEAUTH: case IEEE80211_FC0_SUBTYPE_DISASSOC: IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh, NULL, "%s", "not handled"); vap->iv_stats.is_rx_mgtdiscard++; return; default: IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, wh, "mgt", "subtype 0x%x not handled", subtype); vap->iv_stats.is_rx_badsubtype++; break; } } static void mesh_recv_action(struct ieee80211_node *ni, struct mbuf *m0) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211_action *ia; struct ieee80211_frame *wh; struct ieee80211_meshid_ie *meshid = NULL; struct ieee80211_meshconf_ie *meshconf = NULL; struct ieee80211_meshpeer_ie *meshpeer = NULL; uint8_t *frm, *efrm; union ieee80211_send_action_args vargs; 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; /* * We received an action for an unknown neighbor. * XXX: wait for it to beacon or create ieee80211_node? */ if (ni == vap->iv_bss) { return; } switch (ia->ia_category) { /* * Mesh Peer Link Management Finite State Machine handling. */ case IEEE80211_ACTION_CAT_MESHPEERING: /* * Compute the start of fixed/tagged parameters. */ switch (ia->ia_action) { case IEEE80211_ACTION_MESHPEERING_OPEN: frm += 2; /* capabilites */ break; case IEEE80211_ACTION_MESHPEERING_CONFIRM: frm += 2 + 2 + 2; /* capabilites + status code + AID */ break; case IEEE80211_ACTION_MESHPEERING_CLOSE: break; } /* * And parse them. */ while (efrm - frm > 1) { IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return); switch (*frm) { case IEEE80211_ELEMID_MESHID: meshid = (struct ieee80211_meshid_ie *) frm; break; case IEEE80211_ELEMID_MESHCONF: meshconf = (struct ieee80211_meshconf_ie *) frm; break; case IEEE80211_ELEMID_MESHPEER: meshpeer = (struct ieee80211_meshpeer_ie *) frm; meshpeer->peer_llinkid = LE_READ_2(&meshpeer->peer_llinkid); meshpeer->peer_linkid = LE_READ_2(&meshpeer->peer_linkid); meshpeer->peer_rcode = LE_READ_2(&meshpeer->peer_rcode); break; } frm += frm[1] + 2; } if (mesh_verify_meshid(vap, meshid) || mesh_verify_meshconf(vap, meshconf) || !meshpeer) { IEEE80211_DISCARD(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, wh, NULL, "%s", "action frame not for our mesh"); vap->iv_stats.is_rx_mgtdiscard++; return; } #ifdef notyet if (!(meshconf->conf_cap & IEEE80211_MESHCONF_CAP_AP)) { IEEE80211_DISCARD(vap, IEEE80211_MSG_MESH, wh, NULL, "%s", "neighbor not accepting peers"); } #endif switch (ia->ia_action) { case IEEE80211_ACTION_MESHPEERING_OPEN: IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, ni, "recv PEER OPEN, lid 0x%x", meshpeer->peer_llinkid); switch (ni->ni_mlstate) { case IEEE80211_NODE_MESH_IDLE: ni->ni_mlstate = IEEE80211_NODE_MESH_OPENRCV; IEEE80211_NOTE(vap, IEEE80211_MSG_MESH, ni, "peer link: switching to state %s", nodemeshstates[ni->ni_mlstate]); ni->ni_mllid = meshpeer->peer_llinkid; get_random_bytes(&ni->ni_mlpid, 2); vargs.fixedarg[0] = ni->ni_mlpid; /* Announce we're open too... */ ieee80211_send_action(ni, IEEE80211_ACTION_CAT_MESHPEERING, IEEE80211_ACTION_MESHPEERING_OPEN, vargs); /* ...and confirm the link. */ vargs.fixedarg[0] = ni->ni_mlpid; vargs.fixedarg[1] = ni->ni_mllid; ieee80211_send_action(ni, IEEE80211_ACTION_CAT_MESHPEERING, IEEE80211_ACTION_MESHPEERING_CONFIRM, vargs); mesh_peer_timeout_setup(ni); break; case IEEE80211_NODE_MESH_OPENRCV: /* Duplicate open, confirm again. */ ni->ni_mllid = meshpeer->peer_llinkid; vargs.fixedarg[0] = ni->ni_mlpid; vargs.fixedarg[1] = ni->ni_mllid; ieee80211_send_action(ni, IEEE80211_ACTION_CAT_MESHPEERING, IEEE80211_ACTION_MESHPEERING_CONFIRM, vargs); break; case IEEE80211_NODE_MESH_OPENSNT: ni->ni_mlstate = IEEE80211_NODE_MESH_OPENRCV; ni->ni_mllid = meshpeer->peer_llinkid; IEEE80211_NOTE(vap, IEEE80211_MSG_MESH, ni, "peer link: switching to state %s", nodemeshstates[ni->ni_mlstate]); vargs.fixedarg[0] = ni->ni_mlpid; vargs.fixedarg[1] = ni->ni_mllid; ieee80211_send_action(ni, IEEE80211_ACTION_CAT_MESHPEERING, IEEE80211_ACTION_MESHPEERING_CONFIRM, vargs); /* NB: don't setup/clear any timeout */ break; case IEEE80211_NODE_MESH_CONFIRMRECV: ni->ni_mlstate = IEEE80211_NODE_MESH_ESTABLISHED; IEEE80211_NOTE(vap, IEEE80211_MSG_MESH, ni, "peer link: switching to state %s", nodemeshstates[ni->ni_mlstate]); ni->ni_mllid = meshpeer->peer_llinkid; vargs.fixedarg[0] = ni->ni_mlpid; vargs.fixedarg[1] = ni->ni_mllid; ieee80211_send_action(ni, IEEE80211_ACTION_CAT_MESHPEERING, IEEE80211_ACTION_MESHPEERING_CONFIRM, vargs); mesh_peer_timeout_stop(ni); break; case IEEE80211_NODE_MESH_ESTABLISHED: vargs.fixedarg[0] = ni->ni_mlpid; vargs.fixedarg[1] = ni->ni_mllid; ieee80211_send_action(ni, IEEE80211_ACTION_CAT_MESHPEERING, IEEE80211_ACTION_MESHPEERING_CONFIRM, vargs); break; case IEEE80211_NODE_MESH_HOLDING: vargs.fixedarg[0] = ni->ni_mllid; vargs.fixedarg[1] = ni->ni_mlpid; vargs.fixedarg[2] = IEEE80211_REASON_MESH_MAX_RETRIES; ieee80211_send_action(ni, IEEE80211_ACTION_CAT_MESHPEERING, IEEE80211_ACTION_MESHPEERING_CLOSE, vargs); break; } break; case IEEE80211_ACTION_MESHPEERING_CONFIRM: IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, ni, "recv PEER CONFIRM, local id 0x%x, peer id 0x%x", meshpeer->peer_llinkid, meshpeer->peer_linkid); switch (ni->ni_mlstate) { case IEEE80211_NODE_MESH_OPENRCV: ni->ni_mlstate = IEEE80211_NODE_MESH_ESTABLISHED; IEEE80211_NOTE(vap, IEEE80211_MSG_MESH, ni, "peer link: switching to state %s", nodemeshstates[ni->ni_mlstate]); mesh_peer_timeout_stop(ni); break; case IEEE80211_NODE_MESH_OPENSNT: ni->ni_mlstate = IEEE80211_NODE_MESH_CONFIRMRECV; IEEE80211_NOTE(vap, IEEE80211_MSG_MESH, ni, "peer link: switching to state %s", nodemeshstates[ni->ni_mlstate]); break; case IEEE80211_NODE_MESH_HOLDING: vargs.fixedarg[0] = ni->ni_mllid; vargs.fixedarg[1] = ni->ni_mlpid; vargs.fixedarg[2] = IEEE80211_REASON_MESH_MAX_RETRIES; ieee80211_send_action(ni, IEEE80211_ACTION_CAT_MESHPEERING, IEEE80211_ACTION_MESHPEERING_CLOSE, vargs); break; default: IEEE80211_DISCARD(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, wh, NULL, "received confirm in invalid " "state %d", ni->ni_mlstate); vap->iv_stats.is_rx_mgtdiscard++; } break; case IEEE80211_ACTION_MESHPEERING_CLOSE: IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, ni, "%s", "recv PEER CLOSE"); switch (ni->ni_mlstate) { case IEEE80211_NODE_MESH_IDLE: case IEEE80211_NODE_MESH_CONFIRMRECV: case IEEE80211_NODE_MESH_ESTABLISHED: break; case IEEE80211_NODE_MESH_OPENRCV: ni->ni_mlstate = IEEE80211_NODE_MESH_HOLDING; mesh_peer_timeout_setup(ni); break; case IEEE80211_NODE_MESH_OPENSNT: break; case IEEE80211_NODE_MESH_HOLDING: break; } break; } break; /* * Mesh Path action frames are handled by the HWMP module. */ case IEEE80211_ACTION_CAT_MESHPATH: ieee80211_hwmp_recv_action(vap, ni, m0); break; /* * Link Metric handling. */ case IEEE80211_ACTION_CAT_MESHLMETRIC: switch (ia->ia_action) { case IEEE80211_ACTION_MESHLMETRIC_REQ: { uint32_t metric; /* XXX: check if we are using airtime or aother algorithm */ - metric = ieee80211_airtime_calc(vap, ni); + metric = ieee80211_airtime_calc(ni); vargs.ptrarg = &metric; ieee80211_send_action(ni, IEEE80211_ACTION_CAT_MESHLMETRIC, IEEE80211_ACTION_MESHLMETRIC_REP, vargs); } break; case IEEE80211_ACTION_MESHLMETRIC_REP: break; default: IEEE80211_DISCARD(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, wh, NULL, "mesh link metric using reserved " "action %d", ia->ia_action); vap->iv_stats.is_rx_mgtdiscard++; } break; default: IEEE80211_DISCARD(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, wh, NULL, "%s", "not handled"); vap->iv_stats.is_rx_mgtdiscard++; } } static inline void mesh_peer_timeout_setup(struct ieee80211_node *ni) { switch (ni->ni_mlstate) { case IEEE80211_NODE_MESH_HOLDING: ni->ni_mltval = HOLDING_TIMEOUT; break; case IEEE80211_NODE_MESH_CONFIRMRECV: ni->ni_mltval = CONFIRM_TIMEOUT; break; default: ni->ni_mltval = RETRY_TIMEOUT; } callout_reset(&ni->ni_mltimer, ni->ni_mltval, mesh_peer_timeout_cb, ni); } /* * Same as above but backoffs timer statisically 50%. */ static void mesh_peer_timeout_backoff(struct ieee80211_node *ni) { uint32_t r; r = arc4random(); ni->ni_mltval += r % ni->ni_mltval; callout_reset(&ni->ni_mltimer, ni->ni_mltval, mesh_peer_timeout_cb, ni); } static inline void mesh_peer_timeout_stop(struct ieee80211_node *ni) { callout_stop(&ni->ni_mltimer); } /* * Mesh Peer Link Management FSM timeout handling. */ static void mesh_peer_timeout_cb(void *arg) { struct ieee80211_node *ni = (struct ieee80211_node *)arg; struct ieee80211vap *vap = ni->ni_vap; union ieee80211_send_action_args vargs; IEEE80211_NOTE(vap, IEEE80211_MSG_MESH, ni, "mesh link timeout, state %s, retry counter %d", nodemeshstates[ni->ni_mlstate], ni->ni_mlrcnt); switch (ni->ni_mlstate) { case IEEE80211_NODE_MESH_IDLE: case IEEE80211_NODE_MESH_ESTABLISHED: break; case IEEE80211_NODE_MESH_OPENSNT: case IEEE80211_NODE_MESH_OPENRCV: if (ni->ni_mlrcnt == ieee80211_mesh_maxretries) { vargs.fixedarg[0] = ni->ni_mlpid; vargs.fixedarg[2] = IEEE80211_REASON_MESH_MAX_RETRIES; ieee80211_send_action(ni, IEEE80211_ACTION_CAT_MESHPEERING, IEEE80211_ACTION_MESHPEERING_CLOSE, vargs); ni->ni_mlrcnt = 0; ni->ni_mlstate = IEEE80211_NODE_MESH_HOLDING; IEEE80211_NOTE(vap, IEEE80211_MSG_MESH, ni, "peer link: switching to state %s", nodemeshstates[ni->ni_mlstate]); mesh_peer_timeout_setup(ni); } else { vargs.fixedarg[0] = ni->ni_mlpid; ieee80211_send_action(ni, IEEE80211_ACTION_CAT_MESHPEERING, IEEE80211_ACTION_MESHPEERING_OPEN, vargs); ni->ni_mlrcnt++; mesh_peer_timeout_backoff(ni); } break; case IEEE80211_NODE_MESH_CONFIRMRECV: if (ni->ni_mlrcnt == ieee80211_mesh_maxretries) { vargs.fixedarg[0] = ni->ni_mlpid; vargs.fixedarg[2] = IEEE80211_REASON_MESH_CONFIRM_TIMEOUT; ieee80211_send_action(ni, IEEE80211_ACTION_CAT_MESHPEERING, IEEE80211_ACTION_MESHPEERING_CLOSE, vargs); ni->ni_mlrcnt = 0; ni->ni_mlstate = IEEE80211_NODE_MESH_HOLDING; IEEE80211_NOTE(vap, IEEE80211_MSG_MESH, ni, "peer link: switching to state %s", nodemeshstates[ni->ni_mlstate]); mesh_peer_timeout_setup(ni); } else { ni->ni_mlrcnt++; mesh_peer_timeout_setup(ni); } break; case IEEE80211_NODE_MESH_HOLDING: ni->ni_mlstate = IEEE80211_NODE_MESH_IDLE; IEEE80211_NOTE(vap, IEEE80211_MSG_MESH, ni, "peer link: switching to state %s", nodemeshstates[ni->ni_mlstate]); break; } } static inline int mesh_verify_meshid(struct ieee80211vap *vap, struct ieee80211_meshid_ie *meshid) { if (meshid == NULL) return 1; return memcmp(vap->iv_meshid, (uint8_t *)&meshid[1], vap->iv_meshidlen); } static int mesh_verify_meshconf(struct ieee80211vap *vap, struct ieee80211_meshconf_ie *meshconf) { #ifdef notyet static const uint8_t null[4] = IEEE80211_MESHCONF_NULL; static const uint8_t hwmp[4] = IEEE80211_MESHCONF_HWMP; static const uint8_t airtime[4] = IEEE80211_MESHCONF_AIRTIME; #endif if (meshconf == NULL) return 1; #ifdef notyet if (meshconf->conf_ver != IEEE80211_MESHCONF_VERSION) return 1; if (memcmp(meshconf->conf_pselid, hwmp, 4) != 0) return 1; if (memcmp(meshconf->conf_pmetid, airtime, 4) != 0) return 1; if (memcmp(meshconf->conf_ccid, null, 4) != 0) return 1; if (memcmp(meshconf->conf_syncid, null, 4) != 0) return 1; if (memcmp(meshconf->conf_authid, null, 4) != 0) return 1; #endif return 0; } #define ADDSHORT(frm, v) do { \ frm[0] = (v) & 0xff; \ frm[1] = (v) >> 8; \ frm += 2; \ } while (0) #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 ID IE to a frame. */ uint8_t * ieee80211_add_meshid(uint8_t *frm, struct ieee80211vap *vap) { KASSERT(vap->iv_opmode == IEEE80211_M_MBSS, ("not a mbss vap")); *frm++ = IEEE80211_ELEMID_MESHID; *frm++ = vap->iv_meshidlen; memcpy(frm, vap->iv_meshid, vap->iv_meshidlen); return frm + vap->iv_meshidlen; } /* * Add a Mesh Configuration IE to a frame. * For now just use HWMP routing, Airtime link metric, Null Congestion * Signaling, Null Sync Protocol and Null Authentication. */ uint8_t * ieee80211_add_meshconf(uint8_t *frm, struct ieee80211vap *vap) { static const struct ieee80211_meshconf_ie ie = { .conf_ie = IEEE80211_ELEMID_MESHCONF, .conf_len = sizeof(struct ieee80211_meshconf_ie) - 2, .conf_ver = IEEE80211_MESHCONF_VERSION, .conf_pselid = IEEE80211_MESHCONF_HWMP, .conf_pmetid = IEEE80211_MESHCONF_AIRTIME, .conf_ccid = IEEE80211_MESHCONF_CCSIG, .conf_syncid = { 0, 0, 0, 0 }, /* XXX */ .conf_authid = { 0x80, 0, 0, 0 }, /* XXX */ .conf_form = 0, /* XXX */ .conf_cap = 1, /* XXX */ }; KASSERT(vap->iv_opmode == IEEE80211_M_MBSS, ("not a MBSS vap")); memcpy(frm, &ie, sizeof(ie)); return frm + sizeof(ie); } /* * Add a Mesh Peer Management IE to a frame. */ uint8_t * ieee80211_add_meshpeer(uint8_t *frm, uint8_t subtype, uint16_t localid, uint16_t peerid, uint16_t reason) { *frm++ = IEEE80211_ELEMID_MESHPEER; switch (subtype) { case IEEE80211_MESH_PEER_LINK_OPEN: *frm++ = 3; /* length */ *frm++ = subtype; ADDSHORT(frm, localid); /* local ID */ break; case IEEE80211_MESH_PEER_LINK_CONFIRM: *frm++ = 5; /* length */ *frm++ = subtype; ADDSHORT(frm, localid); /* local ID */ ADDSHORT(frm, peerid); /* peer ID */ break; case IEEE80211_MESH_PEER_LINK_CLOSE: if (peerid) *frm++ = 7; /* length */ else *frm++ = 5; /* length */ *frm++ = subtype; ADDSHORT(frm, localid); /* local ID */ if (peerid) ADDSHORT(frm, peerid); /* peer ID */ ADDSHORT(frm, reason); break; } return frm; } /* * Compute an Airtime Link Metric for the link with this node. * * Based on D3.0. */ uint32_t -ieee80211_airtime_calc(struct ieee80211vap *vap, struct ieee80211_node *ni) +ieee80211_airtime_calc(struct ieee80211_node *ni) { #define M_BITS 8 #define S_FACTOR (2 * M_BITS) uint64_t res; uint32_t overhead, rate, errrate; const static int nbits = 8192 << M_BITS; /* Channel access overhead */ overhead = 123 << M_BITS; /* XXX */ /* In Mbps */ rate = ni->ni_txrate; /* In percentage */ errrate = (10 << M_BITS) / 100; res = (overhead + (nbits / rate)) * ((1 << S_FACTOR) / ((1 << M_BITS) - errrate)); return (uint32_t) (res >> S_FACTOR); #undef M_BITS #undef S_FACTOR } /* * Add a Mesh Link Metric report IE to a frame. */ uint8_t * ieee80211_add_meshlmetric(uint8_t *frm, uint32_t metric) { *frm++ = IEEE80211_ELEMID_MESHLINK; *frm++ = 4; ADDWORD(frm, metric); return frm; } #undef ADDSHORT #undef ADDWORD void ieee80211_create_mbss(struct ieee80211vap *vap, struct ieee80211_channel *chan) { struct ieee80211com *ic = vap->iv_ic; struct ieee80211_node *ni; IEEE80211_DPRINTF(vap, IEEE80211_MSG_MESH, "%s: creating MBSS on channel %u\n", __func__, ieee80211_chan2ieee(ic, chan)); ni = ieee80211_alloc_node(&ic->ic_sta, vap, vap->iv_myaddr); if (ni == NULL) { /* XXX recovery? */ return; } IEEE80211_ADDR_COPY(ni->ni_bssid, vap->iv_myaddr); ni->ni_meshidlen = vap->iv_meshidlen; memcpy(ni->ni_meshid, vap->iv_meshid, ni->ni_meshidlen); ni->ni_intval = ic->ic_bintval; /* * Fix the channel and related attributes. */ /* clear DFS CAC state on previous channel */ if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && ic->ic_bsschan->ic_freq != chan->ic_freq && IEEE80211_IS_CHAN_CACDONE(ic->ic_bsschan)) ieee80211_dfs_cac_clear(ic, ic->ic_bsschan); ic->ic_bsschan = chan; ieee80211_node_set_chan(ni, chan); ic->ic_curmode = ieee80211_chan2mode(chan); /* * Do mode-specific setup. */ if (IEEE80211_IS_CHAN_FULL(chan)) { if (IEEE80211_IS_CHAN_ANYG(chan)) { /* * Use a mixed 11b/11g basic rate set. */ ieee80211_setbasicrates(&ni->ni_rates, IEEE80211_MODE_11G); if (vap->iv_flags & IEEE80211_F_PUREG) { /* * Also mark OFDM rates basic so 11b * stations do not join (WiFi compliance). */ ieee80211_addbasicrates(&ni->ni_rates, IEEE80211_MODE_11A); } } else if (IEEE80211_IS_CHAN_B(chan)) { /* * Force pure 11b rate set. */ ieee80211_setbasicrates(&ni->ni_rates, IEEE80211_MODE_11B); } } ieee80211_ref_node(ni); vap->iv_bss = ni; ieee80211_setcurchan(ic, ni->ni_chan); /* * Set the erp state (mostly the slot time) to deal with * the auto-select case; this should be redundant if the * mode is locked. */ ieee80211_reset_erp(ic); ieee80211_wme_initparams(vap); ieee80211_new_state(vap, IEEE80211_S_RUN, -1); } uint32_t ieee80211_mesh_getseq(void) { static uint32_t seq = 0; return seq++; } static int mesh_ioctl_get80211(struct ieee80211vap *vap, struct ieee80211req *ireq) { int error; uint8_t tmpmeshid[IEEE80211_NWID_LEN]; error = 0; switch (ireq->i_type) { case IEEE80211_IOC_MESH_ID: if (vap->iv_opmode != IEEE80211_M_MBSS) return EINVAL; ireq->i_len = vap->iv_meshidlen; memcpy(tmpmeshid, vap->iv_meshid, ireq->i_len); error = copyout(tmpmeshid, ireq->i_data, ireq->i_len); break; default: return ENOSYS; } return error; } IEEE80211_IOCTL_GET(mesh, mesh_ioctl_get80211); static int mesh_ioctl_set80211(struct ieee80211vap *vap, struct ieee80211req *ireq) { int error; uint8_t tmpmeshid[IEEE80211_NWID_LEN]; error = 0; switch (ireq->i_type) { case IEEE80211_IOC_MESH_ID: if (ireq->i_val != 0 || ireq->i_len > IEEE80211_NWID_LEN) return EINVAL; error = copyin(ireq->i_data, tmpmeshid, ireq->i_len); if (error) break; memset(vap->iv_meshid, 0, IEEE80211_NWID_LEN); vap->iv_meshidlen = ireq->i_len; memcpy(vap->iv_meshid, tmpmeshid, ireq->i_len); break; default: return ENOSYS; } return error; } IEEE80211_IOCTL_SET(mesh, mesh_ioctl_set80211); Index: projects/mesh11s/sys/net80211/ieee80211_mesh.h =================================================================== --- projects/mesh11s/sys/net80211/ieee80211_mesh.h (revision 193045) +++ projects/mesh11s/sys/net80211/ieee80211_mesh.h (revision 193046) @@ -1,362 +1,361 @@ /*- * 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. * * $FreeBSD$ */ #ifndef _NET80211_IEEE80211_MESH_H_ #define _NET80211_IEEE80211_MESH_H_ /* * 802.11s Information Elements. */ /* Mesh Configuration */ struct ieee80211_meshconf_ie { uint8_t conf_ie; /* IEEE80211_ELEMID_MESHCONF */ uint8_t conf_len; uint8_t conf_ver; uint8_t conf_pselid[4]; /* Active Path Sel. Proto. ID */ uint8_t conf_pmetid[4]; /* APS Metric Identifier */ uint8_t conf_ccid[4]; /* Congestion Control Mode ID */ uint8_t conf_syncid[4]; /* Sync. Protocol ID */ uint8_t conf_authid[4]; /* Auth. Protocol ID */ uint8_t conf_form; /* Formation Information */ uint8_t conf_cap; } __packed; #define IEEE80211_MESHCONF_VERSION 1 /* Null Protocol */ #define IEEE80211_MESHCONF_NULL_OUI 0x00, 0x0f, 0xac #define IEEE80211_MESHCONF_NULL_VALUE 0xff #define IEEE80211_MESHCONF_NULL { IEEE80211_MESHCONF_NULL_OUI, \ IEEE80211_MESHCONF_NULL_VALUE } /* Hybrid Wireless Mesh Protocol */ #define IEEE80211_MESHCONF_HWMP_OUI 0x00, 0x0f, 0xac #define IEEE80211_MESHCONF_HWMP_VALUE 0xff /* XXX Linux */ #define IEEE80211_MESHCONF_HWMP { IEEE80211_MESHCONF_HWMP_OUI, \ IEEE80211_MESHCONF_HWMP_VALUE } /* Airtime Link Metric */ #define IEEE80211_MESHCONF_AIRTIME_OUI 0x00, 0x0f, 0xac #define IEEE80211_MESHCONF_AIRTIME_VALUE 0xff /* XXX Linux */ #define IEEE80211_MESHCONF_AIRTIME { IEEE80211_MESHCONF_AIRTIME_OUI, \ IEEE80211_MESHCONF_AIRTIME_VALUE } /* Congestion Control Signaling */ #define IEEE80211_MESHCONF_CCSIG_OUI 0x00, 0x0f, 0xac #define IEEE80211_MESHCONF_CCSIG_VALUE 0xff /* XXX Linux */ #define IEEE80211_MESHCONF_CCSIG { IEEE80211_MESHCONF_CCSIG_OUI,\ IEEE80211_MESHCONF_CCSIG_VALUE } /* Neighbour Offset */ #define IEEE80211_MESHCONF_NEIGHOFF_OUI 0x00, 0x0f, 0xac #define IEEE80211_MESHCONF_NEIGHOFF_VALUE 0x00 #define IEEE80211_MESHCONF_NEIGHOFF { IEEE80211_MESHCONF_NEIGHOFF_OUI, \ IEEE80211_MESHCONF_NEIGHOFF_VALUE } /* Simultaneous Authenticaction of Equals */ #define IEEE80211_MESHCONF_SAE_OUI 0x00, 0x0f, 0xac #define IEEE80211_MESHCONF_SAE_VALUE 0x01 #define IEEE80211_MESHCONF_SAE { IEEE80211_MESHCONF_SAE_OUI, \ IEEE80211_MESHCONF_SAE_VALUE } #define IEEE80211_MESHCONF_FORM_MP 0x01 /* Connected to Portal */ #define IEEE80211_MESHCONF_FORM_NNEIGH_MASK 0x04 /* Number of Neighbours */ #define IEEE80211_MESHCONF_CAP_AP 0x01 /* Accepting Peers */ #define IEEE80211_MESHCONF_CAP_MCCAS 0x02 /* MCCA supported */ #define IEEE80211_MESHCONF_CAP_MCCAE 0x04 /* MCCA enabled */ #define IEEE80211_MESHCONF_CAP_FRWD 0x08 /* forwarding enabled */ #define IEEE80211_MESHCONF_CAP_BTR 0x10 /* Beacon Timing Report Enab */ #define IEEE80211_MESHCONF_CAP_TBTTA 0x20 /* TBTT Adj. Enabled */ #define IEEE80211_MESHCONF_CAP_PSL 0x40 /* Power Save Level */ /* Mesh Identifier */ struct ieee80211_meshid_ie { uint8_t id_ie; /* IEEE80211_ELEMID_MESHID */ uint8_t id_len; } __packed; #define IEEE80211_MESHID_LEN IEEE80211_NWID_LEN /* Link Metric Report */ struct ieee80211_meshlmetric_ie { uint8_t lm_ie; /* IEEE80211_ELEMID_MESHLINK */ uint8_t lm_len; uint32_t lm_metric; #define IEEE80211_MESHLMETRIC_INITIALVAL 0 } __packed; /* Congestion Notification */ struct ieee80211_meshcngst_ie { uint8_t cngst_ie; /* IEEE80211_ELEMID_MESHCNGST */ uint8_t cngst_len; uint16_t cngst_timer[4]; /* Expiration Timers: AC_BK, AC_BE, AC_VI, AC_VO */ } __packed; /* Peer Link Management */ struct ieee80211_meshpeer_ie { uint8_t peer_ie; /* IEEE80211_ELEMID_MESHPEER */ uint8_t peer_len; uint8_t peer_subtype; uint16_t peer_llinkid; /* Local Link ID */ uint16_t peer_linkid; /* Peer Link ID */ uint16_t peer_rcode; } __packed; enum { IEEE80211_MESH_PEER_LINK_OPEN = 0, IEEE80211_MESH_PEER_LINK_CONFIRM = 1, IEEE80211_MESH_PEER_LINK_CLOSE = 2, /* values 3-255 are reserved */ }; #ifdef notyet /* Mesh Channel Switch Annoucement */ struct ieee80211_meshcsa_ie { uint8_t csa_ie; /* IEEE80211_ELEMID_MESHCSA */ uint8_t csa_len; uint8_t csa_mode; uint8_t csa_newclass; /* New Regulatory Class */ uint8_t csa_newchan; uint8_t csa_precvalue; /* Precedence Value */ uint8_t csa_count; } __packed; /* Mesh TIM */ /* Equal to the non Mesh version */ /* Mesh Awake Window */ struct ieee80211_meshawakew_ie { uint8_t awakew_ie; /* IEEE80211_ELEMID_MESHAWAKEW */ uint8_t awakew_len; uint8_t awakew_windowlen; /* in TUs */ } __packed; /* Mesh Beacon Timing */ struct ieee80211_meshbeacont_ie { uint8_t beacont_ie; /* IEEE80211_ELEMID_MESHBEACONT */ uint8_t beacont_len; struct { uint8_t mp_aid; /* Least Octet of AID */ uint16_t mp_btime; /* Beacon Time */ uint16_t mp_bint; /* Beacon Interval */ } mp[1] __packed; /* NB: variable size */ } __packed; /* MDAOP Setup Request */ struct ieee80211_meshdaopsreq_ie { uint8_t daopsreq_ie; /* IEEE80211_ELEMID_MESHDAOPSREQ */ uint8_t daopsreq_len; uint8_t daopsreq_setid; uint32_t daposreq_reserv; /* MDAOP Reservation */ } __packed; /* MDAOP Setup Reply */ struct ieee80211_meshdaopsrep_ie { uint8_t daopsrep_ie; /* IEEE80211_ELEMID_MESHDAOPSREPLY */ uint8_t daopsrep_len; uint8_t daopsrep_setid; uint8_t daopsrep_code; /* Reply Code */ uint8_t daopsrep_reserv; /* MDAOP Reservation */ } __packed; enum { IEEE80211_MESH_DAOP_REPLY_ACCEPT = 0, IEEE80211_MESH_DAOP_REPLY_RESERV_CONFLICT = 1, IEEE80211_MESH_DAOP_REPLY_MAF_LIMIT = 2, /* everything else is reserved */ }; /* MDAOP Advertisements */ /* TBD */ /* MDAOP Set Teardown */ struct ieee80211_meshdaopst_ie { uint8_t daopsrep_ie; /* IEEE80211_ELEMID_MESHDAOPSREP */ uint8_t daopsrep_len; uint8_t daopsrep_setid; uint8_t daopsrep_setowner[IEEE80211_ADDR_LEN]; } __packed; #endif /* Portal (MP) Annoucement */ struct ieee80211_meshpann_ie { uint8_t pann_ie; /* IEEE80211_ELEMID_MESHPANN */ uint8_t pann_len; uint8_t pann_flags; uint8_t pann_hopcount; uint8_t pann_ttl; uint8_t pann_addr[IEEE80211_ADDR_LEN]; uint8_t pann_seq; /* PANN Sequence Number */ } __packed; /* Root (MP) Annoucement */ struct ieee80211_meshrann_ie { uint8_t rann_ie; /* IEEE80211_ELEMID_MESHRANN */ uint8_t rann_len; uint8_t rann_flags; #define IEEE80211_MESHRANN_FLAGS_PR 0x01 /* Portal Role */ uint8_t rann_hopcount; uint8_t rann_ttl; uint8_t rann_addr[IEEE80211_ADDR_LEN]; uint32_t rann_seq; /* HWMP Sequence Number */ uint32_t rann_metric; } __packed; /* Mesh Path Request */ struct ieee80211_meshpreq_ie { uint8_t preq_ie; /* IEEE80211_ELEMID_MESHPREQ */ uint8_t preq_len; uint8_t preq_flags; #define IEEE80211_MESHPREQ_FLAGS_PR 0x01 /* Portal Role */ #define IEEE80211_MESHPREQ_FLAGS_AE 0x02 /* Address Extension */ #define IEEE80211_MESHPREQ_FLAGS_PP 0x04 /* Proactive PREP */ uint8_t preq_hopcount; uint8_t preq_ttl; uint32_t preq_id; uint8_t preq_origaddr[IEEE80211_ADDR_LEN]; uint32_t preq_origseq; /* HWMP Sequence Number */ /* NB: may have Originator Proxied Address */ uint32_t preq_lifetime; uint32_t preq_metric; uint8_t preq_tcount; /* target count */ struct { uint8_t target_flags; #define IEEE80211_MESHPREQ_TFLAGS_TO 0x01 /* Target Only */ #define IEEE80211_MESHPREQ_TFLAGS_RF 0x02 /* Reply and Forward */ #define IEEE80211_MESHPREQ_TFLAGS_USN 0x04 /* Unknown HWMP seq number */ uint8_t target_addr[IEEE80211_ADDR_LEN]; uint32_t target_seq; /* HWMP Sequence Number */ } preq_targets[1] __packed; /* NB: variable size */ } __packed; /* Mesh Path Reply */ struct ieee80211_meshprep_ie { uint8_t prep_ie; /* IEEE80211_ELEMID_MESHPREP */ uint8_t prep_len; uint8_t prep_flags; uint8_t prep_hopcount; uint8_t prep_ttl; uint8_t prep_targetaddr[IEEE80211_ADDR_LEN]; uint32_t prep_targetseq; /* NB: May have Target Proxied Address */ uint32_t prep_lifetime; uint32_t prep_metric; uint8_t prep_origaddr[IEEE80211_ADDR_LEN]; uint32_t prep_origseq; /* HWMP Sequence Number */ } __packed; /* Mesh Path Error */ struct ieee80211_meshperr_ie { uint8_t perr_ie; /* IEEE80211_ELEMID_MESHPERR */ uint8_t perr_len; uint8_t perr_mode; uint8_t perr_ndests; /* Number of Destinations */ struct { uint8_t dest_addr[IEEE80211_ADDR_LEN]; uint32_t dest_seq; /* HWMP Sequence Number */ } perr_dests[1] __packed; /* NB: variable size */ } __packed; #ifdef notyet /* Mesh Proxy Update */ struct ieee80211_meshpu_ie { uint8_t pu_ie; /* IEEE80211_ELEMID_MESHPU */ uint8_t pu_len; /* XXXRP: TBD */ } __packed; /* Mesh Proxy Update Confirmation */ struct ieee80211_meshpuc_ie { uint8_t puc_ie; /* IEEE80211_ELEMID_MESHPUC */ uint8_t puc_len; uint8_t puc_flags; uint8_t puc_seq; /* PU Sequence Number */ uint8_t puc_daddr[IEEE80211_ADDR_LEN]; } __packed; #endif /* * 802.11s Action Frames */ #define IEEE80211_ACTION_CAT_MESHPEERING 30 /* XXX Linux */ #define IEEE80211_ACTION_CAT_MESHLMETRIC 13 #define IEEE80211_ACTION_CAT_MESHPATH 32 /* XXX Linux */ #define IEEE80211_ACTION_CAT_INTERWORK 15 #define IEEE80211_ACTION_CAT_RESOURCE 16 #define IEEE80211_ACTION_CAT_PROXY 17 /* * Mesh Peering Action codes. */ enum { IEEE80211_ACTION_MESHPEERING_OPEN = 0, IEEE80211_ACTION_MESHPEERING_CONFIRM = 1, IEEE80211_ACTION_MESHPEERING_CLOSE = 2, /* 3-255 reserved */ }; /* * Mesh Path Selection Action codes. */ enum { IEEE80211_ACTION_MESHPATH_REQ = 0, IEEE80211_ACTION_MESHPATH_REP = 1, IEEE80211_ACTION_MESHPATH_ERR = 2, IEEE80211_ACTION_MESHPATH_RANN = 3, /* 4-255 reserved */ }; /* * Mesh Link Metric Action codes. */ enum { IEEE80211_ACTION_MESHLMETRIC_REQ = 0, /* Link Metric Request */ IEEE80211_ACTION_MESHLMETRIC_REP = 1, /* Link Metric Report */ /* 2-255 reserved */ }; struct ieee80211_meshcntl { uint8_t mc_flags; uint8_t mc_ttl; uint32_t mc_seq; uint16_t _pad; /* more mesh addresses follow */ } __packed; void ieee80211_mesh_attach(struct ieee80211com *); void ieee80211_mesh_detach(struct ieee80211com *); void ieee80211_parse_meshid(struct ieee80211_node *, const uint8_t *); uint8_t * ieee80211_add_meshid(uint8_t *, struct ieee80211vap *); uint8_t * ieee80211_add_meshconf(uint8_t *, struct ieee80211vap *); uint8_t * ieee80211_add_meshpeer(uint8_t *, uint8_t, uint16_t, uint16_t, uint16_t); -uint32_t ieee80211_airtime_calc(struct ieee80211vap *, - struct ieee80211_node *); +uint32_t ieee80211_airtime_calc(struct ieee80211_node *); uint8_t * ieee80211_add_meshlmetric(uint8_t *, uint32_t); void ieee80211_create_mbss(struct ieee80211vap *, struct ieee80211_channel *); uint32_t ieee80211_mesh_getseq(void); #endif /* !_NET80211_IEEE80211_MESH_H_ */