1195618Srpaulo/*- 2195618Srpaulo * Copyright (c) 2009 The FreeBSD Foundation 3195618Srpaulo * All rights reserved. 4195618Srpaulo * 5195618Srpaulo * This software was developed by Rui Paulo under sponsorship from the 6195618Srpaulo * FreeBSD Foundation. 7195618Srpaulo * 8195618Srpaulo * Redistribution and use in source and binary forms, with or without 9195618Srpaulo * modification, are permitted provided that the following conditions 10195618Srpaulo * are met: 11195618Srpaulo * 1. Redistributions of source code must retain the above copyright 12195618Srpaulo * notice, this list of conditions and the following disclaimer. 13195618Srpaulo * 2. Redistributions in binary form must reproduce the above copyright 14195618Srpaulo * notice, this list of conditions and the following disclaimer in the 15195618Srpaulo * documentation and/or other materials provided with the distribution. 16195618Srpaulo * 17195618Srpaulo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18195618Srpaulo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19195618Srpaulo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20195618Srpaulo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21195618Srpaulo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22195618Srpaulo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23195618Srpaulo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24195618Srpaulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25195618Srpaulo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26195618Srpaulo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27195618Srpaulo * SUCH DAMAGE. 28195618Srpaulo */ 29195618Srpaulo#include <sys/cdefs.h> 30195618Srpaulo#ifdef __FreeBSD__ 31195618Srpaulo__FBSDID("$FreeBSD$"); 32195618Srpaulo#endif 33195618Srpaulo 34195618Srpaulo/* 35195618Srpaulo * IEEE 802.11s Hybrid Wireless Mesh Protocol, HWMP. 36195618Srpaulo * 37195618Srpaulo * Based on March 2009, D3.0 802.11s draft spec. 38195618Srpaulo */ 39195618Srpaulo#include "opt_inet.h" 40195618Srpaulo#include "opt_wlan.h" 41195618Srpaulo 42195618Srpaulo#include <sys/param.h> 43195618Srpaulo#include <sys/systm.h> 44195618Srpaulo#include <sys/mbuf.h> 45195618Srpaulo#include <sys/malloc.h> 46195618Srpaulo#include <sys/kernel.h> 47195618Srpaulo 48195618Srpaulo#include <sys/socket.h> 49195618Srpaulo#include <sys/sockio.h> 50195618Srpaulo#include <sys/endian.h> 51195618Srpaulo#include <sys/errno.h> 52195618Srpaulo#include <sys/proc.h> 53195618Srpaulo#include <sys/sysctl.h> 54195618Srpaulo 55195618Srpaulo#include <net/if.h> 56195618Srpaulo#include <net/if_media.h> 57195618Srpaulo#include <net/if_llc.h> 58195618Srpaulo#include <net/ethernet.h> 59195618Srpaulo 60195618Srpaulo#include <net/bpf.h> 61195618Srpaulo 62195618Srpaulo#include <net80211/ieee80211_var.h> 63195618Srpaulo#include <net80211/ieee80211_action.h> 64195618Srpaulo#include <net80211/ieee80211_input.h> 65195618Srpaulo#include <net80211/ieee80211_mesh.h> 66195618Srpaulo 67195618Srpaulostatic void hwmp_vattach(struct ieee80211vap *); 68195618Srpaulostatic void hwmp_vdetach(struct ieee80211vap *); 69195618Srpaulostatic int hwmp_newstate(struct ieee80211vap *, 70195618Srpaulo enum ieee80211_state, int); 71195618Srpaulostatic int hwmp_send_action(struct ieee80211_node *, 72195618Srpaulo const uint8_t [IEEE80211_ADDR_LEN], 73195618Srpaulo const uint8_t [IEEE80211_ADDR_LEN], 74195618Srpaulo uint8_t *, size_t); 75195618Srpaulostatic uint8_t * hwmp_add_meshpreq(uint8_t *, 76195618Srpaulo const struct ieee80211_meshpreq_ie *); 77195618Srpaulostatic uint8_t * hwmp_add_meshprep(uint8_t *, 78195618Srpaulo const struct ieee80211_meshprep_ie *); 79195618Srpaulostatic uint8_t * hwmp_add_meshperr(uint8_t *, 80195618Srpaulo const struct ieee80211_meshperr_ie *); 81195618Srpaulostatic uint8_t * hwmp_add_meshrann(uint8_t *, 82195618Srpaulo const struct ieee80211_meshrann_ie *); 83195618Srpaulostatic void hwmp_rootmode_setup(struct ieee80211vap *); 84195618Srpaulostatic void hwmp_rootmode_cb(void *); 85195618Srpaulostatic void hwmp_rootmode_rann_cb(void *); 86195618Srpaulostatic void hwmp_recv_preq(struct ieee80211vap *, struct ieee80211_node *, 87195618Srpaulo const struct ieee80211_frame *, 88195618Srpaulo const struct ieee80211_meshpreq_ie *); 89195618Srpaulostatic int hwmp_send_preq(struct ieee80211_node *, 90195618Srpaulo const uint8_t [IEEE80211_ADDR_LEN], 91195618Srpaulo const uint8_t [IEEE80211_ADDR_LEN], 92195618Srpaulo struct ieee80211_meshpreq_ie *); 93195618Srpaulostatic void hwmp_recv_prep(struct ieee80211vap *, struct ieee80211_node *, 94195618Srpaulo const struct ieee80211_frame *, 95195618Srpaulo const struct ieee80211_meshprep_ie *); 96195618Srpaulostatic int hwmp_send_prep(struct ieee80211_node *, 97195618Srpaulo const uint8_t [IEEE80211_ADDR_LEN], 98195618Srpaulo const uint8_t [IEEE80211_ADDR_LEN], 99195618Srpaulo struct ieee80211_meshprep_ie *); 100195618Srpaulostatic void hwmp_recv_perr(struct ieee80211vap *, struct ieee80211_node *, 101195618Srpaulo const struct ieee80211_frame *, 102195618Srpaulo const struct ieee80211_meshperr_ie *); 103195618Srpaulostatic int hwmp_send_perr(struct ieee80211_node *, 104195618Srpaulo const uint8_t [IEEE80211_ADDR_LEN], 105195618Srpaulo const uint8_t [IEEE80211_ADDR_LEN], 106195618Srpaulo struct ieee80211_meshperr_ie *); 107195618Srpaulostatic void hwmp_recv_rann(struct ieee80211vap *, struct ieee80211_node *, 108195618Srpaulo const struct ieee80211_frame *, 109195618Srpaulo const struct ieee80211_meshrann_ie *); 110195618Srpaulostatic int hwmp_send_rann(struct ieee80211_node *, 111195618Srpaulo const uint8_t [IEEE80211_ADDR_LEN], 112195618Srpaulo const uint8_t [IEEE80211_ADDR_LEN], 113195618Srpaulo struct ieee80211_meshrann_ie *); 114195618Srpaulostatic struct ieee80211_node * 115195618Srpaulo hwmp_discover(struct ieee80211vap *, 116195618Srpaulo const uint8_t [IEEE80211_ADDR_LEN], struct mbuf *); 117195618Srpaulostatic void hwmp_peerdown(struct ieee80211_node *); 118195618Srpaulo 119195813Ssamstatic struct timeval ieee80211_hwmp_preqminint = { 0, 100000 }; 120195813Ssamstatic struct timeval ieee80211_hwmp_perrminint = { 0, 100000 }; 121195618Srpaulo 122195618Srpaulo/* unalligned little endian access */ 123195618Srpaulo#define LE_WRITE_2(p, v) do { \ 124195618Srpaulo ((uint8_t *)(p))[0] = (v) & 0xff; \ 125195618Srpaulo ((uint8_t *)(p))[1] = ((v) >> 8) & 0xff; \ 126195618Srpaulo} while (0) 127195618Srpaulo#define LE_WRITE_4(p, v) do { \ 128195618Srpaulo ((uint8_t *)(p))[0] = (v) & 0xff; \ 129195618Srpaulo ((uint8_t *)(p))[1] = ((v) >> 8) & 0xff; \ 130195618Srpaulo ((uint8_t *)(p))[2] = ((v) >> 16) & 0xff; \ 131195618Srpaulo ((uint8_t *)(p))[3] = ((v) >> 24) & 0xff; \ 132195618Srpaulo} while (0) 133195618Srpaulo 134195618Srpaulo 135195618Srpaulo/* NB: the Target Address set in a Proactive PREQ is the broadcast address. */ 136195618Srpaulostatic const uint8_t broadcastaddr[IEEE80211_ADDR_LEN] = 137195618Srpaulo { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; 138195618Srpaulo 139195618Srpaulotypedef uint32_t ieee80211_hwmp_seq; 140195908Srpaulo#define HWMP_SEQ_LT(a, b) ((int32_t)((a)-(b)) < 0) 141195908Srpaulo#define HWMP_SEQ_LEQ(a, b) ((int32_t)((a)-(b)) <= 0) 142195908Srpaulo#define HWMP_SEQ_GT(a, b) ((int32_t)((a)-(b)) > 0) 143195908Srpaulo#define HWMP_SEQ_GEQ(a, b) ((int32_t)((a)-(b)) >= 0) 144195618Srpaulo 145195618Srpaulo/* 146195618Srpaulo * Private extension of ieee80211_mesh_route. 147195618Srpaulo */ 148195618Srpaulostruct ieee80211_hwmp_route { 149195908Srpaulo ieee80211_hwmp_seq hr_seq; /* last HWMP seq seen from dst*/ 150195908Srpaulo ieee80211_hwmp_seq hr_preqid; /* last PREQ ID seen from dst */ 151198230Srpaulo ieee80211_hwmp_seq hr_origseq; /* seq. no. on our latest PREQ*/ 152195618Srpaulo int hr_preqretries; 153195618Srpaulo}; 154195618Srpaulostruct ieee80211_hwmp_state { 155195618Srpaulo ieee80211_hwmp_seq hs_seq; /* next seq to be used */ 156195618Srpaulo ieee80211_hwmp_seq hs_preqid; /* next PREQ ID to be used */ 157195618Srpaulo struct timeval hs_lastpreq; /* last time we sent a PREQ */ 158195618Srpaulo struct timeval hs_lastperr; /* last time we sent a PERR */ 159195618Srpaulo int hs_rootmode; /* proactive HWMP */ 160195618Srpaulo struct callout hs_roottimer; 161195618Srpaulo uint8_t hs_maxhops; /* max hop count */ 162195618Srpaulo}; 163195618Srpaulo 164248085Smariusstatic SYSCTL_NODE(_net_wlan, OID_AUTO, hwmp, CTLFLAG_RD, 0, 165195618Srpaulo "IEEE 802.11s HWMP parameters"); 166195813Ssamstatic int ieee80211_hwmp_targetonly = 0; 167195618SrpauloSYSCTL_INT(_net_wlan_hwmp, OID_AUTO, targetonly, CTLTYPE_INT | CTLFLAG_RW, 168195618Srpaulo &ieee80211_hwmp_targetonly, 0, "Set TO bit on generated PREQs"); 169195813Ssamstatic int ieee80211_hwmp_replyforward = 1; 170195618SrpauloSYSCTL_INT(_net_wlan_hwmp, OID_AUTO, replyforward, CTLTYPE_INT | CTLFLAG_RW, 171195618Srpaulo &ieee80211_hwmp_replyforward, 0, "Set RF bit on generated PREQs"); 172195813Ssamstatic int ieee80211_hwmp_pathtimeout = -1; 173195813SsamSYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, pathlifetime, CTLTYPE_INT | CTLFLAG_RW, 174195813Ssam &ieee80211_hwmp_pathtimeout, 0, ieee80211_sysctl_msecs_ticks, "I", 175195813Ssam "path entry lifetime (ms)"); 176195813Ssamstatic int ieee80211_hwmp_roottimeout = -1; 177195813SsamSYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, roottimeout, CTLTYPE_INT | CTLFLAG_RW, 178195813Ssam &ieee80211_hwmp_roottimeout, 0, ieee80211_sysctl_msecs_ticks, "I", 179195813Ssam "root PREQ timeout (ms)"); 180195813Ssamstatic int ieee80211_hwmp_rootint = -1; 181195813SsamSYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, rootint, CTLTYPE_INT | CTLFLAG_RW, 182195813Ssam &ieee80211_hwmp_rootint, 0, ieee80211_sysctl_msecs_ticks, "I", 183195813Ssam "root interval (ms)"); 184195813Ssamstatic int ieee80211_hwmp_rannint = -1; 185195813SsamSYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, rannint, CTLTYPE_INT | CTLFLAG_RW, 186195813Ssam &ieee80211_hwmp_rannint, 0, ieee80211_sysctl_msecs_ticks, "I", 187195813Ssam "root announcement interval (ms)"); 188195618Srpaulo 189195618Srpaulo#define IEEE80211_HWMP_DEFAULT_MAXHOPS 31 190195618Srpaulo 191197413Srpaulostatic ieee80211_recv_action_func hwmp_recv_action_meshpath; 192195618Srpaulo 193195813Ssamstatic struct ieee80211_mesh_proto_path mesh_proto_hwmp = { 194195618Srpaulo .mpp_descr = "HWMP", 195197975Srpaulo .mpp_ie = IEEE80211_MESHCONF_PATH_HWMP, 196195618Srpaulo .mpp_discover = hwmp_discover, 197195618Srpaulo .mpp_peerdown = hwmp_peerdown, 198195618Srpaulo .mpp_vattach = hwmp_vattach, 199195618Srpaulo .mpp_vdetach = hwmp_vdetach, 200195618Srpaulo .mpp_newstate = hwmp_newstate, 201195618Srpaulo .mpp_privlen = sizeof(struct ieee80211_hwmp_route), 202195618Srpaulo}; 203195813SsamSYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, inact, CTLTYPE_INT | CTLFLAG_RW, 204195813Ssam &mesh_proto_hwmp.mpp_inact, 0, ieee80211_sysctl_msecs_ticks, "I", 205195813Ssam "mesh route inactivity timeout (ms)"); 206195618Srpaulo 207195618Srpaulo 208195618Srpaulostatic void 209195618Srpauloieee80211_hwmp_init(void) 210195618Srpaulo{ 211195813Ssam ieee80211_hwmp_pathtimeout = msecs_to_ticks(5*1000); 212195813Ssam ieee80211_hwmp_roottimeout = msecs_to_ticks(5*1000); 213195813Ssam ieee80211_hwmp_rootint = msecs_to_ticks(2*1000); 214195813Ssam ieee80211_hwmp_rannint = msecs_to_ticks(1*1000); 215195813Ssam 216195618Srpaulo /* 217197413Srpaulo * Register action frame handler. 218195618Srpaulo */ 219195618Srpaulo ieee80211_recv_action_register(IEEE80211_ACTION_CAT_MESHPATH, 220197413Srpaulo IEEE80211_ACTION_MESHPATH_SEL, hwmp_recv_action_meshpath); 221195618Srpaulo 222195813Ssam /* NB: default is 5 secs per spec */ 223195813Ssam mesh_proto_hwmp.mpp_inact = msecs_to_ticks(5*1000); 224195813Ssam 225195618Srpaulo /* 226195618Srpaulo * Register HWMP. 227195618Srpaulo */ 228195618Srpaulo ieee80211_mesh_register_proto_path(&mesh_proto_hwmp); 229195618Srpaulo} 230195618SrpauloSYSINIT(wlan_hwmp, SI_SUB_DRIVERS, SI_ORDER_SECOND, ieee80211_hwmp_init, NULL); 231195618Srpaulo 232195618Srpaulovoid 233195618Srpaulohwmp_vattach(struct ieee80211vap *vap) 234195618Srpaulo{ 235195618Srpaulo struct ieee80211_hwmp_state *hs; 236195618Srpaulo 237195618Srpaulo KASSERT(vap->iv_opmode == IEEE80211_M_MBSS, 238195618Srpaulo ("not a mesh vap, opmode %d", vap->iv_opmode)); 239195618Srpaulo 240195618Srpaulo hs = malloc(sizeof(struct ieee80211_hwmp_state), M_80211_VAP, 241195618Srpaulo M_NOWAIT | M_ZERO); 242195618Srpaulo if (hs == NULL) { 243195618Srpaulo printf("%s: couldn't alloc HWMP state\n", __func__); 244195618Srpaulo return; 245195618Srpaulo } 246195618Srpaulo hs->hs_maxhops = IEEE80211_HWMP_DEFAULT_MAXHOPS; 247195618Srpaulo callout_init(&hs->hs_roottimer, CALLOUT_MPSAFE); 248195618Srpaulo vap->iv_hwmp = hs; 249195618Srpaulo} 250195618Srpaulo 251195618Srpaulovoid 252195618Srpaulohwmp_vdetach(struct ieee80211vap *vap) 253195618Srpaulo{ 254195618Srpaulo struct ieee80211_hwmp_state *hs = vap->iv_hwmp; 255195618Srpaulo 256195784Srpaulo callout_drain(&hs->hs_roottimer); 257195618Srpaulo free(vap->iv_hwmp, M_80211_VAP); 258195618Srpaulo vap->iv_hwmp = NULL; 259195618Srpaulo} 260195618Srpaulo 261195618Srpauloint 262195618Srpaulohwmp_newstate(struct ieee80211vap *vap, enum ieee80211_state ostate, int arg) 263195618Srpaulo{ 264195618Srpaulo enum ieee80211_state nstate = vap->iv_state; 265195618Srpaulo struct ieee80211_hwmp_state *hs = vap->iv_hwmp; 266195618Srpaulo 267195618Srpaulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n", 268195618Srpaulo __func__, ieee80211_state_name[ostate], 269195618Srpaulo ieee80211_state_name[nstate], arg); 270195618Srpaulo 271195618Srpaulo if (nstate != IEEE80211_S_RUN && ostate == IEEE80211_S_RUN) 272195618Srpaulo callout_drain(&hs->hs_roottimer); 273195784Srpaulo if (nstate == IEEE80211_S_RUN) 274195784Srpaulo hwmp_rootmode_setup(vap); 275195618Srpaulo return 0; 276195618Srpaulo} 277195618Srpaulo 278195618Srpaulostatic int 279197413Srpaulohwmp_recv_action_meshpath(struct ieee80211_node *ni, 280195618Srpaulo const struct ieee80211_frame *wh, 281195618Srpaulo const uint8_t *frm, const uint8_t *efrm) 282195618Srpaulo{ 283195618Srpaulo struct ieee80211vap *vap = ni->ni_vap; 284195618Srpaulo struct ieee80211_meshpreq_ie preq; 285197413Srpaulo struct ieee80211_meshprep_ie prep; 286197413Srpaulo struct ieee80211_meshperr_ie perr; 287197413Srpaulo struct ieee80211_meshrann_ie rann; 288195618Srpaulo const uint8_t *iefrm = frm + 2; /* action + code */ 289197413Srpaulo int found = 0; 290195618Srpaulo 291195618Srpaulo while (efrm - iefrm > 1) { 292195618Srpaulo IEEE80211_VERIFY_LENGTH(efrm - iefrm, iefrm[1] + 2, return 0); 293197413Srpaulo switch (*iefrm) { 294197413Srpaulo case IEEE80211_ELEMID_MESHPREQ: 295197413Srpaulo { 296195618Srpaulo const struct ieee80211_meshpreq_ie *mpreq = 297195618Srpaulo (const struct ieee80211_meshpreq_ie *) iefrm; 298195618Srpaulo /* XXX > 1 target */ 299195618Srpaulo if (mpreq->preq_len != 300195618Srpaulo sizeof(struct ieee80211_meshpreq_ie) - 2) { 301195618Srpaulo IEEE80211_DISCARD(vap, 302195618Srpaulo IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP, 303195618Srpaulo wh, NULL, "%s", "PREQ with wrong len"); 304195618Srpaulo vap->iv_stats.is_rx_mgtdiscard++; 305197413Srpaulo break; 306195618Srpaulo } 307195618Srpaulo memcpy(&preq, mpreq, sizeof(preq)); 308195618Srpaulo preq.preq_id = LE_READ_4(&mpreq->preq_id); 309195618Srpaulo preq.preq_origseq = LE_READ_4(&mpreq->preq_origseq); 310195618Srpaulo preq.preq_lifetime = LE_READ_4(&mpreq->preq_lifetime); 311195618Srpaulo preq.preq_metric = LE_READ_4(&mpreq->preq_metric); 312195618Srpaulo preq.preq_targets[0].target_seq = 313195618Srpaulo LE_READ_4(&mpreq->preq_targets[0].target_seq); 314195618Srpaulo hwmp_recv_preq(vap, ni, wh, &preq); 315197413Srpaulo found++; 316197413Srpaulo break; 317195618Srpaulo } 318197413Srpaulo case IEEE80211_ELEMID_MESHPREP: 319197413Srpaulo { 320195618Srpaulo const struct ieee80211_meshprep_ie *mprep = 321195618Srpaulo (const struct ieee80211_meshprep_ie *) iefrm; 322195618Srpaulo if (mprep->prep_len != 323195618Srpaulo sizeof(struct ieee80211_meshprep_ie) - 2) { 324195618Srpaulo IEEE80211_DISCARD(vap, 325195618Srpaulo IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP, 326195618Srpaulo wh, NULL, "%s", "PREP with wrong len"); 327195618Srpaulo vap->iv_stats.is_rx_mgtdiscard++; 328197413Srpaulo break; 329195618Srpaulo } 330195618Srpaulo memcpy(&prep, mprep, sizeof(prep)); 331195618Srpaulo prep.prep_targetseq = LE_READ_4(&mprep->prep_targetseq); 332195618Srpaulo prep.prep_lifetime = LE_READ_4(&mprep->prep_lifetime); 333195618Srpaulo prep.prep_metric = LE_READ_4(&mprep->prep_metric); 334195618Srpaulo prep.prep_origseq = LE_READ_4(&mprep->prep_origseq); 335195618Srpaulo hwmp_recv_prep(vap, ni, wh, &prep); 336197413Srpaulo found++; 337197413Srpaulo break; 338195618Srpaulo } 339197413Srpaulo case IEEE80211_ELEMID_MESHPERR: 340197413Srpaulo { 341195618Srpaulo const struct ieee80211_meshperr_ie *mperr = 342195618Srpaulo (const struct ieee80211_meshperr_ie *) iefrm; 343195618Srpaulo /* XXX > 1 target */ 344195618Srpaulo if (mperr->perr_len != 345195618Srpaulo sizeof(struct ieee80211_meshperr_ie) - 2) { 346195618Srpaulo IEEE80211_DISCARD(vap, 347195618Srpaulo IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP, 348195618Srpaulo wh, NULL, "%s", "PERR with wrong len"); 349195618Srpaulo vap->iv_stats.is_rx_mgtdiscard++; 350197413Srpaulo break; 351195618Srpaulo } 352195618Srpaulo memcpy(&perr, mperr, sizeof(perr)); 353195618Srpaulo perr.perr_dests[0].dest_seq = 354195618Srpaulo LE_READ_4(&mperr->perr_dests[0].dest_seq); 355195618Srpaulo hwmp_recv_perr(vap, ni, wh, &perr); 356197413Srpaulo found++; 357197413Srpaulo break; 358195618Srpaulo } 359197413Srpaulo case IEEE80211_ELEMID_MESHRANN: 360197413Srpaulo { 361195618Srpaulo const struct ieee80211_meshrann_ie *mrann = 362195618Srpaulo (const struct ieee80211_meshrann_ie *) iefrm; 363195618Srpaulo if (mrann->rann_len != 364195618Srpaulo sizeof(struct ieee80211_meshrann_ie) - 2) { 365195618Srpaulo IEEE80211_DISCARD(vap, 366195618Srpaulo IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP, 367195618Srpaulo wh, NULL, "%s", "RAN with wrong len"); 368195618Srpaulo vap->iv_stats.is_rx_mgtdiscard++; 369195618Srpaulo return 1; 370195618Srpaulo } 371195618Srpaulo memcpy(&rann, mrann, sizeof(rann)); 372195618Srpaulo rann.rann_seq = LE_READ_4(&mrann->rann_seq); 373195618Srpaulo rann.rann_metric = LE_READ_4(&mrann->rann_metric); 374195618Srpaulo hwmp_recv_rann(vap, ni, wh, &rann); 375197413Srpaulo found++; 376197413Srpaulo break; 377195618Srpaulo } 378197413Srpaulo } 379195618Srpaulo iefrm += iefrm[1] + 2; 380195618Srpaulo } 381197413Srpaulo if (!found) { 382197413Srpaulo IEEE80211_DISCARD(vap, 383197413Srpaulo IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP, 384197413Srpaulo wh, NULL, "%s", "PATH SEL action without IE"); 385197413Srpaulo vap->iv_stats.is_rx_mgtdiscard++; 386197413Srpaulo } 387195618Srpaulo return 0; 388195618Srpaulo} 389195618Srpaulo 390195618Srpaulostatic int 391195618Srpaulohwmp_send_action(struct ieee80211_node *ni, 392195618Srpaulo const uint8_t sa[IEEE80211_ADDR_LEN], 393195618Srpaulo const uint8_t da[IEEE80211_ADDR_LEN], 394195618Srpaulo uint8_t *ie, size_t len) 395195618Srpaulo{ 396195618Srpaulo struct ieee80211vap *vap = ni->ni_vap; 397195618Srpaulo struct ieee80211com *ic = ni->ni_ic; 398195618Srpaulo struct ieee80211_bpf_params params; 399195618Srpaulo struct mbuf *m; 400195618Srpaulo uint8_t *frm; 401195618Srpaulo 402195618Srpaulo if (vap->iv_state == IEEE80211_S_CAC) { 403195618Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, ni, 404195618Srpaulo "block %s frame in CAC state", "HWMP action"); 405195618Srpaulo vap->iv_stats.is_tx_badstate++; 406195618Srpaulo return EIO; /* XXX */ 407195618Srpaulo } 408195618Srpaulo 409195618Srpaulo KASSERT(ni != NULL, ("null node")); 410195618Srpaulo /* 411195618Srpaulo * Hold a reference on the node so it doesn't go away until after 412195618Srpaulo * the xmit is complete all the way in the driver. On error we 413195618Srpaulo * will remove our reference. 414195618Srpaulo */ 415195618Srpaulo#ifdef IEEE80211_DEBUG_REFCNT 416195618Srpaulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, 417195618Srpaulo "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", 418195618Srpaulo __func__, __LINE__, 419195618Srpaulo ni, ether_sprintf(ni->ni_macaddr), 420195618Srpaulo ieee80211_node_refcnt(ni)+1); 421195618Srpaulo#endif 422195618Srpaulo ieee80211_ref_node(ni); 423195618Srpaulo 424195618Srpaulo m = ieee80211_getmgtframe(&frm, 425195618Srpaulo ic->ic_headroom + sizeof(struct ieee80211_frame), 426195618Srpaulo sizeof(struct ieee80211_action) + len 427195618Srpaulo ); 428195618Srpaulo if (m == NULL) { 429195618Srpaulo ieee80211_free_node(ni); 430195618Srpaulo vap->iv_stats.is_tx_nobuf++; 431195618Srpaulo return ENOMEM; 432195618Srpaulo } 433195618Srpaulo *frm++ = IEEE80211_ACTION_CAT_MESHPATH; 434197413Srpaulo *frm++ = IEEE80211_ACTION_MESHPATH_SEL; 435195618Srpaulo switch (*ie) { 436195618Srpaulo case IEEE80211_ELEMID_MESHPREQ: 437195618Srpaulo frm = hwmp_add_meshpreq(frm, 438195618Srpaulo (struct ieee80211_meshpreq_ie *)ie); 439195618Srpaulo break; 440195618Srpaulo case IEEE80211_ELEMID_MESHPREP: 441195618Srpaulo frm = hwmp_add_meshprep(frm, 442195618Srpaulo (struct ieee80211_meshprep_ie *)ie); 443195618Srpaulo break; 444195618Srpaulo case IEEE80211_ELEMID_MESHPERR: 445195618Srpaulo frm = hwmp_add_meshperr(frm, 446195618Srpaulo (struct ieee80211_meshperr_ie *)ie); 447195618Srpaulo break; 448195618Srpaulo case IEEE80211_ELEMID_MESHRANN: 449195618Srpaulo frm = hwmp_add_meshrann(frm, 450195618Srpaulo (struct ieee80211_meshrann_ie *)ie); 451195618Srpaulo break; 452195618Srpaulo } 453195618Srpaulo 454195618Srpaulo m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); 455195618Srpaulo M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT); 456195618Srpaulo if (m == NULL) { 457195618Srpaulo ieee80211_free_node(ni); 458195618Srpaulo vap->iv_stats.is_tx_nobuf++; 459195618Srpaulo return ENOMEM; 460195618Srpaulo } 461195618Srpaulo ieee80211_send_setup(ni, m, 462195618Srpaulo IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_ACTION, 463195618Srpaulo IEEE80211_NONQOS_TID, sa, da, sa); 464195618Srpaulo 465195618Srpaulo m->m_flags |= M_ENCAP; /* mark encapsulated */ 466195618Srpaulo IEEE80211_NODE_STAT(ni, tx_mgmt); 467195618Srpaulo 468195618Srpaulo memset(¶ms, 0, sizeof(params)); 469195618Srpaulo params.ibp_pri = WME_AC_VO; 470195618Srpaulo params.ibp_rate0 = ni->ni_txparms->mgmtrate; 471195618Srpaulo if (IEEE80211_IS_MULTICAST(da)) 472195618Srpaulo params.ibp_try0 = 1; 473195618Srpaulo else 474195618Srpaulo params.ibp_try0 = ni->ni_txparms->maxretry; 475195618Srpaulo params.ibp_power = ni->ni_txpower; 476195618Srpaulo return ic->ic_raw_xmit(ni, m, ¶ms); 477195618Srpaulo} 478195618Srpaulo 479197413Srpaulo#define ADDSHORT(frm, v) do { \ 480197413Srpaulo frm[0] = (v) & 0xff; \ 481197413Srpaulo frm[1] = (v) >> 8; \ 482197413Srpaulo frm += 2; \ 483197413Srpaulo} while (0) 484195618Srpaulo#define ADDWORD(frm, v) do { \ 485195618Srpaulo LE_WRITE_4(frm, v); \ 486195618Srpaulo frm += 4; \ 487195618Srpaulo} while (0) 488195618Srpaulo/* 489195618Srpaulo * Add a Mesh Path Request IE to a frame. 490195618Srpaulo */ 491195618Srpaulostatic uint8_t * 492195618Srpaulohwmp_add_meshpreq(uint8_t *frm, const struct ieee80211_meshpreq_ie *preq) 493195618Srpaulo{ 494195618Srpaulo int i; 495195618Srpaulo 496195618Srpaulo *frm++ = IEEE80211_ELEMID_MESHPREQ; 497195618Srpaulo *frm++ = sizeof(struct ieee80211_meshpreq_ie) - 2 + 498195618Srpaulo (preq->preq_tcount - 1) * sizeof(*preq->preq_targets); 499195618Srpaulo *frm++ = preq->preq_flags; 500195618Srpaulo *frm++ = preq->preq_hopcount; 501195618Srpaulo *frm++ = preq->preq_ttl; 502195618Srpaulo ADDWORD(frm, preq->preq_id); 503195618Srpaulo IEEE80211_ADDR_COPY(frm, preq->preq_origaddr); frm += 6; 504195618Srpaulo ADDWORD(frm, preq->preq_origseq); 505195618Srpaulo ADDWORD(frm, preq->preq_lifetime); 506195618Srpaulo ADDWORD(frm, preq->preq_metric); 507195618Srpaulo *frm++ = preq->preq_tcount; 508195618Srpaulo for (i = 0; i < preq->preq_tcount; i++) { 509195618Srpaulo *frm++ = preq->preq_targets[i].target_flags; 510195618Srpaulo IEEE80211_ADDR_COPY(frm, preq->preq_targets[i].target_addr); 511195618Srpaulo frm += 6; 512195618Srpaulo ADDWORD(frm, preq->preq_targets[i].target_seq); 513195618Srpaulo } 514195618Srpaulo return frm; 515195618Srpaulo} 516195618Srpaulo 517195618Srpaulo/* 518195618Srpaulo * Add a Mesh Path Reply IE to a frame. 519195618Srpaulo */ 520195618Srpaulostatic uint8_t * 521195618Srpaulohwmp_add_meshprep(uint8_t *frm, const struct ieee80211_meshprep_ie *prep) 522195618Srpaulo{ 523195618Srpaulo *frm++ = IEEE80211_ELEMID_MESHPREP; 524195618Srpaulo *frm++ = sizeof(struct ieee80211_meshprep_ie) - 2; 525195618Srpaulo *frm++ = prep->prep_flags; 526195618Srpaulo *frm++ = prep->prep_hopcount; 527195618Srpaulo *frm++ = prep->prep_ttl; 528195618Srpaulo IEEE80211_ADDR_COPY(frm, prep->prep_targetaddr); frm += 6; 529195618Srpaulo ADDWORD(frm, prep->prep_targetseq); 530195618Srpaulo ADDWORD(frm, prep->prep_lifetime); 531195618Srpaulo ADDWORD(frm, prep->prep_metric); 532195618Srpaulo IEEE80211_ADDR_COPY(frm, prep->prep_origaddr); frm += 6; 533195618Srpaulo ADDWORD(frm, prep->prep_origseq); 534195618Srpaulo return frm; 535195618Srpaulo} 536195618Srpaulo 537195618Srpaulo/* 538195618Srpaulo * Add a Mesh Path Error IE to a frame. 539195618Srpaulo */ 540195618Srpaulostatic uint8_t * 541195618Srpaulohwmp_add_meshperr(uint8_t *frm, const struct ieee80211_meshperr_ie *perr) 542195618Srpaulo{ 543195618Srpaulo int i; 544195618Srpaulo 545195618Srpaulo *frm++ = IEEE80211_ELEMID_MESHPERR; 546195618Srpaulo *frm++ = sizeof(struct ieee80211_meshperr_ie) - 2 + 547195618Srpaulo (perr->perr_ndests - 1) * sizeof(*perr->perr_dests); 548197413Srpaulo *frm++ = perr->perr_ttl; 549195618Srpaulo *frm++ = perr->perr_ndests; 550195618Srpaulo for (i = 0; i < perr->perr_ndests; i++) { 551198260Srpaulo *frm++ = perr->perr_dests[i].dest_flags; 552195618Srpaulo IEEE80211_ADDR_COPY(frm, perr->perr_dests[i].dest_addr); 553195618Srpaulo frm += 6; 554195618Srpaulo ADDWORD(frm, perr->perr_dests[i].dest_seq); 555197413Srpaulo ADDSHORT(frm, perr->perr_dests[i].dest_rcode); 556195618Srpaulo } 557195618Srpaulo return frm; 558195618Srpaulo} 559195618Srpaulo 560195618Srpaulo/* 561195618Srpaulo * Add a Root Annoucement IE to a frame. 562195618Srpaulo */ 563195618Srpaulostatic uint8_t * 564195618Srpaulohwmp_add_meshrann(uint8_t *frm, const struct ieee80211_meshrann_ie *rann) 565195618Srpaulo{ 566195618Srpaulo *frm++ = IEEE80211_ELEMID_MESHRANN; 567195618Srpaulo *frm++ = sizeof(struct ieee80211_meshrann_ie) - 2; 568195618Srpaulo *frm++ = rann->rann_flags; 569195618Srpaulo *frm++ = rann->rann_hopcount; 570195618Srpaulo *frm++ = rann->rann_ttl; 571195618Srpaulo IEEE80211_ADDR_COPY(frm, rann->rann_addr); frm += 6; 572195618Srpaulo ADDWORD(frm, rann->rann_seq); 573195618Srpaulo ADDWORD(frm, rann->rann_metric); 574195618Srpaulo return frm; 575195618Srpaulo} 576195618Srpaulo 577195618Srpaulostatic void 578195618Srpaulohwmp_rootmode_setup(struct ieee80211vap *vap) 579195618Srpaulo{ 580195618Srpaulo struct ieee80211_hwmp_state *hs = vap->iv_hwmp; 581195618Srpaulo 582195618Srpaulo switch (hs->hs_rootmode) { 583195618Srpaulo case IEEE80211_HWMP_ROOTMODE_DISABLED: 584195618Srpaulo callout_drain(&hs->hs_roottimer); 585195618Srpaulo break; 586195618Srpaulo case IEEE80211_HWMP_ROOTMODE_NORMAL: 587195618Srpaulo case IEEE80211_HWMP_ROOTMODE_PROACTIVE: 588195813Ssam callout_reset(&hs->hs_roottimer, ieee80211_hwmp_rootint, 589195618Srpaulo hwmp_rootmode_cb, vap); 590195618Srpaulo break; 591195618Srpaulo case IEEE80211_HWMP_ROOTMODE_RANN: 592195813Ssam callout_reset(&hs->hs_roottimer, ieee80211_hwmp_rannint, 593195618Srpaulo hwmp_rootmode_rann_cb, vap); 594195618Srpaulo break; 595195618Srpaulo } 596195618Srpaulo} 597195618Srpaulo 598195618Srpaulo/* 599195618Srpaulo * Send a broadcast Path Request to find all nodes on the mesh. We are 600195618Srpaulo * called when the vap is configured as a HWMP root node. 601195618Srpaulo */ 602195618Srpaulo#define PREQ_TFLAGS(n) preq.preq_targets[n].target_flags 603195618Srpaulo#define PREQ_TADDR(n) preq.preq_targets[n].target_addr 604195618Srpaulo#define PREQ_TSEQ(n) preq.preq_targets[n].target_seq 605195618Srpaulostatic void 606195618Srpaulohwmp_rootmode_cb(void *arg) 607195618Srpaulo{ 608195618Srpaulo struct ieee80211vap *vap = (struct ieee80211vap *)arg; 609195618Srpaulo struct ieee80211_hwmp_state *hs = vap->iv_hwmp; 610195618Srpaulo struct ieee80211_mesh_state *ms = vap->iv_mesh; 611195618Srpaulo struct ieee80211_meshpreq_ie preq; 612195618Srpaulo 613195618Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, vap->iv_bss, 614195784Srpaulo "%s", "send broadcast PREQ"); 615195618Srpaulo 616195618Srpaulo preq.preq_flags = IEEE80211_MESHPREQ_FLAGS_AM; 617195908Srpaulo if (ms->ms_flags & IEEE80211_MESHFLAGS_PORTAL) 618195908Srpaulo preq.preq_flags |= IEEE80211_MESHPREQ_FLAGS_PR; 619195618Srpaulo if (hs->hs_rootmode == IEEE80211_HWMP_ROOTMODE_PROACTIVE) 620195618Srpaulo preq.preq_flags |= IEEE80211_MESHPREQ_FLAGS_PP; 621195618Srpaulo preq.preq_hopcount = 0; 622195618Srpaulo preq.preq_ttl = ms->ms_ttl; 623195618Srpaulo preq.preq_id = ++hs->hs_preqid; 624195618Srpaulo IEEE80211_ADDR_COPY(preq.preq_origaddr, vap->iv_myaddr); 625195618Srpaulo preq.preq_origseq = ++hs->hs_seq; 626195813Ssam preq.preq_lifetime = ticks_to_msecs(ieee80211_hwmp_roottimeout); 627195618Srpaulo preq.preq_metric = IEEE80211_MESHLMETRIC_INITIALVAL; 628195618Srpaulo preq.preq_tcount = 1; 629195618Srpaulo IEEE80211_ADDR_COPY(PREQ_TADDR(0), broadcastaddr); 630195618Srpaulo PREQ_TFLAGS(0) = IEEE80211_MESHPREQ_TFLAGS_TO | 631195618Srpaulo IEEE80211_MESHPREQ_TFLAGS_RF; 632195618Srpaulo PREQ_TSEQ(0) = 0; 633195618Srpaulo vap->iv_stats.is_hwmp_rootreqs++; 634195618Srpaulo hwmp_send_preq(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &preq); 635195618Srpaulo hwmp_rootmode_setup(vap); 636195618Srpaulo} 637195618Srpaulo#undef PREQ_TFLAGS 638195618Srpaulo#undef PREQ_TADDR 639195618Srpaulo#undef PREQ_TSEQ 640195618Srpaulo 641195618Srpaulo/* 642195618Srpaulo * Send a Root Annoucement (RANN) to find all the nodes on the mesh. We are 643195618Srpaulo * called when the vap is configured as a HWMP RANN root node. 644195618Srpaulo */ 645195618Srpaulostatic void 646195618Srpaulohwmp_rootmode_rann_cb(void *arg) 647195618Srpaulo{ 648195618Srpaulo struct ieee80211vap *vap = (struct ieee80211vap *)arg; 649195618Srpaulo struct ieee80211_hwmp_state *hs = vap->iv_hwmp; 650195618Srpaulo struct ieee80211_mesh_state *ms = vap->iv_mesh; 651195618Srpaulo struct ieee80211_meshrann_ie rann; 652195618Srpaulo 653195618Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, vap->iv_bss, 654195784Srpaulo "%s", "send broadcast RANN"); 655195618Srpaulo 656198369Srpaulo rann.rann_flags = 0; 657195908Srpaulo if (ms->ms_flags & IEEE80211_MESHFLAGS_PORTAL) 658195908Srpaulo rann.rann_flags |= IEEE80211_MESHRANN_FLAGS_PR; 659195618Srpaulo rann.rann_hopcount = 0; 660195618Srpaulo rann.rann_ttl = ms->ms_ttl; 661195618Srpaulo IEEE80211_ADDR_COPY(rann.rann_addr, vap->iv_myaddr); 662195618Srpaulo rann.rann_seq = ++hs->hs_seq; 663195618Srpaulo rann.rann_metric = IEEE80211_MESHLMETRIC_INITIALVAL; 664195618Srpaulo 665195618Srpaulo vap->iv_stats.is_hwmp_rootrann++; 666195618Srpaulo hwmp_send_rann(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &rann); 667195618Srpaulo hwmp_rootmode_setup(vap); 668195618Srpaulo} 669195618Srpaulo 670195618Srpaulo#define PREQ_TFLAGS(n) preq->preq_targets[n].target_flags 671195618Srpaulo#define PREQ_TADDR(n) preq->preq_targets[n].target_addr 672195618Srpaulo#define PREQ_TSEQ(n) preq->preq_targets[n].target_seq 673195618Srpaulostatic void 674195618Srpaulohwmp_recv_preq(struct ieee80211vap *vap, struct ieee80211_node *ni, 675195618Srpaulo const struct ieee80211_frame *wh, const struct ieee80211_meshpreq_ie *preq) 676195618Srpaulo{ 677195618Srpaulo struct ieee80211_mesh_state *ms = vap->iv_mesh; 678195618Srpaulo struct ieee80211_mesh_route *rt = NULL; 679195908Srpaulo struct ieee80211_mesh_route *rtorig = NULL; 680195908Srpaulo struct ieee80211_hwmp_route *hrorig; 681195618Srpaulo struct ieee80211_hwmp_state *hs = vap->iv_hwmp; 682195618Srpaulo struct ieee80211_meshprep_ie prep; 683195618Srpaulo 684195618Srpaulo if (ni == vap->iv_bss || 685195618Srpaulo ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED) 686195618Srpaulo return; 687195618Srpaulo /* 688195618Srpaulo * Ignore PREQs from us. Could happen because someone forward it 689195618Srpaulo * back to us. 690195618Srpaulo */ 691195618Srpaulo if (IEEE80211_ADDR_EQ(vap->iv_myaddr, preq->preq_origaddr)) 692195618Srpaulo return; 693195618Srpaulo 694195618Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 695195618Srpaulo "received PREQ, source %s", ether_sprintf(preq->preq_origaddr)); 696195618Srpaulo 697195618Srpaulo /* 698195618Srpaulo * Acceptance criteria: if the PREQ is not for us and 699195618Srpaulo * forwarding is disabled, discard this PREQ. 700195618Srpaulo */ 701195618Srpaulo if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0)) && 702195618Srpaulo !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) { 703195618Srpaulo IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_HWMP, 704195618Srpaulo preq->preq_origaddr, NULL, "%s", "not accepting PREQ"); 705195618Srpaulo return; 706195618Srpaulo } 707195908Srpaulo rtorig = ieee80211_mesh_rt_find(vap, preq->preq_origaddr); 708195908Srpaulo if (rtorig == NULL) 709195908Srpaulo rtorig = ieee80211_mesh_rt_add(vap, preq->preq_origaddr); 710209013Srpaulo if (rtorig == NULL) { 711208696Srpaulo /* XXX stat */ 712208696Srpaulo return; 713209013Srpaulo } 714195908Srpaulo hrorig = IEEE80211_MESH_ROUTE_PRIV(rtorig, struct ieee80211_hwmp_route); 715195618Srpaulo /* 716195908Srpaulo * Sequence number validation. 717195908Srpaulo */ 718195908Srpaulo if (HWMP_SEQ_LEQ(preq->preq_id, hrorig->hr_preqid) && 719195908Srpaulo HWMP_SEQ_LEQ(preq->preq_origseq, hrorig->hr_seq)) { 720195908Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 721195908Srpaulo "discard PREQ from %s, old seq no %u <= %u", 722195908Srpaulo ether_sprintf(preq->preq_origaddr), 723195908Srpaulo preq->preq_origseq, hrorig->hr_seq); 724195908Srpaulo return; 725195908Srpaulo } 726195908Srpaulo hrorig->hr_preqid = preq->preq_id; 727195908Srpaulo hrorig->hr_seq = preq->preq_origseq; 728195908Srpaulo 729195908Srpaulo /* 730195618Srpaulo * Check if the PREQ is addressed to us. 731195618Srpaulo */ 732195618Srpaulo if (IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0))) { 733195618Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 734195784Srpaulo "reply to %s", ether_sprintf(preq->preq_origaddr)); 735195618Srpaulo /* 736195618Srpaulo * Build and send a PREP frame. 737195618Srpaulo */ 738195618Srpaulo prep.prep_flags = 0; 739195618Srpaulo prep.prep_hopcount = 0; 740195618Srpaulo prep.prep_ttl = ms->ms_ttl; 741198230Srpaulo IEEE80211_ADDR_COPY(prep.prep_targetaddr, vap->iv_myaddr); 742198230Srpaulo prep.prep_targetseq = ++hs->hs_seq; 743195618Srpaulo prep.prep_lifetime = preq->preq_lifetime; 744195618Srpaulo prep.prep_metric = IEEE80211_MESHLMETRIC_INITIALVAL; 745198230Srpaulo IEEE80211_ADDR_COPY(prep.prep_origaddr, preq->preq_origaddr); 746198230Srpaulo prep.prep_origseq = preq->preq_origseq; 747195618Srpaulo hwmp_send_prep(ni, vap->iv_myaddr, wh->i_addr2, &prep); 748195618Srpaulo /* 749195618Srpaulo * Build the reverse path, if we don't have it already. 750195618Srpaulo */ 751195618Srpaulo rt = ieee80211_mesh_rt_find(vap, preq->preq_origaddr); 752195618Srpaulo if (rt == NULL) 753195618Srpaulo hwmp_discover(vap, preq->preq_origaddr, NULL); 754195784Srpaulo else if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0) 755195618Srpaulo hwmp_discover(vap, rt->rt_dest, NULL); 756195618Srpaulo return; 757195618Srpaulo } 758195618Srpaulo /* 759195618Srpaulo * Proactive PREQ: reply with a proactive PREP to the 760195618Srpaulo * root STA if requested. 761195618Srpaulo */ 762195618Srpaulo if (IEEE80211_ADDR_EQ(PREQ_TADDR(0), broadcastaddr) && 763195618Srpaulo (PREQ_TFLAGS(0) & 764195618Srpaulo ((IEEE80211_MESHPREQ_TFLAGS_TO|IEEE80211_MESHPREQ_TFLAGS_RF) == 765195618Srpaulo (IEEE80211_MESHPREQ_TFLAGS_TO|IEEE80211_MESHPREQ_TFLAGS_RF)))) { 766195618Srpaulo uint8_t rootmac[IEEE80211_ADDR_LEN]; 767195618Srpaulo 768195618Srpaulo IEEE80211_ADDR_COPY(rootmac, preq->preq_origaddr); 769195618Srpaulo rt = ieee80211_mesh_rt_find(vap, rootmac); 770195784Srpaulo if (rt == NULL) { 771195618Srpaulo rt = ieee80211_mesh_rt_add(vap, rootmac); 772195784Srpaulo if (rt == NULL) { 773195784Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 774195784Srpaulo "unable to add root mesh path to %s", 775195784Srpaulo ether_sprintf(rootmac)); 776195784Srpaulo vap->iv_stats.is_mesh_rtaddfailed++; 777195784Srpaulo return; 778195784Srpaulo } 779195784Srpaulo } 780195784Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 781195784Srpaulo "root mesh station @ %s", ether_sprintf(rootmac)); 782195784Srpaulo 783195618Srpaulo /* 784195618Srpaulo * Reply with a PREP if we don't have a path to the root 785195618Srpaulo * or if the root sent us a proactive PREQ. 786195618Srpaulo */ 787195784Srpaulo if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0 || 788195618Srpaulo (preq->preq_flags & IEEE80211_MESHPREQ_FLAGS_PP)) { 789195618Srpaulo prep.prep_flags = 0; 790195618Srpaulo prep.prep_hopcount = 0; 791195618Srpaulo prep.prep_ttl = ms->ms_ttl; 792198230Srpaulo IEEE80211_ADDR_COPY(prep.prep_origaddr, rootmac); 793195618Srpaulo prep.prep_origseq = preq->preq_origseq; 794195618Srpaulo prep.prep_lifetime = preq->preq_lifetime; 795195618Srpaulo prep.prep_metric = IEEE80211_MESHLMETRIC_INITIALVAL; 796198230Srpaulo IEEE80211_ADDR_COPY(prep.prep_targetaddr, 797198230Srpaulo vap->iv_myaddr); 798198230Srpaulo prep.prep_targetseq = ++hs->hs_seq; 799195618Srpaulo hwmp_send_prep(vap->iv_bss, vap->iv_myaddr, 800195618Srpaulo broadcastaddr, &prep); 801195618Srpaulo } 802195618Srpaulo hwmp_discover(vap, rootmac, NULL); 803195618Srpaulo return; 804195618Srpaulo } 805195618Srpaulo rt = ieee80211_mesh_rt_find(vap, PREQ_TADDR(0)); 806195618Srpaulo 807195618Srpaulo /* 808195618Srpaulo * Forwarding and Intermediate reply for PREQs with 1 target. 809195618Srpaulo */ 810195618Srpaulo if (preq->preq_tcount == 1) { 811195618Srpaulo struct ieee80211_meshpreq_ie ppreq; /* propagated PREQ */ 812195618Srpaulo 813195618Srpaulo memcpy(&ppreq, preq, sizeof(ppreq)); 814195618Srpaulo /* 815195618Srpaulo * We have a valid route to this node. 816195618Srpaulo */ 817195618Srpaulo if (rt != NULL && 818195784Srpaulo (rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID)) { 819195618Srpaulo if (preq->preq_ttl > 1 && 820195618Srpaulo preq->preq_hopcount < hs->hs_maxhops) { 821195618Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 822195784Srpaulo "forward PREQ from %s", 823195618Srpaulo ether_sprintf(preq->preq_origaddr)); 824195618Srpaulo /* 825195618Srpaulo * Propagate the original PREQ. 826195618Srpaulo */ 827195618Srpaulo ppreq.preq_hopcount += 1; 828195618Srpaulo ppreq.preq_ttl -= 1; 829195618Srpaulo ppreq.preq_metric += 830195618Srpaulo ms->ms_pmetric->mpm_metric(ni); 831195618Srpaulo /* 832195618Srpaulo * Set TO and unset RF bits because we are going 833195618Srpaulo * to send a PREP next. 834195618Srpaulo */ 835195618Srpaulo ppreq.preq_targets[0].target_flags |= 836195618Srpaulo IEEE80211_MESHPREQ_TFLAGS_TO; 837195618Srpaulo ppreq.preq_targets[0].target_flags &= 838195618Srpaulo ~IEEE80211_MESHPREQ_TFLAGS_RF; 839195618Srpaulo hwmp_send_preq(ni, vap->iv_myaddr, 840195618Srpaulo broadcastaddr, &ppreq); 841195618Srpaulo } 842195618Srpaulo /* 843195618Srpaulo * Check if we can send an intermediate Path Reply, 844195618Srpaulo * i.e., Target Only bit is not set. 845195618Srpaulo */ 846195618Srpaulo if (!(PREQ_TFLAGS(0) & IEEE80211_MESHPREQ_TFLAGS_TO)) { 847195618Srpaulo struct ieee80211_meshprep_ie prep; 848195618Srpaulo 849195618Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 850195618Srpaulo "intermediate reply for PREQ from %s", 851195618Srpaulo ether_sprintf(preq->preq_origaddr)); 852195618Srpaulo prep.prep_flags = 0; 853195618Srpaulo prep.prep_hopcount = rt->rt_nhops + 1; 854195618Srpaulo prep.prep_ttl = ms->ms_ttl; 855195618Srpaulo IEEE80211_ADDR_COPY(&prep.prep_targetaddr, 856198230Srpaulo PREQ_TADDR(0)); 857195908Srpaulo prep.prep_targetseq = hrorig->hr_seq; 858195618Srpaulo prep.prep_lifetime = preq->preq_lifetime; 859195618Srpaulo prep.prep_metric = rt->rt_metric + 860195618Srpaulo ms->ms_pmetric->mpm_metric(ni); 861195618Srpaulo IEEE80211_ADDR_COPY(&prep.prep_origaddr, 862198230Srpaulo preq->preq_origaddr); 863195908Srpaulo prep.prep_origseq = hrorig->hr_seq; 864195618Srpaulo hwmp_send_prep(ni, vap->iv_myaddr, 865195618Srpaulo broadcastaddr, &prep); 866195618Srpaulo } 867195618Srpaulo /* 868195618Srpaulo * We have no information about this path, 869195618Srpaulo * propagate the PREQ. 870195618Srpaulo */ 871195618Srpaulo } else if (preq->preq_ttl > 1 && 872195618Srpaulo preq->preq_hopcount < hs->hs_maxhops) { 873195784Srpaulo if (rt == NULL) { 874195618Srpaulo rt = ieee80211_mesh_rt_add(vap, PREQ_TADDR(0)); 875195784Srpaulo if (rt == NULL) { 876195784Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, 877195784Srpaulo ni, "unable to add PREQ path to %s", 878195784Srpaulo ether_sprintf(PREQ_TADDR(0))); 879195784Srpaulo vap->iv_stats.is_mesh_rtaddfailed++; 880195784Srpaulo return; 881195784Srpaulo } 882195784Srpaulo } 883195618Srpaulo rt->rt_metric = preq->preq_metric; 884195618Srpaulo rt->rt_lifetime = preq->preq_lifetime; 885195908Srpaulo hrorig = IEEE80211_MESH_ROUTE_PRIV(rt, 886195908Srpaulo struct ieee80211_hwmp_route); 887195908Srpaulo hrorig->hr_seq = preq->preq_origseq; 888195908Srpaulo hrorig->hr_preqid = preq->preq_id; 889195618Srpaulo 890195618Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 891195784Srpaulo "forward PREQ from %s", 892195618Srpaulo ether_sprintf(preq->preq_origaddr)); 893195618Srpaulo ppreq.preq_hopcount += 1; 894195618Srpaulo ppreq.preq_ttl -= 1; 895195618Srpaulo ppreq.preq_metric += ms->ms_pmetric->mpm_metric(ni); 896195618Srpaulo hwmp_send_preq(ni, vap->iv_myaddr, broadcastaddr, 897195618Srpaulo &ppreq); 898195618Srpaulo } 899195618Srpaulo } 900195618Srpaulo 901195618Srpaulo} 902195618Srpaulo#undef PREQ_TFLAGS 903195618Srpaulo#undef PREQ_TADDR 904195618Srpaulo#undef PREQ_TSEQ 905195618Srpaulo 906195618Srpaulostatic int 907195618Srpaulohwmp_send_preq(struct ieee80211_node *ni, 908195618Srpaulo const uint8_t sa[IEEE80211_ADDR_LEN], 909195618Srpaulo const uint8_t da[IEEE80211_ADDR_LEN], 910195618Srpaulo struct ieee80211_meshpreq_ie *preq) 911195618Srpaulo{ 912195618Srpaulo struct ieee80211_hwmp_state *hs = ni->ni_vap->iv_hwmp; 913195618Srpaulo 914195618Srpaulo /* 915195618Srpaulo * Enforce PREQ interval. 916195618Srpaulo */ 917195618Srpaulo if (ratecheck(&hs->hs_lastpreq, &ieee80211_hwmp_preqminint) == 0) 918195618Srpaulo return EALREADY; 919195618Srpaulo getmicrouptime(&hs->hs_lastpreq); 920195618Srpaulo 921195618Srpaulo /* 922195618Srpaulo * mesh preq action frame format 923195618Srpaulo * [6] da 924195618Srpaulo * [6] sa 925195618Srpaulo * [6] addr3 = sa 926195618Srpaulo * [1] action 927195618Srpaulo * [1] category 928195618Srpaulo * [tlv] mesh path request 929195618Srpaulo */ 930195618Srpaulo preq->preq_ie = IEEE80211_ELEMID_MESHPREQ; 931195618Srpaulo return hwmp_send_action(ni, sa, da, (uint8_t *)preq, 932195618Srpaulo sizeof(struct ieee80211_meshpreq_ie)); 933195618Srpaulo} 934195618Srpaulo 935195618Srpaulostatic void 936195618Srpaulohwmp_recv_prep(struct ieee80211vap *vap, struct ieee80211_node *ni, 937195618Srpaulo const struct ieee80211_frame *wh, const struct ieee80211_meshprep_ie *prep) 938195618Srpaulo{ 939195618Srpaulo struct ieee80211_mesh_state *ms = vap->iv_mesh; 940195618Srpaulo struct ieee80211_hwmp_state *hs = vap->iv_hwmp; 941195618Srpaulo struct ieee80211_mesh_route *rt = NULL; 942195618Srpaulo struct ieee80211_hwmp_route *hr; 943195618Srpaulo struct ieee80211com *ic = vap->iv_ic; 944195618Srpaulo struct ifnet *ifp = vap->iv_ifp; 945195618Srpaulo struct mbuf *m, *next; 946195618Srpaulo 947195618Srpaulo /* 948195618Srpaulo * Acceptance criteria: if the corresponding PREQ was not generated 949195618Srpaulo * by us and forwarding is disabled, discard this PREP. 950195618Srpaulo */ 951195618Srpaulo if (ni == vap->iv_bss || 952195618Srpaulo ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED) 953195618Srpaulo return; 954195618Srpaulo if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, prep->prep_origaddr) && 955195618Srpaulo !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) 956195618Srpaulo return; 957195618Srpaulo 958195618Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 959198230Srpaulo "received PREP from %s", ether_sprintf(prep->prep_targetaddr)); 960195618Srpaulo 961198230Srpaulo rt = ieee80211_mesh_rt_find(vap, prep->prep_targetaddr); 962195618Srpaulo if (rt == NULL) { 963195618Srpaulo /* 964195618Srpaulo * If we have no entry this could be a reply to a root PREQ. 965195618Srpaulo */ 966195618Srpaulo if (hs->hs_rootmode != IEEE80211_HWMP_ROOTMODE_DISABLED) { 967198230Srpaulo rt = ieee80211_mesh_rt_add(vap, prep->prep_targetaddr); 968195784Srpaulo if (rt == NULL) { 969195784Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, 970195784Srpaulo ni, "unable to add PREP path to %s", 971198230Srpaulo ether_sprintf(prep->prep_targetaddr)); 972195784Srpaulo vap->iv_stats.is_mesh_rtaddfailed++; 973195784Srpaulo return; 974195784Srpaulo } 975195618Srpaulo IEEE80211_ADDR_COPY(rt->rt_nexthop, wh->i_addr2); 976195618Srpaulo rt->rt_nhops = prep->prep_hopcount; 977195618Srpaulo rt->rt_lifetime = prep->prep_lifetime; 978195618Srpaulo rt->rt_metric = prep->prep_metric; 979195784Srpaulo rt->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID; 980195908Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 981195908Srpaulo "add root path to %s nhops %d metric %d (PREP)", 982198230Srpaulo ether_sprintf(prep->prep_targetaddr), 983195908Srpaulo rt->rt_nhops, rt->rt_metric); 984195618Srpaulo return; 985195618Srpaulo } 986195618Srpaulo return; 987195618Srpaulo } 988195908Srpaulo /* 989195908Srpaulo * Sequence number validation. 990195908Srpaulo */ 991195618Srpaulo hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route); 992198230Srpaulo if (HWMP_SEQ_LEQ(prep->prep_targetseq, hr->hr_seq)) { 993195908Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 994195908Srpaulo "discard PREP from %s, old seq no %u <= %u", 995198230Srpaulo ether_sprintf(prep->prep_targetaddr), 996198230Srpaulo prep->prep_targetseq, hr->hr_seq); 997195908Srpaulo return; 998195908Srpaulo } 999198230Srpaulo hr->hr_seq = prep->prep_targetseq; 1000195908Srpaulo /* 1001195908Srpaulo * If it's NOT for us, propagate the PREP. 1002195908Srpaulo */ 1003198230Srpaulo if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, prep->prep_origaddr) && 1004195908Srpaulo prep->prep_ttl > 1 && prep->prep_hopcount < hs->hs_maxhops) { 1005195908Srpaulo struct ieee80211_meshprep_ie pprep; /* propagated PREP */ 1006195908Srpaulo 1007195908Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 1008195908Srpaulo "propagate PREP from %s", 1009198230Srpaulo ether_sprintf(prep->prep_targetaddr)); 1010195908Srpaulo 1011195908Srpaulo memcpy(&pprep, prep, sizeof(pprep)); 1012195908Srpaulo pprep.prep_hopcount += 1; 1013195908Srpaulo pprep.prep_ttl -= 1; 1014195908Srpaulo pprep.prep_metric += ms->ms_pmetric->mpm_metric(ni); 1015198230Srpaulo IEEE80211_ADDR_COPY(pprep.prep_targetaddr, vap->iv_myaddr); 1016195908Srpaulo hwmp_send_prep(ni, vap->iv_myaddr, broadcastaddr, &pprep); 1017195908Srpaulo } 1018195908Srpaulo hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route); 1019195908Srpaulo if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY) { 1020195908Srpaulo /* NB: never clobber a proxy entry */; 1021195908Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 1022195908Srpaulo "discard PREP for %s, route is marked PROXY", 1023198230Srpaulo ether_sprintf(prep->prep_targetaddr)); 1024195908Srpaulo vap->iv_stats.is_hwmp_proxy++; 1025198230Srpaulo } else if (prep->prep_origseq == hr->hr_origseq) { 1026195618Srpaulo /* 1027195618Srpaulo * Check if we already have a path to this node. 1028195618Srpaulo * If we do, check if this path reply contains a 1029195618Srpaulo * better route. 1030195618Srpaulo */ 1031195908Srpaulo if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0 || 1032195908Srpaulo (prep->prep_hopcount < rt->rt_nhops || 1033195908Srpaulo prep->prep_metric < rt->rt_metric)) { 1034195908Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 1035195908Srpaulo "%s path to %s, hopcount %d:%d metric %d:%d", 1036195908Srpaulo rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID ? 1037195908Srpaulo "prefer" : "update", 1038195908Srpaulo ether_sprintf(prep->prep_origaddr), 1039195908Srpaulo rt->rt_nhops, prep->prep_hopcount, 1040195908Srpaulo rt->rt_metric, prep->prep_metric); 1041195618Srpaulo IEEE80211_ADDR_COPY(rt->rt_nexthop, wh->i_addr2); 1042195618Srpaulo rt->rt_nhops = prep->prep_hopcount; 1043195618Srpaulo rt->rt_lifetime = prep->prep_lifetime; 1044195618Srpaulo rt->rt_metric = prep->prep_metric; 1045195784Srpaulo rt->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID; 1046195908Srpaulo } else { 1047195908Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 1048195908Srpaulo "ignore PREP for %s, hopcount %d:%d metric %d:%d", 1049198230Srpaulo ether_sprintf(prep->prep_targetaddr), 1050195908Srpaulo rt->rt_nhops, prep->prep_hopcount, 1051195908Srpaulo rt->rt_metric, prep->prep_metric); 1052195618Srpaulo } 1053195618Srpaulo } else { 1054195618Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 1055195908Srpaulo "discard PREP for %s, wrong seqno %u != %u", 1056198230Srpaulo ether_sprintf(prep->prep_targetaddr), prep->prep_origseq, 1057195618Srpaulo hr->hr_seq); 1058195618Srpaulo vap->iv_stats.is_hwmp_wrongseq++; 1059195618Srpaulo } 1060195618Srpaulo /* 1061195618Srpaulo * Check for frames queued awaiting path discovery. 1062195618Srpaulo * XXX probably can tell exactly and avoid remove call 1063195618Srpaulo * NB: hash may have false matches, if so they will get 1064195618Srpaulo * stuck back on the stageq because there won't be 1065195618Srpaulo * a path. 1066195618Srpaulo */ 1067195618Srpaulo m = ieee80211_ageq_remove(&ic->ic_stageq, 1068195618Srpaulo (struct ieee80211_node *)(uintptr_t) 1069195618Srpaulo ieee80211_mac_hash(ic, rt->rt_dest)); 1070195618Srpaulo for (; m != NULL; m = next) { 1071195618Srpaulo next = m->m_nextpkt; 1072195618Srpaulo m->m_nextpkt = NULL; 1073195784Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 1074195784Srpaulo "flush queued frame %p len %d", m, m->m_pkthdr.len); 1075195618Srpaulo ifp->if_transmit(ifp, m); 1076195618Srpaulo } 1077195618Srpaulo} 1078195618Srpaulo 1079195618Srpaulostatic int 1080195618Srpaulohwmp_send_prep(struct ieee80211_node *ni, 1081195618Srpaulo const uint8_t sa[IEEE80211_ADDR_LEN], 1082195618Srpaulo const uint8_t da[IEEE80211_ADDR_LEN], 1083195618Srpaulo struct ieee80211_meshprep_ie *prep) 1084195618Srpaulo{ 1085195784Srpaulo /* NB: there's no PREP minimum interval. */ 1086195618Srpaulo 1087195618Srpaulo /* 1088195618Srpaulo * mesh prep action frame format 1089195618Srpaulo * [6] da 1090195618Srpaulo * [6] sa 1091195618Srpaulo * [6] addr3 = sa 1092195618Srpaulo * [1] action 1093195618Srpaulo * [1] category 1094195618Srpaulo * [tlv] mesh path reply 1095195618Srpaulo */ 1096195618Srpaulo prep->prep_ie = IEEE80211_ELEMID_MESHPREP; 1097195618Srpaulo return hwmp_send_action(ni, sa, da, (uint8_t *)prep, 1098195618Srpaulo sizeof(struct ieee80211_meshprep_ie)); 1099195618Srpaulo} 1100195618Srpaulo 1101197413Srpaulo#define PERR_DFLAGS(n) perr.perr_dests[n].dest_flags 1102195618Srpaulo#define PERR_DADDR(n) perr.perr_dests[n].dest_addr 1103195618Srpaulo#define PERR_DSEQ(n) perr.perr_dests[n].dest_seq 1104197413Srpaulo#define PERR_DRCODE(n) perr.perr_dests[n].dest_rcode 1105195618Srpaulostatic void 1106195618Srpaulohwmp_peerdown(struct ieee80211_node *ni) 1107195618Srpaulo{ 1108195618Srpaulo struct ieee80211vap *vap = ni->ni_vap; 1109197413Srpaulo struct ieee80211_mesh_state *ms = vap->iv_mesh; 1110195618Srpaulo struct ieee80211_meshperr_ie perr; 1111195618Srpaulo struct ieee80211_mesh_route *rt; 1112195618Srpaulo struct ieee80211_hwmp_route *hr; 1113195618Srpaulo 1114195618Srpaulo rt = ieee80211_mesh_rt_find(vap, ni->ni_macaddr); 1115195618Srpaulo if (rt == NULL) 1116195618Srpaulo return; 1117195618Srpaulo hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route); 1118195618Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 1119195784Srpaulo "%s", "delete route entry"); 1120197413Srpaulo perr.perr_ttl = ms->ms_ttl; 1121195618Srpaulo perr.perr_ndests = 1; 1122198260Srpaulo PERR_DFLAGS(0) = 0; 1123197413Srpaulo if (hr->hr_seq == 0) 1124197413Srpaulo PERR_DFLAGS(0) |= IEEE80211_MESHPERR_DFLAGS_USN; 1125197413Srpaulo PERR_DFLAGS(0) |= IEEE80211_MESHPERR_DFLAGS_RC; 1126195618Srpaulo IEEE80211_ADDR_COPY(PERR_DADDR(0), rt->rt_dest); 1127195618Srpaulo PERR_DSEQ(0) = hr->hr_seq; 1128197413Srpaulo PERR_DRCODE(0) = IEEE80211_REASON_MESH_PERR_DEST_UNREACH; 1129195908Srpaulo /* NB: flush everything passing through peer */ 1130195908Srpaulo ieee80211_mesh_rt_flush_peer(vap, ni->ni_macaddr); 1131195618Srpaulo hwmp_send_perr(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &perr); 1132195618Srpaulo} 1133197413Srpaulo#undef PERR_DFLAGS 1134195618Srpaulo#undef PERR_DADDR 1135195618Srpaulo#undef PERR_DSEQ 1136197413Srpaulo#undef PERR_DRCODE 1137195618Srpaulo 1138197413Srpaulo#define PERR_DFLAGS(n) perr->perr_dests[n].dest_flags 1139195618Srpaulo#define PERR_DADDR(n) perr->perr_dests[n].dest_addr 1140195618Srpaulo#define PERR_DSEQ(n) perr->perr_dests[n].dest_seq 1141197413Srpaulo#define PERR_DRCODE(n) perr->perr_dests[n].dest_rcode 1142195618Srpaulostatic void 1143195618Srpaulohwmp_recv_perr(struct ieee80211vap *vap, struct ieee80211_node *ni, 1144195618Srpaulo const struct ieee80211_frame *wh, const struct ieee80211_meshperr_ie *perr) 1145195618Srpaulo{ 1146195618Srpaulo struct ieee80211_mesh_state *ms = vap->iv_mesh; 1147195618Srpaulo struct ieee80211_mesh_route *rt = NULL; 1148195618Srpaulo struct ieee80211_hwmp_route *hr; 1149195618Srpaulo struct ieee80211_meshperr_ie pperr; 1150195618Srpaulo int i, forward = 0; 1151195618Srpaulo 1152195618Srpaulo /* 1153195618Srpaulo * Acceptance criteria: check if we received a PERR from a 1154195618Srpaulo * neighbor and forwarding is enabled. 1155195618Srpaulo */ 1156195618Srpaulo if (ni == vap->iv_bss || 1157195618Srpaulo ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED || 1158195618Srpaulo !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) 1159195618Srpaulo return; 1160195618Srpaulo /* 1161195618Srpaulo * Find all routing entries that match and delete them. 1162195618Srpaulo */ 1163195618Srpaulo for (i = 0; i < perr->perr_ndests; i++) { 1164195618Srpaulo rt = ieee80211_mesh_rt_find(vap, PERR_DADDR(i)); 1165195618Srpaulo if (rt == NULL) 1166195618Srpaulo continue; 1167197413Srpaulo hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route); 1168197413Srpaulo if (!(PERR_DFLAGS(0) & IEEE80211_MESHPERR_DFLAGS_USN) && 1169197413Srpaulo HWMP_SEQ_GEQ(PERR_DSEQ(i), hr->hr_seq)) { 1170195618Srpaulo ieee80211_mesh_rt_del(vap, rt->rt_dest); 1171195908Srpaulo ieee80211_mesh_rt_flush_peer(vap, rt->rt_dest); 1172195618Srpaulo rt = NULL; 1173195618Srpaulo forward = 1; 1174195618Srpaulo } 1175195618Srpaulo } 1176195618Srpaulo /* 1177195618Srpaulo * Propagate the PERR if we previously found it on our routing table. 1178195618Srpaulo * XXX handle ndest > 1 1179195618Srpaulo */ 1180197413Srpaulo if (forward && perr->perr_ttl > 1) { 1181195618Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 1182195784Srpaulo "propagate PERR from %s", ether_sprintf(wh->i_addr2)); 1183195618Srpaulo memcpy(&pperr, perr, sizeof(*perr)); 1184197413Srpaulo pperr.perr_ttl--; 1185195908Srpaulo hwmp_send_perr(vap->iv_bss, vap->iv_myaddr, broadcastaddr, 1186195908Srpaulo &pperr); 1187195618Srpaulo } 1188195618Srpaulo} 1189195618Srpaulo#undef PEER_DADDR 1190195618Srpaulo#undef PERR_DSEQ 1191195618Srpaulo 1192195618Srpaulostatic int 1193195618Srpaulohwmp_send_perr(struct ieee80211_node *ni, 1194195618Srpaulo const uint8_t sa[IEEE80211_ADDR_LEN], 1195195618Srpaulo const uint8_t da[IEEE80211_ADDR_LEN], 1196195618Srpaulo struct ieee80211_meshperr_ie *perr) 1197195618Srpaulo{ 1198195618Srpaulo struct ieee80211_hwmp_state *hs = ni->ni_vap->iv_hwmp; 1199195618Srpaulo 1200195618Srpaulo /* 1201195618Srpaulo * Enforce PERR interval. 1202195618Srpaulo */ 1203195618Srpaulo if (ratecheck(&hs->hs_lastperr, &ieee80211_hwmp_perrminint) == 0) 1204195618Srpaulo return EALREADY; 1205195618Srpaulo getmicrouptime(&hs->hs_lastperr); 1206195618Srpaulo 1207195618Srpaulo /* 1208195618Srpaulo * mesh perr action frame format 1209195618Srpaulo * [6] da 1210195618Srpaulo * [6] sa 1211195618Srpaulo * [6] addr3 = sa 1212195618Srpaulo * [1] action 1213195618Srpaulo * [1] category 1214195618Srpaulo * [tlv] mesh path error 1215195618Srpaulo */ 1216195618Srpaulo perr->perr_ie = IEEE80211_ELEMID_MESHPERR; 1217195618Srpaulo return hwmp_send_action(ni, sa, da, (uint8_t *)perr, 1218195618Srpaulo sizeof(struct ieee80211_meshperr_ie)); 1219195618Srpaulo} 1220195618Srpaulo 1221195618Srpaulostatic void 1222195618Srpaulohwmp_recv_rann(struct ieee80211vap *vap, struct ieee80211_node *ni, 1223195618Srpaulo const struct ieee80211_frame *wh, const struct ieee80211_meshrann_ie *rann) 1224195618Srpaulo{ 1225195618Srpaulo struct ieee80211_mesh_state *ms = vap->iv_mesh; 1226195618Srpaulo struct ieee80211_hwmp_state *hs = vap->iv_hwmp; 1227195618Srpaulo struct ieee80211_mesh_route *rt = NULL; 1228195618Srpaulo struct ieee80211_hwmp_route *hr; 1229195618Srpaulo struct ieee80211_meshrann_ie prann; 1230195618Srpaulo 1231195618Srpaulo if (ni == vap->iv_bss || 1232198230Srpaulo ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED || 1233198230Srpaulo IEEE80211_ADDR_EQ(rann->rann_addr, vap->iv_myaddr)) 1234195618Srpaulo return; 1235195618Srpaulo 1236195618Srpaulo rt = ieee80211_mesh_rt_find(vap, rann->rann_addr); 1237195618Srpaulo /* 1238195618Srpaulo * Discover the path to the root mesh STA. 1239195618Srpaulo * If we already know it, propagate the RANN element. 1240195618Srpaulo */ 1241195618Srpaulo if (rt == NULL) { 1242195618Srpaulo hwmp_discover(vap, rann->rann_addr, NULL); 1243195618Srpaulo return; 1244195618Srpaulo } 1245195618Srpaulo hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route); 1246198581Srpaulo if (HWMP_SEQ_GT(rann->rann_seq, hr->hr_seq)) { 1247198581Srpaulo hr->hr_seq = rann->rann_seq; 1248198581Srpaulo if (rann->rann_ttl > 1 && 1249198581Srpaulo rann->rann_hopcount < hs->hs_maxhops && 1250198581Srpaulo (ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) { 1251198581Srpaulo memcpy(&prann, rann, sizeof(prann)); 1252198581Srpaulo prann.rann_hopcount += 1; 1253198581Srpaulo prann.rann_ttl -= 1; 1254198581Srpaulo prann.rann_metric += ms->ms_pmetric->mpm_metric(ni); 1255198581Srpaulo hwmp_send_rann(vap->iv_bss, vap->iv_myaddr, 1256198581Srpaulo broadcastaddr, &prann); 1257198581Srpaulo } 1258195618Srpaulo } 1259195618Srpaulo} 1260195618Srpaulo 1261195618Srpaulostatic int 1262195618Srpaulohwmp_send_rann(struct ieee80211_node *ni, 1263195618Srpaulo const uint8_t sa[IEEE80211_ADDR_LEN], 1264195618Srpaulo const uint8_t da[IEEE80211_ADDR_LEN], 1265195618Srpaulo struct ieee80211_meshrann_ie *rann) 1266195618Srpaulo{ 1267195618Srpaulo /* 1268195618Srpaulo * mesh rann action frame format 1269195618Srpaulo * [6] da 1270195618Srpaulo * [6] sa 1271195618Srpaulo * [6] addr3 = sa 1272195618Srpaulo * [1] action 1273195618Srpaulo * [1] category 1274195618Srpaulo * [tlv] root annoucement 1275195618Srpaulo */ 1276195618Srpaulo rann->rann_ie = IEEE80211_ELEMID_MESHRANN; 1277195618Srpaulo return hwmp_send_action(ni, sa, da, (uint8_t *)rann, 1278195618Srpaulo sizeof(struct ieee80211_meshrann_ie)); 1279195618Srpaulo} 1280195618Srpaulo 1281195618Srpaulo#define PREQ_TFLAGS(n) preq.preq_targets[n].target_flags 1282195618Srpaulo#define PREQ_TADDR(n) preq.preq_targets[n].target_addr 1283195618Srpaulo#define PREQ_TSEQ(n) preq.preq_targets[n].target_seq 1284195618Srpaulostatic struct ieee80211_node * 1285195618Srpaulohwmp_discover(struct ieee80211vap *vap, 1286195618Srpaulo const uint8_t dest[IEEE80211_ADDR_LEN], struct mbuf *m) 1287195618Srpaulo{ 1288195618Srpaulo struct ieee80211_hwmp_state *hs = vap->iv_hwmp; 1289195618Srpaulo struct ieee80211_mesh_state *ms = vap->iv_mesh; 1290195618Srpaulo struct ieee80211_mesh_route *rt = NULL; 1291195618Srpaulo struct ieee80211_hwmp_route *hr; 1292195618Srpaulo struct ieee80211_meshpreq_ie preq; 1293195618Srpaulo struct ieee80211_node *ni; 1294195618Srpaulo int sendpreq = 0; 1295195618Srpaulo 1296195618Srpaulo KASSERT(vap->iv_opmode == IEEE80211_M_MBSS, 1297195618Srpaulo ("not a mesh vap, opmode %d", vap->iv_opmode)); 1298195618Srpaulo 1299195618Srpaulo KASSERT(!IEEE80211_ADDR_EQ(vap->iv_myaddr, dest), 1300195618Srpaulo ("%s: discovering self!", __func__)); 1301195618Srpaulo 1302195618Srpaulo ni = NULL; 1303195618Srpaulo if (!IEEE80211_IS_MULTICAST(dest)) { 1304195618Srpaulo rt = ieee80211_mesh_rt_find(vap, dest); 1305195618Srpaulo if (rt == NULL) { 1306195618Srpaulo rt = ieee80211_mesh_rt_add(vap, dest); 1307195618Srpaulo if (rt == NULL) { 1308195784Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, 1309195784Srpaulo ni, "unable to add discovery path to %s", 1310195784Srpaulo ether_sprintf(dest)); 1311195784Srpaulo vap->iv_stats.is_mesh_rtaddfailed++; 1312195618Srpaulo goto done; 1313195618Srpaulo } 1314195618Srpaulo } 1315195618Srpaulo hr = IEEE80211_MESH_ROUTE_PRIV(rt, 1316195618Srpaulo struct ieee80211_hwmp_route); 1317195784Srpaulo if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0) { 1318198230Srpaulo if (hr->hr_origseq == 0) 1319198230Srpaulo hr->hr_origseq = ++hs->hs_seq; 1320195618Srpaulo rt->rt_metric = IEEE80211_MESHLMETRIC_INITIALVAL; 1321195618Srpaulo rt->rt_lifetime = 1322195813Ssam ticks_to_msecs(ieee80211_hwmp_pathtimeout); 1323195618Srpaulo /* XXX check preq retries */ 1324195618Srpaulo sendpreq = 1; 1325195618Srpaulo IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP, dest, 1326195784Srpaulo "start path discovery (src %s)", 1327195784Srpaulo m == NULL ? "<none>" : ether_sprintf( 1328195784Srpaulo mtod(m, struct ether_header *)->ether_shost)); 1329195618Srpaulo /* 1330195618Srpaulo * Try to discover the path for this node. 1331195618Srpaulo */ 1332195618Srpaulo preq.preq_flags = 0; 1333195618Srpaulo preq.preq_hopcount = 0; 1334195618Srpaulo preq.preq_ttl = ms->ms_ttl; 1335195908Srpaulo preq.preq_id = ++hs->hs_preqid; 1336195618Srpaulo IEEE80211_ADDR_COPY(preq.preq_origaddr, vap->iv_myaddr); 1337198230Srpaulo preq.preq_origseq = hr->hr_origseq; 1338195618Srpaulo preq.preq_lifetime = rt->rt_lifetime; 1339195618Srpaulo preq.preq_metric = rt->rt_metric; 1340195618Srpaulo preq.preq_tcount = 1; 1341195618Srpaulo IEEE80211_ADDR_COPY(PREQ_TADDR(0), dest); 1342195618Srpaulo PREQ_TFLAGS(0) = 0; 1343195618Srpaulo if (ieee80211_hwmp_targetonly) 1344195618Srpaulo PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_TO; 1345195618Srpaulo if (ieee80211_hwmp_replyforward) 1346195618Srpaulo PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_RF; 1347195618Srpaulo PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_USN; 1348195618Srpaulo PREQ_TSEQ(0) = 0; 1349195618Srpaulo /* XXX check return value */ 1350195618Srpaulo hwmp_send_preq(vap->iv_bss, vap->iv_myaddr, 1351195618Srpaulo broadcastaddr, &preq); 1352195618Srpaulo } 1353195784Srpaulo if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) 1354195618Srpaulo ni = ieee80211_find_txnode(vap, rt->rt_nexthop); 1355195618Srpaulo } else { 1356195618Srpaulo ni = ieee80211_find_txnode(vap, dest); 1357195784Srpaulo /* NB: if null then we leak mbuf */ 1358195784Srpaulo KASSERT(ni != NULL, ("leak mcast frame")); 1359195618Srpaulo return ni; 1360195618Srpaulo } 1361195618Srpaulodone: 1362195618Srpaulo if (ni == NULL && m != NULL) { 1363195618Srpaulo if (sendpreq) { 1364195618Srpaulo struct ieee80211com *ic = vap->iv_ic; 1365195618Srpaulo /* 1366195618Srpaulo * Queue packet for transmit when path discovery 1367195618Srpaulo * completes. If discovery never completes the 1368195618Srpaulo * frame will be flushed by way of the aging timer. 1369195618Srpaulo */ 1370195784Srpaulo IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP, dest, 1371195784Srpaulo "%s", "queue frame until path found"); 1372195618Srpaulo m->m_pkthdr.rcvif = (void *)(uintptr_t) 1373195618Srpaulo ieee80211_mac_hash(ic, dest); 1374195618Srpaulo /* XXX age chosen randomly */ 1375195618Srpaulo ieee80211_ageq_append(&ic->ic_stageq, m, 1376195618Srpaulo IEEE80211_INACT_WAIT); 1377195784Srpaulo } else { 1378195784Srpaulo IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_HWMP, 1379195784Srpaulo dest, NULL, "%s", "no valid path to this node"); 1380195618Srpaulo m_freem(m); 1381195784Srpaulo } 1382195618Srpaulo } 1383195618Srpaulo return ni; 1384195618Srpaulo} 1385195618Srpaulo#undef PREQ_TFLAGS 1386195618Srpaulo#undef PREQ_TADDR 1387195618Srpaulo#undef PREQ_TSEQ 1388195618Srpaulo 1389195618Srpaulostatic int 1390195618Srpaulohwmp_ioctl_get80211(struct ieee80211vap *vap, struct ieee80211req *ireq) 1391195618Srpaulo{ 1392195618Srpaulo struct ieee80211_hwmp_state *hs = vap->iv_hwmp; 1393195618Srpaulo int error; 1394195618Srpaulo 1395195618Srpaulo if (vap->iv_opmode != IEEE80211_M_MBSS) 1396195618Srpaulo return ENOSYS; 1397195618Srpaulo error = 0; 1398195618Srpaulo switch (ireq->i_type) { 1399195618Srpaulo case IEEE80211_IOC_HWMP_ROOTMODE: 1400195618Srpaulo ireq->i_val = hs->hs_rootmode; 1401195618Srpaulo break; 1402195618Srpaulo case IEEE80211_IOC_HWMP_MAXHOPS: 1403195618Srpaulo ireq->i_val = hs->hs_maxhops; 1404195618Srpaulo break; 1405195618Srpaulo default: 1406195618Srpaulo return ENOSYS; 1407195618Srpaulo } 1408195618Srpaulo return error; 1409195618Srpaulo} 1410195618SrpauloIEEE80211_IOCTL_GET(hwmp, hwmp_ioctl_get80211); 1411195618Srpaulo 1412195618Srpaulostatic int 1413195618Srpaulohwmp_ioctl_set80211(struct ieee80211vap *vap, struct ieee80211req *ireq) 1414195618Srpaulo{ 1415195618Srpaulo struct ieee80211_hwmp_state *hs = vap->iv_hwmp; 1416195618Srpaulo int error; 1417195618Srpaulo 1418195618Srpaulo if (vap->iv_opmode != IEEE80211_M_MBSS) 1419195618Srpaulo return ENOSYS; 1420195618Srpaulo error = 0; 1421195618Srpaulo switch (ireq->i_type) { 1422195618Srpaulo case IEEE80211_IOC_HWMP_ROOTMODE: 1423195618Srpaulo if (ireq->i_val < 0 || ireq->i_val > 3) 1424195618Srpaulo return EINVAL; 1425195618Srpaulo hs->hs_rootmode = ireq->i_val; 1426195618Srpaulo hwmp_rootmode_setup(vap); 1427195618Srpaulo break; 1428195618Srpaulo case IEEE80211_IOC_HWMP_MAXHOPS: 1429195618Srpaulo if (ireq->i_val <= 0 || ireq->i_val > 255) 1430195618Srpaulo return EINVAL; 1431195618Srpaulo hs->hs_maxhops = ireq->i_val; 1432195618Srpaulo break; 1433195618Srpaulo default: 1434195618Srpaulo return ENOSYS; 1435195618Srpaulo } 1436195618Srpaulo return error; 1437195618Srpaulo} 1438195618SrpauloIEEE80211_IOCTL_SET(hwmp, hwmp_ioctl_set80211); 1439