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