ieee80211_hwmp.c revision 195784
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 195784 2009-07-20 19:12:08Z 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 119195618Srpaulostatic int ieee80211_hwmp_targetonly = 0; 120195618Srpaulostatic int ieee80211_hwmp_replyforward = 1; 121195618Srpaulostatic const int ieee80211_hwmp_maxprepretries = 3; 122195618Srpaulostatic const struct timeval ieee80211_hwmp_maxhopstime = { 0, 500000 }; 123195618Srpaulostatic const struct timeval ieee80211_hwmp_preqminint = { 0, 100000 }; 124195618Srpaulostatic const struct timeval ieee80211_hwmp_perrminint = { 0, 100000 }; 125195618Srpaulostatic const struct timeval ieee80211_hwmp_roottimeout = { 5, 0 }; 126195618Srpaulostatic const struct timeval ieee80211_hwmp_pathtimeout = { 5, 0 }; 127195618Srpaulostatic const struct timeval ieee80211_hwmp_pathtoroottimeout = { 5, 0 }; 128195618Srpaulostatic const struct timeval ieee80211_hwmp_rootint = { 2, 0 }; 129195618Srpaulostatic const struct timeval ieee80211_hwmp_rannint = { 1, 0 }; 130195618Srpaulostatic const struct timeval ieee80211_hwmp_pathmaintenanceint = { 2, 0 }; 131195618Srpaulostatic const struct timeval ieee80211_hwmp_confirmint = { 2, 0 }; 132195618Srpaulo 133195618Srpaulo#define timeval2msecs(tv) (tv.tv_sec * 1000 + tv.tv_usec / 1000) 134195618Srpaulo 135195618Srpaulo#define HWMP_ROOTMODEINT msecs_to_ticks(timeval2msecs(ieee80211_hwmp_rootint)) 136195618Srpaulo#define HWMP_RANNMODEINT msecs_to_ticks(timeval2msecs(ieee80211_hwmp_rannint)) 137195618Srpaulo 138195618Srpaulo/* unalligned little endian access */ 139195618Srpaulo#define LE_WRITE_2(p, v) do { \ 140195618Srpaulo ((uint8_t *)(p))[0] = (v) & 0xff; \ 141195618Srpaulo ((uint8_t *)(p))[1] = ((v) >> 8) & 0xff; \ 142195618Srpaulo} while (0) 143195618Srpaulo#define LE_WRITE_4(p, v) do { \ 144195618Srpaulo ((uint8_t *)(p))[0] = (v) & 0xff; \ 145195618Srpaulo ((uint8_t *)(p))[1] = ((v) >> 8) & 0xff; \ 146195618Srpaulo ((uint8_t *)(p))[2] = ((v) >> 16) & 0xff; \ 147195618Srpaulo ((uint8_t *)(p))[3] = ((v) >> 24) & 0xff; \ 148195618Srpaulo} while (0) 149195618Srpaulo 150195618Srpaulo 151195618Srpaulo/* NB: the Target Address set in a Proactive PREQ is the broadcast address. */ 152195618Srpaulostatic const uint8_t broadcastaddr[IEEE80211_ADDR_LEN] = 153195618Srpaulo { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; 154195618Srpaulo 155195618Srpaulotypedef uint32_t ieee80211_hwmp_seq; 156195618Srpaulo#define IEEE80211_HWMP_SEQ_LEQ(a, b) ((int32_t)((a)-(b)) <= 0) 157195618Srpaulo#define IEEE80211_HWMP_SEQ_GEQ(a, b) ((int32_t)((a)-(b)) >= 0) 158195618Srpaulo 159195618Srpaulo/* 160195618Srpaulo * Private extension of ieee80211_mesh_route. 161195618Srpaulo */ 162195618Srpaulostruct ieee80211_hwmp_route { 163195618Srpaulo ieee80211_hwmp_seq hr_seq; /* HWMP sequence number */ 164195618Srpaulo ieee80211_hwmp_seq hr_preqid; /* Last PREQ ID seen */ 165195618Srpaulo int hr_preqretries; 166195618Srpaulo}; 167195618Srpaulostruct ieee80211_hwmp_state { 168195618Srpaulo ieee80211_hwmp_seq hs_seq; /* next seq to be used */ 169195618Srpaulo ieee80211_hwmp_seq hs_preqid; /* next PREQ ID to be used */ 170195618Srpaulo struct timeval hs_lastpreq; /* last time we sent a PREQ */ 171195618Srpaulo struct timeval hs_lastperr; /* last time we sent a PERR */ 172195618Srpaulo int hs_rootmode; /* proactive HWMP */ 173195618Srpaulo struct callout hs_roottimer; 174195618Srpaulo uint8_t hs_maxhops; /* max hop count */ 175195618Srpaulo}; 176195618Srpaulo 177195618SrpauloSYSCTL_NODE(_net_wlan, OID_AUTO, hwmp, CTLFLAG_RD, 0, 178195618Srpaulo "IEEE 802.11s HWMP parameters"); 179195618SrpauloSYSCTL_INT(_net_wlan_hwmp, OID_AUTO, targetonly, CTLTYPE_INT | CTLFLAG_RW, 180195618Srpaulo &ieee80211_hwmp_targetonly, 0, "Set TO bit on generated PREQs"); 181195618SrpauloSYSCTL_INT(_net_wlan_hwmp, OID_AUTO, replyforward, CTLTYPE_INT | CTLFLAG_RW, 182195618Srpaulo &ieee80211_hwmp_replyforward, 0, "Set RF bit on generated PREQs"); 183195618Srpaulo 184195618Srpaulo#define IEEE80211_HWMP_DEFAULT_MAXHOPS 31 185195618Srpaulo 186195618Srpaulostatic ieee80211_recv_action_func hwmp_recv_action_meshpath_preq; 187195618Srpaulostatic ieee80211_recv_action_func hwmp_recv_action_meshpath_prep; 188195618Srpaulostatic ieee80211_recv_action_func hwmp_recv_action_meshpath_perr; 189195618Srpaulostatic ieee80211_recv_action_func hwmp_recv_action_meshpath_rann; 190195618Srpaulo 191195618Srpaulostatic const struct ieee80211_mesh_proto_path mesh_proto_hwmp = { 192195618Srpaulo .mpp_descr = "HWMP", 193195618Srpaulo .mpp_ie = IEEE80211_MESHCONF_HWMP, 194195618Srpaulo .mpp_discover = hwmp_discover, 195195618Srpaulo .mpp_peerdown = hwmp_peerdown, 196195618Srpaulo .mpp_vattach = hwmp_vattach, 197195618Srpaulo .mpp_vdetach = hwmp_vdetach, 198195618Srpaulo .mpp_newstate = hwmp_newstate, 199195618Srpaulo .mpp_privlen = sizeof(struct ieee80211_hwmp_route), 200195784Srpaulo /* ieee80211_hwmp_pathtimeout */ 201195784Srpaulo .mpp_inact = { 5, 0 }, 202195618Srpaulo}; 203195618Srpaulo 204195618Srpaulo 205195618Srpaulostatic void 206195618Srpauloieee80211_hwmp_init(void) 207195618Srpaulo{ 208195618Srpaulo /* 209195618Srpaulo * Register action frame handlers. 210195618Srpaulo */ 211195618Srpaulo ieee80211_recv_action_register(IEEE80211_ACTION_CAT_MESHPATH, 212195618Srpaulo IEEE80211_ACTION_MESHPATH_REQ, hwmp_recv_action_meshpath_preq); 213195618Srpaulo ieee80211_recv_action_register(IEEE80211_ACTION_CAT_MESHPATH, 214195618Srpaulo IEEE80211_ACTION_MESHPATH_REP, hwmp_recv_action_meshpath_prep); 215195618Srpaulo ieee80211_recv_action_register(IEEE80211_ACTION_CAT_MESHPATH, 216195618Srpaulo IEEE80211_ACTION_MESHPATH_ERR, hwmp_recv_action_meshpath_perr); 217195618Srpaulo ieee80211_recv_action_register(IEEE80211_ACTION_CAT_MESHPATH, 218195618Srpaulo IEEE80211_ACTION_MESHPATH_RANN, hwmp_recv_action_meshpath_rann); 219195618Srpaulo 220195618Srpaulo /* 221195618Srpaulo * Register HWMP. 222195618Srpaulo */ 223195618Srpaulo ieee80211_mesh_register_proto_path(&mesh_proto_hwmp); 224195618Srpaulo} 225195618SrpauloSYSINIT(wlan_hwmp, SI_SUB_DRIVERS, SI_ORDER_SECOND, ieee80211_hwmp_init, NULL); 226195618Srpaulo 227195618Srpaulovoid 228195618Srpaulohwmp_vattach(struct ieee80211vap *vap) 229195618Srpaulo{ 230195618Srpaulo struct ieee80211_hwmp_state *hs; 231195618Srpaulo 232195618Srpaulo KASSERT(vap->iv_opmode == IEEE80211_M_MBSS, 233195618Srpaulo ("not a mesh vap, opmode %d", vap->iv_opmode)); 234195618Srpaulo 235195618Srpaulo hs = malloc(sizeof(struct ieee80211_hwmp_state), M_80211_VAP, 236195618Srpaulo M_NOWAIT | M_ZERO); 237195618Srpaulo if (hs == NULL) { 238195618Srpaulo printf("%s: couldn't alloc HWMP state\n", __func__); 239195618Srpaulo return; 240195618Srpaulo } 241195618Srpaulo hs->hs_maxhops = IEEE80211_HWMP_DEFAULT_MAXHOPS; 242195618Srpaulo callout_init(&hs->hs_roottimer, CALLOUT_MPSAFE); 243195618Srpaulo vap->iv_hwmp = hs; 244195618Srpaulo} 245195618Srpaulo 246195618Srpaulovoid 247195618Srpaulohwmp_vdetach(struct ieee80211vap *vap) 248195618Srpaulo{ 249195618Srpaulo struct ieee80211_hwmp_state *hs = vap->iv_hwmp; 250195618Srpaulo 251195784Srpaulo callout_drain(&hs->hs_roottimer); 252195618Srpaulo free(vap->iv_hwmp, M_80211_VAP); 253195618Srpaulo vap->iv_hwmp = NULL; 254195618Srpaulo} 255195618Srpaulo 256195618Srpauloint 257195618Srpaulohwmp_newstate(struct ieee80211vap *vap, enum ieee80211_state ostate, int arg) 258195618Srpaulo{ 259195618Srpaulo enum ieee80211_state nstate = vap->iv_state; 260195618Srpaulo struct ieee80211_hwmp_state *hs = vap->iv_hwmp; 261195618Srpaulo 262195618Srpaulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n", 263195618Srpaulo __func__, ieee80211_state_name[ostate], 264195618Srpaulo ieee80211_state_name[nstate], arg); 265195618Srpaulo 266195618Srpaulo /* Flush the table on RUN -> !RUN, e.g. interface down & up */ 267195618Srpaulo if (nstate != IEEE80211_S_RUN && ostate == IEEE80211_S_RUN) 268195618Srpaulo callout_drain(&hs->hs_roottimer); 269195784Srpaulo if (nstate == IEEE80211_S_RUN) 270195784Srpaulo hwmp_rootmode_setup(vap); 271195618Srpaulo return 0; 272195618Srpaulo} 273195618Srpaulo 274195618Srpaulostatic int 275195618Srpaulohwmp_recv_action_meshpath_preq(struct ieee80211_node *ni, 276195618Srpaulo const struct ieee80211_frame *wh, 277195618Srpaulo const uint8_t *frm, const uint8_t *efrm) 278195618Srpaulo{ 279195618Srpaulo struct ieee80211vap *vap = ni->ni_vap; 280195618Srpaulo struct ieee80211_meshpreq_ie preq; 281195618Srpaulo const uint8_t *iefrm = frm + 2; /* action + code */ 282195618Srpaulo 283195618Srpaulo while (efrm - iefrm > 1) { 284195618Srpaulo IEEE80211_VERIFY_LENGTH(efrm - iefrm, iefrm[1] + 2, return 0); 285195618Srpaulo if (*iefrm == IEEE80211_ELEMID_MESHPREQ) { 286195618Srpaulo const struct ieee80211_meshpreq_ie *mpreq = 287195618Srpaulo (const struct ieee80211_meshpreq_ie *) iefrm; 288195618Srpaulo /* XXX > 1 target */ 289195618Srpaulo if (mpreq->preq_len != 290195618Srpaulo sizeof(struct ieee80211_meshpreq_ie) - 2) { 291195618Srpaulo IEEE80211_DISCARD(vap, 292195618Srpaulo IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP, 293195618Srpaulo wh, NULL, "%s", "PREQ with wrong len"); 294195618Srpaulo vap->iv_stats.is_rx_mgtdiscard++; 295195618Srpaulo return 1; 296195618Srpaulo } 297195618Srpaulo memcpy(&preq, mpreq, sizeof(preq)); 298195618Srpaulo preq.preq_id = LE_READ_4(&mpreq->preq_id); 299195618Srpaulo preq.preq_origseq = LE_READ_4(&mpreq->preq_origseq); 300195618Srpaulo preq.preq_lifetime = LE_READ_4(&mpreq->preq_lifetime); 301195618Srpaulo preq.preq_metric = LE_READ_4(&mpreq->preq_metric); 302195618Srpaulo preq.preq_targets[0].target_seq = 303195618Srpaulo LE_READ_4(&mpreq->preq_targets[0].target_seq); 304195618Srpaulo hwmp_recv_preq(vap, ni, wh, &preq); 305195618Srpaulo return 0; 306195618Srpaulo } 307195618Srpaulo iefrm += iefrm[1] + 2; 308195618Srpaulo } 309195618Srpaulo IEEE80211_DISCARD(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP, 310195618Srpaulo wh, NULL, "%s", "PREQ without IE"); 311195618Srpaulo vap->iv_stats.is_rx_mgtdiscard++; 312195618Srpaulo return 0; 313195618Srpaulo} 314195618Srpaulo 315195618Srpaulostatic int 316195618Srpaulohwmp_recv_action_meshpath_prep(struct ieee80211_node *ni, 317195618Srpaulo const struct ieee80211_frame *wh, 318195618Srpaulo const uint8_t *frm, const uint8_t *efrm) 319195618Srpaulo{ 320195618Srpaulo struct ieee80211vap *vap = ni->ni_vap; 321195618Srpaulo struct ieee80211_meshprep_ie prep; 322195618Srpaulo const uint8_t *iefrm = frm + 2; /* action + code */ 323195618Srpaulo 324195618Srpaulo while (efrm - iefrm > 1) { 325195618Srpaulo IEEE80211_VERIFY_LENGTH(efrm - iefrm, iefrm[1] + 2, return 0); 326195618Srpaulo if (*iefrm == IEEE80211_ELEMID_MESHPREP) { 327195618Srpaulo const struct ieee80211_meshprep_ie *mprep = 328195618Srpaulo (const struct ieee80211_meshprep_ie *) iefrm; 329195618Srpaulo if (mprep->prep_len != 330195618Srpaulo sizeof(struct ieee80211_meshprep_ie) - 2) { 331195618Srpaulo IEEE80211_DISCARD(vap, 332195618Srpaulo IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP, 333195618Srpaulo wh, NULL, "%s", "PREP with wrong len"); 334195618Srpaulo vap->iv_stats.is_rx_mgtdiscard++; 335195618Srpaulo return 1; 336195618Srpaulo } 337195618Srpaulo memcpy(&prep, mprep, sizeof(prep)); 338195618Srpaulo prep.prep_targetseq = LE_READ_4(&mprep->prep_targetseq); 339195618Srpaulo prep.prep_lifetime = LE_READ_4(&mprep->prep_lifetime); 340195618Srpaulo prep.prep_metric = LE_READ_4(&mprep->prep_metric); 341195618Srpaulo prep.prep_origseq = LE_READ_4(&mprep->prep_origseq); 342195618Srpaulo hwmp_recv_prep(vap, ni, wh, &prep); 343195618Srpaulo return 0; 344195618Srpaulo } 345195618Srpaulo iefrm += iefrm[1] + 2; 346195618Srpaulo } 347195618Srpaulo IEEE80211_DISCARD(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP, 348195618Srpaulo wh, NULL, "%s", "PREP without IE"); 349195618Srpaulo vap->iv_stats.is_rx_mgtdiscard++; 350195618Srpaulo return 0; 351195618Srpaulo} 352195618Srpaulo 353195618Srpaulostatic int 354195618Srpaulohwmp_recv_action_meshpath_perr(struct ieee80211_node *ni, 355195618Srpaulo const struct ieee80211_frame *wh, 356195618Srpaulo const uint8_t *frm, const uint8_t *efrm) 357195618Srpaulo{ 358195618Srpaulo struct ieee80211_meshperr_ie perr; 359195618Srpaulo struct ieee80211vap *vap = ni->ni_vap; 360195618Srpaulo const uint8_t *iefrm = frm + 2; /* action + code */ 361195618Srpaulo 362195618Srpaulo while (efrm - iefrm > 1) { 363195618Srpaulo IEEE80211_VERIFY_LENGTH(efrm - iefrm, iefrm[1] + 2, return 0); 364195618Srpaulo if (*iefrm == IEEE80211_ELEMID_MESHPERR) { 365195618Srpaulo const struct ieee80211_meshperr_ie *mperr = 366195618Srpaulo (const struct ieee80211_meshperr_ie *) iefrm; 367195618Srpaulo /* XXX > 1 target */ 368195618Srpaulo if (mperr->perr_len != 369195618Srpaulo sizeof(struct ieee80211_meshperr_ie) - 2) { 370195618Srpaulo IEEE80211_DISCARD(vap, 371195618Srpaulo IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP, 372195618Srpaulo wh, NULL, "%s", "PERR with wrong len"); 373195618Srpaulo vap->iv_stats.is_rx_mgtdiscard++; 374195618Srpaulo return 1; 375195618Srpaulo } 376195618Srpaulo memcpy(&perr, mperr, sizeof(perr)); 377195618Srpaulo perr.perr_dests[0].dest_seq = 378195618Srpaulo LE_READ_4(&mperr->perr_dests[0].dest_seq); 379195618Srpaulo hwmp_recv_perr(vap, ni, wh, &perr); 380195618Srpaulo return 0; 381195618Srpaulo } 382195618Srpaulo iefrm += iefrm[1] + 2; 383195618Srpaulo } 384195618Srpaulo IEEE80211_DISCARD(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP, 385195618Srpaulo wh, NULL, "%s", "PERR without IE"); 386195618Srpaulo vap->iv_stats.is_rx_mgtdiscard++; 387195618Srpaulo return 0; 388195618Srpaulo} 389195618Srpaulo 390195618Srpaulostatic int 391195618Srpaulohwmp_recv_action_meshpath_rann(struct ieee80211_node *ni, 392195618Srpaulo const struct ieee80211_frame *wh, 393195618Srpaulo const uint8_t *frm, const uint8_t *efrm) 394195618Srpaulo{ 395195618Srpaulo struct ieee80211vap *vap = ni->ni_vap; 396195618Srpaulo struct ieee80211_meshrann_ie rann; 397195618Srpaulo const uint8_t *iefrm = frm + 2; /* action + code */ 398195618Srpaulo 399195618Srpaulo while (efrm - iefrm > 1) { 400195618Srpaulo IEEE80211_VERIFY_LENGTH(efrm - iefrm, iefrm[1] + 2, return 0); 401195618Srpaulo if (*iefrm == IEEE80211_ELEMID_MESHRANN) { 402195618Srpaulo const struct ieee80211_meshrann_ie *mrann = 403195618Srpaulo (const struct ieee80211_meshrann_ie *) iefrm; 404195618Srpaulo if (mrann->rann_len != 405195618Srpaulo sizeof(struct ieee80211_meshrann_ie) - 2) { 406195618Srpaulo IEEE80211_DISCARD(vap, 407195618Srpaulo IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP, 408195618Srpaulo wh, NULL, "%s", "RAN with wrong len"); 409195618Srpaulo vap->iv_stats.is_rx_mgtdiscard++; 410195618Srpaulo return 1; 411195618Srpaulo } 412195618Srpaulo memcpy(&rann, mrann, sizeof(rann)); 413195618Srpaulo rann.rann_seq = LE_READ_4(&mrann->rann_seq); 414195618Srpaulo rann.rann_metric = LE_READ_4(&mrann->rann_metric); 415195618Srpaulo hwmp_recv_rann(vap, ni, wh, &rann); 416195618Srpaulo return 0; 417195618Srpaulo } 418195618Srpaulo iefrm += iefrm[1] + 2; 419195618Srpaulo } 420195618Srpaulo IEEE80211_DISCARD(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP, 421195618Srpaulo wh, NULL, "%s", "RANN without IE"); 422195618Srpaulo vap->iv_stats.is_rx_mgtdiscard++; 423195618Srpaulo return 0; 424195618Srpaulo} 425195618Srpaulo 426195618Srpaulostatic int 427195618Srpaulohwmp_send_action(struct ieee80211_node *ni, 428195618Srpaulo const uint8_t sa[IEEE80211_ADDR_LEN], 429195618Srpaulo const uint8_t da[IEEE80211_ADDR_LEN], 430195618Srpaulo uint8_t *ie, size_t len) 431195618Srpaulo{ 432195618Srpaulo struct ieee80211vap *vap = ni->ni_vap; 433195618Srpaulo struct ieee80211com *ic = ni->ni_ic; 434195618Srpaulo struct ieee80211_bpf_params params; 435195618Srpaulo struct mbuf *m; 436195618Srpaulo uint8_t *frm; 437195618Srpaulo 438195618Srpaulo if (vap->iv_state == IEEE80211_S_CAC) { 439195618Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, ni, 440195618Srpaulo "block %s frame in CAC state", "HWMP action"); 441195618Srpaulo vap->iv_stats.is_tx_badstate++; 442195618Srpaulo return EIO; /* XXX */ 443195618Srpaulo } 444195618Srpaulo 445195618Srpaulo KASSERT(ni != NULL, ("null node")); 446195618Srpaulo /* 447195618Srpaulo * Hold a reference on the node so it doesn't go away until after 448195618Srpaulo * the xmit is complete all the way in the driver. On error we 449195618Srpaulo * will remove our reference. 450195618Srpaulo */ 451195618Srpaulo#ifdef IEEE80211_DEBUG_REFCNT 452195618Srpaulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, 453195618Srpaulo "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", 454195618Srpaulo __func__, __LINE__, 455195618Srpaulo ni, ether_sprintf(ni->ni_macaddr), 456195618Srpaulo ieee80211_node_refcnt(ni)+1); 457195618Srpaulo#endif 458195618Srpaulo ieee80211_ref_node(ni); 459195618Srpaulo 460195618Srpaulo m = ieee80211_getmgtframe(&frm, 461195618Srpaulo ic->ic_headroom + sizeof(struct ieee80211_frame), 462195618Srpaulo sizeof(struct ieee80211_action) + len 463195618Srpaulo ); 464195618Srpaulo if (m == NULL) { 465195618Srpaulo ieee80211_free_node(ni); 466195618Srpaulo vap->iv_stats.is_tx_nobuf++; 467195618Srpaulo return ENOMEM; 468195618Srpaulo } 469195618Srpaulo *frm++ = IEEE80211_ACTION_CAT_MESHPATH; 470195618Srpaulo switch (*ie) { 471195618Srpaulo case IEEE80211_ELEMID_MESHPREQ: 472195618Srpaulo *frm++ = IEEE80211_ACTION_MESHPATH_REQ; 473195618Srpaulo frm = hwmp_add_meshpreq(frm, 474195618Srpaulo (struct ieee80211_meshpreq_ie *)ie); 475195618Srpaulo break; 476195618Srpaulo case IEEE80211_ELEMID_MESHPREP: 477195618Srpaulo *frm++ = IEEE80211_ACTION_MESHPATH_REP; 478195618Srpaulo frm = hwmp_add_meshprep(frm, 479195618Srpaulo (struct ieee80211_meshprep_ie *)ie); 480195618Srpaulo break; 481195618Srpaulo case IEEE80211_ELEMID_MESHPERR: 482195618Srpaulo *frm++ = IEEE80211_ACTION_MESHPATH_ERR; 483195618Srpaulo frm = hwmp_add_meshperr(frm, 484195618Srpaulo (struct ieee80211_meshperr_ie *)ie); 485195618Srpaulo break; 486195618Srpaulo case IEEE80211_ELEMID_MESHRANN: 487195618Srpaulo *frm++ = IEEE80211_ACTION_MESHPATH_RANN; 488195618Srpaulo frm = hwmp_add_meshrann(frm, 489195618Srpaulo (struct ieee80211_meshrann_ie *)ie); 490195618Srpaulo break; 491195618Srpaulo } 492195618Srpaulo 493195618Srpaulo m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); 494195618Srpaulo M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT); 495195618Srpaulo if (m == NULL) { 496195618Srpaulo ieee80211_free_node(ni); 497195618Srpaulo vap->iv_stats.is_tx_nobuf++; 498195618Srpaulo return ENOMEM; 499195618Srpaulo } 500195618Srpaulo ieee80211_send_setup(ni, m, 501195618Srpaulo IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_ACTION, 502195618Srpaulo IEEE80211_NONQOS_TID, sa, da, sa); 503195618Srpaulo 504195618Srpaulo m->m_flags |= M_ENCAP; /* mark encapsulated */ 505195618Srpaulo IEEE80211_NODE_STAT(ni, tx_mgmt); 506195618Srpaulo 507195618Srpaulo memset(¶ms, 0, sizeof(params)); 508195618Srpaulo params.ibp_pri = WME_AC_VO; 509195618Srpaulo params.ibp_rate0 = ni->ni_txparms->mgmtrate; 510195618Srpaulo if (IEEE80211_IS_MULTICAST(da)) 511195618Srpaulo params.ibp_try0 = 1; 512195618Srpaulo else 513195618Srpaulo params.ibp_try0 = ni->ni_txparms->maxretry; 514195618Srpaulo params.ibp_power = ni->ni_txpower; 515195618Srpaulo return ic->ic_raw_xmit(ni, m, ¶ms); 516195618Srpaulo} 517195618Srpaulo 518195618Srpaulo#define ADDWORD(frm, v) do { \ 519195618Srpaulo LE_WRITE_4(frm, v); \ 520195618Srpaulo frm += 4; \ 521195618Srpaulo} while (0) 522195618Srpaulo/* 523195618Srpaulo * Add a Mesh Path Request IE to a frame. 524195618Srpaulo */ 525195618Srpaulostatic uint8_t * 526195618Srpaulohwmp_add_meshpreq(uint8_t *frm, const struct ieee80211_meshpreq_ie *preq) 527195618Srpaulo{ 528195618Srpaulo int i; 529195618Srpaulo 530195618Srpaulo *frm++ = IEEE80211_ELEMID_MESHPREQ; 531195618Srpaulo *frm++ = sizeof(struct ieee80211_meshpreq_ie) - 2 + 532195618Srpaulo (preq->preq_tcount - 1) * sizeof(*preq->preq_targets); 533195618Srpaulo *frm++ = preq->preq_flags; 534195618Srpaulo *frm++ = preq->preq_hopcount; 535195618Srpaulo *frm++ = preq->preq_ttl; 536195618Srpaulo ADDWORD(frm, preq->preq_id); 537195618Srpaulo IEEE80211_ADDR_COPY(frm, preq->preq_origaddr); frm += 6; 538195618Srpaulo ADDWORD(frm, preq->preq_origseq); 539195618Srpaulo ADDWORD(frm, preq->preq_lifetime); 540195618Srpaulo ADDWORD(frm, preq->preq_metric); 541195618Srpaulo *frm++ = preq->preq_tcount; 542195618Srpaulo for (i = 0; i < preq->preq_tcount; i++) { 543195618Srpaulo *frm++ = preq->preq_targets[i].target_flags; 544195618Srpaulo IEEE80211_ADDR_COPY(frm, preq->preq_targets[i].target_addr); 545195618Srpaulo frm += 6; 546195618Srpaulo ADDWORD(frm, preq->preq_targets[i].target_seq); 547195618Srpaulo } 548195618Srpaulo return frm; 549195618Srpaulo} 550195618Srpaulo 551195618Srpaulo/* 552195618Srpaulo * Add a Mesh Path Reply IE to a frame. 553195618Srpaulo */ 554195618Srpaulostatic uint8_t * 555195618Srpaulohwmp_add_meshprep(uint8_t *frm, const struct ieee80211_meshprep_ie *prep) 556195618Srpaulo{ 557195618Srpaulo *frm++ = IEEE80211_ELEMID_MESHPREP; 558195618Srpaulo *frm++ = sizeof(struct ieee80211_meshprep_ie) - 2; 559195618Srpaulo *frm++ = prep->prep_flags; 560195618Srpaulo *frm++ = prep->prep_hopcount; 561195618Srpaulo *frm++ = prep->prep_ttl; 562195618Srpaulo IEEE80211_ADDR_COPY(frm, prep->prep_targetaddr); frm += 6; 563195618Srpaulo ADDWORD(frm, prep->prep_targetseq); 564195618Srpaulo ADDWORD(frm, prep->prep_lifetime); 565195618Srpaulo ADDWORD(frm, prep->prep_metric); 566195618Srpaulo IEEE80211_ADDR_COPY(frm, prep->prep_origaddr); frm += 6; 567195618Srpaulo ADDWORD(frm, prep->prep_origseq); 568195618Srpaulo return frm; 569195618Srpaulo} 570195618Srpaulo 571195618Srpaulo/* 572195618Srpaulo * Add a Mesh Path Error IE to a frame. 573195618Srpaulo */ 574195618Srpaulostatic uint8_t * 575195618Srpaulohwmp_add_meshperr(uint8_t *frm, const struct ieee80211_meshperr_ie *perr) 576195618Srpaulo{ 577195618Srpaulo int i; 578195618Srpaulo 579195618Srpaulo *frm++ = IEEE80211_ELEMID_MESHPERR; 580195618Srpaulo *frm++ = sizeof(struct ieee80211_meshperr_ie) - 2 + 581195618Srpaulo (perr->perr_ndests - 1) * sizeof(*perr->perr_dests); 582195618Srpaulo *frm++ = perr->perr_mode; 583195618Srpaulo *frm++ = perr->perr_ndests; 584195618Srpaulo for (i = 0; i < perr->perr_ndests; i++) { 585195618Srpaulo IEEE80211_ADDR_COPY(frm, perr->perr_dests[i].dest_addr); 586195618Srpaulo frm += 6; 587195618Srpaulo ADDWORD(frm, perr->perr_dests[i].dest_seq); 588195618Srpaulo } 589195618Srpaulo return frm; 590195618Srpaulo} 591195618Srpaulo 592195618Srpaulo/* 593195618Srpaulo * Add a Root Annoucement IE to a frame. 594195618Srpaulo */ 595195618Srpaulostatic uint8_t * 596195618Srpaulohwmp_add_meshrann(uint8_t *frm, const struct ieee80211_meshrann_ie *rann) 597195618Srpaulo{ 598195618Srpaulo *frm++ = IEEE80211_ELEMID_MESHRANN; 599195618Srpaulo *frm++ = sizeof(struct ieee80211_meshrann_ie) - 2; 600195618Srpaulo *frm++ = rann->rann_flags; 601195618Srpaulo *frm++ = rann->rann_hopcount; 602195618Srpaulo *frm++ = rann->rann_ttl; 603195618Srpaulo IEEE80211_ADDR_COPY(frm, rann->rann_addr); frm += 6; 604195618Srpaulo ADDWORD(frm, rann->rann_seq); 605195618Srpaulo ADDWORD(frm, rann->rann_metric); 606195618Srpaulo return frm; 607195618Srpaulo} 608195618Srpaulo 609195618Srpaulostatic void 610195618Srpaulohwmp_rootmode_setup(struct ieee80211vap *vap) 611195618Srpaulo{ 612195618Srpaulo struct ieee80211_hwmp_state *hs = vap->iv_hwmp; 613195618Srpaulo 614195618Srpaulo switch (hs->hs_rootmode) { 615195618Srpaulo case IEEE80211_HWMP_ROOTMODE_DISABLED: 616195618Srpaulo callout_drain(&hs->hs_roottimer); 617195618Srpaulo break; 618195618Srpaulo case IEEE80211_HWMP_ROOTMODE_NORMAL: 619195618Srpaulo case IEEE80211_HWMP_ROOTMODE_PROACTIVE: 620195618Srpaulo callout_reset(&hs->hs_roottimer, HWMP_ROOTMODEINT, 621195618Srpaulo hwmp_rootmode_cb, vap); 622195618Srpaulo break; 623195618Srpaulo case IEEE80211_HWMP_ROOTMODE_RANN: 624195618Srpaulo callout_reset(&hs->hs_roottimer, HWMP_RANNMODEINT, 625195618Srpaulo hwmp_rootmode_rann_cb, vap); 626195618Srpaulo break; 627195618Srpaulo } 628195618Srpaulo} 629195618Srpaulo 630195618Srpaulo/* 631195618Srpaulo * Send a broadcast Path Request to find all nodes on the mesh. We are 632195618Srpaulo * called when the vap is configured as a HWMP root node. 633195618Srpaulo */ 634195618Srpaulo#define PREQ_TFLAGS(n) preq.preq_targets[n].target_flags 635195618Srpaulo#define PREQ_TADDR(n) preq.preq_targets[n].target_addr 636195618Srpaulo#define PREQ_TSEQ(n) preq.preq_targets[n].target_seq 637195618Srpaulostatic void 638195618Srpaulohwmp_rootmode_cb(void *arg) 639195618Srpaulo{ 640195618Srpaulo struct ieee80211vap *vap = (struct ieee80211vap *)arg; 641195618Srpaulo struct ieee80211_hwmp_state *hs = vap->iv_hwmp; 642195618Srpaulo struct ieee80211_mesh_state *ms = vap->iv_mesh; 643195618Srpaulo struct ieee80211_meshpreq_ie preq; 644195618Srpaulo 645195618Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, vap->iv_bss, 646195784Srpaulo "%s", "send broadcast PREQ"); 647195618Srpaulo 648195618Srpaulo /* XXX check portal role */ 649195618Srpaulo preq.preq_flags = IEEE80211_MESHPREQ_FLAGS_AM; 650195618Srpaulo if (hs->hs_rootmode == IEEE80211_HWMP_ROOTMODE_PROACTIVE) 651195618Srpaulo preq.preq_flags |= IEEE80211_MESHPREQ_FLAGS_PP; 652195618Srpaulo preq.preq_hopcount = 0; 653195618Srpaulo preq.preq_ttl = ms->ms_ttl; 654195618Srpaulo preq.preq_id = ++hs->hs_preqid; 655195618Srpaulo IEEE80211_ADDR_COPY(preq.preq_origaddr, vap->iv_myaddr); 656195618Srpaulo preq.preq_origseq = ++hs->hs_seq; 657195618Srpaulo preq.preq_lifetime = timeval2msecs(ieee80211_hwmp_roottimeout); 658195618Srpaulo preq.preq_metric = IEEE80211_MESHLMETRIC_INITIALVAL; 659195618Srpaulo preq.preq_tcount = 1; 660195618Srpaulo IEEE80211_ADDR_COPY(PREQ_TADDR(0), broadcastaddr); 661195618Srpaulo PREQ_TFLAGS(0) = IEEE80211_MESHPREQ_TFLAGS_TO | 662195618Srpaulo IEEE80211_MESHPREQ_TFLAGS_RF; 663195618Srpaulo PREQ_TSEQ(0) = 0; 664195618Srpaulo vap->iv_stats.is_hwmp_rootreqs++; 665195618Srpaulo hwmp_send_preq(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &preq); 666195618Srpaulo hwmp_rootmode_setup(vap); 667195618Srpaulo} 668195618Srpaulo#undef PREQ_TFLAGS 669195618Srpaulo#undef PREQ_TADDR 670195618Srpaulo#undef PREQ_TSEQ 671195618Srpaulo 672195618Srpaulo/* 673195618Srpaulo * Send a Root Annoucement (RANN) to find all the nodes on the mesh. We are 674195618Srpaulo * called when the vap is configured as a HWMP RANN root node. 675195618Srpaulo */ 676195618Srpaulostatic void 677195618Srpaulohwmp_rootmode_rann_cb(void *arg) 678195618Srpaulo{ 679195618Srpaulo struct ieee80211vap *vap = (struct ieee80211vap *)arg; 680195618Srpaulo struct ieee80211_hwmp_state *hs = vap->iv_hwmp; 681195618Srpaulo struct ieee80211_mesh_state *ms = vap->iv_mesh; 682195618Srpaulo struct ieee80211_meshrann_ie rann; 683195618Srpaulo 684195618Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, vap->iv_bss, 685195784Srpaulo "%s", "send broadcast RANN"); 686195618Srpaulo 687195618Srpaulo /* XXX check portal role */ 688195618Srpaulo rann.rann_flags = 0; 689195618Srpaulo rann.rann_hopcount = 0; 690195618Srpaulo rann.rann_ttl = ms->ms_ttl; 691195618Srpaulo IEEE80211_ADDR_COPY(rann.rann_addr, vap->iv_myaddr); 692195618Srpaulo rann.rann_seq = ++hs->hs_seq; 693195618Srpaulo rann.rann_metric = IEEE80211_MESHLMETRIC_INITIALVAL; 694195618Srpaulo 695195618Srpaulo vap->iv_stats.is_hwmp_rootrann++; 696195618Srpaulo hwmp_send_rann(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &rann); 697195618Srpaulo hwmp_rootmode_setup(vap); 698195618Srpaulo} 699195618Srpaulo 700195618Srpaulo#define PREQ_TFLAGS(n) preq->preq_targets[n].target_flags 701195618Srpaulo#define PREQ_TADDR(n) preq->preq_targets[n].target_addr 702195618Srpaulo#define PREQ_TSEQ(n) preq->preq_targets[n].target_seq 703195618Srpaulostatic void 704195618Srpaulohwmp_recv_preq(struct ieee80211vap *vap, struct ieee80211_node *ni, 705195618Srpaulo const struct ieee80211_frame *wh, const struct ieee80211_meshpreq_ie *preq) 706195618Srpaulo{ 707195618Srpaulo struct ieee80211_mesh_state *ms = vap->iv_mesh; 708195618Srpaulo struct ieee80211_mesh_route *rt = NULL; 709195618Srpaulo struct ieee80211_hwmp_route *hr; 710195618Srpaulo struct ieee80211_hwmp_state *hs = vap->iv_hwmp; 711195618Srpaulo struct ieee80211_meshprep_ie prep; 712195618Srpaulo 713195618Srpaulo if (ni == vap->iv_bss || 714195618Srpaulo ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED) 715195618Srpaulo return; 716195618Srpaulo /* 717195618Srpaulo * Ignore PREQs from us. Could happen because someone forward it 718195618Srpaulo * back to us. 719195618Srpaulo */ 720195618Srpaulo if (IEEE80211_ADDR_EQ(vap->iv_myaddr, preq->preq_origaddr)) 721195618Srpaulo return; 722195618Srpaulo 723195618Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 724195618Srpaulo "received PREQ, source %s", ether_sprintf(preq->preq_origaddr)); 725195618Srpaulo 726195618Srpaulo /* 727195618Srpaulo * Acceptance criteria: if the PREQ is not for us and 728195618Srpaulo * forwarding is disabled, discard this PREQ. 729195618Srpaulo */ 730195618Srpaulo if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0)) && 731195618Srpaulo !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) { 732195618Srpaulo IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_HWMP, 733195618Srpaulo preq->preq_origaddr, NULL, "%s", "not accepting PREQ"); 734195618Srpaulo return; 735195618Srpaulo } 736195618Srpaulo /* 737195618Srpaulo * Check if the PREQ is addressed to us. 738195618Srpaulo * XXX: check if this is part of a proxy address. 739195618Srpaulo */ 740195618Srpaulo if (IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0))) { 741195618Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 742195784Srpaulo "reply to %s", ether_sprintf(preq->preq_origaddr)); 743195618Srpaulo /* 744195618Srpaulo * Build and send a PREP frame. 745195618Srpaulo */ 746195618Srpaulo prep.prep_flags = 0; 747195618Srpaulo prep.prep_hopcount = 0; 748195618Srpaulo prep.prep_ttl = ms->ms_ttl; 749195618Srpaulo IEEE80211_ADDR_COPY(prep.prep_targetaddr, preq->preq_origaddr); 750195618Srpaulo prep.prep_targetseq = preq->preq_origseq; 751195618Srpaulo prep.prep_lifetime = preq->preq_lifetime; 752195618Srpaulo prep.prep_metric = IEEE80211_MESHLMETRIC_INITIALVAL; 753195618Srpaulo IEEE80211_ADDR_COPY(prep.prep_origaddr, vap->iv_myaddr); 754195618Srpaulo prep.prep_origseq = ++hs->hs_seq; 755195618Srpaulo hwmp_send_prep(ni, vap->iv_myaddr, wh->i_addr2, &prep); 756195618Srpaulo /* 757195618Srpaulo * Build the reverse path, if we don't have it already. 758195618Srpaulo */ 759195618Srpaulo rt = ieee80211_mesh_rt_find(vap, preq->preq_origaddr); 760195618Srpaulo if (rt == NULL) 761195618Srpaulo hwmp_discover(vap, preq->preq_origaddr, NULL); 762195784Srpaulo else if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0) 763195618Srpaulo hwmp_discover(vap, rt->rt_dest, NULL); 764195618Srpaulo return; 765195618Srpaulo } 766195618Srpaulo /* 767195618Srpaulo * Proactive PREQ: reply with a proactive PREP to the 768195618Srpaulo * root STA if requested. 769195618Srpaulo */ 770195618Srpaulo if (IEEE80211_ADDR_EQ(PREQ_TADDR(0), broadcastaddr) && 771195618Srpaulo (PREQ_TFLAGS(0) & 772195618Srpaulo ((IEEE80211_MESHPREQ_TFLAGS_TO|IEEE80211_MESHPREQ_TFLAGS_RF) == 773195618Srpaulo (IEEE80211_MESHPREQ_TFLAGS_TO|IEEE80211_MESHPREQ_TFLAGS_RF)))) { 774195618Srpaulo uint8_t rootmac[IEEE80211_ADDR_LEN]; 775195618Srpaulo 776195618Srpaulo IEEE80211_ADDR_COPY(rootmac, preq->preq_origaddr); 777195618Srpaulo rt = ieee80211_mesh_rt_find(vap, rootmac); 778195784Srpaulo if (rt == NULL) { 779195618Srpaulo rt = ieee80211_mesh_rt_add(vap, rootmac); 780195784Srpaulo if (rt == NULL) { 781195784Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 782195784Srpaulo "unable to add root mesh path to %s", 783195784Srpaulo ether_sprintf(rootmac)); 784195784Srpaulo vap->iv_stats.is_mesh_rtaddfailed++; 785195784Srpaulo return; 786195784Srpaulo } 787195784Srpaulo } 788195784Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 789195784Srpaulo "root mesh station @ %s", ether_sprintf(rootmac)); 790195784Srpaulo 791195618Srpaulo /* 792195618Srpaulo * Reply with a PREP if we don't have a path to the root 793195618Srpaulo * or if the root sent us a proactive PREQ. 794195618Srpaulo */ 795195784Srpaulo if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0 || 796195618Srpaulo (preq->preq_flags & IEEE80211_MESHPREQ_FLAGS_PP)) { 797195618Srpaulo prep.prep_flags = 0; 798195618Srpaulo prep.prep_hopcount = 0; 799195618Srpaulo prep.prep_ttl = ms->ms_ttl; 800195784Srpaulo IEEE80211_ADDR_COPY(prep.prep_origaddr, vap->iv_myaddr); 801195618Srpaulo prep.prep_origseq = preq->preq_origseq; 802195618Srpaulo prep.prep_targetseq = ++hs->hs_seq; 803195618Srpaulo prep.prep_lifetime = preq->preq_lifetime; 804195618Srpaulo prep.prep_metric = IEEE80211_MESHLMETRIC_INITIALVAL; 805195784Srpaulo IEEE80211_ADDR_COPY(prep.prep_targetaddr, rootmac); 806195618Srpaulo prep.prep_targetseq = PREQ_TSEQ(0); 807195618Srpaulo hwmp_send_prep(vap->iv_bss, vap->iv_myaddr, 808195618Srpaulo broadcastaddr, &prep); 809195618Srpaulo } 810195618Srpaulo hwmp_discover(vap, rootmac, NULL); 811195618Srpaulo return; 812195618Srpaulo } 813195618Srpaulo rt = ieee80211_mesh_rt_find(vap, PREQ_TADDR(0)); 814195618Srpaulo 815195618Srpaulo /* XXX missing. Check for AE bit and update proxy information */ 816195618Srpaulo 817195618Srpaulo /* 818195618Srpaulo * Forwarding and Intermediate reply for PREQs with 1 target. 819195618Srpaulo */ 820195618Srpaulo if (preq->preq_tcount == 1) { 821195618Srpaulo struct ieee80211_meshpreq_ie ppreq; /* propagated PREQ */ 822195618Srpaulo 823195618Srpaulo memcpy(&ppreq, preq, sizeof(ppreq)); 824195618Srpaulo /* 825195618Srpaulo * We have a valid route to this node. 826195618Srpaulo */ 827195618Srpaulo if (rt != NULL && 828195784Srpaulo (rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID)) { 829195618Srpaulo 830195618Srpaulo hr = IEEE80211_MESH_ROUTE_PRIV(rt, 831195618Srpaulo struct ieee80211_hwmp_route); 832195618Srpaulo hr->hr_preqid = preq->preq_id; 833195618Srpaulo hr->hr_seq = preq->preq_origseq; 834195618Srpaulo if (preq->preq_ttl > 1 && 835195618Srpaulo preq->preq_hopcount < hs->hs_maxhops) { 836195618Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 837195784Srpaulo "forward PREQ from %s", 838195618Srpaulo ether_sprintf(preq->preq_origaddr)); 839195618Srpaulo /* 840195618Srpaulo * Propagate the original PREQ. 841195618Srpaulo */ 842195618Srpaulo ppreq.preq_hopcount += 1; 843195618Srpaulo ppreq.preq_ttl -= 1; 844195618Srpaulo ppreq.preq_metric += 845195618Srpaulo ms->ms_pmetric->mpm_metric(ni); 846195618Srpaulo /* 847195618Srpaulo * Set TO and unset RF bits because we are going 848195618Srpaulo * to send a PREP next. 849195618Srpaulo */ 850195618Srpaulo ppreq.preq_targets[0].target_flags |= 851195618Srpaulo IEEE80211_MESHPREQ_TFLAGS_TO; 852195618Srpaulo ppreq.preq_targets[0].target_flags &= 853195618Srpaulo ~IEEE80211_MESHPREQ_TFLAGS_RF; 854195618Srpaulo hwmp_send_preq(ni, vap->iv_myaddr, 855195618Srpaulo broadcastaddr, &ppreq); 856195618Srpaulo } 857195618Srpaulo /* 858195618Srpaulo * Check if we can send an intermediate Path Reply, 859195618Srpaulo * i.e., Target Only bit is not set. 860195618Srpaulo */ 861195618Srpaulo if (!(PREQ_TFLAGS(0) & IEEE80211_MESHPREQ_TFLAGS_TO)) { 862195618Srpaulo struct ieee80211_meshprep_ie prep; 863195618Srpaulo 864195618Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 865195618Srpaulo "intermediate reply for PREQ from %s", 866195618Srpaulo ether_sprintf(preq->preq_origaddr)); 867195618Srpaulo prep.prep_flags = 0; 868195618Srpaulo prep.prep_hopcount = rt->rt_nhops + 1; 869195618Srpaulo prep.prep_ttl = ms->ms_ttl; 870195618Srpaulo IEEE80211_ADDR_COPY(&prep.prep_targetaddr, 871195618Srpaulo preq->preq_origaddr); 872195618Srpaulo prep.prep_targetseq = hr->hr_seq; 873195618Srpaulo prep.prep_lifetime = preq->preq_lifetime; 874195618Srpaulo prep.prep_metric = rt->rt_metric + 875195618Srpaulo ms->ms_pmetric->mpm_metric(ni); 876195618Srpaulo IEEE80211_ADDR_COPY(&prep.prep_origaddr, 877195618Srpaulo PREQ_TADDR(0)); 878195618Srpaulo prep.prep_origseq = hs->hs_seq++; 879195618Srpaulo hwmp_send_prep(ni, vap->iv_myaddr, 880195618Srpaulo broadcastaddr, &prep); 881195618Srpaulo } 882195618Srpaulo /* 883195618Srpaulo * We have no information about this path, 884195618Srpaulo * propagate the PREQ. 885195618Srpaulo */ 886195618Srpaulo } else if (preq->preq_ttl > 1 && 887195618Srpaulo preq->preq_hopcount < hs->hs_maxhops) { 888195784Srpaulo if (rt == NULL) { 889195618Srpaulo rt = ieee80211_mesh_rt_add(vap, PREQ_TADDR(0)); 890195784Srpaulo if (rt == NULL) { 891195784Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, 892195784Srpaulo ni, "unable to add PREQ path to %s", 893195784Srpaulo ether_sprintf(PREQ_TADDR(0))); 894195784Srpaulo vap->iv_stats.is_mesh_rtaddfailed++; 895195784Srpaulo return; 896195784Srpaulo } 897195784Srpaulo } 898195618Srpaulo hr = IEEE80211_MESH_ROUTE_PRIV(rt, 899195618Srpaulo struct ieee80211_hwmp_route); 900195618Srpaulo rt->rt_metric = preq->preq_metric; 901195618Srpaulo rt->rt_lifetime = preq->preq_lifetime; 902195618Srpaulo hr->hr_seq = preq->preq_origseq; 903195618Srpaulo hr->hr_preqid = preq->preq_id; 904195618Srpaulo 905195618Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 906195784Srpaulo "forward PREQ from %s", 907195618Srpaulo ether_sprintf(preq->preq_origaddr)); 908195618Srpaulo ppreq.preq_hopcount += 1; 909195618Srpaulo ppreq.preq_ttl -= 1; 910195618Srpaulo ppreq.preq_metric += ms->ms_pmetric->mpm_metric(ni); 911195618Srpaulo hwmp_send_preq(ni, vap->iv_myaddr, broadcastaddr, 912195618Srpaulo &ppreq); 913195618Srpaulo } 914195618Srpaulo } 915195618Srpaulo 916195618Srpaulo} 917195618Srpaulo#undef PREQ_TFLAGS 918195618Srpaulo#undef PREQ_TADDR 919195618Srpaulo#undef PREQ_TSEQ 920195618Srpaulo 921195618Srpaulostatic int 922195618Srpaulohwmp_send_preq(struct ieee80211_node *ni, 923195618Srpaulo const uint8_t sa[IEEE80211_ADDR_LEN], 924195618Srpaulo const uint8_t da[IEEE80211_ADDR_LEN], 925195618Srpaulo struct ieee80211_meshpreq_ie *preq) 926195618Srpaulo{ 927195618Srpaulo struct ieee80211_hwmp_state *hs = ni->ni_vap->iv_hwmp; 928195618Srpaulo 929195618Srpaulo /* 930195618Srpaulo * Enforce PREQ interval. 931195618Srpaulo */ 932195618Srpaulo if (ratecheck(&hs->hs_lastpreq, &ieee80211_hwmp_preqminint) == 0) 933195618Srpaulo return EALREADY; 934195618Srpaulo getmicrouptime(&hs->hs_lastpreq); 935195618Srpaulo 936195618Srpaulo /* 937195618Srpaulo * mesh preq action frame format 938195618Srpaulo * [6] da 939195618Srpaulo * [6] sa 940195618Srpaulo * [6] addr3 = sa 941195618Srpaulo * [1] action 942195618Srpaulo * [1] category 943195618Srpaulo * [tlv] mesh path request 944195618Srpaulo */ 945195618Srpaulo preq->preq_ie = IEEE80211_ELEMID_MESHPREQ; 946195618Srpaulo return hwmp_send_action(ni, sa, da, (uint8_t *)preq, 947195618Srpaulo sizeof(struct ieee80211_meshpreq_ie)); 948195618Srpaulo} 949195618Srpaulo 950195618Srpaulostatic void 951195618Srpaulohwmp_recv_prep(struct ieee80211vap *vap, struct ieee80211_node *ni, 952195618Srpaulo const struct ieee80211_frame *wh, const struct ieee80211_meshprep_ie *prep) 953195618Srpaulo{ 954195618Srpaulo struct ieee80211_mesh_state *ms = vap->iv_mesh; 955195618Srpaulo struct ieee80211_hwmp_state *hs = vap->iv_hwmp; 956195618Srpaulo struct ieee80211_mesh_route *rt = NULL; 957195618Srpaulo struct ieee80211_hwmp_route *hr; 958195618Srpaulo struct ieee80211com *ic = vap->iv_ic; 959195618Srpaulo struct ifnet *ifp = vap->iv_ifp; 960195618Srpaulo struct mbuf *m, *next; 961195618Srpaulo 962195618Srpaulo /* 963195618Srpaulo * Acceptance criteria: if the corresponding PREQ was not generated 964195618Srpaulo * by us and forwarding is disabled, discard this PREP. 965195618Srpaulo */ 966195618Srpaulo if (ni == vap->iv_bss || 967195618Srpaulo ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED) 968195618Srpaulo return; 969195618Srpaulo if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, prep->prep_origaddr) && 970195618Srpaulo !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) 971195618Srpaulo return; 972195618Srpaulo 973195618Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 974195618Srpaulo "received PREP from %s", ether_sprintf(prep->prep_origaddr)); 975195618Srpaulo 976195618Srpaulo /* 977195618Srpaulo * If it's NOT for us, propagate the PREP. 978195618Srpaulo */ 979195618Srpaulo if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, prep->prep_targetaddr) && 980195618Srpaulo prep->prep_ttl > 1 && prep->prep_hopcount < hs->hs_maxhops) { 981195618Srpaulo struct ieee80211_meshprep_ie pprep; /* propagated PREP */ 982195618Srpaulo 983195618Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 984195784Srpaulo "propagate PREP from %s", 985195618Srpaulo ether_sprintf(prep->prep_origaddr)); 986195618Srpaulo 987195618Srpaulo memcpy(&pprep, prep, sizeof(pprep)); 988195618Srpaulo pprep.prep_hopcount += 1; 989195618Srpaulo pprep.prep_ttl -= 1; 990195618Srpaulo pprep.prep_metric += ms->ms_pmetric->mpm_metric(ni); 991195618Srpaulo IEEE80211_ADDR_COPY(pprep.prep_origaddr, vap->iv_myaddr); 992195618Srpaulo hwmp_send_prep(ni, vap->iv_myaddr, broadcastaddr, &pprep); 993195618Srpaulo } 994195618Srpaulo 995195618Srpaulo rt = ieee80211_mesh_rt_find(vap, prep->prep_origaddr); 996195618Srpaulo if (rt == NULL) { 997195618Srpaulo /* 998195618Srpaulo * If we have no entry this could be a reply to a root PREQ. 999195618Srpaulo */ 1000195618Srpaulo if (hs->hs_rootmode != IEEE80211_HWMP_ROOTMODE_DISABLED) { 1001195618Srpaulo rt = ieee80211_mesh_rt_add(vap, prep->prep_origaddr); 1002195784Srpaulo if (rt == NULL) { 1003195784Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, 1004195784Srpaulo ni, "unable to add PREP path to %s", 1005195784Srpaulo ether_sprintf(prep->prep_origaddr)); 1006195784Srpaulo vap->iv_stats.is_mesh_rtaddfailed++; 1007195784Srpaulo return; 1008195784Srpaulo } 1009195618Srpaulo IEEE80211_ADDR_COPY(rt->rt_nexthop, wh->i_addr2); 1010195618Srpaulo rt->rt_nhops = prep->prep_hopcount; 1011195618Srpaulo rt->rt_lifetime = prep->prep_lifetime; 1012195618Srpaulo rt->rt_metric = prep->prep_metric; 1013195784Srpaulo rt->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID; 1014195618Srpaulo return; 1015195618Srpaulo } 1016195618Srpaulo return; 1017195618Srpaulo } 1018195618Srpaulo hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route); 1019195618Srpaulo if (prep->prep_targetseq == hr->hr_seq) { 1020195618Srpaulo int useprep = 0; 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 */ 1026195784Srpaulo if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0) 1027195618Srpaulo useprep = 1; 1028195618Srpaulo else if (prep->prep_hopcount < rt->rt_nhops || 1029195618Srpaulo prep->prep_metric < rt->rt_metric) 1030195618Srpaulo useprep = 1; 1031195618Srpaulo if (useprep) { 1032195618Srpaulo IEEE80211_ADDR_COPY(rt->rt_nexthop, wh->i_addr2); 1033195618Srpaulo rt->rt_nhops = prep->prep_hopcount; 1034195618Srpaulo rt->rt_lifetime = prep->prep_lifetime; 1035195618Srpaulo rt->rt_metric = prep->prep_metric; 1036195784Srpaulo rt->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID; 1037195618Srpaulo } 1038195618Srpaulo } else { 1039195618Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 1040195618Srpaulo "discard PREP from %s, wrong seqno %u != %u", 1041195618Srpaulo ether_sprintf(prep->prep_origaddr), prep->prep_targetseq, 1042195618Srpaulo hr->hr_seq); 1043195618Srpaulo vap->iv_stats.is_hwmp_wrongseq++; 1044195618Srpaulo } 1045195618Srpaulo 1046195618Srpaulo /* 1047195618Srpaulo * XXX: If it's for us and the AE bit is set, update the 1048195618Srpaulo * proxy information table. 1049195618Srpaulo */ 1050195618Srpaulo 1051195618Srpaulo /* 1052195618Srpaulo * XXX: If it's NOT for us and the AE bit is set, 1053195618Srpaulo * update the proxy information table. 1054195618Srpaulo */ 1055195618Srpaulo 1056195618Srpaulo /* 1057195618Srpaulo * Check for frames queued awaiting path discovery. 1058195618Srpaulo * XXX probably can tell exactly and avoid remove call 1059195618Srpaulo * NB: hash may have false matches, if so they will get 1060195618Srpaulo * stuck back on the stageq because there won't be 1061195618Srpaulo * a path. 1062195618Srpaulo */ 1063195618Srpaulo m = ieee80211_ageq_remove(&ic->ic_stageq, 1064195618Srpaulo (struct ieee80211_node *)(uintptr_t) 1065195618Srpaulo ieee80211_mac_hash(ic, rt->rt_dest)); 1066195618Srpaulo for (; m != NULL; m = next) { 1067195618Srpaulo next = m->m_nextpkt; 1068195618Srpaulo m->m_nextpkt = NULL; 1069195784Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 1070195784Srpaulo "flush queued frame %p len %d", m, m->m_pkthdr.len); 1071195618Srpaulo ifp->if_transmit(ifp, m); 1072195618Srpaulo } 1073195618Srpaulo} 1074195618Srpaulo 1075195618Srpaulostatic int 1076195618Srpaulohwmp_send_prep(struct ieee80211_node *ni, 1077195618Srpaulo const uint8_t sa[IEEE80211_ADDR_LEN], 1078195618Srpaulo const uint8_t da[IEEE80211_ADDR_LEN], 1079195618Srpaulo struct ieee80211_meshprep_ie *prep) 1080195618Srpaulo{ 1081195784Srpaulo /* NB: there's no PREP minimum interval. */ 1082195618Srpaulo 1083195618Srpaulo /* 1084195618Srpaulo * mesh prep action frame format 1085195618Srpaulo * [6] da 1086195618Srpaulo * [6] sa 1087195618Srpaulo * [6] addr3 = sa 1088195618Srpaulo * [1] action 1089195618Srpaulo * [1] category 1090195618Srpaulo * [tlv] mesh path reply 1091195618Srpaulo */ 1092195618Srpaulo prep->prep_ie = IEEE80211_ELEMID_MESHPREP; 1093195618Srpaulo return hwmp_send_action(ni, sa, da, (uint8_t *)prep, 1094195618Srpaulo sizeof(struct ieee80211_meshprep_ie)); 1095195618Srpaulo} 1096195618Srpaulo 1097195618Srpaulo#define PERR_DADDR(n) perr.perr_dests[n].dest_addr 1098195618Srpaulo#define PERR_DSEQ(n) perr.perr_dests[n].dest_seq 1099195618Srpaulostatic void 1100195618Srpaulohwmp_peerdown(struct ieee80211_node *ni) 1101195618Srpaulo{ 1102195618Srpaulo struct ieee80211vap *vap = ni->ni_vap; 1103195618Srpaulo struct ieee80211_meshperr_ie perr; 1104195618Srpaulo struct ieee80211_mesh_route *rt; 1105195618Srpaulo struct ieee80211_hwmp_route *hr; 1106195618Srpaulo 1107195618Srpaulo rt = ieee80211_mesh_rt_find(vap, ni->ni_macaddr); 1108195618Srpaulo if (rt == NULL) 1109195618Srpaulo return; 1110195618Srpaulo hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route); 1111195618Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 1112195784Srpaulo "%s", "delete route entry"); 1113195618Srpaulo perr.perr_mode = 0; 1114195618Srpaulo perr.perr_ndests = 1; 1115195618Srpaulo IEEE80211_ADDR_COPY(PERR_DADDR(0), rt->rt_dest); 1116195618Srpaulo PERR_DSEQ(0) = hr->hr_seq; 1117195618Srpaulo ieee80211_mesh_rt_del(vap, ni->ni_macaddr); 1118195618Srpaulo hwmp_send_perr(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &perr); 1119195618Srpaulo} 1120195618Srpaulo#undef PERR_DADDR 1121195618Srpaulo#undef PERR_DSEQ 1122195618Srpaulo 1123195618Srpaulo#define PERR_DADDR(n) perr->perr_dests[n].dest_addr 1124195618Srpaulo#define PERR_DSEQ(n) perr->perr_dests[n].dest_seq 1125195618Srpaulostatic void 1126195618Srpaulohwmp_recv_perr(struct ieee80211vap *vap, struct ieee80211_node *ni, 1127195618Srpaulo const struct ieee80211_frame *wh, const struct ieee80211_meshperr_ie *perr) 1128195618Srpaulo{ 1129195618Srpaulo struct ieee80211_mesh_state *ms = vap->iv_mesh; 1130195618Srpaulo struct ieee80211_mesh_route *rt = NULL; 1131195618Srpaulo struct ieee80211_hwmp_route *hr; 1132195618Srpaulo struct ieee80211_meshperr_ie pperr; 1133195618Srpaulo int i, forward = 0; 1134195618Srpaulo 1135195618Srpaulo /* 1136195618Srpaulo * Acceptance criteria: check if we received a PERR from a 1137195618Srpaulo * neighbor and forwarding is enabled. 1138195618Srpaulo */ 1139195618Srpaulo if (ni == vap->iv_bss || 1140195618Srpaulo ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED || 1141195618Srpaulo !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) 1142195618Srpaulo return; 1143195618Srpaulo /* 1144195618Srpaulo * Find all routing entries that match and delete them. 1145195618Srpaulo */ 1146195618Srpaulo for (i = 0; i < perr->perr_ndests; i++) { 1147195618Srpaulo rt = ieee80211_mesh_rt_find(vap, PERR_DADDR(i)); 1148195618Srpaulo if (rt == NULL) 1149195618Srpaulo continue; 1150195618Srpaulo hr = IEEE80211_MESH_ROUTE_PRIV(rt, 1151195618Srpaulo struct ieee80211_hwmp_route); 1152195618Srpaulo if (PERR_DSEQ(i) >= hr->hr_seq) { 1153195618Srpaulo ieee80211_mesh_rt_del(vap, rt->rt_dest); 1154195618Srpaulo rt = NULL; 1155195618Srpaulo forward = 1; 1156195618Srpaulo } 1157195618Srpaulo } 1158195618Srpaulo /* 1159195618Srpaulo * Propagate the PERR if we previously found it on our routing table. 1160195618Srpaulo * XXX handle ndest > 1 1161195618Srpaulo */ 1162195618Srpaulo if (forward) { 1163195618Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 1164195784Srpaulo "propagate PERR from %s", ether_sprintf(wh->i_addr2)); 1165195618Srpaulo memcpy(&pperr, perr, sizeof(*perr)); 1166195618Srpaulo hwmp_send_perr(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &pperr); 1167195618Srpaulo } 1168195618Srpaulo} 1169195618Srpaulo#undef PEER_DADDR 1170195618Srpaulo#undef PERR_DSEQ 1171195618Srpaulo 1172195618Srpaulostatic int 1173195618Srpaulohwmp_send_perr(struct ieee80211_node *ni, 1174195618Srpaulo const uint8_t sa[IEEE80211_ADDR_LEN], 1175195618Srpaulo const uint8_t da[IEEE80211_ADDR_LEN], 1176195618Srpaulo struct ieee80211_meshperr_ie *perr) 1177195618Srpaulo{ 1178195618Srpaulo struct ieee80211_hwmp_state *hs = ni->ni_vap->iv_hwmp; 1179195618Srpaulo 1180195618Srpaulo /* 1181195618Srpaulo * Enforce PERR interval. 1182195618Srpaulo */ 1183195618Srpaulo if (ratecheck(&hs->hs_lastperr, &ieee80211_hwmp_perrminint) == 0) 1184195618Srpaulo return EALREADY; 1185195618Srpaulo getmicrouptime(&hs->hs_lastperr); 1186195618Srpaulo 1187195618Srpaulo /* 1188195618Srpaulo * mesh perr action frame format 1189195618Srpaulo * [6] da 1190195618Srpaulo * [6] sa 1191195618Srpaulo * [6] addr3 = sa 1192195618Srpaulo * [1] action 1193195618Srpaulo * [1] category 1194195618Srpaulo * [tlv] mesh path error 1195195618Srpaulo */ 1196195618Srpaulo perr->perr_ie = IEEE80211_ELEMID_MESHPERR; 1197195618Srpaulo return hwmp_send_action(ni, sa, da, (uint8_t *)perr, 1198195618Srpaulo sizeof(struct ieee80211_meshperr_ie)); 1199195618Srpaulo} 1200195618Srpaulo 1201195618Srpaulostatic void 1202195618Srpaulohwmp_recv_rann(struct ieee80211vap *vap, struct ieee80211_node *ni, 1203195618Srpaulo const struct ieee80211_frame *wh, const struct ieee80211_meshrann_ie *rann) 1204195618Srpaulo{ 1205195618Srpaulo struct ieee80211_mesh_state *ms = vap->iv_mesh; 1206195618Srpaulo struct ieee80211_hwmp_state *hs = vap->iv_hwmp; 1207195618Srpaulo struct ieee80211_mesh_route *rt = NULL; 1208195618Srpaulo struct ieee80211_hwmp_route *hr; 1209195618Srpaulo struct ieee80211_meshrann_ie prann; 1210195618Srpaulo 1211195618Srpaulo if (ni == vap->iv_bss || 1212195618Srpaulo ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED) 1213195618Srpaulo return; 1214195618Srpaulo 1215195618Srpaulo rt = ieee80211_mesh_rt_find(vap, rann->rann_addr); 1216195618Srpaulo /* 1217195618Srpaulo * Discover the path to the root mesh STA. 1218195618Srpaulo * If we already know it, propagate the RANN element. 1219195618Srpaulo */ 1220195618Srpaulo if (rt == NULL) { 1221195618Srpaulo hwmp_discover(vap, rann->rann_addr, NULL); 1222195618Srpaulo return; 1223195618Srpaulo } 1224195618Srpaulo hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route); 1225195618Srpaulo if (rann->rann_seq > hr->hr_seq && rann->rann_ttl > 1 && 1226195618Srpaulo rann->rann_hopcount < hs->hs_maxhops && 1227195618Srpaulo (ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) { 1228195618Srpaulo memcpy(&prann, rann, sizeof(prann)); 1229195618Srpaulo prann.rann_hopcount += 1; 1230195618Srpaulo prann.rann_ttl -= 1; 1231195618Srpaulo prann.rann_metric += ms->ms_pmetric->mpm_metric(ni); 1232195618Srpaulo hwmp_send_rann(vap->iv_bss, vap->iv_myaddr, broadcastaddr, 1233195618Srpaulo &prann); 1234195618Srpaulo } 1235195618Srpaulo} 1236195618Srpaulo 1237195618Srpaulostatic int 1238195618Srpaulohwmp_send_rann(struct ieee80211_node *ni, 1239195618Srpaulo const uint8_t sa[IEEE80211_ADDR_LEN], 1240195618Srpaulo const uint8_t da[IEEE80211_ADDR_LEN], 1241195618Srpaulo struct ieee80211_meshrann_ie *rann) 1242195618Srpaulo{ 1243195618Srpaulo /* 1244195618Srpaulo * mesh rann action frame format 1245195618Srpaulo * [6] da 1246195618Srpaulo * [6] sa 1247195618Srpaulo * [6] addr3 = sa 1248195618Srpaulo * [1] action 1249195618Srpaulo * [1] category 1250195618Srpaulo * [tlv] root annoucement 1251195618Srpaulo */ 1252195618Srpaulo rann->rann_ie = IEEE80211_ELEMID_MESHRANN; 1253195618Srpaulo return hwmp_send_action(ni, sa, da, (uint8_t *)rann, 1254195618Srpaulo sizeof(struct ieee80211_meshrann_ie)); 1255195618Srpaulo} 1256195618Srpaulo 1257195618Srpaulo#define PREQ_TFLAGS(n) preq.preq_targets[n].target_flags 1258195618Srpaulo#define PREQ_TADDR(n) preq.preq_targets[n].target_addr 1259195618Srpaulo#define PREQ_TSEQ(n) preq.preq_targets[n].target_seq 1260195618Srpaulostatic struct ieee80211_node * 1261195618Srpaulohwmp_discover(struct ieee80211vap *vap, 1262195618Srpaulo const uint8_t dest[IEEE80211_ADDR_LEN], struct mbuf *m) 1263195618Srpaulo{ 1264195618Srpaulo struct ieee80211_hwmp_state *hs = vap->iv_hwmp; 1265195618Srpaulo struct ieee80211_mesh_state *ms = vap->iv_mesh; 1266195618Srpaulo struct ieee80211_mesh_route *rt = NULL; 1267195618Srpaulo struct ieee80211_hwmp_route *hr; 1268195618Srpaulo struct ieee80211_meshpreq_ie preq; 1269195618Srpaulo struct ieee80211_node *ni; 1270195618Srpaulo int sendpreq = 0; 1271195618Srpaulo 1272195618Srpaulo KASSERT(vap->iv_opmode == IEEE80211_M_MBSS, 1273195618Srpaulo ("not a mesh vap, opmode %d", vap->iv_opmode)); 1274195618Srpaulo 1275195618Srpaulo KASSERT(!IEEE80211_ADDR_EQ(vap->iv_myaddr, dest), 1276195618Srpaulo ("%s: discovering self!", __func__)); 1277195618Srpaulo 1278195618Srpaulo ni = NULL; 1279195618Srpaulo if (!IEEE80211_IS_MULTICAST(dest)) { 1280195618Srpaulo rt = ieee80211_mesh_rt_find(vap, dest); 1281195618Srpaulo if (rt == NULL) { 1282195618Srpaulo rt = ieee80211_mesh_rt_add(vap, dest); 1283195618Srpaulo if (rt == NULL) { 1284195784Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, 1285195784Srpaulo ni, "unable to add discovery path to %s", 1286195784Srpaulo ether_sprintf(dest)); 1287195784Srpaulo vap->iv_stats.is_mesh_rtaddfailed++; 1288195618Srpaulo goto done; 1289195618Srpaulo } 1290195618Srpaulo } 1291195618Srpaulo hr = IEEE80211_MESH_ROUTE_PRIV(rt, 1292195618Srpaulo struct ieee80211_hwmp_route); 1293195784Srpaulo if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0) { 1294195618Srpaulo if (hr->hr_preqid == 0) { 1295195618Srpaulo hr->hr_seq = ++hs->hs_seq; 1296195618Srpaulo hr->hr_preqid = ++hs->hs_preqid; 1297195618Srpaulo } 1298195618Srpaulo rt->rt_metric = IEEE80211_MESHLMETRIC_INITIALVAL; 1299195618Srpaulo rt->rt_lifetime = 1300195618Srpaulo timeval2msecs(ieee80211_hwmp_pathtimeout); 1301195618Srpaulo /* XXX check preq retries */ 1302195618Srpaulo sendpreq = 1; 1303195618Srpaulo IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP, dest, 1304195784Srpaulo "start path discovery (src %s)", 1305195784Srpaulo m == NULL ? "<none>" : ether_sprintf( 1306195784Srpaulo mtod(m, struct ether_header *)->ether_shost)); 1307195618Srpaulo /* 1308195618Srpaulo * Try to discover the path for this node. 1309195618Srpaulo */ 1310195618Srpaulo preq.preq_flags = 0; 1311195618Srpaulo preq.preq_hopcount = 0; 1312195618Srpaulo preq.preq_ttl = ms->ms_ttl; 1313195618Srpaulo preq.preq_id = hr->hr_preqid; 1314195618Srpaulo IEEE80211_ADDR_COPY(preq.preq_origaddr, vap->iv_myaddr); 1315195618Srpaulo preq.preq_origseq = hr->hr_seq; 1316195618Srpaulo preq.preq_lifetime = rt->rt_lifetime; 1317195618Srpaulo preq.preq_metric = rt->rt_metric; 1318195618Srpaulo preq.preq_tcount = 1; 1319195618Srpaulo IEEE80211_ADDR_COPY(PREQ_TADDR(0), dest); 1320195618Srpaulo PREQ_TFLAGS(0) = 0; 1321195618Srpaulo if (ieee80211_hwmp_targetonly) 1322195618Srpaulo PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_TO; 1323195618Srpaulo if (ieee80211_hwmp_replyforward) 1324195618Srpaulo PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_RF; 1325195618Srpaulo PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_USN; 1326195618Srpaulo PREQ_TSEQ(0) = 0; 1327195618Srpaulo /* XXX check return value */ 1328195618Srpaulo hwmp_send_preq(vap->iv_bss, vap->iv_myaddr, 1329195618Srpaulo broadcastaddr, &preq); 1330195618Srpaulo } 1331195784Srpaulo if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) 1332195618Srpaulo ni = ieee80211_find_txnode(vap, rt->rt_nexthop); 1333195618Srpaulo } else { 1334195618Srpaulo ni = ieee80211_find_txnode(vap, dest); 1335195784Srpaulo /* NB: if null then we leak mbuf */ 1336195784Srpaulo KASSERT(ni != NULL, ("leak mcast frame")); 1337195618Srpaulo return ni; 1338195618Srpaulo } 1339195618Srpaulodone: 1340195618Srpaulo if (ni == NULL && m != NULL) { 1341195618Srpaulo if (sendpreq) { 1342195618Srpaulo struct ieee80211com *ic = vap->iv_ic; 1343195618Srpaulo /* 1344195618Srpaulo * Queue packet for transmit when path discovery 1345195618Srpaulo * completes. If discovery never completes the 1346195618Srpaulo * frame will be flushed by way of the aging timer. 1347195618Srpaulo */ 1348195784Srpaulo IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP, dest, 1349195784Srpaulo "%s", "queue frame until path found"); 1350195618Srpaulo m->m_pkthdr.rcvif = (void *)(uintptr_t) 1351195618Srpaulo ieee80211_mac_hash(ic, dest); 1352195618Srpaulo /* XXX age chosen randomly */ 1353195618Srpaulo ieee80211_ageq_append(&ic->ic_stageq, m, 1354195618Srpaulo IEEE80211_INACT_WAIT); 1355195784Srpaulo } else { 1356195784Srpaulo IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_HWMP, 1357195784Srpaulo dest, NULL, "%s", "no valid path to this node"); 1358195618Srpaulo m_freem(m); 1359195784Srpaulo } 1360195618Srpaulo } 1361195618Srpaulo return ni; 1362195618Srpaulo} 1363195618Srpaulo#undef PREQ_TFLAGS 1364195618Srpaulo#undef PREQ_TADDR 1365195618Srpaulo#undef PREQ_TSEQ 1366195618Srpaulo 1367195618Srpaulostatic int 1368195618Srpaulohwmp_ioctl_get80211(struct ieee80211vap *vap, struct ieee80211req *ireq) 1369195618Srpaulo{ 1370195618Srpaulo struct ieee80211_hwmp_state *hs = vap->iv_hwmp; 1371195618Srpaulo int error; 1372195618Srpaulo 1373195618Srpaulo if (vap->iv_opmode != IEEE80211_M_MBSS) 1374195618Srpaulo return ENOSYS; 1375195618Srpaulo error = 0; 1376195618Srpaulo switch (ireq->i_type) { 1377195618Srpaulo case IEEE80211_IOC_HWMP_ROOTMODE: 1378195618Srpaulo ireq->i_val = hs->hs_rootmode; 1379195618Srpaulo break; 1380195618Srpaulo case IEEE80211_IOC_HWMP_MAXHOPS: 1381195618Srpaulo ireq->i_val = hs->hs_maxhops; 1382195618Srpaulo break; 1383195618Srpaulo default: 1384195618Srpaulo return ENOSYS; 1385195618Srpaulo } 1386195618Srpaulo return error; 1387195618Srpaulo} 1388195618SrpauloIEEE80211_IOCTL_GET(hwmp, hwmp_ioctl_get80211); 1389195618Srpaulo 1390195618Srpaulostatic int 1391195618Srpaulohwmp_ioctl_set80211(struct ieee80211vap *vap, struct ieee80211req *ireq) 1392195618Srpaulo{ 1393195618Srpaulo struct ieee80211_hwmp_state *hs = vap->iv_hwmp; 1394195618Srpaulo int error; 1395195618Srpaulo 1396195618Srpaulo if (vap->iv_opmode != IEEE80211_M_MBSS) 1397195618Srpaulo return ENOSYS; 1398195618Srpaulo error = 0; 1399195618Srpaulo switch (ireq->i_type) { 1400195618Srpaulo case IEEE80211_IOC_HWMP_ROOTMODE: 1401195618Srpaulo if (ireq->i_val < 0 || ireq->i_val > 3) 1402195618Srpaulo return EINVAL; 1403195618Srpaulo hs->hs_rootmode = ireq->i_val; 1404195618Srpaulo hwmp_rootmode_setup(vap); 1405195618Srpaulo break; 1406195618Srpaulo case IEEE80211_IOC_HWMP_MAXHOPS: 1407195618Srpaulo if (ireq->i_val <= 0 || ireq->i_val > 255) 1408195618Srpaulo return EINVAL; 1409195618Srpaulo hs->hs_maxhops = ireq->i_val; 1410195618Srpaulo break; 1411195618Srpaulo default: 1412195618Srpaulo return ENOSYS; 1413195618Srpaulo } 1414195618Srpaulo return error; 1415195618Srpaulo} 1416195618SrpauloIEEE80211_IOCTL_SET(hwmp, hwmp_ioctl_set80211); 1417