132176cfdSRui Paulo /*- 232176cfdSRui Paulo * Copyright (c) 2009 The FreeBSD Foundation 332176cfdSRui Paulo * All rights reserved. 432176cfdSRui Paulo * 532176cfdSRui Paulo * This software was developed by Rui Paulo under sponsorship from the 632176cfdSRui Paulo * FreeBSD Foundation. 732176cfdSRui Paulo * 832176cfdSRui Paulo * Redistribution and use in source and binary forms, with or without 932176cfdSRui Paulo * modification, are permitted provided that the following conditions 1032176cfdSRui Paulo * are met: 1132176cfdSRui Paulo * 1. Redistributions of source code must retain the above copyright 1232176cfdSRui Paulo * notice, this list of conditions and the following disclaimer. 1332176cfdSRui Paulo * 2. Redistributions in binary form must reproduce the above copyright 1432176cfdSRui Paulo * notice, this list of conditions and the following disclaimer in the 1532176cfdSRui Paulo * documentation and/or other materials provided with the distribution. 1632176cfdSRui Paulo * 1732176cfdSRui Paulo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1832176cfdSRui Paulo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1932176cfdSRui Paulo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2032176cfdSRui Paulo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2132176cfdSRui Paulo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2232176cfdSRui Paulo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2332176cfdSRui Paulo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2432176cfdSRui Paulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2532176cfdSRui Paulo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2632176cfdSRui Paulo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2732176cfdSRui Paulo * SUCH DAMAGE. 2832176cfdSRui Paulo * 2932176cfdSRui Paulo * $FreeBSD: head/sys/net80211/ieee80211_hwmp.c 198581 2009-10-29 12:19:10Z rpaulo $ 3032176cfdSRui Paulo * $DragonFly$ 3132176cfdSRui Paulo */ 3232176cfdSRui Paulo 3332176cfdSRui Paulo /* 3432176cfdSRui Paulo * IEEE 802.11s Hybrid Wireless Mesh Protocol, HWMP. 3532176cfdSRui Paulo * 3632176cfdSRui Paulo * Based on March 2009, D3.0 802.11s draft spec. 3732176cfdSRui Paulo */ 3832176cfdSRui Paulo #include "opt_inet.h" 3932176cfdSRui Paulo #include "opt_wlan.h" 4032176cfdSRui Paulo 4132176cfdSRui Paulo #include <sys/param.h> 4232176cfdSRui Paulo #include <sys/systm.h> 4332176cfdSRui Paulo #include <sys/mbuf.h> 4432176cfdSRui Paulo #include <sys/malloc.h> 4532176cfdSRui Paulo #include <sys/kernel.h> 4632176cfdSRui Paulo 4732176cfdSRui Paulo #include <sys/socket.h> 4832176cfdSRui Paulo #include <sys/sockio.h> 4932176cfdSRui Paulo #include <sys/endian.h> 5032176cfdSRui Paulo #include <sys/errno.h> 5132176cfdSRui Paulo #include <sys/proc.h> 5232176cfdSRui Paulo #include <sys/sysctl.h> 5332176cfdSRui Paulo 5432176cfdSRui Paulo #include <net/if.h> 5532176cfdSRui Paulo #include <net/if_media.h> 5632176cfdSRui Paulo #include <net/if_llc.h> 5732176cfdSRui Paulo #include <net/ethernet.h> 5832176cfdSRui Paulo #include <net/route.h> 5932176cfdSRui Paulo 6032176cfdSRui Paulo #include <net/bpf.h> 6132176cfdSRui Paulo 6232176cfdSRui Paulo #include <netproto/802_11/ieee80211_var.h> 6332176cfdSRui Paulo #include <netproto/802_11/ieee80211_action.h> 6432176cfdSRui Paulo #include <netproto/802_11/ieee80211_input.h> 6532176cfdSRui Paulo #include <netproto/802_11/ieee80211_mesh.h> 6632176cfdSRui Paulo 6732176cfdSRui Paulo static void hwmp_vattach(struct ieee80211vap *); 6832176cfdSRui Paulo static void hwmp_vdetach(struct ieee80211vap *); 6932176cfdSRui Paulo static int hwmp_newstate(struct ieee80211vap *, 7032176cfdSRui Paulo enum ieee80211_state, int); 7132176cfdSRui Paulo static int hwmp_send_action(struct ieee80211_node *, 7232176cfdSRui Paulo const uint8_t [IEEE80211_ADDR_LEN], 7332176cfdSRui Paulo const uint8_t [IEEE80211_ADDR_LEN], 7432176cfdSRui Paulo uint8_t *, size_t); 7532176cfdSRui Paulo static uint8_t * hwmp_add_meshpreq(uint8_t *, 7632176cfdSRui Paulo const struct ieee80211_meshpreq_ie *); 7732176cfdSRui Paulo static uint8_t * hwmp_add_meshprep(uint8_t *, 7832176cfdSRui Paulo const struct ieee80211_meshprep_ie *); 7932176cfdSRui Paulo static uint8_t * hwmp_add_meshperr(uint8_t *, 8032176cfdSRui Paulo const struct ieee80211_meshperr_ie *); 8132176cfdSRui Paulo static uint8_t * hwmp_add_meshrann(uint8_t *, 8232176cfdSRui Paulo const struct ieee80211_meshrann_ie *); 8332176cfdSRui Paulo static void hwmp_rootmode_setup(struct ieee80211vap *); 8432176cfdSRui Paulo static void hwmp_rootmode_cb(void *); 8532176cfdSRui Paulo static void hwmp_rootmode_rann_cb(void *); 8632176cfdSRui Paulo static void hwmp_recv_preq(struct ieee80211vap *, struct ieee80211_node *, 8732176cfdSRui Paulo const struct ieee80211_frame *, 8832176cfdSRui Paulo const struct ieee80211_meshpreq_ie *); 8932176cfdSRui Paulo static int hwmp_send_preq(struct ieee80211_node *, 9032176cfdSRui Paulo const uint8_t [IEEE80211_ADDR_LEN], 9132176cfdSRui Paulo const uint8_t [IEEE80211_ADDR_LEN], 9232176cfdSRui Paulo struct ieee80211_meshpreq_ie *); 9332176cfdSRui Paulo static void hwmp_recv_prep(struct ieee80211vap *, struct ieee80211_node *, 9432176cfdSRui Paulo const struct ieee80211_frame *, 9532176cfdSRui Paulo const struct ieee80211_meshprep_ie *); 9632176cfdSRui Paulo static int hwmp_send_prep(struct ieee80211_node *, 9732176cfdSRui Paulo const uint8_t [IEEE80211_ADDR_LEN], 9832176cfdSRui Paulo const uint8_t [IEEE80211_ADDR_LEN], 9932176cfdSRui Paulo struct ieee80211_meshprep_ie *); 10032176cfdSRui Paulo static void hwmp_recv_perr(struct ieee80211vap *, struct ieee80211_node *, 10132176cfdSRui Paulo const struct ieee80211_frame *, 10232176cfdSRui Paulo const struct ieee80211_meshperr_ie *); 10332176cfdSRui Paulo static int hwmp_send_perr(struct ieee80211_node *, 10432176cfdSRui Paulo const uint8_t [IEEE80211_ADDR_LEN], 10532176cfdSRui Paulo const uint8_t [IEEE80211_ADDR_LEN], 10632176cfdSRui Paulo struct ieee80211_meshperr_ie *); 10732176cfdSRui Paulo static void hwmp_recv_rann(struct ieee80211vap *, struct ieee80211_node *, 10832176cfdSRui Paulo const struct ieee80211_frame *, 10932176cfdSRui Paulo const struct ieee80211_meshrann_ie *); 11032176cfdSRui Paulo static int hwmp_send_rann(struct ieee80211_node *, 11132176cfdSRui Paulo const uint8_t [IEEE80211_ADDR_LEN], 11232176cfdSRui Paulo const uint8_t [IEEE80211_ADDR_LEN], 11332176cfdSRui Paulo struct ieee80211_meshrann_ie *); 11432176cfdSRui Paulo static struct ieee80211_node * 11532176cfdSRui Paulo hwmp_discover(struct ieee80211vap *, 11632176cfdSRui Paulo const uint8_t [IEEE80211_ADDR_LEN], struct mbuf *); 11732176cfdSRui Paulo static void hwmp_peerdown(struct ieee80211_node *); 11832176cfdSRui Paulo 11932176cfdSRui Paulo static struct timeval ieee80211_hwmp_preqminint = { 0, 100000 }; 12032176cfdSRui Paulo static struct timeval ieee80211_hwmp_perrminint = { 0, 100000 }; 12132176cfdSRui Paulo 12232176cfdSRui Paulo /* unalligned little endian access */ 12332176cfdSRui Paulo #define LE_WRITE_2(p, v) do { \ 12432176cfdSRui Paulo ((uint8_t *)(p))[0] = (v) & 0xff; \ 12532176cfdSRui Paulo ((uint8_t *)(p))[1] = ((v) >> 8) & 0xff; \ 12632176cfdSRui Paulo } while (0) 12732176cfdSRui Paulo #define LE_WRITE_4(p, v) do { \ 12832176cfdSRui Paulo ((uint8_t *)(p))[0] = (v) & 0xff; \ 12932176cfdSRui Paulo ((uint8_t *)(p))[1] = ((v) >> 8) & 0xff; \ 13032176cfdSRui Paulo ((uint8_t *)(p))[2] = ((v) >> 16) & 0xff; \ 13132176cfdSRui Paulo ((uint8_t *)(p))[3] = ((v) >> 24) & 0xff; \ 13232176cfdSRui Paulo } while (0) 13332176cfdSRui Paulo 13432176cfdSRui Paulo 13532176cfdSRui Paulo /* NB: the Target Address set in a Proactive PREQ is the broadcast address. */ 13632176cfdSRui Paulo static const uint8_t broadcastaddr[IEEE80211_ADDR_LEN] = 13732176cfdSRui Paulo { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; 13832176cfdSRui Paulo 13932176cfdSRui Paulo typedef uint32_t ieee80211_hwmp_seq; 14032176cfdSRui Paulo #define HWMP_SEQ_LT(a, b) ((int32_t)((a)-(b)) < 0) 14132176cfdSRui Paulo #define HWMP_SEQ_LEQ(a, b) ((int32_t)((a)-(b)) <= 0) 14232176cfdSRui Paulo #define HWMP_SEQ_GT(a, b) ((int32_t)((a)-(b)) > 0) 14332176cfdSRui Paulo #define HWMP_SEQ_GEQ(a, b) ((int32_t)((a)-(b)) >= 0) 14432176cfdSRui Paulo 14532176cfdSRui Paulo /* 14632176cfdSRui Paulo * Private extension of ieee80211_mesh_route. 14732176cfdSRui Paulo */ 14832176cfdSRui Paulo struct ieee80211_hwmp_route { 14932176cfdSRui Paulo ieee80211_hwmp_seq hr_seq; /* last HWMP seq seen from dst*/ 15032176cfdSRui Paulo ieee80211_hwmp_seq hr_preqid; /* last PREQ ID seen from dst */ 15132176cfdSRui Paulo ieee80211_hwmp_seq hr_origseq; /* seq. no. on our latest PREQ*/ 15232176cfdSRui Paulo int hr_preqretries; 15332176cfdSRui Paulo }; 15432176cfdSRui Paulo struct ieee80211_hwmp_state { 15532176cfdSRui Paulo ieee80211_hwmp_seq hs_seq; /* next seq to be used */ 15632176cfdSRui Paulo ieee80211_hwmp_seq hs_preqid; /* next PREQ ID to be used */ 15732176cfdSRui Paulo struct timeval hs_lastpreq; /* last time we sent a PREQ */ 15832176cfdSRui Paulo struct timeval hs_lastperr; /* last time we sent a PERR */ 15932176cfdSRui Paulo int hs_rootmode; /* proactive HWMP */ 16032176cfdSRui Paulo struct callout hs_roottimer; 16132176cfdSRui Paulo uint8_t hs_maxhops; /* max hop count */ 16232176cfdSRui Paulo }; 16332176cfdSRui Paulo 16432176cfdSRui Paulo SYSCTL_NODE(_net_wlan, OID_AUTO, hwmp, CTLFLAG_RD, 0, 16532176cfdSRui Paulo "IEEE 802.11s HWMP parameters"); 16632176cfdSRui Paulo static int ieee80211_hwmp_targetonly = 0; 16732176cfdSRui Paulo SYSCTL_INT(_net_wlan_hwmp, OID_AUTO, targetonly, CTLTYPE_INT | CTLFLAG_RW, 16832176cfdSRui Paulo &ieee80211_hwmp_targetonly, 0, "Set TO bit on generated PREQs"); 16932176cfdSRui Paulo static int ieee80211_hwmp_replyforward = 1; 17032176cfdSRui Paulo SYSCTL_INT(_net_wlan_hwmp, OID_AUTO, replyforward, CTLTYPE_INT | CTLFLAG_RW, 17132176cfdSRui Paulo &ieee80211_hwmp_replyforward, 0, "Set RF bit on generated PREQs"); 17232176cfdSRui Paulo static int ieee80211_hwmp_pathtimeout = -1; 17332176cfdSRui Paulo SYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, pathlifetime, CTLTYPE_INT | CTLFLAG_RW, 17432176cfdSRui Paulo &ieee80211_hwmp_pathtimeout, 0, ieee80211_sysctl_msecs_ticks, "I", 17532176cfdSRui Paulo "path entry lifetime (ms)"); 17632176cfdSRui Paulo static int ieee80211_hwmp_roottimeout = -1; 17732176cfdSRui Paulo SYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, roottimeout, CTLTYPE_INT | CTLFLAG_RW, 17832176cfdSRui Paulo &ieee80211_hwmp_roottimeout, 0, ieee80211_sysctl_msecs_ticks, "I", 17932176cfdSRui Paulo "root PREQ timeout (ms)"); 18032176cfdSRui Paulo static int ieee80211_hwmp_rootint = -1; 18132176cfdSRui Paulo SYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, rootint, CTLTYPE_INT | CTLFLAG_RW, 18232176cfdSRui Paulo &ieee80211_hwmp_rootint, 0, ieee80211_sysctl_msecs_ticks, "I", 18332176cfdSRui Paulo "root interval (ms)"); 18432176cfdSRui Paulo static int ieee80211_hwmp_rannint = -1; 18532176cfdSRui Paulo SYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, rannint, CTLTYPE_INT | CTLFLAG_RW, 18632176cfdSRui Paulo &ieee80211_hwmp_rannint, 0, ieee80211_sysctl_msecs_ticks, "I", 18732176cfdSRui Paulo "root announcement interval (ms)"); 18832176cfdSRui Paulo 18932176cfdSRui Paulo #define IEEE80211_HWMP_DEFAULT_MAXHOPS 31 19032176cfdSRui Paulo 19132176cfdSRui Paulo static ieee80211_recv_action_func hwmp_recv_action_meshpath; 19232176cfdSRui Paulo 19332176cfdSRui Paulo static struct ieee80211_mesh_proto_path mesh_proto_hwmp = { 19432176cfdSRui Paulo .mpp_descr = "HWMP", 19532176cfdSRui Paulo .mpp_ie = IEEE80211_MESHCONF_PATH_HWMP, 19632176cfdSRui Paulo .mpp_discover = hwmp_discover, 19732176cfdSRui Paulo .mpp_peerdown = hwmp_peerdown, 19832176cfdSRui Paulo .mpp_vattach = hwmp_vattach, 19932176cfdSRui Paulo .mpp_vdetach = hwmp_vdetach, 20032176cfdSRui Paulo .mpp_newstate = hwmp_newstate, 20132176cfdSRui Paulo .mpp_privlen = sizeof(struct ieee80211_hwmp_route), 20232176cfdSRui Paulo }; 20332176cfdSRui Paulo SYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, inact, CTLTYPE_INT | CTLFLAG_RW, 20432176cfdSRui Paulo &mesh_proto_hwmp.mpp_inact, 0, ieee80211_sysctl_msecs_ticks, "I", 20532176cfdSRui Paulo "mesh route inactivity timeout (ms)"); 20632176cfdSRui Paulo 20732176cfdSRui Paulo 20832176cfdSRui Paulo static void 20932176cfdSRui Paulo ieee80211_hwmp_init(void) 21032176cfdSRui Paulo { 21132176cfdSRui Paulo ieee80211_hwmp_pathtimeout = msecs_to_ticks(5*1000); 21232176cfdSRui Paulo ieee80211_hwmp_roottimeout = msecs_to_ticks(5*1000); 21332176cfdSRui Paulo ieee80211_hwmp_rootint = msecs_to_ticks(2*1000); 21432176cfdSRui Paulo ieee80211_hwmp_rannint = msecs_to_ticks(1*1000); 21532176cfdSRui Paulo 21632176cfdSRui Paulo /* 21732176cfdSRui Paulo * Register action frame handler. 21832176cfdSRui Paulo */ 21932176cfdSRui Paulo ieee80211_recv_action_register(IEEE80211_ACTION_CAT_MESHPATH, 22032176cfdSRui Paulo IEEE80211_ACTION_MESHPATH_SEL, hwmp_recv_action_meshpath); 22132176cfdSRui Paulo 22232176cfdSRui Paulo /* NB: default is 5 secs per spec */ 22332176cfdSRui Paulo mesh_proto_hwmp.mpp_inact = msecs_to_ticks(5*1000); 22432176cfdSRui Paulo 22532176cfdSRui Paulo /* 22632176cfdSRui Paulo * Register HWMP. 22732176cfdSRui Paulo */ 22832176cfdSRui Paulo ieee80211_mesh_register_proto_path(&mesh_proto_hwmp); 22932176cfdSRui Paulo } 23032176cfdSRui Paulo SYSINIT(wlan_hwmp, SI_SUB_DRIVERS, SI_ORDER_SECOND, ieee80211_hwmp_init, NULL); 23132176cfdSRui Paulo 23232176cfdSRui Paulo void 23332176cfdSRui Paulo hwmp_vattach(struct ieee80211vap *vap) 23432176cfdSRui Paulo { 23532176cfdSRui Paulo struct ieee80211_hwmp_state *hs; 23632176cfdSRui Paulo 23732176cfdSRui Paulo KASSERT(vap->iv_opmode == IEEE80211_M_MBSS, 23832176cfdSRui Paulo ("not a mesh vap, opmode %d", vap->iv_opmode)); 23932176cfdSRui Paulo 24032176cfdSRui Paulo hs = kmalloc(sizeof(struct ieee80211_hwmp_state), M_80211_VAP, 241*fcaa651dSRui Paulo M_INTWAIT | M_ZERO); 24232176cfdSRui Paulo if (hs == NULL) { 24332176cfdSRui Paulo kprintf("%s: couldn't alloc HWMP state\n", __func__); 24432176cfdSRui Paulo return; 24532176cfdSRui Paulo } 24632176cfdSRui Paulo hs->hs_maxhops = IEEE80211_HWMP_DEFAULT_MAXHOPS; 24734a60cf6SRui Paulo callout_init_mp(&hs->hs_roottimer); 24832176cfdSRui Paulo vap->iv_hwmp = hs; 24932176cfdSRui Paulo } 25032176cfdSRui Paulo 25132176cfdSRui Paulo void 25232176cfdSRui Paulo hwmp_vdetach(struct ieee80211vap *vap) 25332176cfdSRui Paulo { 25432176cfdSRui Paulo struct ieee80211_hwmp_state *hs = vap->iv_hwmp; 25532176cfdSRui Paulo 256dfcf81fdSRui Paulo callout_stop(&hs->hs_roottimer); 25732176cfdSRui Paulo kfree(vap->iv_hwmp, M_80211_VAP); 25832176cfdSRui Paulo vap->iv_hwmp = NULL; 25932176cfdSRui Paulo } 26032176cfdSRui Paulo 26132176cfdSRui Paulo int 26232176cfdSRui Paulo hwmp_newstate(struct ieee80211vap *vap, enum ieee80211_state ostate, int arg) 26332176cfdSRui Paulo { 26432176cfdSRui Paulo enum ieee80211_state nstate = vap->iv_state; 26532176cfdSRui Paulo struct ieee80211_hwmp_state *hs = vap->iv_hwmp; 26632176cfdSRui Paulo 26732176cfdSRui Paulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n", 26832176cfdSRui Paulo __func__, ieee80211_state_name[ostate], 26932176cfdSRui Paulo ieee80211_state_name[nstate], arg); 27032176cfdSRui Paulo 27132176cfdSRui Paulo if (nstate != IEEE80211_S_RUN && ostate == IEEE80211_S_RUN) 272dfcf81fdSRui Paulo callout_stop(&hs->hs_roottimer); 27332176cfdSRui Paulo if (nstate == IEEE80211_S_RUN) 27432176cfdSRui Paulo hwmp_rootmode_setup(vap); 27532176cfdSRui Paulo return 0; 27632176cfdSRui Paulo } 27732176cfdSRui Paulo 27832176cfdSRui Paulo static int 27932176cfdSRui Paulo hwmp_recv_action_meshpath(struct ieee80211_node *ni, 28032176cfdSRui Paulo const struct ieee80211_frame *wh, 28132176cfdSRui Paulo const uint8_t *frm, const uint8_t *efrm) 28232176cfdSRui Paulo { 28332176cfdSRui Paulo struct ieee80211vap *vap = ni->ni_vap; 28432176cfdSRui Paulo struct ieee80211_meshpreq_ie preq; 28532176cfdSRui Paulo struct ieee80211_meshprep_ie prep; 28632176cfdSRui Paulo struct ieee80211_meshperr_ie perr; 28732176cfdSRui Paulo struct ieee80211_meshrann_ie rann; 28832176cfdSRui Paulo const uint8_t *iefrm = frm + 2; /* action + code */ 28932176cfdSRui Paulo int found = 0; 29032176cfdSRui Paulo 29132176cfdSRui Paulo while (efrm - iefrm > 1) { 29232176cfdSRui Paulo IEEE80211_VERIFY_LENGTH(efrm - iefrm, iefrm[1] + 2, return 0); 29332176cfdSRui Paulo switch (*iefrm) { 29432176cfdSRui Paulo case IEEE80211_ELEMID_MESHPREQ: 29532176cfdSRui Paulo { 29632176cfdSRui Paulo const struct ieee80211_meshpreq_ie *mpreq = 29732176cfdSRui Paulo (const struct ieee80211_meshpreq_ie *) iefrm; 29832176cfdSRui Paulo /* XXX > 1 target */ 29932176cfdSRui Paulo if (mpreq->preq_len != 30032176cfdSRui Paulo sizeof(struct ieee80211_meshpreq_ie) - 2) { 30132176cfdSRui Paulo IEEE80211_DISCARD(vap, 30232176cfdSRui Paulo IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP, 30332176cfdSRui Paulo wh, NULL, "%s", "PREQ with wrong len"); 30432176cfdSRui Paulo vap->iv_stats.is_rx_mgtdiscard++; 30532176cfdSRui Paulo break; 30632176cfdSRui Paulo } 30732176cfdSRui Paulo memcpy(&preq, mpreq, sizeof(preq)); 30832176cfdSRui Paulo preq.preq_id = LE_READ_4(&mpreq->preq_id); 30932176cfdSRui Paulo preq.preq_origseq = LE_READ_4(&mpreq->preq_origseq); 31032176cfdSRui Paulo preq.preq_lifetime = LE_READ_4(&mpreq->preq_lifetime); 31132176cfdSRui Paulo preq.preq_metric = LE_READ_4(&mpreq->preq_metric); 31232176cfdSRui Paulo preq.preq_targets[0].target_seq = 31332176cfdSRui Paulo LE_READ_4(&mpreq->preq_targets[0].target_seq); 31432176cfdSRui Paulo hwmp_recv_preq(vap, ni, wh, &preq); 31532176cfdSRui Paulo found++; 31632176cfdSRui Paulo break; 31732176cfdSRui Paulo } 31832176cfdSRui Paulo case IEEE80211_ELEMID_MESHPREP: 31932176cfdSRui Paulo { 32032176cfdSRui Paulo const struct ieee80211_meshprep_ie *mprep = 32132176cfdSRui Paulo (const struct ieee80211_meshprep_ie *) iefrm; 32232176cfdSRui Paulo if (mprep->prep_len != 32332176cfdSRui Paulo sizeof(struct ieee80211_meshprep_ie) - 2) { 32432176cfdSRui Paulo IEEE80211_DISCARD(vap, 32532176cfdSRui Paulo IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP, 32632176cfdSRui Paulo wh, NULL, "%s", "PREP with wrong len"); 32732176cfdSRui Paulo vap->iv_stats.is_rx_mgtdiscard++; 32832176cfdSRui Paulo break; 32932176cfdSRui Paulo } 33032176cfdSRui Paulo memcpy(&prep, mprep, sizeof(prep)); 33132176cfdSRui Paulo prep.prep_targetseq = LE_READ_4(&mprep->prep_targetseq); 33232176cfdSRui Paulo prep.prep_lifetime = LE_READ_4(&mprep->prep_lifetime); 33332176cfdSRui Paulo prep.prep_metric = LE_READ_4(&mprep->prep_metric); 33432176cfdSRui Paulo prep.prep_origseq = LE_READ_4(&mprep->prep_origseq); 33532176cfdSRui Paulo hwmp_recv_prep(vap, ni, wh, &prep); 33632176cfdSRui Paulo found++; 33732176cfdSRui Paulo break; 33832176cfdSRui Paulo } 33932176cfdSRui Paulo case IEEE80211_ELEMID_MESHPERR: 34032176cfdSRui Paulo { 34132176cfdSRui Paulo const struct ieee80211_meshperr_ie *mperr = 34232176cfdSRui Paulo (const struct ieee80211_meshperr_ie *) iefrm; 34332176cfdSRui Paulo /* XXX > 1 target */ 34432176cfdSRui Paulo if (mperr->perr_len != 34532176cfdSRui Paulo sizeof(struct ieee80211_meshperr_ie) - 2) { 34632176cfdSRui Paulo IEEE80211_DISCARD(vap, 34732176cfdSRui Paulo IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP, 34832176cfdSRui Paulo wh, NULL, "%s", "PERR with wrong len"); 34932176cfdSRui Paulo vap->iv_stats.is_rx_mgtdiscard++; 35032176cfdSRui Paulo break; 35132176cfdSRui Paulo } 35232176cfdSRui Paulo memcpy(&perr, mperr, sizeof(perr)); 35332176cfdSRui Paulo perr.perr_dests[0].dest_seq = 35432176cfdSRui Paulo LE_READ_4(&mperr->perr_dests[0].dest_seq); 35532176cfdSRui Paulo hwmp_recv_perr(vap, ni, wh, &perr); 35632176cfdSRui Paulo found++; 35732176cfdSRui Paulo break; 35832176cfdSRui Paulo } 35932176cfdSRui Paulo case IEEE80211_ELEMID_MESHRANN: 36032176cfdSRui Paulo { 36132176cfdSRui Paulo const struct ieee80211_meshrann_ie *mrann = 36232176cfdSRui Paulo (const struct ieee80211_meshrann_ie *) iefrm; 36332176cfdSRui Paulo if (mrann->rann_len != 36432176cfdSRui Paulo sizeof(struct ieee80211_meshrann_ie) - 2) { 36532176cfdSRui Paulo IEEE80211_DISCARD(vap, 36632176cfdSRui Paulo IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP, 36732176cfdSRui Paulo wh, NULL, "%s", "RAN with wrong len"); 36832176cfdSRui Paulo vap->iv_stats.is_rx_mgtdiscard++; 36932176cfdSRui Paulo return 1; 37032176cfdSRui Paulo } 37132176cfdSRui Paulo memcpy(&rann, mrann, sizeof(rann)); 37232176cfdSRui Paulo rann.rann_seq = LE_READ_4(&mrann->rann_seq); 37332176cfdSRui Paulo rann.rann_metric = LE_READ_4(&mrann->rann_metric); 37432176cfdSRui Paulo hwmp_recv_rann(vap, ni, wh, &rann); 37532176cfdSRui Paulo found++; 37632176cfdSRui Paulo break; 37732176cfdSRui Paulo } 37832176cfdSRui Paulo } 37932176cfdSRui Paulo iefrm += iefrm[1] + 2; 38032176cfdSRui Paulo } 38132176cfdSRui Paulo if (!found) { 38232176cfdSRui Paulo IEEE80211_DISCARD(vap, 38332176cfdSRui Paulo IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP, 38432176cfdSRui Paulo wh, NULL, "%s", "PATH SEL action without IE"); 38532176cfdSRui Paulo vap->iv_stats.is_rx_mgtdiscard++; 38632176cfdSRui Paulo } 38732176cfdSRui Paulo return 0; 38832176cfdSRui Paulo } 38932176cfdSRui Paulo 39032176cfdSRui Paulo static int 39132176cfdSRui Paulo hwmp_send_action(struct ieee80211_node *ni, 39232176cfdSRui Paulo const uint8_t sa[IEEE80211_ADDR_LEN], 39332176cfdSRui Paulo const uint8_t da[IEEE80211_ADDR_LEN], 39432176cfdSRui Paulo uint8_t *ie, size_t len) 39532176cfdSRui Paulo { 39632176cfdSRui Paulo struct ieee80211vap *vap = ni->ni_vap; 39732176cfdSRui Paulo struct ieee80211com *ic = ni->ni_ic; 39832176cfdSRui Paulo struct ieee80211_bpf_params params; 39932176cfdSRui Paulo struct mbuf *m; 40032176cfdSRui Paulo uint8_t *frm; 40132176cfdSRui Paulo 40232176cfdSRui Paulo if (vap->iv_state == IEEE80211_S_CAC) { 40332176cfdSRui Paulo IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, ni, 40432176cfdSRui Paulo "block %s frame in CAC state", "HWMP action"); 40532176cfdSRui Paulo vap->iv_stats.is_tx_badstate++; 40632176cfdSRui Paulo return EIO; /* XXX */ 40732176cfdSRui Paulo } 40832176cfdSRui Paulo 40932176cfdSRui Paulo KASSERT(ni != NULL, ("null node")); 41032176cfdSRui Paulo /* 41132176cfdSRui Paulo * Hold a reference on the node so it doesn't go away until after 41232176cfdSRui Paulo * the xmit is complete all the way in the driver. On error we 41332176cfdSRui Paulo * will remove our reference. 41432176cfdSRui Paulo */ 41532176cfdSRui Paulo #ifdef IEEE80211_DEBUG_REFCNT 41632176cfdSRui Paulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, 4176168f72eSRui Paulo "ieee80211_ref_node (%s:%u) %p<%6D> refcnt %d\n", 41832176cfdSRui Paulo __func__, __LINE__, 4196168f72eSRui Paulo ni, ni->ni_macaddr, ":", 42032176cfdSRui Paulo ieee80211_node_refcnt(ni)+1); 42132176cfdSRui Paulo #endif 42232176cfdSRui Paulo ieee80211_ref_node(ni); 42332176cfdSRui Paulo 42432176cfdSRui Paulo m = ieee80211_getmgtframe(&frm, 42532176cfdSRui Paulo ic->ic_headroom + sizeof(struct ieee80211_frame), 42632176cfdSRui Paulo sizeof(struct ieee80211_action) + len 42732176cfdSRui Paulo ); 42832176cfdSRui Paulo if (m == NULL) { 42932176cfdSRui Paulo ieee80211_free_node(ni); 43032176cfdSRui Paulo vap->iv_stats.is_tx_nobuf++; 43132176cfdSRui Paulo return ENOMEM; 43232176cfdSRui Paulo } 43332176cfdSRui Paulo *frm++ = IEEE80211_ACTION_CAT_MESHPATH; 43432176cfdSRui Paulo *frm++ = IEEE80211_ACTION_MESHPATH_SEL; 43532176cfdSRui Paulo switch (*ie) { 43632176cfdSRui Paulo case IEEE80211_ELEMID_MESHPREQ: 43732176cfdSRui Paulo frm = hwmp_add_meshpreq(frm, 43832176cfdSRui Paulo (struct ieee80211_meshpreq_ie *)ie); 43932176cfdSRui Paulo break; 44032176cfdSRui Paulo case IEEE80211_ELEMID_MESHPREP: 44132176cfdSRui Paulo frm = hwmp_add_meshprep(frm, 44232176cfdSRui Paulo (struct ieee80211_meshprep_ie *)ie); 44332176cfdSRui Paulo break; 44432176cfdSRui Paulo case IEEE80211_ELEMID_MESHPERR: 44532176cfdSRui Paulo frm = hwmp_add_meshperr(frm, 44632176cfdSRui Paulo (struct ieee80211_meshperr_ie *)ie); 44732176cfdSRui Paulo break; 44832176cfdSRui Paulo case IEEE80211_ELEMID_MESHRANN: 44932176cfdSRui Paulo frm = hwmp_add_meshrann(frm, 45032176cfdSRui Paulo (struct ieee80211_meshrann_ie *)ie); 45132176cfdSRui Paulo break; 45232176cfdSRui Paulo } 45332176cfdSRui Paulo 45432176cfdSRui Paulo m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); 45532176cfdSRui Paulo M_PREPEND(m, sizeof(struct ieee80211_frame), MB_DONTWAIT); 45632176cfdSRui Paulo if (m == NULL) { 45732176cfdSRui Paulo ieee80211_free_node(ni); 45832176cfdSRui Paulo vap->iv_stats.is_tx_nobuf++; 45932176cfdSRui Paulo return ENOMEM; 46032176cfdSRui Paulo } 46132176cfdSRui Paulo ieee80211_send_setup(ni, m, 46232176cfdSRui Paulo IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_ACTION, 46332176cfdSRui Paulo IEEE80211_NONQOS_TID, sa, da, sa); 46432176cfdSRui Paulo 46532176cfdSRui Paulo m->m_flags |= M_ENCAP; /* mark encapsulated */ 46632176cfdSRui Paulo IEEE80211_NODE_STAT(ni, tx_mgmt); 46732176cfdSRui Paulo 46832176cfdSRui Paulo memset(¶ms, 0, sizeof(params)); 46932176cfdSRui Paulo params.ibp_pri = WME_AC_VO; 47032176cfdSRui Paulo params.ibp_rate0 = ni->ni_txparms->mgmtrate; 47132176cfdSRui Paulo if (IEEE80211_IS_MULTICAST(da)) 47232176cfdSRui Paulo params.ibp_try0 = 1; 47332176cfdSRui Paulo else 47432176cfdSRui Paulo params.ibp_try0 = ni->ni_txparms->maxretry; 47532176cfdSRui Paulo params.ibp_power = ni->ni_txpower; 47632176cfdSRui Paulo return ic->ic_raw_xmit(ni, m, ¶ms); 47732176cfdSRui Paulo } 47832176cfdSRui Paulo 47932176cfdSRui Paulo #define ADDSHORT(frm, v) do { \ 48032176cfdSRui Paulo frm[0] = (v) & 0xff; \ 48132176cfdSRui Paulo frm[1] = (v) >> 8; \ 48232176cfdSRui Paulo frm += 2; \ 48332176cfdSRui Paulo } while (0) 48432176cfdSRui Paulo #define ADDWORD(frm, v) do { \ 48532176cfdSRui Paulo LE_WRITE_4(frm, v); \ 48632176cfdSRui Paulo frm += 4; \ 48732176cfdSRui Paulo } while (0) 48832176cfdSRui Paulo /* 48932176cfdSRui Paulo * Add a Mesh Path Request IE to a frame. 49032176cfdSRui Paulo */ 49132176cfdSRui Paulo static uint8_t * 49232176cfdSRui Paulo hwmp_add_meshpreq(uint8_t *frm, const struct ieee80211_meshpreq_ie *preq) 49332176cfdSRui Paulo { 49432176cfdSRui Paulo int i; 49532176cfdSRui Paulo 49632176cfdSRui Paulo *frm++ = IEEE80211_ELEMID_MESHPREQ; 49732176cfdSRui Paulo *frm++ = sizeof(struct ieee80211_meshpreq_ie) - 2 + 49832176cfdSRui Paulo (preq->preq_tcount - 1) * sizeof(*preq->preq_targets); 49932176cfdSRui Paulo *frm++ = preq->preq_flags; 50032176cfdSRui Paulo *frm++ = preq->preq_hopcount; 50132176cfdSRui Paulo *frm++ = preq->preq_ttl; 50232176cfdSRui Paulo ADDWORD(frm, preq->preq_id); 50332176cfdSRui Paulo IEEE80211_ADDR_COPY(frm, preq->preq_origaddr); frm += 6; 50432176cfdSRui Paulo ADDWORD(frm, preq->preq_origseq); 50532176cfdSRui Paulo ADDWORD(frm, preq->preq_lifetime); 50632176cfdSRui Paulo ADDWORD(frm, preq->preq_metric); 50732176cfdSRui Paulo *frm++ = preq->preq_tcount; 50832176cfdSRui Paulo for (i = 0; i < preq->preq_tcount; i++) { 50932176cfdSRui Paulo *frm++ = preq->preq_targets[i].target_flags; 51032176cfdSRui Paulo IEEE80211_ADDR_COPY(frm, preq->preq_targets[i].target_addr); 51132176cfdSRui Paulo frm += 6; 51232176cfdSRui Paulo ADDWORD(frm, preq->preq_targets[i].target_seq); 51332176cfdSRui Paulo } 51432176cfdSRui Paulo return frm; 51532176cfdSRui Paulo } 51632176cfdSRui Paulo 51732176cfdSRui Paulo /* 51832176cfdSRui Paulo * Add a Mesh Path Reply IE to a frame. 51932176cfdSRui Paulo */ 52032176cfdSRui Paulo static uint8_t * 52132176cfdSRui Paulo hwmp_add_meshprep(uint8_t *frm, const struct ieee80211_meshprep_ie *prep) 52232176cfdSRui Paulo { 52332176cfdSRui Paulo *frm++ = IEEE80211_ELEMID_MESHPREP; 52432176cfdSRui Paulo *frm++ = sizeof(struct ieee80211_meshprep_ie) - 2; 52532176cfdSRui Paulo *frm++ = prep->prep_flags; 52632176cfdSRui Paulo *frm++ = prep->prep_hopcount; 52732176cfdSRui Paulo *frm++ = prep->prep_ttl; 52832176cfdSRui Paulo IEEE80211_ADDR_COPY(frm, prep->prep_targetaddr); frm += 6; 52932176cfdSRui Paulo ADDWORD(frm, prep->prep_targetseq); 53032176cfdSRui Paulo ADDWORD(frm, prep->prep_lifetime); 53132176cfdSRui Paulo ADDWORD(frm, prep->prep_metric); 53232176cfdSRui Paulo IEEE80211_ADDR_COPY(frm, prep->prep_origaddr); frm += 6; 53332176cfdSRui Paulo ADDWORD(frm, prep->prep_origseq); 53432176cfdSRui Paulo return frm; 53532176cfdSRui Paulo } 53632176cfdSRui Paulo 53732176cfdSRui Paulo /* 53832176cfdSRui Paulo * Add a Mesh Path Error IE to a frame. 53932176cfdSRui Paulo */ 54032176cfdSRui Paulo static uint8_t * 54132176cfdSRui Paulo hwmp_add_meshperr(uint8_t *frm, const struct ieee80211_meshperr_ie *perr) 54232176cfdSRui Paulo { 54332176cfdSRui Paulo int i; 54432176cfdSRui Paulo 54532176cfdSRui Paulo *frm++ = IEEE80211_ELEMID_MESHPERR; 54632176cfdSRui Paulo *frm++ = sizeof(struct ieee80211_meshperr_ie) - 2 + 54732176cfdSRui Paulo (perr->perr_ndests - 1) * sizeof(*perr->perr_dests); 54832176cfdSRui Paulo *frm++ = perr->perr_ttl; 54932176cfdSRui Paulo *frm++ = perr->perr_ndests; 55032176cfdSRui Paulo for (i = 0; i < perr->perr_ndests; i++) { 55132176cfdSRui Paulo *frm++ = perr->perr_dests[i].dest_flags; 55232176cfdSRui Paulo IEEE80211_ADDR_COPY(frm, perr->perr_dests[i].dest_addr); 55332176cfdSRui Paulo frm += 6; 55432176cfdSRui Paulo ADDWORD(frm, perr->perr_dests[i].dest_seq); 55532176cfdSRui Paulo ADDSHORT(frm, perr->perr_dests[i].dest_rcode); 55632176cfdSRui Paulo } 55732176cfdSRui Paulo return frm; 55832176cfdSRui Paulo } 55932176cfdSRui Paulo 56032176cfdSRui Paulo /* 56132176cfdSRui Paulo * Add a Root Annoucement IE to a frame. 56232176cfdSRui Paulo */ 56332176cfdSRui Paulo static uint8_t * 56432176cfdSRui Paulo hwmp_add_meshrann(uint8_t *frm, const struct ieee80211_meshrann_ie *rann) 56532176cfdSRui Paulo { 56632176cfdSRui Paulo *frm++ = IEEE80211_ELEMID_MESHRANN; 56732176cfdSRui Paulo *frm++ = sizeof(struct ieee80211_meshrann_ie) - 2; 56832176cfdSRui Paulo *frm++ = rann->rann_flags; 56932176cfdSRui Paulo *frm++ = rann->rann_hopcount; 57032176cfdSRui Paulo *frm++ = rann->rann_ttl; 57132176cfdSRui Paulo IEEE80211_ADDR_COPY(frm, rann->rann_addr); frm += 6; 57232176cfdSRui Paulo ADDWORD(frm, rann->rann_seq); 57332176cfdSRui Paulo ADDWORD(frm, rann->rann_metric); 57432176cfdSRui Paulo return frm; 57532176cfdSRui Paulo } 57632176cfdSRui Paulo 57732176cfdSRui Paulo static void 57832176cfdSRui Paulo hwmp_rootmode_setup(struct ieee80211vap *vap) 57932176cfdSRui Paulo { 58032176cfdSRui Paulo struct ieee80211_hwmp_state *hs = vap->iv_hwmp; 58132176cfdSRui Paulo 58232176cfdSRui Paulo switch (hs->hs_rootmode) { 58332176cfdSRui Paulo case IEEE80211_HWMP_ROOTMODE_DISABLED: 584dfcf81fdSRui Paulo callout_stop(&hs->hs_roottimer); 58532176cfdSRui Paulo break; 58632176cfdSRui Paulo case IEEE80211_HWMP_ROOTMODE_NORMAL: 58732176cfdSRui Paulo case IEEE80211_HWMP_ROOTMODE_PROACTIVE: 58832176cfdSRui Paulo callout_reset(&hs->hs_roottimer, ieee80211_hwmp_rootint, 58932176cfdSRui Paulo hwmp_rootmode_cb, vap); 59032176cfdSRui Paulo break; 59132176cfdSRui Paulo case IEEE80211_HWMP_ROOTMODE_RANN: 59232176cfdSRui Paulo callout_reset(&hs->hs_roottimer, ieee80211_hwmp_rannint, 59332176cfdSRui Paulo hwmp_rootmode_rann_cb, vap); 59432176cfdSRui Paulo break; 59532176cfdSRui Paulo } 59632176cfdSRui Paulo } 59732176cfdSRui Paulo 59832176cfdSRui Paulo /* 59932176cfdSRui Paulo * Send a broadcast Path Request to find all nodes on the mesh. We are 60032176cfdSRui Paulo * called when the vap is configured as a HWMP root node. 60132176cfdSRui Paulo */ 60232176cfdSRui Paulo #define PREQ_TFLAGS(n) preq.preq_targets[n].target_flags 60332176cfdSRui Paulo #define PREQ_TADDR(n) preq.preq_targets[n].target_addr 60432176cfdSRui Paulo #define PREQ_TSEQ(n) preq.preq_targets[n].target_seq 60532176cfdSRui Paulo static void 60632176cfdSRui Paulo hwmp_rootmode_cb(void *arg) 60732176cfdSRui Paulo { 60832176cfdSRui Paulo struct ieee80211vap *vap = (struct ieee80211vap *)arg; 60932176cfdSRui Paulo struct ieee80211_hwmp_state *hs = vap->iv_hwmp; 61032176cfdSRui Paulo struct ieee80211_mesh_state *ms = vap->iv_mesh; 61132176cfdSRui Paulo struct ieee80211_meshpreq_ie preq; 61232176cfdSRui Paulo 61332176cfdSRui Paulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, vap->iv_bss, 61432176cfdSRui Paulo "%s", "send broadcast PREQ"); 61532176cfdSRui Paulo 61632176cfdSRui Paulo preq.preq_flags = IEEE80211_MESHPREQ_FLAGS_AM; 61732176cfdSRui Paulo if (ms->ms_flags & IEEE80211_MESHFLAGS_PORTAL) 61832176cfdSRui Paulo preq.preq_flags |= IEEE80211_MESHPREQ_FLAGS_PR; 61932176cfdSRui Paulo if (hs->hs_rootmode == IEEE80211_HWMP_ROOTMODE_PROACTIVE) 62032176cfdSRui Paulo preq.preq_flags |= IEEE80211_MESHPREQ_FLAGS_PP; 62132176cfdSRui Paulo preq.preq_hopcount = 0; 62232176cfdSRui Paulo preq.preq_ttl = ms->ms_ttl; 62332176cfdSRui Paulo preq.preq_id = ++hs->hs_preqid; 62432176cfdSRui Paulo IEEE80211_ADDR_COPY(preq.preq_origaddr, vap->iv_myaddr); 62532176cfdSRui Paulo preq.preq_origseq = ++hs->hs_seq; 62632176cfdSRui Paulo preq.preq_lifetime = ticks_to_msecs(ieee80211_hwmp_roottimeout); 62732176cfdSRui Paulo preq.preq_metric = IEEE80211_MESHLMETRIC_INITIALVAL; 62832176cfdSRui Paulo preq.preq_tcount = 1; 62932176cfdSRui Paulo IEEE80211_ADDR_COPY(PREQ_TADDR(0), broadcastaddr); 63032176cfdSRui Paulo PREQ_TFLAGS(0) = IEEE80211_MESHPREQ_TFLAGS_TO | 63132176cfdSRui Paulo IEEE80211_MESHPREQ_TFLAGS_RF; 63232176cfdSRui Paulo PREQ_TSEQ(0) = 0; 63332176cfdSRui Paulo vap->iv_stats.is_hwmp_rootreqs++; 63432176cfdSRui Paulo hwmp_send_preq(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &preq); 63532176cfdSRui Paulo hwmp_rootmode_setup(vap); 63632176cfdSRui Paulo } 63732176cfdSRui Paulo #undef PREQ_TFLAGS 63832176cfdSRui Paulo #undef PREQ_TADDR 63932176cfdSRui Paulo #undef PREQ_TSEQ 64032176cfdSRui Paulo 64132176cfdSRui Paulo /* 64232176cfdSRui Paulo * Send a Root Annoucement (RANN) to find all the nodes on the mesh. We are 64332176cfdSRui Paulo * called when the vap is configured as a HWMP RANN root node. 64432176cfdSRui Paulo */ 64532176cfdSRui Paulo static void 64632176cfdSRui Paulo hwmp_rootmode_rann_cb(void *arg) 64732176cfdSRui Paulo { 64832176cfdSRui Paulo struct ieee80211vap *vap = (struct ieee80211vap *)arg; 64932176cfdSRui Paulo struct ieee80211_hwmp_state *hs = vap->iv_hwmp; 65032176cfdSRui Paulo struct ieee80211_mesh_state *ms = vap->iv_mesh; 65132176cfdSRui Paulo struct ieee80211_meshrann_ie rann; 65232176cfdSRui Paulo 65332176cfdSRui Paulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, vap->iv_bss, 65432176cfdSRui Paulo "%s", "send broadcast RANN"); 65532176cfdSRui Paulo 65632176cfdSRui Paulo rann.rann_flags = 0; 65732176cfdSRui Paulo if (ms->ms_flags & IEEE80211_MESHFLAGS_PORTAL) 65832176cfdSRui Paulo rann.rann_flags |= IEEE80211_MESHRANN_FLAGS_PR; 65932176cfdSRui Paulo rann.rann_hopcount = 0; 66032176cfdSRui Paulo rann.rann_ttl = ms->ms_ttl; 66132176cfdSRui Paulo IEEE80211_ADDR_COPY(rann.rann_addr, vap->iv_myaddr); 66232176cfdSRui Paulo rann.rann_seq = ++hs->hs_seq; 66332176cfdSRui Paulo rann.rann_metric = IEEE80211_MESHLMETRIC_INITIALVAL; 66432176cfdSRui Paulo 66532176cfdSRui Paulo vap->iv_stats.is_hwmp_rootrann++; 66632176cfdSRui Paulo hwmp_send_rann(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &rann); 66732176cfdSRui Paulo hwmp_rootmode_setup(vap); 66832176cfdSRui Paulo } 66932176cfdSRui Paulo 67032176cfdSRui Paulo #define PREQ_TFLAGS(n) preq->preq_targets[n].target_flags 67132176cfdSRui Paulo #define PREQ_TADDR(n) preq->preq_targets[n].target_addr 67232176cfdSRui Paulo #define PREQ_TSEQ(n) preq->preq_targets[n].target_seq 67332176cfdSRui Paulo static void 67432176cfdSRui Paulo hwmp_recv_preq(struct ieee80211vap *vap, struct ieee80211_node *ni, 67532176cfdSRui Paulo const struct ieee80211_frame *wh, const struct ieee80211_meshpreq_ie *preq) 67632176cfdSRui Paulo { 67732176cfdSRui Paulo struct ieee80211_mesh_state *ms = vap->iv_mesh; 67832176cfdSRui Paulo struct ieee80211_mesh_route *rt = NULL; 67932176cfdSRui Paulo struct ieee80211_mesh_route *rtorig = NULL; 68032176cfdSRui Paulo struct ieee80211_hwmp_route *hrorig; 68132176cfdSRui Paulo struct ieee80211_hwmp_state *hs = vap->iv_hwmp; 68232176cfdSRui Paulo struct ieee80211_meshprep_ie prep; 68332176cfdSRui Paulo 68432176cfdSRui Paulo if (ni == vap->iv_bss || 68532176cfdSRui Paulo ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED) 68632176cfdSRui Paulo return; 68732176cfdSRui Paulo /* 68832176cfdSRui Paulo * Ignore PREQs from us. Could happen because someone forward it 68932176cfdSRui Paulo * back to us. 69032176cfdSRui Paulo */ 69132176cfdSRui Paulo if (IEEE80211_ADDR_EQ(vap->iv_myaddr, preq->preq_origaddr)) 69232176cfdSRui Paulo return; 69332176cfdSRui Paulo 69432176cfdSRui Paulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 6956168f72eSRui Paulo "received PREQ, source %6D", preq->preq_origaddr, ":"); 69632176cfdSRui Paulo 69732176cfdSRui Paulo /* 69832176cfdSRui Paulo * Acceptance criteria: if the PREQ is not for us and 69932176cfdSRui Paulo * forwarding is disabled, discard this PREQ. 70032176cfdSRui Paulo */ 70132176cfdSRui Paulo if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0)) && 70232176cfdSRui Paulo !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) { 70332176cfdSRui Paulo IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_HWMP, 70432176cfdSRui Paulo preq->preq_origaddr, NULL, "%s", "not accepting PREQ"); 70532176cfdSRui Paulo return; 70632176cfdSRui Paulo } 70732176cfdSRui Paulo rtorig = ieee80211_mesh_rt_find(vap, preq->preq_origaddr); 70832176cfdSRui Paulo if (rtorig == NULL) 70932176cfdSRui Paulo rtorig = ieee80211_mesh_rt_add(vap, preq->preq_origaddr); 71032176cfdSRui Paulo hrorig = IEEE80211_MESH_ROUTE_PRIV(rtorig, struct ieee80211_hwmp_route); 71132176cfdSRui Paulo /* 71232176cfdSRui Paulo * Sequence number validation. 71332176cfdSRui Paulo */ 71432176cfdSRui Paulo if (HWMP_SEQ_LEQ(preq->preq_id, hrorig->hr_preqid) && 71532176cfdSRui Paulo HWMP_SEQ_LEQ(preq->preq_origseq, hrorig->hr_seq)) { 71632176cfdSRui Paulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 7176168f72eSRui Paulo "discard PREQ from %6D, old seq no %u <= %u", 7186168f72eSRui Paulo preq->preq_origaddr, ":", 71932176cfdSRui Paulo preq->preq_origseq, hrorig->hr_seq); 72032176cfdSRui Paulo return; 72132176cfdSRui Paulo } 72232176cfdSRui Paulo hrorig->hr_preqid = preq->preq_id; 72332176cfdSRui Paulo hrorig->hr_seq = preq->preq_origseq; 72432176cfdSRui Paulo 72532176cfdSRui Paulo /* 72632176cfdSRui Paulo * Check if the PREQ is addressed to us. 72732176cfdSRui Paulo */ 72832176cfdSRui Paulo if (IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0))) { 72932176cfdSRui Paulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 7306168f72eSRui Paulo "reply to %6D", preq->preq_origaddr, ":"); 73132176cfdSRui Paulo /* 73232176cfdSRui Paulo * Build and send a PREP frame. 73332176cfdSRui Paulo */ 73432176cfdSRui Paulo prep.prep_flags = 0; 73532176cfdSRui Paulo prep.prep_hopcount = 0; 73632176cfdSRui Paulo prep.prep_ttl = ms->ms_ttl; 73732176cfdSRui Paulo IEEE80211_ADDR_COPY(prep.prep_targetaddr, vap->iv_myaddr); 73832176cfdSRui Paulo prep.prep_targetseq = ++hs->hs_seq; 73932176cfdSRui Paulo prep.prep_lifetime = preq->preq_lifetime; 74032176cfdSRui Paulo prep.prep_metric = IEEE80211_MESHLMETRIC_INITIALVAL; 74132176cfdSRui Paulo IEEE80211_ADDR_COPY(prep.prep_origaddr, preq->preq_origaddr); 74232176cfdSRui Paulo prep.prep_origseq = preq->preq_origseq; 74332176cfdSRui Paulo hwmp_send_prep(ni, vap->iv_myaddr, wh->i_addr2, &prep); 74432176cfdSRui Paulo /* 74532176cfdSRui Paulo * Build the reverse path, if we don't have it already. 74632176cfdSRui Paulo */ 74732176cfdSRui Paulo rt = ieee80211_mesh_rt_find(vap, preq->preq_origaddr); 74832176cfdSRui Paulo if (rt == NULL) 74932176cfdSRui Paulo hwmp_discover(vap, preq->preq_origaddr, NULL); 75032176cfdSRui Paulo else if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0) 75132176cfdSRui Paulo hwmp_discover(vap, rt->rt_dest, NULL); 75232176cfdSRui Paulo return; 75332176cfdSRui Paulo } 75432176cfdSRui Paulo /* 75532176cfdSRui Paulo * Proactive PREQ: reply with a proactive PREP to the 75632176cfdSRui Paulo * root STA if requested. 75732176cfdSRui Paulo */ 75832176cfdSRui Paulo if (IEEE80211_ADDR_EQ(PREQ_TADDR(0), broadcastaddr) && 75932176cfdSRui Paulo (PREQ_TFLAGS(0) & 76032176cfdSRui Paulo ((IEEE80211_MESHPREQ_TFLAGS_TO|IEEE80211_MESHPREQ_TFLAGS_RF) == 76132176cfdSRui Paulo (IEEE80211_MESHPREQ_TFLAGS_TO|IEEE80211_MESHPREQ_TFLAGS_RF)))) { 76232176cfdSRui Paulo uint8_t rootmac[IEEE80211_ADDR_LEN]; 76332176cfdSRui Paulo 76432176cfdSRui Paulo IEEE80211_ADDR_COPY(rootmac, preq->preq_origaddr); 76532176cfdSRui Paulo rt = ieee80211_mesh_rt_find(vap, rootmac); 76632176cfdSRui Paulo if (rt == NULL) { 76732176cfdSRui Paulo rt = ieee80211_mesh_rt_add(vap, rootmac); 76832176cfdSRui Paulo if (rt == NULL) { 76932176cfdSRui Paulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 7706168f72eSRui Paulo "unable to add root mesh path to %6D", 7716168f72eSRui Paulo rootmac, ":"); 77232176cfdSRui Paulo vap->iv_stats.is_mesh_rtaddfailed++; 77332176cfdSRui Paulo return; 77432176cfdSRui Paulo } 77532176cfdSRui Paulo } 77632176cfdSRui Paulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 7776168f72eSRui Paulo "root mesh station @ %6D", rootmac, ":"); 77832176cfdSRui Paulo 77932176cfdSRui Paulo /* 78032176cfdSRui Paulo * Reply with a PREP if we don't have a path to the root 78132176cfdSRui Paulo * or if the root sent us a proactive PREQ. 78232176cfdSRui Paulo */ 78332176cfdSRui Paulo if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0 || 78432176cfdSRui Paulo (preq->preq_flags & IEEE80211_MESHPREQ_FLAGS_PP)) { 78532176cfdSRui Paulo prep.prep_flags = 0; 78632176cfdSRui Paulo prep.prep_hopcount = 0; 78732176cfdSRui Paulo prep.prep_ttl = ms->ms_ttl; 78832176cfdSRui Paulo IEEE80211_ADDR_COPY(prep.prep_origaddr, rootmac); 78932176cfdSRui Paulo prep.prep_origseq = preq->preq_origseq; 79032176cfdSRui Paulo prep.prep_lifetime = preq->preq_lifetime; 79132176cfdSRui Paulo prep.prep_metric = IEEE80211_MESHLMETRIC_INITIALVAL; 79232176cfdSRui Paulo IEEE80211_ADDR_COPY(prep.prep_targetaddr, 79332176cfdSRui Paulo vap->iv_myaddr); 79432176cfdSRui Paulo prep.prep_targetseq = ++hs->hs_seq; 79532176cfdSRui Paulo hwmp_send_prep(vap->iv_bss, vap->iv_myaddr, 79632176cfdSRui Paulo broadcastaddr, &prep); 79732176cfdSRui Paulo } 79832176cfdSRui Paulo hwmp_discover(vap, rootmac, NULL); 79932176cfdSRui Paulo return; 80032176cfdSRui Paulo } 80132176cfdSRui Paulo rt = ieee80211_mesh_rt_find(vap, PREQ_TADDR(0)); 80232176cfdSRui Paulo 80332176cfdSRui Paulo /* 80432176cfdSRui Paulo * Forwarding and Intermediate reply for PREQs with 1 target. 80532176cfdSRui Paulo */ 80632176cfdSRui Paulo if (preq->preq_tcount == 1) { 80732176cfdSRui Paulo struct ieee80211_meshpreq_ie ppreq; /* propagated PREQ */ 80832176cfdSRui Paulo 80932176cfdSRui Paulo memcpy(&ppreq, preq, sizeof(ppreq)); 81032176cfdSRui Paulo /* 81132176cfdSRui Paulo * We have a valid route to this node. 81232176cfdSRui Paulo */ 81332176cfdSRui Paulo if (rt != NULL && 81432176cfdSRui Paulo (rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID)) { 81532176cfdSRui Paulo if (preq->preq_ttl > 1 && 81632176cfdSRui Paulo preq->preq_hopcount < hs->hs_maxhops) { 81732176cfdSRui Paulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 8186168f72eSRui Paulo "forward PREQ from %6D", 8196168f72eSRui Paulo preq->preq_origaddr, ":"); 82032176cfdSRui Paulo /* 82132176cfdSRui Paulo * Propagate the original PREQ. 82232176cfdSRui Paulo */ 82332176cfdSRui Paulo ppreq.preq_hopcount += 1; 82432176cfdSRui Paulo ppreq.preq_ttl -= 1; 82532176cfdSRui Paulo ppreq.preq_metric += 82632176cfdSRui Paulo ms->ms_pmetric->mpm_metric(ni); 82732176cfdSRui Paulo /* 82832176cfdSRui Paulo * Set TO and unset RF bits because we are going 82932176cfdSRui Paulo * to send a PREP next. 83032176cfdSRui Paulo */ 83132176cfdSRui Paulo ppreq.preq_targets[0].target_flags |= 83232176cfdSRui Paulo IEEE80211_MESHPREQ_TFLAGS_TO; 83332176cfdSRui Paulo ppreq.preq_targets[0].target_flags &= 83432176cfdSRui Paulo ~IEEE80211_MESHPREQ_TFLAGS_RF; 83532176cfdSRui Paulo hwmp_send_preq(ni, vap->iv_myaddr, 83632176cfdSRui Paulo broadcastaddr, &ppreq); 83732176cfdSRui Paulo } 83832176cfdSRui Paulo /* 83932176cfdSRui Paulo * Check if we can send an intermediate Path Reply, 84032176cfdSRui Paulo * i.e., Target Only bit is not set. 84132176cfdSRui Paulo */ 84232176cfdSRui Paulo if (!(PREQ_TFLAGS(0) & IEEE80211_MESHPREQ_TFLAGS_TO)) { 84332176cfdSRui Paulo struct ieee80211_meshprep_ie prep; 84432176cfdSRui Paulo 84532176cfdSRui Paulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 8466168f72eSRui Paulo "intermediate reply for PREQ from %6D", 8476168f72eSRui Paulo preq->preq_origaddr, ":"); 84832176cfdSRui Paulo prep.prep_flags = 0; 84932176cfdSRui Paulo prep.prep_hopcount = rt->rt_nhops + 1; 85032176cfdSRui Paulo prep.prep_ttl = ms->ms_ttl; 85132176cfdSRui Paulo IEEE80211_ADDR_COPY(&prep.prep_targetaddr, 85232176cfdSRui Paulo PREQ_TADDR(0)); 85332176cfdSRui Paulo prep.prep_targetseq = hrorig->hr_seq; 85432176cfdSRui Paulo prep.prep_lifetime = preq->preq_lifetime; 85532176cfdSRui Paulo prep.prep_metric = rt->rt_metric + 85632176cfdSRui Paulo ms->ms_pmetric->mpm_metric(ni); 85732176cfdSRui Paulo IEEE80211_ADDR_COPY(&prep.prep_origaddr, 85832176cfdSRui Paulo preq->preq_origaddr); 85932176cfdSRui Paulo prep.prep_origseq = hrorig->hr_seq; 86032176cfdSRui Paulo hwmp_send_prep(ni, vap->iv_myaddr, 86132176cfdSRui Paulo broadcastaddr, &prep); 86232176cfdSRui Paulo } 86332176cfdSRui Paulo /* 86432176cfdSRui Paulo * We have no information about this path, 86532176cfdSRui Paulo * propagate the PREQ. 86632176cfdSRui Paulo */ 86732176cfdSRui Paulo } else if (preq->preq_ttl > 1 && 86832176cfdSRui Paulo preq->preq_hopcount < hs->hs_maxhops) { 86932176cfdSRui Paulo if (rt == NULL) { 87032176cfdSRui Paulo rt = ieee80211_mesh_rt_add(vap, PREQ_TADDR(0)); 87132176cfdSRui Paulo if (rt == NULL) { 87232176cfdSRui Paulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, 8736168f72eSRui Paulo ni, "unable to add PREQ path to %6D", 8746168f72eSRui Paulo PREQ_TADDR(0), ":"); 87532176cfdSRui Paulo vap->iv_stats.is_mesh_rtaddfailed++; 87632176cfdSRui Paulo return; 87732176cfdSRui Paulo } 87832176cfdSRui Paulo } 87932176cfdSRui Paulo rt->rt_metric = preq->preq_metric; 88032176cfdSRui Paulo rt->rt_lifetime = preq->preq_lifetime; 88132176cfdSRui Paulo hrorig = IEEE80211_MESH_ROUTE_PRIV(rt, 88232176cfdSRui Paulo struct ieee80211_hwmp_route); 88332176cfdSRui Paulo hrorig->hr_seq = preq->preq_origseq; 88432176cfdSRui Paulo hrorig->hr_preqid = preq->preq_id; 88532176cfdSRui Paulo 88632176cfdSRui Paulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 8876168f72eSRui Paulo "forward PREQ from %6D", 8886168f72eSRui Paulo preq->preq_origaddr, ":"); 88932176cfdSRui Paulo ppreq.preq_hopcount += 1; 89032176cfdSRui Paulo ppreq.preq_ttl -= 1; 89132176cfdSRui Paulo ppreq.preq_metric += ms->ms_pmetric->mpm_metric(ni); 89232176cfdSRui Paulo hwmp_send_preq(ni, vap->iv_myaddr, broadcastaddr, 89332176cfdSRui Paulo &ppreq); 89432176cfdSRui Paulo } 89532176cfdSRui Paulo } 89632176cfdSRui Paulo 89732176cfdSRui Paulo } 89832176cfdSRui Paulo #undef PREQ_TFLAGS 89932176cfdSRui Paulo #undef PREQ_TADDR 90032176cfdSRui Paulo #undef PREQ_TSEQ 90132176cfdSRui Paulo 90232176cfdSRui Paulo static int 90332176cfdSRui Paulo hwmp_send_preq(struct ieee80211_node *ni, 90432176cfdSRui Paulo const uint8_t sa[IEEE80211_ADDR_LEN], 90532176cfdSRui Paulo const uint8_t da[IEEE80211_ADDR_LEN], 90632176cfdSRui Paulo struct ieee80211_meshpreq_ie *preq) 90732176cfdSRui Paulo { 90832176cfdSRui Paulo struct ieee80211_hwmp_state *hs = ni->ni_vap->iv_hwmp; 90932176cfdSRui Paulo 91032176cfdSRui Paulo /* 91132176cfdSRui Paulo * Enforce PREQ interval. 91232176cfdSRui Paulo */ 91332176cfdSRui Paulo if (ratecheck(&hs->hs_lastpreq, &ieee80211_hwmp_preqminint) == 0) 91432176cfdSRui Paulo return EALREADY; 91532176cfdSRui Paulo getmicrouptime(&hs->hs_lastpreq); 91632176cfdSRui Paulo 91732176cfdSRui Paulo /* 91832176cfdSRui Paulo * mesh preq action frame format 91932176cfdSRui Paulo * [6] da 92032176cfdSRui Paulo * [6] sa 92132176cfdSRui Paulo * [6] addr3 = sa 92232176cfdSRui Paulo * [1] action 92332176cfdSRui Paulo * [1] category 92432176cfdSRui Paulo * [tlv] mesh path request 92532176cfdSRui Paulo */ 92632176cfdSRui Paulo preq->preq_ie = IEEE80211_ELEMID_MESHPREQ; 92732176cfdSRui Paulo return hwmp_send_action(ni, sa, da, (uint8_t *)preq, 92832176cfdSRui Paulo sizeof(struct ieee80211_meshpreq_ie)); 92932176cfdSRui Paulo } 93032176cfdSRui Paulo 93132176cfdSRui Paulo static void 93232176cfdSRui Paulo hwmp_recv_prep(struct ieee80211vap *vap, struct ieee80211_node *ni, 93332176cfdSRui Paulo const struct ieee80211_frame *wh, const struct ieee80211_meshprep_ie *prep) 93432176cfdSRui Paulo { 93532176cfdSRui Paulo struct ieee80211_mesh_state *ms = vap->iv_mesh; 93632176cfdSRui Paulo struct ieee80211_hwmp_state *hs = vap->iv_hwmp; 93732176cfdSRui Paulo struct ieee80211_mesh_route *rt = NULL; 93832176cfdSRui Paulo struct ieee80211_hwmp_route *hr; 93932176cfdSRui Paulo struct ieee80211com *ic = vap->iv_ic; 94032176cfdSRui Paulo struct ifnet *ifp = vap->iv_ifp; 94132176cfdSRui Paulo struct mbuf *m, *next; 94232176cfdSRui Paulo 94332176cfdSRui Paulo /* 94432176cfdSRui Paulo * Acceptance criteria: if the corresponding PREQ was not generated 94532176cfdSRui Paulo * by us and forwarding is disabled, discard this PREP. 94632176cfdSRui Paulo */ 94732176cfdSRui Paulo if (ni == vap->iv_bss || 94832176cfdSRui Paulo ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED) 94932176cfdSRui Paulo return; 95032176cfdSRui Paulo if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, prep->prep_origaddr) && 95132176cfdSRui Paulo !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) 95232176cfdSRui Paulo return; 95332176cfdSRui Paulo 95432176cfdSRui Paulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 9556168f72eSRui Paulo "received PREP from %6D", prep->prep_targetaddr, ":"); 95632176cfdSRui Paulo 95732176cfdSRui Paulo rt = ieee80211_mesh_rt_find(vap, prep->prep_targetaddr); 95832176cfdSRui Paulo if (rt == NULL) { 95932176cfdSRui Paulo /* 96032176cfdSRui Paulo * If we have no entry this could be a reply to a root PREQ. 96132176cfdSRui Paulo */ 96232176cfdSRui Paulo if (hs->hs_rootmode != IEEE80211_HWMP_ROOTMODE_DISABLED) { 96332176cfdSRui Paulo rt = ieee80211_mesh_rt_add(vap, prep->prep_targetaddr); 96432176cfdSRui Paulo if (rt == NULL) { 96532176cfdSRui Paulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, 9666168f72eSRui Paulo ni, "unable to add PREP path to %6D", 9676168f72eSRui Paulo prep->prep_targetaddr, ":"); 96832176cfdSRui Paulo vap->iv_stats.is_mesh_rtaddfailed++; 96932176cfdSRui Paulo return; 97032176cfdSRui Paulo } 97132176cfdSRui Paulo IEEE80211_ADDR_COPY(rt->rt_nexthop, wh->i_addr2); 97232176cfdSRui Paulo rt->rt_nhops = prep->prep_hopcount; 97332176cfdSRui Paulo rt->rt_lifetime = prep->prep_lifetime; 97432176cfdSRui Paulo rt->rt_metric = prep->prep_metric; 97532176cfdSRui Paulo rt->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID; 97632176cfdSRui Paulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 9776168f72eSRui Paulo "add root path to %6D nhops %d metric %d (PREP)", 9786168f72eSRui Paulo prep->prep_targetaddr, ":", 97932176cfdSRui Paulo rt->rt_nhops, rt->rt_metric); 98032176cfdSRui Paulo return; 98132176cfdSRui Paulo } 98232176cfdSRui Paulo return; 98332176cfdSRui Paulo } 98432176cfdSRui Paulo /* 98532176cfdSRui Paulo * Sequence number validation. 98632176cfdSRui Paulo */ 98732176cfdSRui Paulo hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route); 98832176cfdSRui Paulo if (HWMP_SEQ_LEQ(prep->prep_targetseq, hr->hr_seq)) { 98932176cfdSRui Paulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 9906168f72eSRui Paulo "discard PREP from %6D, old seq no %u <= %u", 9916168f72eSRui Paulo prep->prep_targetaddr, ":", 99232176cfdSRui Paulo prep->prep_targetseq, hr->hr_seq); 99332176cfdSRui Paulo return; 99432176cfdSRui Paulo } 99532176cfdSRui Paulo hr->hr_seq = prep->prep_targetseq; 99632176cfdSRui Paulo /* 99732176cfdSRui Paulo * If it's NOT for us, propagate the PREP. 99832176cfdSRui Paulo */ 99932176cfdSRui Paulo if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, prep->prep_origaddr) && 100032176cfdSRui Paulo prep->prep_ttl > 1 && prep->prep_hopcount < hs->hs_maxhops) { 100132176cfdSRui Paulo struct ieee80211_meshprep_ie pprep; /* propagated PREP */ 100232176cfdSRui Paulo 100332176cfdSRui Paulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 10046168f72eSRui Paulo "propagate PREP from %6D", 10056168f72eSRui Paulo prep->prep_targetaddr, ":"); 100632176cfdSRui Paulo 100732176cfdSRui Paulo memcpy(&pprep, prep, sizeof(pprep)); 100832176cfdSRui Paulo pprep.prep_hopcount += 1; 100932176cfdSRui Paulo pprep.prep_ttl -= 1; 101032176cfdSRui Paulo pprep.prep_metric += ms->ms_pmetric->mpm_metric(ni); 101132176cfdSRui Paulo IEEE80211_ADDR_COPY(pprep.prep_targetaddr, vap->iv_myaddr); 101232176cfdSRui Paulo hwmp_send_prep(ni, vap->iv_myaddr, broadcastaddr, &pprep); 101332176cfdSRui Paulo } 101432176cfdSRui Paulo hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route); 101532176cfdSRui Paulo if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY) { 101632176cfdSRui Paulo /* NB: never clobber a proxy entry */; 101732176cfdSRui Paulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 10186168f72eSRui Paulo "discard PREP for %6D, route is marked PROXY", 10196168f72eSRui Paulo prep->prep_targetaddr, ":"); 102032176cfdSRui Paulo vap->iv_stats.is_hwmp_proxy++; 102132176cfdSRui Paulo } else if (prep->prep_origseq == hr->hr_origseq) { 102232176cfdSRui Paulo /* 102332176cfdSRui Paulo * Check if we already have a path to this node. 102432176cfdSRui Paulo * If we do, check if this path reply contains a 102532176cfdSRui Paulo * better route. 102632176cfdSRui Paulo */ 102732176cfdSRui Paulo if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0 || 102832176cfdSRui Paulo (prep->prep_hopcount < rt->rt_nhops || 102932176cfdSRui Paulo prep->prep_metric < rt->rt_metric)) { 103032176cfdSRui Paulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 10316168f72eSRui Paulo "%s path to %6D, hopcount %d:%d metric %d:%d", 103232176cfdSRui Paulo rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID ? 103332176cfdSRui Paulo "prefer" : "update", 10346168f72eSRui Paulo prep->prep_origaddr, ":", 103532176cfdSRui Paulo rt->rt_nhops, prep->prep_hopcount, 103632176cfdSRui Paulo rt->rt_metric, prep->prep_metric); 103732176cfdSRui Paulo IEEE80211_ADDR_COPY(rt->rt_nexthop, wh->i_addr2); 103832176cfdSRui Paulo rt->rt_nhops = prep->prep_hopcount; 103932176cfdSRui Paulo rt->rt_lifetime = prep->prep_lifetime; 104032176cfdSRui Paulo rt->rt_metric = prep->prep_metric; 104132176cfdSRui Paulo rt->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID; 104232176cfdSRui Paulo } else { 104332176cfdSRui Paulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 10446168f72eSRui Paulo "ignore PREP for %6D, hopcount %d:%d metric %d:%d", 10456168f72eSRui Paulo prep->prep_targetaddr, ":", 104632176cfdSRui Paulo rt->rt_nhops, prep->prep_hopcount, 104732176cfdSRui Paulo rt->rt_metric, prep->prep_metric); 104832176cfdSRui Paulo } 104932176cfdSRui Paulo } else { 105032176cfdSRui Paulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 10516168f72eSRui Paulo "discard PREP for %6D, wrong seqno %u != %u", 10526168f72eSRui Paulo prep->prep_targetaddr, ":", prep->prep_origseq, 105332176cfdSRui Paulo hr->hr_seq); 105432176cfdSRui Paulo vap->iv_stats.is_hwmp_wrongseq++; 105532176cfdSRui Paulo } 105632176cfdSRui Paulo /* 105732176cfdSRui Paulo * Check for frames queued awaiting path discovery. 105832176cfdSRui Paulo * XXX probably can tell exactly and avoid remove call 105932176cfdSRui Paulo * NB: hash may have false matches, if so they will get 106032176cfdSRui Paulo * stuck back on the stageq because there won't be 106132176cfdSRui Paulo * a path. 106232176cfdSRui Paulo */ 106332176cfdSRui Paulo m = ieee80211_ageq_remove(&ic->ic_stageq, 106432176cfdSRui Paulo (struct ieee80211_node *)(uintptr_t) 106532176cfdSRui Paulo ieee80211_mac_hash(ic, rt->rt_dest)); 106632176cfdSRui Paulo for (; m != NULL; m = next) { 106732176cfdSRui Paulo next = m->m_nextpkt; 106832176cfdSRui Paulo m->m_nextpkt = NULL; 106932176cfdSRui Paulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 107032176cfdSRui Paulo "flush queued frame %p len %d", m, m->m_pkthdr.len); 1071*fcaa651dSRui Paulo ieee80211_handoff(ifp, m); 107232176cfdSRui Paulo } 107332176cfdSRui Paulo } 107432176cfdSRui Paulo 107532176cfdSRui Paulo static int 107632176cfdSRui Paulo hwmp_send_prep(struct ieee80211_node *ni, 107732176cfdSRui Paulo const uint8_t sa[IEEE80211_ADDR_LEN], 107832176cfdSRui Paulo const uint8_t da[IEEE80211_ADDR_LEN], 107932176cfdSRui Paulo struct ieee80211_meshprep_ie *prep) 108032176cfdSRui Paulo { 108132176cfdSRui Paulo /* NB: there's no PREP minimum interval. */ 108232176cfdSRui Paulo 108332176cfdSRui Paulo /* 108432176cfdSRui Paulo * mesh prep action frame format 108532176cfdSRui Paulo * [6] da 108632176cfdSRui Paulo * [6] sa 108732176cfdSRui Paulo * [6] addr3 = sa 108832176cfdSRui Paulo * [1] action 108932176cfdSRui Paulo * [1] category 109032176cfdSRui Paulo * [tlv] mesh path reply 109132176cfdSRui Paulo */ 109232176cfdSRui Paulo prep->prep_ie = IEEE80211_ELEMID_MESHPREP; 109332176cfdSRui Paulo return hwmp_send_action(ni, sa, da, (uint8_t *)prep, 109432176cfdSRui Paulo sizeof(struct ieee80211_meshprep_ie)); 109532176cfdSRui Paulo } 109632176cfdSRui Paulo 109732176cfdSRui Paulo #define PERR_DFLAGS(n) perr.perr_dests[n].dest_flags 109832176cfdSRui Paulo #define PERR_DADDR(n) perr.perr_dests[n].dest_addr 109932176cfdSRui Paulo #define PERR_DSEQ(n) perr.perr_dests[n].dest_seq 110032176cfdSRui Paulo #define PERR_DRCODE(n) perr.perr_dests[n].dest_rcode 110132176cfdSRui Paulo static void 110232176cfdSRui Paulo hwmp_peerdown(struct ieee80211_node *ni) 110332176cfdSRui Paulo { 110432176cfdSRui Paulo struct ieee80211vap *vap = ni->ni_vap; 110532176cfdSRui Paulo struct ieee80211_mesh_state *ms = vap->iv_mesh; 110632176cfdSRui Paulo struct ieee80211_meshperr_ie perr; 110732176cfdSRui Paulo struct ieee80211_mesh_route *rt; 110832176cfdSRui Paulo struct ieee80211_hwmp_route *hr; 110932176cfdSRui Paulo 111032176cfdSRui Paulo rt = ieee80211_mesh_rt_find(vap, ni->ni_macaddr); 111132176cfdSRui Paulo if (rt == NULL) 111232176cfdSRui Paulo return; 111332176cfdSRui Paulo hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route); 111432176cfdSRui Paulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 111532176cfdSRui Paulo "%s", "delete route entry"); 111632176cfdSRui Paulo perr.perr_ttl = ms->ms_ttl; 111732176cfdSRui Paulo perr.perr_ndests = 1; 111832176cfdSRui Paulo PERR_DFLAGS(0) = 0; 111932176cfdSRui Paulo if (hr->hr_seq == 0) 112032176cfdSRui Paulo PERR_DFLAGS(0) |= IEEE80211_MESHPERR_DFLAGS_USN; 112132176cfdSRui Paulo PERR_DFLAGS(0) |= IEEE80211_MESHPERR_DFLAGS_RC; 112232176cfdSRui Paulo IEEE80211_ADDR_COPY(PERR_DADDR(0), rt->rt_dest); 112332176cfdSRui Paulo PERR_DSEQ(0) = hr->hr_seq; 112432176cfdSRui Paulo PERR_DRCODE(0) = IEEE80211_REASON_MESH_PERR_DEST_UNREACH; 112532176cfdSRui Paulo /* NB: flush everything passing through peer */ 112632176cfdSRui Paulo ieee80211_mesh_rt_flush_peer(vap, ni->ni_macaddr); 112732176cfdSRui Paulo hwmp_send_perr(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &perr); 112832176cfdSRui Paulo } 112932176cfdSRui Paulo #undef PERR_DFLAGS 113032176cfdSRui Paulo #undef PERR_DADDR 113132176cfdSRui Paulo #undef PERR_DSEQ 113232176cfdSRui Paulo #undef PERR_DRCODE 113332176cfdSRui Paulo 113432176cfdSRui Paulo #define PERR_DFLAGS(n) perr->perr_dests[n].dest_flags 113532176cfdSRui Paulo #define PERR_DADDR(n) perr->perr_dests[n].dest_addr 113632176cfdSRui Paulo #define PERR_DSEQ(n) perr->perr_dests[n].dest_seq 113732176cfdSRui Paulo #define PERR_DRCODE(n) perr->perr_dests[n].dest_rcode 113832176cfdSRui Paulo static void 113932176cfdSRui Paulo hwmp_recv_perr(struct ieee80211vap *vap, struct ieee80211_node *ni, 114032176cfdSRui Paulo const struct ieee80211_frame *wh, const struct ieee80211_meshperr_ie *perr) 114132176cfdSRui Paulo { 114232176cfdSRui Paulo struct ieee80211_mesh_state *ms = vap->iv_mesh; 114332176cfdSRui Paulo struct ieee80211_mesh_route *rt = NULL; 114432176cfdSRui Paulo struct ieee80211_hwmp_route *hr; 114532176cfdSRui Paulo struct ieee80211_meshperr_ie pperr; 114632176cfdSRui Paulo int i, forward = 0; 114732176cfdSRui Paulo 114832176cfdSRui Paulo /* 114932176cfdSRui Paulo * Acceptance criteria: check if we received a PERR from a 115032176cfdSRui Paulo * neighbor and forwarding is enabled. 115132176cfdSRui Paulo */ 115232176cfdSRui Paulo if (ni == vap->iv_bss || 115332176cfdSRui Paulo ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED || 115432176cfdSRui Paulo !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) 115532176cfdSRui Paulo return; 115632176cfdSRui Paulo /* 115732176cfdSRui Paulo * Find all routing entries that match and delete them. 115832176cfdSRui Paulo */ 115932176cfdSRui Paulo for (i = 0; i < perr->perr_ndests; i++) { 116032176cfdSRui Paulo rt = ieee80211_mesh_rt_find(vap, PERR_DADDR(i)); 116132176cfdSRui Paulo if (rt == NULL) 116232176cfdSRui Paulo continue; 116332176cfdSRui Paulo hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route); 116432176cfdSRui Paulo if (!(PERR_DFLAGS(0) & IEEE80211_MESHPERR_DFLAGS_USN) && 116532176cfdSRui Paulo HWMP_SEQ_GEQ(PERR_DSEQ(i), hr->hr_seq)) { 116632176cfdSRui Paulo ieee80211_mesh_rt_del(vap, rt->rt_dest); 116732176cfdSRui Paulo ieee80211_mesh_rt_flush_peer(vap, rt->rt_dest); 116832176cfdSRui Paulo rt = NULL; 116932176cfdSRui Paulo forward = 1; 117032176cfdSRui Paulo } 117132176cfdSRui Paulo } 117232176cfdSRui Paulo /* 117332176cfdSRui Paulo * Propagate the PERR if we previously found it on our routing table. 117432176cfdSRui Paulo * XXX handle ndest > 1 117532176cfdSRui Paulo */ 117632176cfdSRui Paulo if (forward && perr->perr_ttl > 1) { 117732176cfdSRui Paulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 11786168f72eSRui Paulo "propagate PERR from %6D", wh->i_addr2, ":"); 117932176cfdSRui Paulo memcpy(&pperr, perr, sizeof(*perr)); 118032176cfdSRui Paulo pperr.perr_ttl--; 118132176cfdSRui Paulo hwmp_send_perr(vap->iv_bss, vap->iv_myaddr, broadcastaddr, 118232176cfdSRui Paulo &pperr); 118332176cfdSRui Paulo } 118432176cfdSRui Paulo } 118532176cfdSRui Paulo #undef PEER_DADDR 118632176cfdSRui Paulo #undef PERR_DSEQ 118732176cfdSRui Paulo 118832176cfdSRui Paulo static int 118932176cfdSRui Paulo hwmp_send_perr(struct ieee80211_node *ni, 119032176cfdSRui Paulo const uint8_t sa[IEEE80211_ADDR_LEN], 119132176cfdSRui Paulo const uint8_t da[IEEE80211_ADDR_LEN], 119232176cfdSRui Paulo struct ieee80211_meshperr_ie *perr) 119332176cfdSRui Paulo { 119432176cfdSRui Paulo struct ieee80211_hwmp_state *hs = ni->ni_vap->iv_hwmp; 119532176cfdSRui Paulo 119632176cfdSRui Paulo /* 119732176cfdSRui Paulo * Enforce PERR interval. 119832176cfdSRui Paulo */ 119932176cfdSRui Paulo if (ratecheck(&hs->hs_lastperr, &ieee80211_hwmp_perrminint) == 0) 120032176cfdSRui Paulo return EALREADY; 120132176cfdSRui Paulo getmicrouptime(&hs->hs_lastperr); 120232176cfdSRui Paulo 120332176cfdSRui Paulo /* 120432176cfdSRui Paulo * mesh perr action frame format 120532176cfdSRui Paulo * [6] da 120632176cfdSRui Paulo * [6] sa 120732176cfdSRui Paulo * [6] addr3 = sa 120832176cfdSRui Paulo * [1] action 120932176cfdSRui Paulo * [1] category 121032176cfdSRui Paulo * [tlv] mesh path error 121132176cfdSRui Paulo */ 121232176cfdSRui Paulo perr->perr_ie = IEEE80211_ELEMID_MESHPERR; 121332176cfdSRui Paulo return hwmp_send_action(ni, sa, da, (uint8_t *)perr, 121432176cfdSRui Paulo sizeof(struct ieee80211_meshperr_ie)); 121532176cfdSRui Paulo } 121632176cfdSRui Paulo 121732176cfdSRui Paulo static void 121832176cfdSRui Paulo hwmp_recv_rann(struct ieee80211vap *vap, struct ieee80211_node *ni, 121932176cfdSRui Paulo const struct ieee80211_frame *wh, const struct ieee80211_meshrann_ie *rann) 122032176cfdSRui Paulo { 122132176cfdSRui Paulo struct ieee80211_mesh_state *ms = vap->iv_mesh; 122232176cfdSRui Paulo struct ieee80211_hwmp_state *hs = vap->iv_hwmp; 122332176cfdSRui Paulo struct ieee80211_mesh_route *rt = NULL; 122432176cfdSRui Paulo struct ieee80211_hwmp_route *hr; 122532176cfdSRui Paulo struct ieee80211_meshrann_ie prann; 122632176cfdSRui Paulo 122732176cfdSRui Paulo if (ni == vap->iv_bss || 122832176cfdSRui Paulo ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED || 122932176cfdSRui Paulo IEEE80211_ADDR_EQ(rann->rann_addr, vap->iv_myaddr)) 123032176cfdSRui Paulo return; 123132176cfdSRui Paulo 123232176cfdSRui Paulo rt = ieee80211_mesh_rt_find(vap, rann->rann_addr); 123332176cfdSRui Paulo /* 123432176cfdSRui Paulo * Discover the path to the root mesh STA. 123532176cfdSRui Paulo * If we already know it, propagate the RANN element. 123632176cfdSRui Paulo */ 123732176cfdSRui Paulo if (rt == NULL) { 123832176cfdSRui Paulo hwmp_discover(vap, rann->rann_addr, NULL); 123932176cfdSRui Paulo return; 124032176cfdSRui Paulo } 124132176cfdSRui Paulo hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route); 124232176cfdSRui Paulo if (HWMP_SEQ_GT(rann->rann_seq, hr->hr_seq)) { 124332176cfdSRui Paulo hr->hr_seq = rann->rann_seq; 124432176cfdSRui Paulo if (rann->rann_ttl > 1 && 124532176cfdSRui Paulo rann->rann_hopcount < hs->hs_maxhops && 124632176cfdSRui Paulo (ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) { 124732176cfdSRui Paulo memcpy(&prann, rann, sizeof(prann)); 124832176cfdSRui Paulo prann.rann_hopcount += 1; 124932176cfdSRui Paulo prann.rann_ttl -= 1; 125032176cfdSRui Paulo prann.rann_metric += ms->ms_pmetric->mpm_metric(ni); 125132176cfdSRui Paulo hwmp_send_rann(vap->iv_bss, vap->iv_myaddr, 125232176cfdSRui Paulo broadcastaddr, &prann); 125332176cfdSRui Paulo } 125432176cfdSRui Paulo } 125532176cfdSRui Paulo } 125632176cfdSRui Paulo 125732176cfdSRui Paulo static int 125832176cfdSRui Paulo hwmp_send_rann(struct ieee80211_node *ni, 125932176cfdSRui Paulo const uint8_t sa[IEEE80211_ADDR_LEN], 126032176cfdSRui Paulo const uint8_t da[IEEE80211_ADDR_LEN], 126132176cfdSRui Paulo struct ieee80211_meshrann_ie *rann) 126232176cfdSRui Paulo { 126332176cfdSRui Paulo /* 126432176cfdSRui Paulo * mesh rann action frame format 126532176cfdSRui Paulo * [6] da 126632176cfdSRui Paulo * [6] sa 126732176cfdSRui Paulo * [6] addr3 = sa 126832176cfdSRui Paulo * [1] action 126932176cfdSRui Paulo * [1] category 127032176cfdSRui Paulo * [tlv] root annoucement 127132176cfdSRui Paulo */ 127232176cfdSRui Paulo rann->rann_ie = IEEE80211_ELEMID_MESHRANN; 127332176cfdSRui Paulo return hwmp_send_action(ni, sa, da, (uint8_t *)rann, 127432176cfdSRui Paulo sizeof(struct ieee80211_meshrann_ie)); 127532176cfdSRui Paulo } 127632176cfdSRui Paulo 127732176cfdSRui Paulo #define PREQ_TFLAGS(n) preq.preq_targets[n].target_flags 127832176cfdSRui Paulo #define PREQ_TADDR(n) preq.preq_targets[n].target_addr 127932176cfdSRui Paulo #define PREQ_TSEQ(n) preq.preq_targets[n].target_seq 128032176cfdSRui Paulo static struct ieee80211_node * 128132176cfdSRui Paulo hwmp_discover(struct ieee80211vap *vap, 128232176cfdSRui Paulo const uint8_t dest[IEEE80211_ADDR_LEN], struct mbuf *m) 128332176cfdSRui Paulo { 128432176cfdSRui Paulo struct ieee80211_hwmp_state *hs = vap->iv_hwmp; 128532176cfdSRui Paulo struct ieee80211_mesh_state *ms = vap->iv_mesh; 128632176cfdSRui Paulo struct ieee80211_mesh_route *rt = NULL; 128732176cfdSRui Paulo struct ieee80211_hwmp_route *hr; 128832176cfdSRui Paulo struct ieee80211_meshpreq_ie preq; 128932176cfdSRui Paulo struct ieee80211_node *ni; 129032176cfdSRui Paulo int sendpreq = 0; 129132176cfdSRui Paulo 129232176cfdSRui Paulo KASSERT(vap->iv_opmode == IEEE80211_M_MBSS, 129332176cfdSRui Paulo ("not a mesh vap, opmode %d", vap->iv_opmode)); 129432176cfdSRui Paulo 129532176cfdSRui Paulo KASSERT(!IEEE80211_ADDR_EQ(vap->iv_myaddr, dest), 129632176cfdSRui Paulo ("%s: discovering self!", __func__)); 129732176cfdSRui Paulo 129832176cfdSRui Paulo ni = NULL; 129932176cfdSRui Paulo if (!IEEE80211_IS_MULTICAST(dest)) { 130032176cfdSRui Paulo rt = ieee80211_mesh_rt_find(vap, dest); 130132176cfdSRui Paulo if (rt == NULL) { 130232176cfdSRui Paulo rt = ieee80211_mesh_rt_add(vap, dest); 130332176cfdSRui Paulo if (rt == NULL) { 130432176cfdSRui Paulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, 13056168f72eSRui Paulo ni, "unable to add discovery path to %6D", 13066168f72eSRui Paulo dest, ":"); 130732176cfdSRui Paulo vap->iv_stats.is_mesh_rtaddfailed++; 130832176cfdSRui Paulo goto done; 130932176cfdSRui Paulo } 131032176cfdSRui Paulo } 131132176cfdSRui Paulo hr = IEEE80211_MESH_ROUTE_PRIV(rt, 131232176cfdSRui Paulo struct ieee80211_hwmp_route); 131332176cfdSRui Paulo if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0) { 131432176cfdSRui Paulo if (hr->hr_origseq == 0) 131532176cfdSRui Paulo hr->hr_origseq = ++hs->hs_seq; 131632176cfdSRui Paulo rt->rt_metric = IEEE80211_MESHLMETRIC_INITIALVAL; 131732176cfdSRui Paulo rt->rt_lifetime = 131832176cfdSRui Paulo ticks_to_msecs(ieee80211_hwmp_pathtimeout); 131932176cfdSRui Paulo /* XXX check preq retries */ 132032176cfdSRui Paulo sendpreq = 1; 1321ea86af0dSRui Paulo if (m != NULL) { 1322ea86af0dSRui Paulo IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP, 1323ea86af0dSRui Paulo dest, "%s", 1324ea86af0dSRui Paulo "start path discovery (src <none>)"); 1325ea86af0dSRui Paulo } else { 1326ea86af0dSRui Paulo IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP, 1327ea86af0dSRui Paulo dest, 13286168f72eSRui Paulo "start path discovery (src %6D)", 13296168f72eSRui Paulo mtod(m, struct ether_header *)->ether_shost, 13306168f72eSRui Paulo ":"); 1331ea86af0dSRui Paulo } 133232176cfdSRui Paulo /* 133332176cfdSRui Paulo * Try to discover the path for this node. 133432176cfdSRui Paulo */ 133532176cfdSRui Paulo preq.preq_flags = 0; 133632176cfdSRui Paulo preq.preq_hopcount = 0; 133732176cfdSRui Paulo preq.preq_ttl = ms->ms_ttl; 133832176cfdSRui Paulo preq.preq_id = ++hs->hs_preqid; 133932176cfdSRui Paulo IEEE80211_ADDR_COPY(preq.preq_origaddr, vap->iv_myaddr); 134032176cfdSRui Paulo preq.preq_origseq = hr->hr_origseq; 134132176cfdSRui Paulo preq.preq_lifetime = rt->rt_lifetime; 134232176cfdSRui Paulo preq.preq_metric = rt->rt_metric; 134332176cfdSRui Paulo preq.preq_tcount = 1; 134432176cfdSRui Paulo IEEE80211_ADDR_COPY(PREQ_TADDR(0), dest); 134532176cfdSRui Paulo PREQ_TFLAGS(0) = 0; 134632176cfdSRui Paulo if (ieee80211_hwmp_targetonly) 134732176cfdSRui Paulo PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_TO; 134832176cfdSRui Paulo if (ieee80211_hwmp_replyforward) 134932176cfdSRui Paulo PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_RF; 135032176cfdSRui Paulo PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_USN; 135132176cfdSRui Paulo PREQ_TSEQ(0) = 0; 135232176cfdSRui Paulo /* XXX check return value */ 135332176cfdSRui Paulo hwmp_send_preq(vap->iv_bss, vap->iv_myaddr, 135432176cfdSRui Paulo broadcastaddr, &preq); 135532176cfdSRui Paulo } 135632176cfdSRui Paulo if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) 135732176cfdSRui Paulo ni = ieee80211_find_txnode(vap, rt->rt_nexthop); 135832176cfdSRui Paulo } else { 135932176cfdSRui Paulo ni = ieee80211_find_txnode(vap, dest); 136032176cfdSRui Paulo /* NB: if null then we leak mbuf */ 136132176cfdSRui Paulo KASSERT(ni != NULL, ("leak mcast frame")); 136232176cfdSRui Paulo return ni; 136332176cfdSRui Paulo } 136432176cfdSRui Paulo done: 136532176cfdSRui Paulo if (ni == NULL && m != NULL) { 136632176cfdSRui Paulo if (sendpreq) { 136732176cfdSRui Paulo struct ieee80211com *ic = vap->iv_ic; 136832176cfdSRui Paulo /* 136932176cfdSRui Paulo * Queue packet for transmit when path discovery 137032176cfdSRui Paulo * completes. If discovery never completes the 137132176cfdSRui Paulo * frame will be flushed by way of the aging timer. 137232176cfdSRui Paulo */ 137332176cfdSRui Paulo IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP, dest, 137432176cfdSRui Paulo "%s", "queue frame until path found"); 137532176cfdSRui Paulo m->m_pkthdr.rcvif = (void *)(uintptr_t) 137632176cfdSRui Paulo ieee80211_mac_hash(ic, dest); 137732176cfdSRui Paulo /* XXX age chosen randomly */ 137832176cfdSRui Paulo ieee80211_ageq_append(&ic->ic_stageq, m, 137932176cfdSRui Paulo IEEE80211_INACT_WAIT); 138032176cfdSRui Paulo } else { 138132176cfdSRui Paulo IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_HWMP, 138232176cfdSRui Paulo dest, NULL, "%s", "no valid path to this node"); 138332176cfdSRui Paulo m_freem(m); 138432176cfdSRui Paulo } 138532176cfdSRui Paulo } 138632176cfdSRui Paulo return ni; 138732176cfdSRui Paulo } 138832176cfdSRui Paulo #undef PREQ_TFLAGS 138932176cfdSRui Paulo #undef PREQ_TADDR 139032176cfdSRui Paulo #undef PREQ_TSEQ 139132176cfdSRui Paulo 139232176cfdSRui Paulo static int 139332176cfdSRui Paulo hwmp_ioctl_get80211(struct ieee80211vap *vap, struct ieee80211req *ireq) 139432176cfdSRui Paulo { 139532176cfdSRui Paulo struct ieee80211_hwmp_state *hs = vap->iv_hwmp; 139632176cfdSRui Paulo int error; 139732176cfdSRui Paulo 139832176cfdSRui Paulo if (vap->iv_opmode != IEEE80211_M_MBSS) 139932176cfdSRui Paulo return ENOSYS; 140032176cfdSRui Paulo error = 0; 140132176cfdSRui Paulo switch (ireq->i_type) { 140232176cfdSRui Paulo case IEEE80211_IOC_HWMP_ROOTMODE: 140332176cfdSRui Paulo ireq->i_val = hs->hs_rootmode; 140432176cfdSRui Paulo break; 140532176cfdSRui Paulo case IEEE80211_IOC_HWMP_MAXHOPS: 140632176cfdSRui Paulo ireq->i_val = hs->hs_maxhops; 140732176cfdSRui Paulo break; 140832176cfdSRui Paulo default: 140932176cfdSRui Paulo return ENOSYS; 141032176cfdSRui Paulo } 141132176cfdSRui Paulo return error; 141232176cfdSRui Paulo } 141332176cfdSRui Paulo IEEE80211_IOCTL_GET(hwmp, hwmp_ioctl_get80211); 141432176cfdSRui Paulo 141532176cfdSRui Paulo static int 141632176cfdSRui Paulo hwmp_ioctl_set80211(struct ieee80211vap *vap, struct ieee80211req *ireq) 141732176cfdSRui Paulo { 141832176cfdSRui Paulo struct ieee80211_hwmp_state *hs = vap->iv_hwmp; 141932176cfdSRui Paulo int error; 142032176cfdSRui Paulo 142132176cfdSRui Paulo if (vap->iv_opmode != IEEE80211_M_MBSS) 142232176cfdSRui Paulo return ENOSYS; 142332176cfdSRui Paulo error = 0; 142432176cfdSRui Paulo switch (ireq->i_type) { 142532176cfdSRui Paulo case IEEE80211_IOC_HWMP_ROOTMODE: 142632176cfdSRui Paulo if (ireq->i_val < 0 || ireq->i_val > 3) 142732176cfdSRui Paulo return EINVAL; 142832176cfdSRui Paulo hs->hs_rootmode = ireq->i_val; 142932176cfdSRui Paulo hwmp_rootmode_setup(vap); 143032176cfdSRui Paulo break; 143132176cfdSRui Paulo case IEEE80211_IOC_HWMP_MAXHOPS: 143232176cfdSRui Paulo if (ireq->i_val <= 0 || ireq->i_val > 255) 143332176cfdSRui Paulo return EINVAL; 143432176cfdSRui Paulo hs->hs_maxhops = ireq->i_val; 143532176cfdSRui Paulo break; 143632176cfdSRui Paulo default: 143732176cfdSRui Paulo return ENOSYS; 143832176cfdSRui Paulo } 143932176cfdSRui Paulo return error; 144032176cfdSRui Paulo } 144132176cfdSRui Paulo IEEE80211_IOCTL_SET(hwmp, hwmp_ioctl_set80211); 1442