ieee80211_hwmp.c revision 198260
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: head/sys/net80211/ieee80211_hwmp.c 198260 2009-10-19 23:11:43Z rpaulo $"); 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 164195618SrpauloSYSCTL_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 656195908Srpaulo if (ms->ms_flags & IEEE80211_MESHFLAGS_PORTAL) 657195908Srpaulo rann.rann_flags |= IEEE80211_MESHRANN_FLAGS_PR; 658195618Srpaulo rann.rann_hopcount = 0; 659195618Srpaulo rann.rann_ttl = ms->ms_ttl; 660195618Srpaulo IEEE80211_ADDR_COPY(rann.rann_addr, vap->iv_myaddr); 661195618Srpaulo rann.rann_seq = ++hs->hs_seq; 662195618Srpaulo rann.rann_metric = IEEE80211_MESHLMETRIC_INITIALVAL; 663195618Srpaulo 664195618Srpaulo vap->iv_stats.is_hwmp_rootrann++; 665195618Srpaulo hwmp_send_rann(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &rann); 666195618Srpaulo hwmp_rootmode_setup(vap); 667195618Srpaulo} 668195618Srpaulo 669195618Srpaulo#define PREQ_TFLAGS(n) preq->preq_targets[n].target_flags 670195618Srpaulo#define PREQ_TADDR(n) preq->preq_targets[n].target_addr 671195618Srpaulo#define PREQ_TSEQ(n) preq->preq_targets[n].target_seq 672195618Srpaulostatic void 673195618Srpaulohwmp_recv_preq(struct ieee80211vap *vap, struct ieee80211_node *ni, 674195618Srpaulo const struct ieee80211_frame *wh, const struct ieee80211_meshpreq_ie *preq) 675195618Srpaulo{ 676195618Srpaulo struct ieee80211_mesh_state *ms = vap->iv_mesh; 677195618Srpaulo struct ieee80211_mesh_route *rt = NULL; 678195908Srpaulo struct ieee80211_mesh_route *rtorig = NULL; 679195908Srpaulo struct ieee80211_hwmp_route *hrorig; 680195618Srpaulo struct ieee80211_hwmp_state *hs = vap->iv_hwmp; 681195618Srpaulo struct ieee80211_meshprep_ie prep; 682195618Srpaulo 683195618Srpaulo if (ni == vap->iv_bss || 684195618Srpaulo ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED) 685195618Srpaulo return; 686195618Srpaulo /* 687195618Srpaulo * Ignore PREQs from us. Could happen because someone forward it 688195618Srpaulo * back to us. 689195618Srpaulo */ 690195618Srpaulo if (IEEE80211_ADDR_EQ(vap->iv_myaddr, preq->preq_origaddr)) 691195618Srpaulo return; 692195618Srpaulo 693195618Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 694195618Srpaulo "received PREQ, source %s", ether_sprintf(preq->preq_origaddr)); 695195618Srpaulo 696195618Srpaulo /* 697195618Srpaulo * Acceptance criteria: if the PREQ is not for us and 698195618Srpaulo * forwarding is disabled, discard this PREQ. 699195618Srpaulo */ 700195618Srpaulo if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0)) && 701195618Srpaulo !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) { 702195618Srpaulo IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_HWMP, 703195618Srpaulo preq->preq_origaddr, NULL, "%s", "not accepting PREQ"); 704195618Srpaulo return; 705195618Srpaulo } 706195908Srpaulo rtorig = ieee80211_mesh_rt_find(vap, preq->preq_origaddr); 707195908Srpaulo if (rtorig == NULL) 708195908Srpaulo rtorig = ieee80211_mesh_rt_add(vap, preq->preq_origaddr); 709195908Srpaulo hrorig = IEEE80211_MESH_ROUTE_PRIV(rtorig, struct ieee80211_hwmp_route); 710195618Srpaulo /* 711195908Srpaulo * Sequence number validation. 712195908Srpaulo */ 713195908Srpaulo if (HWMP_SEQ_LEQ(preq->preq_id, hrorig->hr_preqid) && 714195908Srpaulo HWMP_SEQ_LEQ(preq->preq_origseq, hrorig->hr_seq)) { 715195908Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 716195908Srpaulo "discard PREQ from %s, old seq no %u <= %u", 717195908Srpaulo ether_sprintf(preq->preq_origaddr), 718195908Srpaulo preq->preq_origseq, hrorig->hr_seq); 719195908Srpaulo return; 720195908Srpaulo } 721195908Srpaulo hrorig->hr_preqid = preq->preq_id; 722195908Srpaulo hrorig->hr_seq = preq->preq_origseq; 723195908Srpaulo 724195908Srpaulo /* 725195618Srpaulo * Check if the PREQ is addressed to us. 726195618Srpaulo */ 727195618Srpaulo if (IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0))) { 728195618Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 729195784Srpaulo "reply to %s", ether_sprintf(preq->preq_origaddr)); 730195618Srpaulo /* 731195618Srpaulo * Build and send a PREP frame. 732195618Srpaulo */ 733195618Srpaulo prep.prep_flags = 0; 734195618Srpaulo prep.prep_hopcount = 0; 735195618Srpaulo prep.prep_ttl = ms->ms_ttl; 736198230Srpaulo IEEE80211_ADDR_COPY(prep.prep_targetaddr, vap->iv_myaddr); 737198230Srpaulo prep.prep_targetseq = ++hs->hs_seq; 738195618Srpaulo prep.prep_lifetime = preq->preq_lifetime; 739195618Srpaulo prep.prep_metric = IEEE80211_MESHLMETRIC_INITIALVAL; 740198230Srpaulo IEEE80211_ADDR_COPY(prep.prep_origaddr, preq->preq_origaddr); 741198230Srpaulo prep.prep_origseq = preq->preq_origseq; 742195618Srpaulo hwmp_send_prep(ni, vap->iv_myaddr, wh->i_addr2, &prep); 743195618Srpaulo /* 744195618Srpaulo * Build the reverse path, if we don't have it already. 745195618Srpaulo */ 746195618Srpaulo rt = ieee80211_mesh_rt_find(vap, preq->preq_origaddr); 747195618Srpaulo if (rt == NULL) 748195618Srpaulo hwmp_discover(vap, preq->preq_origaddr, NULL); 749195784Srpaulo else if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0) 750195618Srpaulo hwmp_discover(vap, rt->rt_dest, NULL); 751195618Srpaulo return; 752195618Srpaulo } 753195618Srpaulo /* 754195618Srpaulo * Proactive PREQ: reply with a proactive PREP to the 755195618Srpaulo * root STA if requested. 756195618Srpaulo */ 757195618Srpaulo if (IEEE80211_ADDR_EQ(PREQ_TADDR(0), broadcastaddr) && 758195618Srpaulo (PREQ_TFLAGS(0) & 759195618Srpaulo ((IEEE80211_MESHPREQ_TFLAGS_TO|IEEE80211_MESHPREQ_TFLAGS_RF) == 760195618Srpaulo (IEEE80211_MESHPREQ_TFLAGS_TO|IEEE80211_MESHPREQ_TFLAGS_RF)))) { 761195618Srpaulo uint8_t rootmac[IEEE80211_ADDR_LEN]; 762195618Srpaulo 763195618Srpaulo IEEE80211_ADDR_COPY(rootmac, preq->preq_origaddr); 764195618Srpaulo rt = ieee80211_mesh_rt_find(vap, rootmac); 765195784Srpaulo if (rt == NULL) { 766195618Srpaulo rt = ieee80211_mesh_rt_add(vap, rootmac); 767195784Srpaulo if (rt == NULL) { 768195784Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 769195784Srpaulo "unable to add root mesh path to %s", 770195784Srpaulo ether_sprintf(rootmac)); 771195784Srpaulo vap->iv_stats.is_mesh_rtaddfailed++; 772195784Srpaulo return; 773195784Srpaulo } 774195784Srpaulo } 775195784Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 776195784Srpaulo "root mesh station @ %s", ether_sprintf(rootmac)); 777195784Srpaulo 778195618Srpaulo /* 779195618Srpaulo * Reply with a PREP if we don't have a path to the root 780195618Srpaulo * or if the root sent us a proactive PREQ. 781195618Srpaulo */ 782195784Srpaulo if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0 || 783195618Srpaulo (preq->preq_flags & IEEE80211_MESHPREQ_FLAGS_PP)) { 784195618Srpaulo prep.prep_flags = 0; 785195618Srpaulo prep.prep_hopcount = 0; 786195618Srpaulo prep.prep_ttl = ms->ms_ttl; 787198230Srpaulo IEEE80211_ADDR_COPY(prep.prep_origaddr, rootmac); 788195618Srpaulo prep.prep_origseq = preq->preq_origseq; 789195618Srpaulo prep.prep_lifetime = preq->preq_lifetime; 790195618Srpaulo prep.prep_metric = IEEE80211_MESHLMETRIC_INITIALVAL; 791198230Srpaulo IEEE80211_ADDR_COPY(prep.prep_targetaddr, 792198230Srpaulo vap->iv_myaddr); 793198230Srpaulo prep.prep_targetseq = ++hs->hs_seq; 794195618Srpaulo hwmp_send_prep(vap->iv_bss, vap->iv_myaddr, 795195618Srpaulo broadcastaddr, &prep); 796195618Srpaulo } 797195618Srpaulo hwmp_discover(vap, rootmac, NULL); 798195618Srpaulo return; 799195618Srpaulo } 800195618Srpaulo rt = ieee80211_mesh_rt_find(vap, PREQ_TADDR(0)); 801195618Srpaulo 802195618Srpaulo /* 803195618Srpaulo * Forwarding and Intermediate reply for PREQs with 1 target. 804195618Srpaulo */ 805195618Srpaulo if (preq->preq_tcount == 1) { 806195618Srpaulo struct ieee80211_meshpreq_ie ppreq; /* propagated PREQ */ 807195618Srpaulo 808195618Srpaulo memcpy(&ppreq, preq, sizeof(ppreq)); 809195618Srpaulo /* 810195618Srpaulo * We have a valid route to this node. 811195618Srpaulo */ 812195618Srpaulo if (rt != NULL && 813195784Srpaulo (rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID)) { 814195618Srpaulo if (preq->preq_ttl > 1 && 815195618Srpaulo preq->preq_hopcount < hs->hs_maxhops) { 816195618Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 817195784Srpaulo "forward PREQ from %s", 818195618Srpaulo ether_sprintf(preq->preq_origaddr)); 819195618Srpaulo /* 820195618Srpaulo * Propagate the original PREQ. 821195618Srpaulo */ 822195618Srpaulo ppreq.preq_hopcount += 1; 823195618Srpaulo ppreq.preq_ttl -= 1; 824195618Srpaulo ppreq.preq_metric += 825195618Srpaulo ms->ms_pmetric->mpm_metric(ni); 826195618Srpaulo /* 827195618Srpaulo * Set TO and unset RF bits because we are going 828195618Srpaulo * to send a PREP next. 829195618Srpaulo */ 830195618Srpaulo ppreq.preq_targets[0].target_flags |= 831195618Srpaulo IEEE80211_MESHPREQ_TFLAGS_TO; 832195618Srpaulo ppreq.preq_targets[0].target_flags &= 833195618Srpaulo ~IEEE80211_MESHPREQ_TFLAGS_RF; 834195618Srpaulo hwmp_send_preq(ni, vap->iv_myaddr, 835195618Srpaulo broadcastaddr, &ppreq); 836195618Srpaulo } 837195618Srpaulo /* 838195618Srpaulo * Check if we can send an intermediate Path Reply, 839195618Srpaulo * i.e., Target Only bit is not set. 840195618Srpaulo */ 841195618Srpaulo if (!(PREQ_TFLAGS(0) & IEEE80211_MESHPREQ_TFLAGS_TO)) { 842195618Srpaulo struct ieee80211_meshprep_ie prep; 843195618Srpaulo 844195618Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 845195618Srpaulo "intermediate reply for PREQ from %s", 846195618Srpaulo ether_sprintf(preq->preq_origaddr)); 847195618Srpaulo prep.prep_flags = 0; 848195618Srpaulo prep.prep_hopcount = rt->rt_nhops + 1; 849195618Srpaulo prep.prep_ttl = ms->ms_ttl; 850195618Srpaulo IEEE80211_ADDR_COPY(&prep.prep_targetaddr, 851198230Srpaulo PREQ_TADDR(0)); 852195908Srpaulo prep.prep_targetseq = hrorig->hr_seq; 853195618Srpaulo prep.prep_lifetime = preq->preq_lifetime; 854195618Srpaulo prep.prep_metric = rt->rt_metric + 855195618Srpaulo ms->ms_pmetric->mpm_metric(ni); 856195618Srpaulo IEEE80211_ADDR_COPY(&prep.prep_origaddr, 857198230Srpaulo preq->preq_origaddr); 858195908Srpaulo prep.prep_origseq = hrorig->hr_seq; 859195618Srpaulo hwmp_send_prep(ni, vap->iv_myaddr, 860195618Srpaulo broadcastaddr, &prep); 861195618Srpaulo } 862195618Srpaulo /* 863195618Srpaulo * We have no information about this path, 864195618Srpaulo * propagate the PREQ. 865195618Srpaulo */ 866195618Srpaulo } else if (preq->preq_ttl > 1 && 867195618Srpaulo preq->preq_hopcount < hs->hs_maxhops) { 868195784Srpaulo if (rt == NULL) { 869195618Srpaulo rt = ieee80211_mesh_rt_add(vap, PREQ_TADDR(0)); 870195784Srpaulo if (rt == NULL) { 871195784Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, 872195784Srpaulo ni, "unable to add PREQ path to %s", 873195784Srpaulo ether_sprintf(PREQ_TADDR(0))); 874195784Srpaulo vap->iv_stats.is_mesh_rtaddfailed++; 875195784Srpaulo return; 876195784Srpaulo } 877195784Srpaulo } 878195618Srpaulo rt->rt_metric = preq->preq_metric; 879195618Srpaulo rt->rt_lifetime = preq->preq_lifetime; 880195908Srpaulo hrorig = IEEE80211_MESH_ROUTE_PRIV(rt, 881195908Srpaulo struct ieee80211_hwmp_route); 882195908Srpaulo hrorig->hr_seq = preq->preq_origseq; 883195908Srpaulo hrorig->hr_preqid = preq->preq_id; 884195618Srpaulo 885195618Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 886195784Srpaulo "forward PREQ from %s", 887195618Srpaulo ether_sprintf(preq->preq_origaddr)); 888195618Srpaulo ppreq.preq_hopcount += 1; 889195618Srpaulo ppreq.preq_ttl -= 1; 890195618Srpaulo ppreq.preq_metric += ms->ms_pmetric->mpm_metric(ni); 891195618Srpaulo hwmp_send_preq(ni, vap->iv_myaddr, broadcastaddr, 892195618Srpaulo &ppreq); 893195618Srpaulo } 894195618Srpaulo } 895195618Srpaulo 896195618Srpaulo} 897195618Srpaulo#undef PREQ_TFLAGS 898195618Srpaulo#undef PREQ_TADDR 899195618Srpaulo#undef PREQ_TSEQ 900195618Srpaulo 901195618Srpaulostatic int 902195618Srpaulohwmp_send_preq(struct ieee80211_node *ni, 903195618Srpaulo const uint8_t sa[IEEE80211_ADDR_LEN], 904195618Srpaulo const uint8_t da[IEEE80211_ADDR_LEN], 905195618Srpaulo struct ieee80211_meshpreq_ie *preq) 906195618Srpaulo{ 907195618Srpaulo struct ieee80211_hwmp_state *hs = ni->ni_vap->iv_hwmp; 908195618Srpaulo 909195618Srpaulo /* 910195618Srpaulo * Enforce PREQ interval. 911195618Srpaulo */ 912195618Srpaulo if (ratecheck(&hs->hs_lastpreq, &ieee80211_hwmp_preqminint) == 0) 913195618Srpaulo return EALREADY; 914195618Srpaulo getmicrouptime(&hs->hs_lastpreq); 915195618Srpaulo 916195618Srpaulo /* 917195618Srpaulo * mesh preq action frame format 918195618Srpaulo * [6] da 919195618Srpaulo * [6] sa 920195618Srpaulo * [6] addr3 = sa 921195618Srpaulo * [1] action 922195618Srpaulo * [1] category 923195618Srpaulo * [tlv] mesh path request 924195618Srpaulo */ 925195618Srpaulo preq->preq_ie = IEEE80211_ELEMID_MESHPREQ; 926195618Srpaulo return hwmp_send_action(ni, sa, da, (uint8_t *)preq, 927195618Srpaulo sizeof(struct ieee80211_meshpreq_ie)); 928195618Srpaulo} 929195618Srpaulo 930195618Srpaulostatic void 931195618Srpaulohwmp_recv_prep(struct ieee80211vap *vap, struct ieee80211_node *ni, 932195618Srpaulo const struct ieee80211_frame *wh, const struct ieee80211_meshprep_ie *prep) 933195618Srpaulo{ 934195618Srpaulo struct ieee80211_mesh_state *ms = vap->iv_mesh; 935195618Srpaulo struct ieee80211_hwmp_state *hs = vap->iv_hwmp; 936195618Srpaulo struct ieee80211_mesh_route *rt = NULL; 937195618Srpaulo struct ieee80211_hwmp_route *hr; 938195618Srpaulo struct ieee80211com *ic = vap->iv_ic; 939195618Srpaulo struct ifnet *ifp = vap->iv_ifp; 940195618Srpaulo struct mbuf *m, *next; 941195618Srpaulo 942195618Srpaulo /* 943195618Srpaulo * Acceptance criteria: if the corresponding PREQ was not generated 944195618Srpaulo * by us and forwarding is disabled, discard this PREP. 945195618Srpaulo */ 946195618Srpaulo if (ni == vap->iv_bss || 947195618Srpaulo ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED) 948195618Srpaulo return; 949195618Srpaulo if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, prep->prep_origaddr) && 950195618Srpaulo !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) 951195618Srpaulo return; 952195618Srpaulo 953195618Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 954198230Srpaulo "received PREP from %s", ether_sprintf(prep->prep_targetaddr)); 955195618Srpaulo 956198230Srpaulo rt = ieee80211_mesh_rt_find(vap, prep->prep_targetaddr); 957195618Srpaulo if (rt == NULL) { 958195618Srpaulo /* 959195618Srpaulo * If we have no entry this could be a reply to a root PREQ. 960195618Srpaulo */ 961195618Srpaulo if (hs->hs_rootmode != IEEE80211_HWMP_ROOTMODE_DISABLED) { 962198230Srpaulo rt = ieee80211_mesh_rt_add(vap, prep->prep_targetaddr); 963195784Srpaulo if (rt == NULL) { 964195784Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, 965195784Srpaulo ni, "unable to add PREP path to %s", 966198230Srpaulo ether_sprintf(prep->prep_targetaddr)); 967195784Srpaulo vap->iv_stats.is_mesh_rtaddfailed++; 968195784Srpaulo return; 969195784Srpaulo } 970195618Srpaulo IEEE80211_ADDR_COPY(rt->rt_nexthop, wh->i_addr2); 971195618Srpaulo rt->rt_nhops = prep->prep_hopcount; 972195618Srpaulo rt->rt_lifetime = prep->prep_lifetime; 973195618Srpaulo rt->rt_metric = prep->prep_metric; 974195784Srpaulo rt->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID; 975195908Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 976195908Srpaulo "add root path to %s nhops %d metric %d (PREP)", 977198230Srpaulo ether_sprintf(prep->prep_targetaddr), 978195908Srpaulo rt->rt_nhops, rt->rt_metric); 979195618Srpaulo return; 980195618Srpaulo } 981195618Srpaulo return; 982195618Srpaulo } 983195908Srpaulo /* 984195908Srpaulo * Sequence number validation. 985195908Srpaulo */ 986195618Srpaulo hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route); 987198230Srpaulo if (HWMP_SEQ_LEQ(prep->prep_targetseq, hr->hr_seq)) { 988195908Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 989195908Srpaulo "discard PREP from %s, old seq no %u <= %u", 990198230Srpaulo ether_sprintf(prep->prep_targetaddr), 991198230Srpaulo prep->prep_targetseq, hr->hr_seq); 992195908Srpaulo return; 993195908Srpaulo } 994198230Srpaulo hr->hr_seq = prep->prep_targetseq; 995195908Srpaulo /* 996195908Srpaulo * If it's NOT for us, propagate the PREP. 997195908Srpaulo */ 998198230Srpaulo if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, prep->prep_origaddr) && 999195908Srpaulo prep->prep_ttl > 1 && prep->prep_hopcount < hs->hs_maxhops) { 1000195908Srpaulo struct ieee80211_meshprep_ie pprep; /* propagated PREP */ 1001195908Srpaulo 1002195908Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 1003195908Srpaulo "propagate PREP from %s", 1004198230Srpaulo ether_sprintf(prep->prep_targetaddr)); 1005195908Srpaulo 1006195908Srpaulo memcpy(&pprep, prep, sizeof(pprep)); 1007195908Srpaulo pprep.prep_hopcount += 1; 1008195908Srpaulo pprep.prep_ttl -= 1; 1009195908Srpaulo pprep.prep_metric += ms->ms_pmetric->mpm_metric(ni); 1010198230Srpaulo IEEE80211_ADDR_COPY(pprep.prep_targetaddr, vap->iv_myaddr); 1011195908Srpaulo hwmp_send_prep(ni, vap->iv_myaddr, broadcastaddr, &pprep); 1012195908Srpaulo } 1013195908Srpaulo hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route); 1014195908Srpaulo if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY) { 1015195908Srpaulo /* NB: never clobber a proxy entry */; 1016195908Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 1017195908Srpaulo "discard PREP for %s, route is marked PROXY", 1018198230Srpaulo ether_sprintf(prep->prep_targetaddr)); 1019195908Srpaulo vap->iv_stats.is_hwmp_proxy++; 1020198230Srpaulo } else if (prep->prep_origseq == hr->hr_origseq) { 1021195618Srpaulo /* 1022195618Srpaulo * Check if we already have a path to this node. 1023195618Srpaulo * If we do, check if this path reply contains a 1024195618Srpaulo * better route. 1025195618Srpaulo */ 1026195908Srpaulo if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0 || 1027195908Srpaulo (prep->prep_hopcount < rt->rt_nhops || 1028195908Srpaulo prep->prep_metric < rt->rt_metric)) { 1029195908Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 1030195908Srpaulo "%s path to %s, hopcount %d:%d metric %d:%d", 1031195908Srpaulo rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID ? 1032195908Srpaulo "prefer" : "update", 1033195908Srpaulo ether_sprintf(prep->prep_origaddr), 1034195908Srpaulo rt->rt_nhops, prep->prep_hopcount, 1035195908Srpaulo rt->rt_metric, prep->prep_metric); 1036195618Srpaulo IEEE80211_ADDR_COPY(rt->rt_nexthop, wh->i_addr2); 1037195618Srpaulo rt->rt_nhops = prep->prep_hopcount; 1038195618Srpaulo rt->rt_lifetime = prep->prep_lifetime; 1039195618Srpaulo rt->rt_metric = prep->prep_metric; 1040195784Srpaulo rt->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID; 1041195908Srpaulo } else { 1042195908Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 1043195908Srpaulo "ignore PREP for %s, hopcount %d:%d metric %d:%d", 1044198230Srpaulo ether_sprintf(prep->prep_targetaddr), 1045195908Srpaulo rt->rt_nhops, prep->prep_hopcount, 1046195908Srpaulo rt->rt_metric, prep->prep_metric); 1047195618Srpaulo } 1048195618Srpaulo } else { 1049195618Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 1050195908Srpaulo "discard PREP for %s, wrong seqno %u != %u", 1051198230Srpaulo ether_sprintf(prep->prep_targetaddr), prep->prep_origseq, 1052195618Srpaulo hr->hr_seq); 1053195618Srpaulo vap->iv_stats.is_hwmp_wrongseq++; 1054195618Srpaulo } 1055195618Srpaulo /* 1056195618Srpaulo * Check for frames queued awaiting path discovery. 1057195618Srpaulo * XXX probably can tell exactly and avoid remove call 1058195618Srpaulo * NB: hash may have false matches, if so they will get 1059195618Srpaulo * stuck back on the stageq because there won't be 1060195618Srpaulo * a path. 1061195618Srpaulo */ 1062195618Srpaulo m = ieee80211_ageq_remove(&ic->ic_stageq, 1063195618Srpaulo (struct ieee80211_node *)(uintptr_t) 1064195618Srpaulo ieee80211_mac_hash(ic, rt->rt_dest)); 1065195618Srpaulo for (; m != NULL; m = next) { 1066195618Srpaulo next = m->m_nextpkt; 1067195618Srpaulo m->m_nextpkt = NULL; 1068195784Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 1069195784Srpaulo "flush queued frame %p len %d", m, m->m_pkthdr.len); 1070195618Srpaulo ifp->if_transmit(ifp, m); 1071195618Srpaulo } 1072195618Srpaulo} 1073195618Srpaulo 1074195618Srpaulostatic int 1075195618Srpaulohwmp_send_prep(struct ieee80211_node *ni, 1076195618Srpaulo const uint8_t sa[IEEE80211_ADDR_LEN], 1077195618Srpaulo const uint8_t da[IEEE80211_ADDR_LEN], 1078195618Srpaulo struct ieee80211_meshprep_ie *prep) 1079195618Srpaulo{ 1080195784Srpaulo /* NB: there's no PREP minimum interval. */ 1081195618Srpaulo 1082195618Srpaulo /* 1083195618Srpaulo * mesh prep action frame format 1084195618Srpaulo * [6] da 1085195618Srpaulo * [6] sa 1086195618Srpaulo * [6] addr3 = sa 1087195618Srpaulo * [1] action 1088195618Srpaulo * [1] category 1089195618Srpaulo * [tlv] mesh path reply 1090195618Srpaulo */ 1091195618Srpaulo prep->prep_ie = IEEE80211_ELEMID_MESHPREP; 1092195618Srpaulo return hwmp_send_action(ni, sa, da, (uint8_t *)prep, 1093195618Srpaulo sizeof(struct ieee80211_meshprep_ie)); 1094195618Srpaulo} 1095195618Srpaulo 1096197413Srpaulo#define PERR_DFLAGS(n) perr.perr_dests[n].dest_flags 1097195618Srpaulo#define PERR_DADDR(n) perr.perr_dests[n].dest_addr 1098195618Srpaulo#define PERR_DSEQ(n) perr.perr_dests[n].dest_seq 1099197413Srpaulo#define PERR_DRCODE(n) perr.perr_dests[n].dest_rcode 1100195618Srpaulostatic void 1101195618Srpaulohwmp_peerdown(struct ieee80211_node *ni) 1102195618Srpaulo{ 1103195618Srpaulo struct ieee80211vap *vap = ni->ni_vap; 1104197413Srpaulo struct ieee80211_mesh_state *ms = vap->iv_mesh; 1105195618Srpaulo struct ieee80211_meshperr_ie perr; 1106195618Srpaulo struct ieee80211_mesh_route *rt; 1107195618Srpaulo struct ieee80211_hwmp_route *hr; 1108195618Srpaulo 1109195618Srpaulo rt = ieee80211_mesh_rt_find(vap, ni->ni_macaddr); 1110195618Srpaulo if (rt == NULL) 1111195618Srpaulo return; 1112195618Srpaulo hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route); 1113195618Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 1114195784Srpaulo "%s", "delete route entry"); 1115197413Srpaulo perr.perr_ttl = ms->ms_ttl; 1116195618Srpaulo perr.perr_ndests = 1; 1117198260Srpaulo PERR_DFLAGS(0) = 0; 1118197413Srpaulo if (hr->hr_seq == 0) 1119197413Srpaulo PERR_DFLAGS(0) |= IEEE80211_MESHPERR_DFLAGS_USN; 1120197413Srpaulo PERR_DFLAGS(0) |= IEEE80211_MESHPERR_DFLAGS_RC; 1121195618Srpaulo IEEE80211_ADDR_COPY(PERR_DADDR(0), rt->rt_dest); 1122195618Srpaulo PERR_DSEQ(0) = hr->hr_seq; 1123197413Srpaulo PERR_DRCODE(0) = IEEE80211_REASON_MESH_PERR_DEST_UNREACH; 1124195908Srpaulo /* NB: flush everything passing through peer */ 1125195908Srpaulo ieee80211_mesh_rt_flush_peer(vap, ni->ni_macaddr); 1126195618Srpaulo hwmp_send_perr(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &perr); 1127195618Srpaulo} 1128197413Srpaulo#undef PERR_DFLAGS 1129195618Srpaulo#undef PERR_DADDR 1130195618Srpaulo#undef PERR_DSEQ 1131197413Srpaulo#undef PERR_DRCODE 1132195618Srpaulo 1133197413Srpaulo#define PERR_DFLAGS(n) perr->perr_dests[n].dest_flags 1134195618Srpaulo#define PERR_DADDR(n) perr->perr_dests[n].dest_addr 1135195618Srpaulo#define PERR_DSEQ(n) perr->perr_dests[n].dest_seq 1136197413Srpaulo#define PERR_DRCODE(n) perr->perr_dests[n].dest_rcode 1137195618Srpaulostatic void 1138195618Srpaulohwmp_recv_perr(struct ieee80211vap *vap, struct ieee80211_node *ni, 1139195618Srpaulo const struct ieee80211_frame *wh, const struct ieee80211_meshperr_ie *perr) 1140195618Srpaulo{ 1141195618Srpaulo struct ieee80211_mesh_state *ms = vap->iv_mesh; 1142195618Srpaulo struct ieee80211_mesh_route *rt = NULL; 1143195618Srpaulo struct ieee80211_hwmp_route *hr; 1144195618Srpaulo struct ieee80211_meshperr_ie pperr; 1145195618Srpaulo int i, forward = 0; 1146195618Srpaulo 1147195618Srpaulo /* 1148195618Srpaulo * Acceptance criteria: check if we received a PERR from a 1149195618Srpaulo * neighbor and forwarding is enabled. 1150195618Srpaulo */ 1151195618Srpaulo if (ni == vap->iv_bss || 1152195618Srpaulo ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED || 1153195618Srpaulo !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) 1154195618Srpaulo return; 1155195618Srpaulo /* 1156195618Srpaulo * Find all routing entries that match and delete them. 1157195618Srpaulo */ 1158195618Srpaulo for (i = 0; i < perr->perr_ndests; i++) { 1159195618Srpaulo rt = ieee80211_mesh_rt_find(vap, PERR_DADDR(i)); 1160195618Srpaulo if (rt == NULL) 1161195618Srpaulo continue; 1162197413Srpaulo hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route); 1163197413Srpaulo if (!(PERR_DFLAGS(0) & IEEE80211_MESHPERR_DFLAGS_USN) && 1164197413Srpaulo HWMP_SEQ_GEQ(PERR_DSEQ(i), hr->hr_seq)) { 1165195618Srpaulo ieee80211_mesh_rt_del(vap, rt->rt_dest); 1166195908Srpaulo ieee80211_mesh_rt_flush_peer(vap, rt->rt_dest); 1167195618Srpaulo rt = NULL; 1168195618Srpaulo forward = 1; 1169195618Srpaulo } 1170195618Srpaulo } 1171195618Srpaulo /* 1172195618Srpaulo * Propagate the PERR if we previously found it on our routing table. 1173195618Srpaulo * XXX handle ndest > 1 1174195618Srpaulo */ 1175197413Srpaulo if (forward && perr->perr_ttl > 1) { 1176195618Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 1177195784Srpaulo "propagate PERR from %s", ether_sprintf(wh->i_addr2)); 1178195618Srpaulo memcpy(&pperr, perr, sizeof(*perr)); 1179197413Srpaulo pperr.perr_ttl--; 1180195908Srpaulo hwmp_send_perr(vap->iv_bss, vap->iv_myaddr, broadcastaddr, 1181195908Srpaulo &pperr); 1182195618Srpaulo } 1183195618Srpaulo} 1184195618Srpaulo#undef PEER_DADDR 1185195618Srpaulo#undef PERR_DSEQ 1186195618Srpaulo 1187195618Srpaulostatic int 1188195618Srpaulohwmp_send_perr(struct ieee80211_node *ni, 1189195618Srpaulo const uint8_t sa[IEEE80211_ADDR_LEN], 1190195618Srpaulo const uint8_t da[IEEE80211_ADDR_LEN], 1191195618Srpaulo struct ieee80211_meshperr_ie *perr) 1192195618Srpaulo{ 1193195618Srpaulo struct ieee80211_hwmp_state *hs = ni->ni_vap->iv_hwmp; 1194195618Srpaulo 1195195618Srpaulo /* 1196195618Srpaulo * Enforce PERR interval. 1197195618Srpaulo */ 1198195618Srpaulo if (ratecheck(&hs->hs_lastperr, &ieee80211_hwmp_perrminint) == 0) 1199195618Srpaulo return EALREADY; 1200195618Srpaulo getmicrouptime(&hs->hs_lastperr); 1201195618Srpaulo 1202195618Srpaulo /* 1203195618Srpaulo * mesh perr action frame format 1204195618Srpaulo * [6] da 1205195618Srpaulo * [6] sa 1206195618Srpaulo * [6] addr3 = sa 1207195618Srpaulo * [1] action 1208195618Srpaulo * [1] category 1209195618Srpaulo * [tlv] mesh path error 1210195618Srpaulo */ 1211195618Srpaulo perr->perr_ie = IEEE80211_ELEMID_MESHPERR; 1212195618Srpaulo return hwmp_send_action(ni, sa, da, (uint8_t *)perr, 1213195618Srpaulo sizeof(struct ieee80211_meshperr_ie)); 1214195618Srpaulo} 1215195618Srpaulo 1216195618Srpaulostatic void 1217195618Srpaulohwmp_recv_rann(struct ieee80211vap *vap, struct ieee80211_node *ni, 1218195618Srpaulo const struct ieee80211_frame *wh, const struct ieee80211_meshrann_ie *rann) 1219195618Srpaulo{ 1220195618Srpaulo struct ieee80211_mesh_state *ms = vap->iv_mesh; 1221195618Srpaulo struct ieee80211_hwmp_state *hs = vap->iv_hwmp; 1222195618Srpaulo struct ieee80211_mesh_route *rt = NULL; 1223195618Srpaulo struct ieee80211_hwmp_route *hr; 1224195618Srpaulo struct ieee80211_meshrann_ie prann; 1225195618Srpaulo 1226195618Srpaulo if (ni == vap->iv_bss || 1227198230Srpaulo ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED || 1228198230Srpaulo IEEE80211_ADDR_EQ(rann->rann_addr, vap->iv_myaddr)) 1229195618Srpaulo return; 1230195618Srpaulo 1231195618Srpaulo rt = ieee80211_mesh_rt_find(vap, rann->rann_addr); 1232195618Srpaulo /* 1233195618Srpaulo * Discover the path to the root mesh STA. 1234195618Srpaulo * If we already know it, propagate the RANN element. 1235195618Srpaulo */ 1236195618Srpaulo if (rt == NULL) { 1237195618Srpaulo hwmp_discover(vap, rann->rann_addr, NULL); 1238195618Srpaulo return; 1239195618Srpaulo } 1240195618Srpaulo hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route); 1241195908Srpaulo if (HWMP_SEQ_GT(rann->rann_seq, hr->hr_seq) && rann->rann_ttl > 1 && 1242195618Srpaulo rann->rann_hopcount < hs->hs_maxhops && 1243195618Srpaulo (ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) { 1244195618Srpaulo memcpy(&prann, rann, sizeof(prann)); 1245195618Srpaulo prann.rann_hopcount += 1; 1246195618Srpaulo prann.rann_ttl -= 1; 1247195618Srpaulo prann.rann_metric += ms->ms_pmetric->mpm_metric(ni); 1248195618Srpaulo hwmp_send_rann(vap->iv_bss, vap->iv_myaddr, broadcastaddr, 1249195618Srpaulo &prann); 1250195618Srpaulo } 1251195618Srpaulo} 1252195618Srpaulo 1253195618Srpaulostatic int 1254195618Srpaulohwmp_send_rann(struct ieee80211_node *ni, 1255195618Srpaulo const uint8_t sa[IEEE80211_ADDR_LEN], 1256195618Srpaulo const uint8_t da[IEEE80211_ADDR_LEN], 1257195618Srpaulo struct ieee80211_meshrann_ie *rann) 1258195618Srpaulo{ 1259195618Srpaulo /* 1260195618Srpaulo * mesh rann action frame format 1261195618Srpaulo * [6] da 1262195618Srpaulo * [6] sa 1263195618Srpaulo * [6] addr3 = sa 1264195618Srpaulo * [1] action 1265195618Srpaulo * [1] category 1266195618Srpaulo * [tlv] root annoucement 1267195618Srpaulo */ 1268195618Srpaulo rann->rann_ie = IEEE80211_ELEMID_MESHRANN; 1269195618Srpaulo return hwmp_send_action(ni, sa, da, (uint8_t *)rann, 1270195618Srpaulo sizeof(struct ieee80211_meshrann_ie)); 1271195618Srpaulo} 1272195618Srpaulo 1273195618Srpaulo#define PREQ_TFLAGS(n) preq.preq_targets[n].target_flags 1274195618Srpaulo#define PREQ_TADDR(n) preq.preq_targets[n].target_addr 1275195618Srpaulo#define PREQ_TSEQ(n) preq.preq_targets[n].target_seq 1276195618Srpaulostatic struct ieee80211_node * 1277195618Srpaulohwmp_discover(struct ieee80211vap *vap, 1278195618Srpaulo const uint8_t dest[IEEE80211_ADDR_LEN], struct mbuf *m) 1279195618Srpaulo{ 1280195618Srpaulo struct ieee80211_hwmp_state *hs = vap->iv_hwmp; 1281195618Srpaulo struct ieee80211_mesh_state *ms = vap->iv_mesh; 1282195618Srpaulo struct ieee80211_mesh_route *rt = NULL; 1283195618Srpaulo struct ieee80211_hwmp_route *hr; 1284195618Srpaulo struct ieee80211_meshpreq_ie preq; 1285195618Srpaulo struct ieee80211_node *ni; 1286195618Srpaulo int sendpreq = 0; 1287195618Srpaulo 1288195618Srpaulo KASSERT(vap->iv_opmode == IEEE80211_M_MBSS, 1289195618Srpaulo ("not a mesh vap, opmode %d", vap->iv_opmode)); 1290195618Srpaulo 1291195618Srpaulo KASSERT(!IEEE80211_ADDR_EQ(vap->iv_myaddr, dest), 1292195618Srpaulo ("%s: discovering self!", __func__)); 1293195618Srpaulo 1294195618Srpaulo ni = NULL; 1295195618Srpaulo if (!IEEE80211_IS_MULTICAST(dest)) { 1296195618Srpaulo rt = ieee80211_mesh_rt_find(vap, dest); 1297195618Srpaulo if (rt == NULL) { 1298195618Srpaulo rt = ieee80211_mesh_rt_add(vap, dest); 1299195618Srpaulo if (rt == NULL) { 1300195784Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, 1301195784Srpaulo ni, "unable to add discovery path to %s", 1302195784Srpaulo ether_sprintf(dest)); 1303195784Srpaulo vap->iv_stats.is_mesh_rtaddfailed++; 1304195618Srpaulo goto done; 1305195618Srpaulo } 1306195618Srpaulo } 1307195618Srpaulo hr = IEEE80211_MESH_ROUTE_PRIV(rt, 1308195618Srpaulo struct ieee80211_hwmp_route); 1309195784Srpaulo if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0) { 1310198230Srpaulo if (hr->hr_origseq == 0) 1311198230Srpaulo hr->hr_origseq = ++hs->hs_seq; 1312195618Srpaulo rt->rt_metric = IEEE80211_MESHLMETRIC_INITIALVAL; 1313195618Srpaulo rt->rt_lifetime = 1314195813Ssam ticks_to_msecs(ieee80211_hwmp_pathtimeout); 1315195618Srpaulo /* XXX check preq retries */ 1316195618Srpaulo sendpreq = 1; 1317195618Srpaulo IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP, dest, 1318195784Srpaulo "start path discovery (src %s)", 1319195784Srpaulo m == NULL ? "<none>" : ether_sprintf( 1320195784Srpaulo mtod(m, struct ether_header *)->ether_shost)); 1321195618Srpaulo /* 1322195618Srpaulo * Try to discover the path for this node. 1323195618Srpaulo */ 1324195618Srpaulo preq.preq_flags = 0; 1325195618Srpaulo preq.preq_hopcount = 0; 1326195618Srpaulo preq.preq_ttl = ms->ms_ttl; 1327195908Srpaulo preq.preq_id = ++hs->hs_preqid; 1328195618Srpaulo IEEE80211_ADDR_COPY(preq.preq_origaddr, vap->iv_myaddr); 1329198230Srpaulo preq.preq_origseq = hr->hr_origseq; 1330195618Srpaulo preq.preq_lifetime = rt->rt_lifetime; 1331195618Srpaulo preq.preq_metric = rt->rt_metric; 1332195618Srpaulo preq.preq_tcount = 1; 1333195618Srpaulo IEEE80211_ADDR_COPY(PREQ_TADDR(0), dest); 1334195618Srpaulo PREQ_TFLAGS(0) = 0; 1335195618Srpaulo if (ieee80211_hwmp_targetonly) 1336195618Srpaulo PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_TO; 1337195618Srpaulo if (ieee80211_hwmp_replyforward) 1338195618Srpaulo PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_RF; 1339195618Srpaulo PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_USN; 1340195618Srpaulo PREQ_TSEQ(0) = 0; 1341195618Srpaulo /* XXX check return value */ 1342195618Srpaulo hwmp_send_preq(vap->iv_bss, vap->iv_myaddr, 1343195618Srpaulo broadcastaddr, &preq); 1344195618Srpaulo } 1345195784Srpaulo if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) 1346195618Srpaulo ni = ieee80211_find_txnode(vap, rt->rt_nexthop); 1347195618Srpaulo } else { 1348195618Srpaulo ni = ieee80211_find_txnode(vap, dest); 1349195784Srpaulo /* NB: if null then we leak mbuf */ 1350195784Srpaulo KASSERT(ni != NULL, ("leak mcast frame")); 1351195618Srpaulo return ni; 1352195618Srpaulo } 1353195618Srpaulodone: 1354195618Srpaulo if (ni == NULL && m != NULL) { 1355195618Srpaulo if (sendpreq) { 1356195618Srpaulo struct ieee80211com *ic = vap->iv_ic; 1357195618Srpaulo /* 1358195618Srpaulo * Queue packet for transmit when path discovery 1359195618Srpaulo * completes. If discovery never completes the 1360195618Srpaulo * frame will be flushed by way of the aging timer. 1361195618Srpaulo */ 1362195784Srpaulo IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP, dest, 1363195784Srpaulo "%s", "queue frame until path found"); 1364195618Srpaulo m->m_pkthdr.rcvif = (void *)(uintptr_t) 1365195618Srpaulo ieee80211_mac_hash(ic, dest); 1366195618Srpaulo /* XXX age chosen randomly */ 1367195618Srpaulo ieee80211_ageq_append(&ic->ic_stageq, m, 1368195618Srpaulo IEEE80211_INACT_WAIT); 1369195784Srpaulo } else { 1370195784Srpaulo IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_HWMP, 1371195784Srpaulo dest, NULL, "%s", "no valid path to this node"); 1372195618Srpaulo m_freem(m); 1373195784Srpaulo } 1374195618Srpaulo } 1375195618Srpaulo return ni; 1376195618Srpaulo} 1377195618Srpaulo#undef PREQ_TFLAGS 1378195618Srpaulo#undef PREQ_TADDR 1379195618Srpaulo#undef PREQ_TSEQ 1380195618Srpaulo 1381195618Srpaulostatic int 1382195618Srpaulohwmp_ioctl_get80211(struct ieee80211vap *vap, struct ieee80211req *ireq) 1383195618Srpaulo{ 1384195618Srpaulo struct ieee80211_hwmp_state *hs = vap->iv_hwmp; 1385195618Srpaulo int error; 1386195618Srpaulo 1387195618Srpaulo if (vap->iv_opmode != IEEE80211_M_MBSS) 1388195618Srpaulo return ENOSYS; 1389195618Srpaulo error = 0; 1390195618Srpaulo switch (ireq->i_type) { 1391195618Srpaulo case IEEE80211_IOC_HWMP_ROOTMODE: 1392195618Srpaulo ireq->i_val = hs->hs_rootmode; 1393195618Srpaulo break; 1394195618Srpaulo case IEEE80211_IOC_HWMP_MAXHOPS: 1395195618Srpaulo ireq->i_val = hs->hs_maxhops; 1396195618Srpaulo break; 1397195618Srpaulo default: 1398195618Srpaulo return ENOSYS; 1399195618Srpaulo } 1400195618Srpaulo return error; 1401195618Srpaulo} 1402195618SrpauloIEEE80211_IOCTL_GET(hwmp, hwmp_ioctl_get80211); 1403195618Srpaulo 1404195618Srpaulostatic int 1405195618Srpaulohwmp_ioctl_set80211(struct ieee80211vap *vap, struct ieee80211req *ireq) 1406195618Srpaulo{ 1407195618Srpaulo struct ieee80211_hwmp_state *hs = vap->iv_hwmp; 1408195618Srpaulo int error; 1409195618Srpaulo 1410195618Srpaulo if (vap->iv_opmode != IEEE80211_M_MBSS) 1411195618Srpaulo return ENOSYS; 1412195618Srpaulo error = 0; 1413195618Srpaulo switch (ireq->i_type) { 1414195618Srpaulo case IEEE80211_IOC_HWMP_ROOTMODE: 1415195618Srpaulo if (ireq->i_val < 0 || ireq->i_val > 3) 1416195618Srpaulo return EINVAL; 1417195618Srpaulo hs->hs_rootmode = ireq->i_val; 1418195618Srpaulo hwmp_rootmode_setup(vap); 1419195618Srpaulo break; 1420195618Srpaulo case IEEE80211_IOC_HWMP_MAXHOPS: 1421195618Srpaulo if (ireq->i_val <= 0 || ireq->i_val > 255) 1422195618Srpaulo return EINVAL; 1423195618Srpaulo hs->hs_maxhops = ireq->i_val; 1424195618Srpaulo break; 1425195618Srpaulo default: 1426195618Srpaulo return ENOSYS; 1427195618Srpaulo } 1428195618Srpaulo return error; 1429195618Srpaulo} 1430195618SrpauloIEEE80211_IOCTL_SET(hwmp, hwmp_ioctl_set80211); 1431