ieee80211_mesh.c revision 234890
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_mesh.c 234890 2012-05-01 16:12:39Z monthadar $"); 32195618Srpaulo#endif 33195618Srpaulo 34195618Srpaulo/* 35195618Srpaulo * IEEE 802.11s Mesh Point (MBSS) support. 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 <net80211/ieee80211_var.h> 61195618Srpaulo#include <net80211/ieee80211_action.h> 62195618Srpaulo#include <net80211/ieee80211_input.h> 63195618Srpaulo#include <net80211/ieee80211_mesh.h> 64195618Srpaulo 65195784Srpaulostatic void mesh_rt_flush_invalid(struct ieee80211vap *); 66195618Srpaulostatic int mesh_select_proto_path(struct ieee80211vap *, const char *); 67195618Srpaulostatic int mesh_select_proto_metric(struct ieee80211vap *, const char *); 68195618Srpaulostatic void mesh_vattach(struct ieee80211vap *); 69195618Srpaulostatic int mesh_newstate(struct ieee80211vap *, enum ieee80211_state, int); 70195784Srpaulostatic void mesh_rt_cleanup_cb(void *); 71195661Srpaulostatic void mesh_linkchange(struct ieee80211_node *, 72195661Srpaulo enum ieee80211_mesh_mlstate); 73195618Srpaulostatic void mesh_checkid(void *, struct ieee80211_node *); 74195618Srpaulostatic uint32_t mesh_generateid(struct ieee80211vap *); 75195618Srpaulostatic int mesh_checkpseq(struct ieee80211vap *, 76195618Srpaulo const uint8_t [IEEE80211_ADDR_LEN], uint32_t); 77195618Srpaulostatic struct ieee80211_node * 78195618Srpaulo mesh_find_txnode(struct ieee80211vap *, 79195618Srpaulo const uint8_t [IEEE80211_ADDR_LEN]); 80195618Srpaulostatic void mesh_forward(struct ieee80211vap *, struct mbuf *, 81195618Srpaulo const struct ieee80211_meshcntl *); 82195618Srpaulostatic int mesh_input(struct ieee80211_node *, struct mbuf *, int, int); 83195618Srpaulostatic void mesh_recv_mgmt(struct ieee80211_node *, struct mbuf *, int, 84195618Srpaulo int, int); 85205277Srpaulostatic void mesh_recv_ctl(struct ieee80211_node *, struct mbuf *, int); 86195618Srpaulostatic void mesh_peer_timeout_setup(struct ieee80211_node *); 87195618Srpaulostatic void mesh_peer_timeout_backoff(struct ieee80211_node *); 88195618Srpaulostatic void mesh_peer_timeout_cb(void *); 89195618Srpaulostatic __inline void 90195618Srpaulo mesh_peer_timeout_stop(struct ieee80211_node *); 91195618Srpaulostatic int mesh_verify_meshid(struct ieee80211vap *, const uint8_t *); 92195618Srpaulostatic int mesh_verify_meshconf(struct ieee80211vap *, const uint8_t *); 93197413Srpaulostatic int mesh_verify_meshpeer(struct ieee80211vap *, uint8_t, 94197413Srpaulo const uint8_t *); 95195618Srpaulouint32_t mesh_airtime_calc(struct ieee80211_node *); 96195618Srpaulo 97195618Srpaulo/* 98195618Srpaulo * Timeout values come from the specification and are in milliseconds. 99195618Srpaulo */ 100227309Sedstatic SYSCTL_NODE(_net_wlan, OID_AUTO, mesh, CTLFLAG_RD, 0, 101195618Srpaulo "IEEE 802.11s parameters"); 102195618Srpaulostatic int ieee80211_mesh_retrytimeout = -1; 103195618SrpauloSYSCTL_PROC(_net_wlan_mesh, OID_AUTO, retrytimeout, CTLTYPE_INT | CTLFLAG_RW, 104195618Srpaulo &ieee80211_mesh_retrytimeout, 0, ieee80211_sysctl_msecs_ticks, "I", 105195618Srpaulo "Retry timeout (msec)"); 106195618Srpaulostatic int ieee80211_mesh_holdingtimeout = -1; 107195618SrpauloSYSCTL_PROC(_net_wlan_mesh, OID_AUTO, holdingtimeout, CTLTYPE_INT | CTLFLAG_RW, 108195618Srpaulo &ieee80211_mesh_holdingtimeout, 0, ieee80211_sysctl_msecs_ticks, "I", 109195618Srpaulo "Holding state timeout (msec)"); 110195618Srpaulostatic int ieee80211_mesh_confirmtimeout = -1; 111195618SrpauloSYSCTL_PROC(_net_wlan_mesh, OID_AUTO, confirmtimeout, CTLTYPE_INT | CTLFLAG_RW, 112195618Srpaulo &ieee80211_mesh_confirmtimeout, 0, ieee80211_sysctl_msecs_ticks, "I", 113195618Srpaulo "Confirm state timeout (msec)"); 114195618Srpaulostatic int ieee80211_mesh_maxretries = 2; 115195618SrpauloSYSCTL_INT(_net_wlan_mesh, OID_AUTO, maxretries, CTLTYPE_INT | CTLFLAG_RW, 116195618Srpaulo &ieee80211_mesh_maxretries, 0, 117195618Srpaulo "Maximum retries during peer link establishment"); 118195618Srpaulo 119195618Srpaulostatic const uint8_t broadcastaddr[IEEE80211_ADDR_LEN] = 120195618Srpaulo { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; 121195618Srpaulo 122195618Srpaulostatic ieee80211_recv_action_func mesh_recv_action_meshpeering_open; 123195618Srpaulostatic ieee80211_recv_action_func mesh_recv_action_meshpeering_confirm; 124195618Srpaulostatic ieee80211_recv_action_func mesh_recv_action_meshpeering_close; 125232479Sadrianstatic ieee80211_recv_action_func mesh_recv_action_meshlmetric; 126195618Srpaulo 127195618Srpaulostatic ieee80211_send_action_func mesh_send_action_meshpeering_open; 128195618Srpaulostatic ieee80211_send_action_func mesh_send_action_meshpeering_confirm; 129195618Srpaulostatic ieee80211_send_action_func mesh_send_action_meshpeering_close; 130232479Sadrianstatic ieee80211_send_action_func mesh_send_action_meshlmetric; 131195618Srpaulo 132195618Srpaulostatic const struct ieee80211_mesh_proto_metric mesh_metric_airtime = { 133195618Srpaulo .mpm_descr = "AIRTIME", 134197975Srpaulo .mpm_ie = IEEE80211_MESHCONF_METRIC_AIRTIME, 135195618Srpaulo .mpm_metric = mesh_airtime_calc, 136195618Srpaulo}; 137195618Srpaulo 138195618Srpaulostatic struct ieee80211_mesh_proto_path mesh_proto_paths[4]; 139195618Srpaulostatic struct ieee80211_mesh_proto_metric mesh_proto_metrics[4]; 140195618Srpaulo 141234877Smonthadar#define RT_ENTRY_LOCK(rt) mtx_lock(&(rt)->rt_lock) 142234877Smonthadar#define RT_ENTRY_LOCK_ASSERT(rt) mtx_assert(&(rt)->rt_lock, MA_OWNED) 143234877Smonthadar#define RT_ENTRY_UNLOCK(rt) mtx_unlock(&(rt)->rt_lock) 144234877Smonthadar 145195618Srpaulo#define MESH_RT_LOCK(ms) mtx_lock(&(ms)->ms_rt_lock) 146195784Srpaulo#define MESH_RT_LOCK_ASSERT(ms) mtx_assert(&(ms)->ms_rt_lock, MA_OWNED) 147195618Srpaulo#define MESH_RT_UNLOCK(ms) mtx_unlock(&(ms)->ms_rt_lock) 148195618Srpaulo 149232625SadrianMALLOC_DEFINE(M_80211_MESH_PREQ, "80211preq", "802.11 MESH Path Request frame"); 150232625SadrianMALLOC_DEFINE(M_80211_MESH_PREP, "80211prep", "802.11 MESH Path Reply frame"); 151232625SadrianMALLOC_DEFINE(M_80211_MESH_PERR, "80211perr", "802.11 MESH Path Error frame"); 152232625Sadrian 153234877Smonthadar/* The longer one of the lifetime should be stored as new lifetime */ 154234877Smonthadar#define MESH_ROUTE_LIFETIME_MAX(a, b) (a > b ? a : b) 155234877Smonthadar 156195618SrpauloMALLOC_DEFINE(M_80211_MESH_RT, "80211mesh", "802.11s routing table"); 157195618Srpaulo 158195618Srpaulo/* 159195618Srpaulo * Helper functions to manipulate the Mesh routing table. 160195618Srpaulo */ 161195784Srpaulo 162195784Srpaulostatic struct ieee80211_mesh_route * 163195784Srpaulomesh_rt_find_locked(struct ieee80211_mesh_state *ms, 164195784Srpaulo const uint8_t dest[IEEE80211_ADDR_LEN]) 165195784Srpaulo{ 166195784Srpaulo struct ieee80211_mesh_route *rt; 167195784Srpaulo 168195784Srpaulo MESH_RT_LOCK_ASSERT(ms); 169195784Srpaulo 170195784Srpaulo TAILQ_FOREACH(rt, &ms->ms_routes, rt_next) { 171195784Srpaulo if (IEEE80211_ADDR_EQ(dest, rt->rt_dest)) 172195784Srpaulo return rt; 173195784Srpaulo } 174195784Srpaulo return NULL; 175195784Srpaulo} 176195784Srpaulo 177195784Srpaulostatic struct ieee80211_mesh_route * 178195784Srpaulomesh_rt_add_locked(struct ieee80211_mesh_state *ms, 179195784Srpaulo const uint8_t dest[IEEE80211_ADDR_LEN]) 180195784Srpaulo{ 181195784Srpaulo struct ieee80211_mesh_route *rt; 182195784Srpaulo 183195784Srpaulo KASSERT(!IEEE80211_ADDR_EQ(broadcastaddr, dest), 184195784Srpaulo ("%s: adding broadcast to the routing table", __func__)); 185195784Srpaulo 186195784Srpaulo MESH_RT_LOCK_ASSERT(ms); 187195784Srpaulo 188195784Srpaulo rt = malloc(ALIGN(sizeof(struct ieee80211_mesh_route)) + 189195784Srpaulo ms->ms_ppath->mpp_privlen, M_80211_MESH_RT, M_NOWAIT | M_ZERO); 190195784Srpaulo if (rt != NULL) { 191195784Srpaulo IEEE80211_ADDR_COPY(rt->rt_dest, dest); 192195784Srpaulo rt->rt_priv = (void *)ALIGN(&rt[1]); 193234877Smonthadar mtx_init(&rt->rt_lock, "MBSS_RT", "802.11s route entry", MTX_DEF); 194234877Smonthadar rt->rt_updtime = ticks; /* create time */ 195195784Srpaulo TAILQ_INSERT_TAIL(&ms->ms_routes, rt, rt_next); 196195784Srpaulo } 197195784Srpaulo return rt; 198195784Srpaulo} 199195784Srpaulo 200195618Srpaulostruct ieee80211_mesh_route * 201195618Srpauloieee80211_mesh_rt_find(struct ieee80211vap *vap, 202195618Srpaulo const uint8_t dest[IEEE80211_ADDR_LEN]) 203195618Srpaulo{ 204195618Srpaulo struct ieee80211_mesh_state *ms = vap->iv_mesh; 205195618Srpaulo struct ieee80211_mesh_route *rt; 206195618Srpaulo 207195618Srpaulo MESH_RT_LOCK(ms); 208195784Srpaulo rt = mesh_rt_find_locked(ms, dest); 209195618Srpaulo MESH_RT_UNLOCK(ms); 210195784Srpaulo return rt; 211195618Srpaulo} 212195618Srpaulo 213195618Srpaulostruct ieee80211_mesh_route * 214195618Srpauloieee80211_mesh_rt_add(struct ieee80211vap *vap, 215195618Srpaulo const uint8_t dest[IEEE80211_ADDR_LEN]) 216195618Srpaulo{ 217195618Srpaulo struct ieee80211_mesh_state *ms = vap->iv_mesh; 218195618Srpaulo struct ieee80211_mesh_route *rt; 219195618Srpaulo 220195618Srpaulo KASSERT(ieee80211_mesh_rt_find(vap, dest) == NULL, 221195618Srpaulo ("%s: duplicate entry in the routing table", __func__)); 222195618Srpaulo KASSERT(!IEEE80211_ADDR_EQ(vap->iv_myaddr, dest), 223195618Srpaulo ("%s: adding self to the routing table", __func__)); 224195618Srpaulo 225195618Srpaulo MESH_RT_LOCK(ms); 226195784Srpaulo rt = mesh_rt_add_locked(ms, dest); 227195618Srpaulo MESH_RT_UNLOCK(ms); 228195618Srpaulo return rt; 229195618Srpaulo} 230195618Srpaulo 231195784Srpaulo/* 232234877Smonthadar * Update the route lifetime and returns the updated lifetime. 233234877Smonthadar * If new_lifetime is zero and route is timedout it will be invalidated. 234234877Smonthadar * new_lifetime is in msec 235234877Smonthadar */ 236234877Smonthadarint 237234877Smonthadarieee80211_mesh_rt_update(struct ieee80211_mesh_route *rt, int new_lifetime) 238234877Smonthadar{ 239234877Smonthadar int timesince, now; 240234877Smonthadar uint32_t lifetime = 0; 241234877Smonthadar 242234881Smonthadar KASSERT(rt != NULL, ("route is NULL")); 243234881Smonthadar 244234877Smonthadar now = ticks; 245234877Smonthadar RT_ENTRY_LOCK(rt); 246234879Smonthadar 247234879Smonthadar /* dont clobber a proxy entry gated by us */ 248234879Smonthadar if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY && rt->rt_nhops == 0) { 249234879Smonthadar RT_ENTRY_UNLOCK(rt); 250234879Smonthadar return rt->rt_lifetime; 251234879Smonthadar } 252234879Smonthadar 253234877Smonthadar timesince = ticks_to_msecs(now - rt->rt_updtime); 254234877Smonthadar rt->rt_updtime = now; 255234877Smonthadar if (timesince >= rt->rt_lifetime) { 256234877Smonthadar if (new_lifetime != 0) { 257234877Smonthadar rt->rt_lifetime = new_lifetime; 258234877Smonthadar } 259234877Smonthadar else { 260234877Smonthadar rt->rt_flags &= ~IEEE80211_MESHRT_FLAGS_VALID; 261234877Smonthadar rt->rt_lifetime = 0; 262234877Smonthadar } 263234877Smonthadar } else { 264234877Smonthadar /* update what is left of lifetime */ 265234877Smonthadar rt->rt_lifetime = rt->rt_lifetime - timesince; 266234877Smonthadar rt->rt_lifetime = MESH_ROUTE_LIFETIME_MAX( 267234877Smonthadar new_lifetime, rt->rt_lifetime); 268234877Smonthadar } 269234877Smonthadar lifetime = rt->rt_lifetime; 270234877Smonthadar RT_ENTRY_UNLOCK(rt); 271234877Smonthadar 272234877Smonthadar return lifetime; 273234877Smonthadar} 274234877Smonthadar 275234877Smonthadar/* 276195784Srpaulo * Add a proxy route (as needed) for the specified destination. 277195784Srpaulo */ 278195618Srpaulovoid 279195784Srpauloieee80211_mesh_proxy_check(struct ieee80211vap *vap, 280195784Srpaulo const uint8_t dest[IEEE80211_ADDR_LEN]) 281195784Srpaulo{ 282195784Srpaulo struct ieee80211_mesh_state *ms = vap->iv_mesh; 283195784Srpaulo struct ieee80211_mesh_route *rt; 284195784Srpaulo 285195784Srpaulo MESH_RT_LOCK(ms); 286195784Srpaulo rt = mesh_rt_find_locked(ms, dest); 287195784Srpaulo if (rt == NULL) { 288195784Srpaulo rt = mesh_rt_add_locked(ms, dest); 289195784Srpaulo if (rt == NULL) { 290195784Srpaulo IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, dest, 291195784Srpaulo "%s", "unable to add proxy entry"); 292195784Srpaulo vap->iv_stats.is_mesh_rtaddfailed++; 293195784Srpaulo } else { 294195908Srpaulo IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, dest, 295195908Srpaulo "%s", "add proxy entry"); 296234878Smonthadar IEEE80211_ADDR_COPY(rt->rt_mesh_gate, vap->iv_myaddr); 297195784Srpaulo IEEE80211_ADDR_COPY(rt->rt_nexthop, vap->iv_myaddr); 298195784Srpaulo rt->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID 299195784Srpaulo | IEEE80211_MESHRT_FLAGS_PROXY; 300195784Srpaulo } 301195784Srpaulo } else if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0) { 302234878Smonthadar KASSERT(rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY, 303234878Smonthadar ("no proxy flag for poxy entry")); 304195784Srpaulo struct ieee80211com *ic = vap->iv_ic; 305195784Srpaulo /* 306195784Srpaulo * Fix existing entry created by received frames from 307195784Srpaulo * stations that have some memory of dest. We also 308195784Srpaulo * flush any frames held on the staging queue; delivering 309195784Srpaulo * them is too much trouble right now. 310195784Srpaulo */ 311195784Srpaulo IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, dest, 312195784Srpaulo "%s", "fix proxy entry"); 313195784Srpaulo IEEE80211_ADDR_COPY(rt->rt_nexthop, vap->iv_myaddr); 314195784Srpaulo rt->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID 315195784Srpaulo | IEEE80211_MESHRT_FLAGS_PROXY; 316195784Srpaulo /* XXX belongs in hwmp */ 317195784Srpaulo ieee80211_ageq_drain_node(&ic->ic_stageq, 318195784Srpaulo (void *)(uintptr_t) ieee80211_mac_hash(ic, dest)); 319195784Srpaulo /* XXX stat? */ 320195784Srpaulo } 321195784Srpaulo MESH_RT_UNLOCK(ms); 322195784Srpaulo} 323195784Srpaulo 324195784Srpaulostatic __inline void 325195784Srpaulomesh_rt_del(struct ieee80211_mesh_state *ms, struct ieee80211_mesh_route *rt) 326195784Srpaulo{ 327195784Srpaulo TAILQ_REMOVE(&ms->ms_routes, rt, rt_next); 328234877Smonthadar /* 329234877Smonthadar * Grab the lock before destroying it, to be sure no one else 330234877Smonthadar * is holding the route. 331234877Smonthadar */ 332234877Smonthadar RT_ENTRY_LOCK(rt); 333234877Smonthadar mtx_destroy(&rt->rt_lock); 334195784Srpaulo free(rt, M_80211_MESH_RT); 335195784Srpaulo} 336195784Srpaulo 337195784Srpaulovoid 338195618Srpauloieee80211_mesh_rt_del(struct ieee80211vap *vap, 339195618Srpaulo const uint8_t dest[IEEE80211_ADDR_LEN]) 340195618Srpaulo{ 341195618Srpaulo struct ieee80211_mesh_state *ms = vap->iv_mesh; 342195618Srpaulo struct ieee80211_mesh_route *rt, *next; 343195618Srpaulo 344195618Srpaulo MESH_RT_LOCK(ms); 345195618Srpaulo TAILQ_FOREACH_SAFE(rt, &ms->ms_routes, rt_next, next) { 346195618Srpaulo if (IEEE80211_ADDR_EQ(rt->rt_dest, dest)) { 347234890Smonthadar if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY) { 348234890Smonthadar ms->ms_ppath->mpp_senderror(vap, dest, rt, 349234890Smonthadar IEEE80211_REASON_MESH_PERR_NO_PROXY); 350234890Smonthadar } else { 351234890Smonthadar ms->ms_ppath->mpp_senderror(vap, dest, rt, 352234890Smonthadar IEEE80211_REASON_MESH_PERR_DEST_UNREACH); 353234890Smonthadar } 354195784Srpaulo mesh_rt_del(ms, rt); 355195618Srpaulo MESH_RT_UNLOCK(ms); 356195618Srpaulo return; 357195618Srpaulo } 358195618Srpaulo } 359195618Srpaulo MESH_RT_UNLOCK(ms); 360195618Srpaulo} 361195618Srpaulo 362195618Srpaulovoid 363195618Srpauloieee80211_mesh_rt_flush(struct ieee80211vap *vap) 364195618Srpaulo{ 365195618Srpaulo struct ieee80211_mesh_state *ms = vap->iv_mesh; 366195618Srpaulo struct ieee80211_mesh_route *rt, *next; 367195618Srpaulo 368195618Srpaulo if (ms == NULL) 369195618Srpaulo return; 370195618Srpaulo MESH_RT_LOCK(ms); 371195784Srpaulo TAILQ_FOREACH_SAFE(rt, &ms->ms_routes, rt_next, next) 372195784Srpaulo mesh_rt_del(ms, rt); 373195784Srpaulo MESH_RT_UNLOCK(ms); 374195784Srpaulo} 375195784Srpaulo 376195908Srpaulovoid 377195908Srpauloieee80211_mesh_rt_flush_peer(struct ieee80211vap *vap, 378195908Srpaulo const uint8_t peer[IEEE80211_ADDR_LEN]) 379195908Srpaulo{ 380195908Srpaulo struct ieee80211_mesh_state *ms = vap->iv_mesh; 381195908Srpaulo struct ieee80211_mesh_route *rt, *next; 382195908Srpaulo 383195908Srpaulo MESH_RT_LOCK(ms); 384195908Srpaulo TAILQ_FOREACH_SAFE(rt, &ms->ms_routes, rt_next, next) { 385195908Srpaulo if (IEEE80211_ADDR_EQ(rt->rt_nexthop, peer)) 386195908Srpaulo mesh_rt_del(ms, rt); 387195908Srpaulo } 388195908Srpaulo MESH_RT_UNLOCK(ms); 389195908Srpaulo} 390195908Srpaulo 391195784Srpaulo/* 392195784Srpaulo * Flush expired routing entries, i.e. those in invalid state for 393195784Srpaulo * some time. 394195784Srpaulo */ 395195784Srpaulostatic void 396195784Srpaulomesh_rt_flush_invalid(struct ieee80211vap *vap) 397195784Srpaulo{ 398195784Srpaulo struct ieee80211_mesh_state *ms = vap->iv_mesh; 399195784Srpaulo struct ieee80211_mesh_route *rt, *next; 400195784Srpaulo 401195784Srpaulo if (ms == NULL) 402195784Srpaulo return; 403195784Srpaulo MESH_RT_LOCK(ms); 404195618Srpaulo TAILQ_FOREACH_SAFE(rt, &ms->ms_routes, rt_next, next) { 405234877Smonthadar ieee80211_mesh_rt_update(rt, 0); 406234877Smonthadar /* 407234877Smonthadar * NB: we check for lifetime == 0 so that we give a chance 408234877Smonthadar * for route discovery to complete. 409234877Smonthadar */ 410195784Srpaulo if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0 && 411234877Smonthadar rt->rt_lifetime == 0) 412195784Srpaulo mesh_rt_del(ms, rt); 413195618Srpaulo } 414195618Srpaulo MESH_RT_UNLOCK(ms); 415195618Srpaulo} 416195618Srpaulo 417195618Srpaulo#define N(a) (sizeof(a) / sizeof(a[0])) 418195618Srpauloint 419195618Srpauloieee80211_mesh_register_proto_path(const struct ieee80211_mesh_proto_path *mpp) 420195618Srpaulo{ 421195618Srpaulo int i, firstempty = -1; 422195618Srpaulo 423195618Srpaulo for (i = 0; i < N(mesh_proto_paths); i++) { 424197975Srpaulo if (strncmp(mpp->mpp_descr, mesh_proto_paths[i].mpp_descr, 425197975Srpaulo IEEE80211_MESH_PROTO_DSZ) == 0) 426195618Srpaulo return EEXIST; 427197975Srpaulo if (!mesh_proto_paths[i].mpp_active && firstempty == -1) 428195618Srpaulo firstempty = i; 429195618Srpaulo } 430195618Srpaulo if (firstempty < 0) 431195618Srpaulo return ENOSPC; 432195618Srpaulo memcpy(&mesh_proto_paths[firstempty], mpp, sizeof(*mpp)); 433197975Srpaulo mesh_proto_paths[firstempty].mpp_active = 1; 434195618Srpaulo return 0; 435195618Srpaulo} 436195618Srpaulo 437195618Srpauloint 438195618Srpauloieee80211_mesh_register_proto_metric(const struct 439195618Srpaulo ieee80211_mesh_proto_metric *mpm) 440195618Srpaulo{ 441195618Srpaulo int i, firstempty = -1; 442195618Srpaulo 443195618Srpaulo for (i = 0; i < N(mesh_proto_metrics); i++) { 444197975Srpaulo if (strncmp(mpm->mpm_descr, mesh_proto_metrics[i].mpm_descr, 445197975Srpaulo IEEE80211_MESH_PROTO_DSZ) == 0) 446195618Srpaulo return EEXIST; 447197975Srpaulo if (!mesh_proto_metrics[i].mpm_active && firstempty == -1) 448195618Srpaulo firstempty = i; 449195618Srpaulo } 450195618Srpaulo if (firstempty < 0) 451195618Srpaulo return ENOSPC; 452195618Srpaulo memcpy(&mesh_proto_metrics[firstempty], mpm, sizeof(*mpm)); 453197975Srpaulo mesh_proto_metrics[firstempty].mpm_active = 1; 454195618Srpaulo return 0; 455195618Srpaulo} 456195618Srpaulo 457195618Srpaulostatic int 458195618Srpaulomesh_select_proto_path(struct ieee80211vap *vap, const char *name) 459195618Srpaulo{ 460195618Srpaulo struct ieee80211_mesh_state *ms = vap->iv_mesh; 461195618Srpaulo int i; 462195618Srpaulo 463195618Srpaulo for (i = 0; i < N(mesh_proto_paths); i++) { 464195618Srpaulo if (strcasecmp(mesh_proto_paths[i].mpp_descr, name) == 0) { 465195618Srpaulo ms->ms_ppath = &mesh_proto_paths[i]; 466195618Srpaulo return 0; 467195618Srpaulo } 468195618Srpaulo } 469195618Srpaulo return ENOENT; 470195618Srpaulo} 471195618Srpaulo 472195618Srpaulostatic int 473195618Srpaulomesh_select_proto_metric(struct ieee80211vap *vap, const char *name) 474195618Srpaulo{ 475195618Srpaulo struct ieee80211_mesh_state *ms = vap->iv_mesh; 476195618Srpaulo int i; 477195618Srpaulo 478195618Srpaulo for (i = 0; i < N(mesh_proto_metrics); i++) { 479195618Srpaulo if (strcasecmp(mesh_proto_metrics[i].mpm_descr, name) == 0) { 480195618Srpaulo ms->ms_pmetric = &mesh_proto_metrics[i]; 481195618Srpaulo return 0; 482195618Srpaulo } 483195618Srpaulo } 484195618Srpaulo return ENOENT; 485195618Srpaulo} 486195618Srpaulo#undef N 487195618Srpaulo 488195618Srpaulostatic void 489195618Srpauloieee80211_mesh_init(void) 490195618Srpaulo{ 491195618Srpaulo 492195618Srpaulo memset(mesh_proto_paths, 0, sizeof(mesh_proto_paths)); 493195618Srpaulo memset(mesh_proto_metrics, 0, sizeof(mesh_proto_metrics)); 494195618Srpaulo 495195618Srpaulo /* 496195618Srpaulo * Setup mesh parameters that depends on the clock frequency. 497195618Srpaulo */ 498195618Srpaulo ieee80211_mesh_retrytimeout = msecs_to_ticks(40); 499195618Srpaulo ieee80211_mesh_holdingtimeout = msecs_to_ticks(40); 500195618Srpaulo ieee80211_mesh_confirmtimeout = msecs_to_ticks(40); 501195618Srpaulo 502195618Srpaulo /* 503195618Srpaulo * Register action frame handlers. 504195618Srpaulo */ 505234874Smonthadar ieee80211_recv_action_register(IEEE80211_ACTION_CAT_SELF_PROT, 506195618Srpaulo IEEE80211_ACTION_MESHPEERING_OPEN, 507195618Srpaulo mesh_recv_action_meshpeering_open); 508234874Smonthadar ieee80211_recv_action_register(IEEE80211_ACTION_CAT_SELF_PROT, 509195618Srpaulo IEEE80211_ACTION_MESHPEERING_CONFIRM, 510195618Srpaulo mesh_recv_action_meshpeering_confirm); 511234874Smonthadar ieee80211_recv_action_register(IEEE80211_ACTION_CAT_SELF_PROT, 512195618Srpaulo IEEE80211_ACTION_MESHPEERING_CLOSE, 513195618Srpaulo mesh_recv_action_meshpeering_close); 514232479Sadrian ieee80211_recv_action_register(IEEE80211_ACTION_CAT_MESH, 515232479Sadrian IEEE80211_ACTION_MESH_LMETRIC, mesh_recv_action_meshlmetric); 516195618Srpaulo 517234874Smonthadar ieee80211_send_action_register(IEEE80211_ACTION_CAT_SELF_PROT, 518195618Srpaulo IEEE80211_ACTION_MESHPEERING_OPEN, 519195618Srpaulo mesh_send_action_meshpeering_open); 520234874Smonthadar ieee80211_send_action_register(IEEE80211_ACTION_CAT_SELF_PROT, 521195618Srpaulo IEEE80211_ACTION_MESHPEERING_CONFIRM, 522195618Srpaulo mesh_send_action_meshpeering_confirm); 523234874Smonthadar ieee80211_send_action_register(IEEE80211_ACTION_CAT_SELF_PROT, 524195618Srpaulo IEEE80211_ACTION_MESHPEERING_CLOSE, 525195618Srpaulo mesh_send_action_meshpeering_close); 526232479Sadrian ieee80211_send_action_register(IEEE80211_ACTION_CAT_MESH, 527232479Sadrian IEEE80211_ACTION_MESH_LMETRIC, 528232479Sadrian mesh_send_action_meshlmetric); 529195618Srpaulo 530195618Srpaulo /* 531195618Srpaulo * Register Airtime Link Metric. 532195618Srpaulo */ 533195618Srpaulo ieee80211_mesh_register_proto_metric(&mesh_metric_airtime); 534195618Srpaulo 535195618Srpaulo} 536195618SrpauloSYSINIT(wlan_mesh, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_mesh_init, NULL); 537195618Srpaulo 538195618Srpaulovoid 539195618Srpauloieee80211_mesh_attach(struct ieee80211com *ic) 540195618Srpaulo{ 541195618Srpaulo ic->ic_vattach[IEEE80211_M_MBSS] = mesh_vattach; 542195618Srpaulo} 543195618Srpaulo 544195618Srpaulovoid 545195618Srpauloieee80211_mesh_detach(struct ieee80211com *ic) 546195618Srpaulo{ 547195618Srpaulo} 548195618Srpaulo 549195618Srpaulostatic void 550195618Srpaulomesh_vdetach_peers(void *arg, struct ieee80211_node *ni) 551195618Srpaulo{ 552195618Srpaulo struct ieee80211com *ic = ni->ni_ic; 553195618Srpaulo uint16_t args[3]; 554195618Srpaulo 555195618Srpaulo if (ni->ni_mlstate == IEEE80211_NODE_MESH_ESTABLISHED) { 556195618Srpaulo args[0] = ni->ni_mlpid; 557195618Srpaulo args[1] = ni->ni_mllid; 558195618Srpaulo args[2] = IEEE80211_REASON_PEER_LINK_CANCELED; 559195618Srpaulo ieee80211_send_action(ni, 560234874Smonthadar IEEE80211_ACTION_CAT_SELF_PROT, 561195618Srpaulo IEEE80211_ACTION_MESHPEERING_CLOSE, 562195618Srpaulo args); 563195618Srpaulo } 564195784Srpaulo callout_drain(&ni->ni_mltimer); 565195618Srpaulo /* XXX belongs in hwmp */ 566195618Srpaulo ieee80211_ageq_drain_node(&ic->ic_stageq, 567195618Srpaulo (void *)(uintptr_t) ieee80211_mac_hash(ic, ni->ni_macaddr)); 568195618Srpaulo} 569195618Srpaulo 570195618Srpaulostatic void 571195618Srpaulomesh_vdetach(struct ieee80211vap *vap) 572195618Srpaulo{ 573195618Srpaulo struct ieee80211_mesh_state *ms = vap->iv_mesh; 574195618Srpaulo 575195784Srpaulo callout_drain(&ms->ms_cleantimer); 576195618Srpaulo ieee80211_iterate_nodes(&vap->iv_ic->ic_sta, mesh_vdetach_peers, 577195618Srpaulo NULL); 578195618Srpaulo ieee80211_mesh_rt_flush(vap); 579195618Srpaulo mtx_destroy(&ms->ms_rt_lock); 580195618Srpaulo ms->ms_ppath->mpp_vdetach(vap); 581195618Srpaulo free(vap->iv_mesh, M_80211_VAP); 582195618Srpaulo vap->iv_mesh = NULL; 583195618Srpaulo} 584195618Srpaulo 585195618Srpaulostatic void 586195618Srpaulomesh_vattach(struct ieee80211vap *vap) 587195618Srpaulo{ 588195618Srpaulo struct ieee80211_mesh_state *ms; 589195618Srpaulo vap->iv_newstate = mesh_newstate; 590195618Srpaulo vap->iv_input = mesh_input; 591195618Srpaulo vap->iv_opdetach = mesh_vdetach; 592195618Srpaulo vap->iv_recv_mgmt = mesh_recv_mgmt; 593205277Srpaulo vap->iv_recv_ctl = mesh_recv_ctl; 594195618Srpaulo ms = malloc(sizeof(struct ieee80211_mesh_state), M_80211_VAP, 595195618Srpaulo M_NOWAIT | M_ZERO); 596195618Srpaulo if (ms == NULL) { 597195618Srpaulo printf("%s: couldn't alloc MBSS state\n", __func__); 598195784Srpaulo return; 599195618Srpaulo } 600195618Srpaulo vap->iv_mesh = ms; 601195618Srpaulo ms->ms_seq = 0; 602195618Srpaulo ms->ms_flags = (IEEE80211_MESHFLAGS_AP | IEEE80211_MESHFLAGS_FWD); 603195618Srpaulo ms->ms_ttl = IEEE80211_MESH_DEFAULT_TTL; 604195618Srpaulo TAILQ_INIT(&ms->ms_routes); 605195618Srpaulo mtx_init(&ms->ms_rt_lock, "MBSS", "802.11s routing table", MTX_DEF); 606195784Srpaulo callout_init(&ms->ms_cleantimer, CALLOUT_MPSAFE); 607195618Srpaulo mesh_select_proto_metric(vap, "AIRTIME"); 608195618Srpaulo KASSERT(ms->ms_pmetric, ("ms_pmetric == NULL")); 609195618Srpaulo mesh_select_proto_path(vap, "HWMP"); 610195618Srpaulo KASSERT(ms->ms_ppath, ("ms_ppath == NULL")); 611195618Srpaulo ms->ms_ppath->mpp_vattach(vap); 612195618Srpaulo} 613195618Srpaulo 614195618Srpaulo/* 615195618Srpaulo * IEEE80211_M_MBSS vap state machine handler. 616195618Srpaulo */ 617195618Srpaulostatic int 618195618Srpaulomesh_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) 619195618Srpaulo{ 620195618Srpaulo struct ieee80211_mesh_state *ms = vap->iv_mesh; 621195618Srpaulo struct ieee80211com *ic = vap->iv_ic; 622195618Srpaulo struct ieee80211_node *ni; 623195618Srpaulo enum ieee80211_state ostate; 624195618Srpaulo 625195618Srpaulo IEEE80211_LOCK_ASSERT(ic); 626195618Srpaulo 627195618Srpaulo ostate = vap->iv_state; 628195618Srpaulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n", 629195618Srpaulo __func__, ieee80211_state_name[ostate], 630195618Srpaulo ieee80211_state_name[nstate], arg); 631195618Srpaulo vap->iv_state = nstate; /* state transition */ 632195618Srpaulo if (ostate != IEEE80211_S_SCAN) 633195618Srpaulo ieee80211_cancel_scan(vap); /* background scan */ 634195618Srpaulo ni = vap->iv_bss; /* NB: no reference held */ 635195784Srpaulo if (nstate != IEEE80211_S_RUN && ostate == IEEE80211_S_RUN) 636195784Srpaulo callout_drain(&ms->ms_cleantimer); 637195618Srpaulo switch (nstate) { 638195618Srpaulo case IEEE80211_S_INIT: 639195618Srpaulo switch (ostate) { 640195618Srpaulo case IEEE80211_S_SCAN: 641195618Srpaulo ieee80211_cancel_scan(vap); 642195618Srpaulo break; 643195618Srpaulo case IEEE80211_S_CAC: 644195618Srpaulo ieee80211_dfs_cac_stop(vap); 645195618Srpaulo break; 646195618Srpaulo case IEEE80211_S_RUN: 647195618Srpaulo ieee80211_iterate_nodes(&ic->ic_sta, 648195618Srpaulo mesh_vdetach_peers, NULL); 649195618Srpaulo break; 650195618Srpaulo default: 651195618Srpaulo break; 652195618Srpaulo } 653195618Srpaulo if (ostate != IEEE80211_S_INIT) { 654195618Srpaulo /* NB: optimize INIT -> INIT case */ 655195618Srpaulo ieee80211_reset_bss(vap); 656195784Srpaulo ieee80211_mesh_rt_flush(vap); 657195618Srpaulo } 658195618Srpaulo break; 659195618Srpaulo case IEEE80211_S_SCAN: 660195618Srpaulo switch (ostate) { 661195618Srpaulo case IEEE80211_S_INIT: 662195618Srpaulo if (vap->iv_des_chan != IEEE80211_CHAN_ANYC && 663195618Srpaulo !IEEE80211_IS_CHAN_RADAR(vap->iv_des_chan) && 664195618Srpaulo ms->ms_idlen != 0) { 665195618Srpaulo /* 666195618Srpaulo * Already have a channel and a mesh ID; bypass 667195618Srpaulo * the scan and startup immediately. 668195618Srpaulo */ 669195618Srpaulo ieee80211_create_ibss(vap, vap->iv_des_chan); 670195618Srpaulo break; 671195618Srpaulo } 672195618Srpaulo /* 673195618Srpaulo * Initiate a scan. We can come here as a result 674195618Srpaulo * of an IEEE80211_IOC_SCAN_REQ too in which case 675195618Srpaulo * the vap will be marked with IEEE80211_FEXT_SCANREQ 676195618Srpaulo * and the scan request parameters will be present 677195618Srpaulo * in iv_scanreq. Otherwise we do the default. 678195618Srpaulo */ 679195618Srpaulo if (vap->iv_flags_ext & IEEE80211_FEXT_SCANREQ) { 680195618Srpaulo ieee80211_check_scan(vap, 681195618Srpaulo vap->iv_scanreq_flags, 682195618Srpaulo vap->iv_scanreq_duration, 683195618Srpaulo vap->iv_scanreq_mindwell, 684195618Srpaulo vap->iv_scanreq_maxdwell, 685195618Srpaulo vap->iv_scanreq_nssid, vap->iv_scanreq_ssid); 686195618Srpaulo vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANREQ; 687195618Srpaulo } else 688195618Srpaulo ieee80211_check_scan_current(vap); 689195618Srpaulo break; 690195618Srpaulo default: 691195618Srpaulo break; 692195618Srpaulo } 693195618Srpaulo break; 694195618Srpaulo case IEEE80211_S_CAC: 695195618Srpaulo /* 696195618Srpaulo * Start CAC on a DFS channel. We come here when starting 697195618Srpaulo * a bss on a DFS channel (see ieee80211_create_ibss). 698195618Srpaulo */ 699195618Srpaulo ieee80211_dfs_cac_start(vap); 700195618Srpaulo break; 701195618Srpaulo case IEEE80211_S_RUN: 702195618Srpaulo switch (ostate) { 703195618Srpaulo case IEEE80211_S_INIT: 704195618Srpaulo /* 705195618Srpaulo * Already have a channel; bypass the 706195618Srpaulo * scan and startup immediately. 707195618Srpaulo * Note that ieee80211_create_ibss will call 708195618Srpaulo * back to do a RUN->RUN state change. 709195618Srpaulo */ 710195618Srpaulo ieee80211_create_ibss(vap, 711195618Srpaulo ieee80211_ht_adjust_channel(ic, 712195618Srpaulo ic->ic_curchan, vap->iv_flags_ht)); 713195618Srpaulo /* NB: iv_bss is changed on return */ 714195618Srpaulo break; 715195618Srpaulo case IEEE80211_S_CAC: 716195618Srpaulo /* 717195618Srpaulo * NB: This is the normal state change when CAC 718195618Srpaulo * expires and no radar was detected; no need to 719195618Srpaulo * clear the CAC timer as it's already expired. 720195618Srpaulo */ 721195618Srpaulo /* fall thru... */ 722195618Srpaulo case IEEE80211_S_CSA: 723195618Srpaulo#if 0 724195618Srpaulo /* 725195618Srpaulo * Shorten inactivity timer of associated stations 726195618Srpaulo * to weed out sta's that don't follow a CSA. 727195618Srpaulo */ 728195618Srpaulo ieee80211_iterate_nodes(&ic->ic_sta, sta_csa, vap); 729195618Srpaulo#endif 730195618Srpaulo /* 731195618Srpaulo * Update bss node channel to reflect where 732195618Srpaulo * we landed after CSA. 733195618Srpaulo */ 734195618Srpaulo ieee80211_node_set_chan(vap->iv_bss, 735195618Srpaulo ieee80211_ht_adjust_channel(ic, ic->ic_curchan, 736195618Srpaulo ieee80211_htchanflags(vap->iv_bss->ni_chan))); 737195618Srpaulo /* XXX bypass debug msgs */ 738195618Srpaulo break; 739195618Srpaulo case IEEE80211_S_SCAN: 740195618Srpaulo case IEEE80211_S_RUN: 741195618Srpaulo#ifdef IEEE80211_DEBUG 742195618Srpaulo if (ieee80211_msg_debug(vap)) { 743195618Srpaulo struct ieee80211_node *ni = vap->iv_bss; 744195618Srpaulo ieee80211_note(vap, 745195618Srpaulo "synchronized with %s meshid ", 746195618Srpaulo ether_sprintf(ni->ni_meshid)); 747195618Srpaulo ieee80211_print_essid(ni->ni_meshid, 748195618Srpaulo ni->ni_meshidlen); 749195618Srpaulo /* XXX MCS/HT */ 750195618Srpaulo printf(" channel %d\n", 751195618Srpaulo ieee80211_chan2ieee(ic, ic->ic_curchan)); 752195618Srpaulo } 753195618Srpaulo#endif 754195618Srpaulo break; 755195618Srpaulo default: 756195618Srpaulo break; 757195618Srpaulo } 758195618Srpaulo ieee80211_node_authorize(vap->iv_bss); 759195813Ssam callout_reset(&ms->ms_cleantimer, ms->ms_ppath->mpp_inact, 760195784Srpaulo mesh_rt_cleanup_cb, vap); 761195618Srpaulo break; 762195618Srpaulo default: 763195618Srpaulo break; 764195618Srpaulo } 765195618Srpaulo /* NB: ostate not nstate */ 766195618Srpaulo ms->ms_ppath->mpp_newstate(vap, ostate, arg); 767195618Srpaulo return 0; 768195618Srpaulo} 769195618Srpaulo 770195784Srpaulostatic void 771195784Srpaulomesh_rt_cleanup_cb(void *arg) 772195784Srpaulo{ 773195784Srpaulo struct ieee80211vap *vap = arg; 774195784Srpaulo struct ieee80211_mesh_state *ms = vap->iv_mesh; 775195784Srpaulo 776195784Srpaulo mesh_rt_flush_invalid(vap); 777195813Ssam callout_reset(&ms->ms_cleantimer, ms->ms_ppath->mpp_inact, 778195784Srpaulo mesh_rt_cleanup_cb, vap); 779195784Srpaulo} 780195784Srpaulo 781195784Srpaulo 782195618Srpaulo/* 783195618Srpaulo * Helper function to note the Mesh Peer Link FSM change. 784195618Srpaulo */ 785195618Srpaulostatic void 786195618Srpaulomesh_linkchange(struct ieee80211_node *ni, enum ieee80211_mesh_mlstate state) 787195618Srpaulo{ 788195618Srpaulo struct ieee80211vap *vap = ni->ni_vap; 789195618Srpaulo struct ieee80211_mesh_state *ms = vap->iv_mesh; 790195618Srpaulo#ifdef IEEE80211_DEBUG 791195618Srpaulo static const char *meshlinkstates[] = { 792195618Srpaulo [IEEE80211_NODE_MESH_IDLE] = "IDLE", 793195618Srpaulo [IEEE80211_NODE_MESH_OPENSNT] = "OPEN SENT", 794195618Srpaulo [IEEE80211_NODE_MESH_OPENRCV] = "OPEN RECEIVED", 795195618Srpaulo [IEEE80211_NODE_MESH_CONFIRMRCV] = "CONFIRM RECEIVED", 796195618Srpaulo [IEEE80211_NODE_MESH_ESTABLISHED] = "ESTABLISHED", 797195618Srpaulo [IEEE80211_NODE_MESH_HOLDING] = "HOLDING" 798195618Srpaulo }; 799195618Srpaulo#endif 800195618Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_MESH, 801195618Srpaulo ni, "peer link: %s -> %s", 802195618Srpaulo meshlinkstates[ni->ni_mlstate], meshlinkstates[state]); 803195618Srpaulo 804195618Srpaulo /* track neighbor count */ 805195618Srpaulo if (state == IEEE80211_NODE_MESH_ESTABLISHED && 806195618Srpaulo ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED) { 807195618Srpaulo KASSERT(ms->ms_neighbors < 65535, ("neighbor count overflow")); 808195618Srpaulo ms->ms_neighbors++; 809198242Srpaulo ieee80211_beacon_notify(vap, IEEE80211_BEACON_MESHCONF); 810195618Srpaulo } else if (ni->ni_mlstate == IEEE80211_NODE_MESH_ESTABLISHED && 811195618Srpaulo state != IEEE80211_NODE_MESH_ESTABLISHED) { 812195618Srpaulo KASSERT(ms->ms_neighbors > 0, ("neighbor count 0")); 813195618Srpaulo ms->ms_neighbors--; 814198242Srpaulo ieee80211_beacon_notify(vap, IEEE80211_BEACON_MESHCONF); 815195618Srpaulo } 816195618Srpaulo ni->ni_mlstate = state; 817195784Srpaulo switch (state) { 818195784Srpaulo case IEEE80211_NODE_MESH_HOLDING: 819195618Srpaulo ms->ms_ppath->mpp_peerdown(ni); 820195784Srpaulo break; 821195784Srpaulo case IEEE80211_NODE_MESH_ESTABLISHED: 822195784Srpaulo ieee80211_mesh_discover(vap, ni->ni_macaddr, NULL); 823195784Srpaulo break; 824195784Srpaulo default: 825195784Srpaulo break; 826195784Srpaulo } 827195618Srpaulo} 828195618Srpaulo 829195618Srpaulo/* 830195618Srpaulo * Helper function to generate a unique local ID required for mesh 831195618Srpaulo * peer establishment. 832195618Srpaulo */ 833195618Srpaulostatic void 834195618Srpaulomesh_checkid(void *arg, struct ieee80211_node *ni) 835195618Srpaulo{ 836195618Srpaulo uint16_t *r = arg; 837195618Srpaulo 838195618Srpaulo if (*r == ni->ni_mllid) 839195618Srpaulo *(uint16_t *)arg = 0; 840195618Srpaulo} 841195618Srpaulo 842195618Srpaulostatic uint32_t 843195618Srpaulomesh_generateid(struct ieee80211vap *vap) 844195618Srpaulo{ 845195618Srpaulo int maxiter = 4; 846195618Srpaulo uint16_t r; 847195618Srpaulo 848195618Srpaulo do { 849195618Srpaulo get_random_bytes(&r, 2); 850195618Srpaulo ieee80211_iterate_nodes(&vap->iv_ic->ic_sta, mesh_checkid, &r); 851195618Srpaulo maxiter--; 852195618Srpaulo } while (r == 0 && maxiter > 0); 853195618Srpaulo return r; 854195618Srpaulo} 855195618Srpaulo 856195618Srpaulo/* 857195618Srpaulo * Verifies if we already received this packet by checking its 858195618Srpaulo * sequence number. 859195908Srpaulo * Returns 0 if the frame is to be accepted, 1 otherwise. 860195618Srpaulo */ 861195618Srpaulostatic int 862195618Srpaulomesh_checkpseq(struct ieee80211vap *vap, 863195618Srpaulo const uint8_t source[IEEE80211_ADDR_LEN], uint32_t seq) 864195618Srpaulo{ 865195618Srpaulo struct ieee80211_mesh_route *rt; 866195618Srpaulo 867195618Srpaulo rt = ieee80211_mesh_rt_find(vap, source); 868195618Srpaulo if (rt == NULL) { 869195908Srpaulo rt = ieee80211_mesh_rt_add(vap, source); 870195908Srpaulo if (rt == NULL) { 871195908Srpaulo IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, source, 872195908Srpaulo "%s", "add mcast route failed"); 873195908Srpaulo vap->iv_stats.is_mesh_rtaddfailed++; 874195908Srpaulo return 1; 875195908Srpaulo } 876195784Srpaulo IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, source, 877195784Srpaulo "add mcast route, mesh seqno %d", seq); 878195908Srpaulo rt->rt_lastmseq = seq; 879195618Srpaulo return 0; 880195618Srpaulo } 881195618Srpaulo if (IEEE80211_MESH_SEQ_GEQ(rt->rt_lastmseq, seq)) { 882195618Srpaulo return 1; 883195618Srpaulo } else { 884195618Srpaulo rt->rt_lastmseq = seq; 885195618Srpaulo return 0; 886195618Srpaulo } 887195618Srpaulo} 888195618Srpaulo 889195618Srpaulo/* 890195618Srpaulo * Iterate the routing table and locate the next hop. 891195618Srpaulo */ 892195618Srpaulostatic struct ieee80211_node * 893195618Srpaulomesh_find_txnode(struct ieee80211vap *vap, 894195618Srpaulo const uint8_t dest[IEEE80211_ADDR_LEN]) 895195618Srpaulo{ 896195618Srpaulo struct ieee80211_mesh_route *rt; 897195618Srpaulo 898195618Srpaulo rt = ieee80211_mesh_rt_find(vap, dest); 899195618Srpaulo if (rt == NULL) 900195618Srpaulo return NULL; 901195784Srpaulo if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0 || 902195784Srpaulo (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY)) { 903195784Srpaulo IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, dest, 904195784Srpaulo "%s: !valid or proxy, flags 0x%x", __func__, rt->rt_flags); 905195784Srpaulo /* XXX stat */ 906195784Srpaulo return NULL; 907195784Srpaulo } 908195618Srpaulo return ieee80211_find_txnode(vap, rt->rt_nexthop); 909195618Srpaulo} 910195618Srpaulo 911195618Srpaulo/* 912195618Srpaulo * Forward the specified frame. 913195618Srpaulo * Decrement the TTL and set TA to our MAC address. 914195618Srpaulo */ 915195618Srpaulostatic void 916195618Srpaulomesh_forward(struct ieee80211vap *vap, struct mbuf *m, 917195618Srpaulo const struct ieee80211_meshcntl *mc) 918195618Srpaulo{ 919195618Srpaulo struct ieee80211com *ic = vap->iv_ic; 920195618Srpaulo struct ieee80211_mesh_state *ms = vap->iv_mesh; 921195618Srpaulo struct ifnet *ifp = vap->iv_ifp; 922195618Srpaulo struct ifnet *parent = ic->ic_ifp; 923195618Srpaulo const struct ieee80211_frame *wh = 924195618Srpaulo mtod(m, const struct ieee80211_frame *); 925195618Srpaulo struct mbuf *mcopy; 926195618Srpaulo struct ieee80211_meshcntl *mccopy; 927195618Srpaulo struct ieee80211_frame *whcopy; 928195618Srpaulo struct ieee80211_node *ni; 929195618Srpaulo int err; 930195618Srpaulo 931234878Smonthadar /* 932234878Smonthadar * mesh ttl of 1 means we are the last one receving it, 933234878Smonthadar * according to amendment we decrement and then check if 934234878Smonthadar * 0, if so we dont forward. 935234878Smonthadar */ 936234878Smonthadar if (mc->mc_ttl < 1) { 937195618Srpaulo IEEE80211_NOTE_FRAME(vap, IEEE80211_MSG_MESH, wh, 938234878Smonthadar "%s", "frame not fwd'd, ttl 1"); 939195618Srpaulo vap->iv_stats.is_mesh_fwd_ttl++; 940195618Srpaulo return; 941195618Srpaulo } 942195618Srpaulo if (!(ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) { 943195618Srpaulo IEEE80211_NOTE_FRAME(vap, IEEE80211_MSG_MESH, wh, 944195618Srpaulo "%s", "frame not fwd'd, fwding disabled"); 945195618Srpaulo vap->iv_stats.is_mesh_fwd_disabled++; 946195618Srpaulo return; 947195618Srpaulo } 948195618Srpaulo mcopy = m_dup(m, M_DONTWAIT); 949195618Srpaulo if (mcopy == NULL) { 950195618Srpaulo IEEE80211_NOTE_FRAME(vap, IEEE80211_MSG_MESH, wh, 951195618Srpaulo "%s", "frame not fwd'd, cannot dup"); 952195618Srpaulo vap->iv_stats.is_mesh_fwd_nobuf++; 953195618Srpaulo ifp->if_oerrors++; 954195618Srpaulo return; 955195618Srpaulo } 956195618Srpaulo mcopy = m_pullup(mcopy, ieee80211_hdrspace(ic, wh) + 957195618Srpaulo sizeof(struct ieee80211_meshcntl)); 958195618Srpaulo if (mcopy == NULL) { 959195618Srpaulo IEEE80211_NOTE_FRAME(vap, IEEE80211_MSG_MESH, wh, 960195618Srpaulo "%s", "frame not fwd'd, too short"); 961195618Srpaulo vap->iv_stats.is_mesh_fwd_tooshort++; 962195618Srpaulo ifp->if_oerrors++; 963195618Srpaulo m_freem(mcopy); 964195618Srpaulo return; 965195618Srpaulo } 966195618Srpaulo whcopy = mtod(mcopy, struct ieee80211_frame *); 967195618Srpaulo mccopy = (struct ieee80211_meshcntl *) 968195618Srpaulo (mtod(mcopy, uint8_t *) + ieee80211_hdrspace(ic, wh)); 969195618Srpaulo /* XXX clear other bits? */ 970195618Srpaulo whcopy->i_fc[1] &= ~IEEE80211_FC1_RETRY; 971195618Srpaulo IEEE80211_ADDR_COPY(whcopy->i_addr2, vap->iv_myaddr); 972195618Srpaulo if (IEEE80211_IS_MULTICAST(wh->i_addr1)) { 973195618Srpaulo ni = ieee80211_ref_node(vap->iv_bss); 974195618Srpaulo mcopy->m_flags |= M_MCAST; 975195618Srpaulo } else { 976195618Srpaulo ni = mesh_find_txnode(vap, whcopy->i_addr3); 977195618Srpaulo if (ni == NULL) { 978234878Smonthadar /* 979234878Smonthadar * [Optional] any of the following three actions: 980234878Smonthadar * o silently discard 981234878Smonthadar * o trigger a path discovery 982234890Smonthadar * o inform TA that meshDA is unknown. 983234878Smonthadar */ 984195618Srpaulo IEEE80211_NOTE_FRAME(vap, IEEE80211_MSG_MESH, wh, 985195618Srpaulo "%s", "frame not fwd'd, no path"); 986234890Smonthadar ms->ms_ppath->mpp_senderror(vap, whcopy->i_addr3, NULL, 987234890Smonthadar IEEE80211_REASON_MESH_PERR_NO_FI); 988195618Srpaulo vap->iv_stats.is_mesh_fwd_nopath++; 989195618Srpaulo m_freem(mcopy); 990195618Srpaulo return; 991195618Srpaulo } 992195618Srpaulo IEEE80211_ADDR_COPY(whcopy->i_addr1, ni->ni_macaddr); 993195618Srpaulo } 994195618Srpaulo KASSERT(mccopy->mc_ttl > 0, ("%s called with wrong ttl", __func__)); 995195618Srpaulo mccopy->mc_ttl--; 996195618Srpaulo 997195618Srpaulo /* XXX calculate priority so drivers can find the tx queue */ 998195618Srpaulo M_WME_SETAC(mcopy, WME_AC_BE); 999195618Srpaulo 1000195618Srpaulo /* XXX do we know m_nextpkt is NULL? */ 1001195618Srpaulo mcopy->m_pkthdr.rcvif = (void *) ni; 1002195618Srpaulo err = parent->if_transmit(parent, mcopy); 1003195618Srpaulo if (err != 0) { 1004195618Srpaulo /* NB: IFQ_HANDOFF reclaims mbuf */ 1005195618Srpaulo ieee80211_free_node(ni); 1006195618Srpaulo } else { 1007195618Srpaulo ifp->if_opackets++; 1008195618Srpaulo } 1009195618Srpaulo} 1010195618Srpaulo 1011195784Srpaulostatic struct mbuf * 1012195784Srpaulomesh_decap(struct ieee80211vap *vap, struct mbuf *m, int hdrlen, int meshdrlen) 1013195784Srpaulo{ 1014234878Smonthadar#define WHDIR(wh) ((wh)->i_fc[1] & IEEE80211_FC1_DIR_MASK) 1015234878Smonthadar#define MC01(mc) ((const struct ieee80211_meshcntl_ae01 *)mc) 1016195784Srpaulo uint8_t b[sizeof(struct ieee80211_qosframe_addr4) + 1017234878Smonthadar sizeof(struct ieee80211_meshcntl_ae10)]; 1018195784Srpaulo const struct ieee80211_qosframe_addr4 *wh; 1019195784Srpaulo const struct ieee80211_meshcntl_ae10 *mc; 1020195784Srpaulo struct ether_header *eh; 1021195784Srpaulo struct llc *llc; 1022195784Srpaulo int ae; 1023195784Srpaulo 1024195784Srpaulo if (m->m_len < hdrlen + sizeof(*llc) && 1025195784Srpaulo (m = m_pullup(m, hdrlen + sizeof(*llc))) == NULL) { 1026195784Srpaulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_ANY, 1027195784Srpaulo "discard data frame: %s", "m_pullup failed"); 1028195784Srpaulo vap->iv_stats.is_rx_tooshort++; 1029195784Srpaulo return NULL; 1030195784Srpaulo } 1031195784Srpaulo memcpy(b, mtod(m, caddr_t), hdrlen); 1032195784Srpaulo wh = (const struct ieee80211_qosframe_addr4 *)&b[0]; 1033195784Srpaulo mc = (const struct ieee80211_meshcntl_ae10 *)&b[hdrlen - meshdrlen]; 1034195784Srpaulo KASSERT(WHDIR(wh) == IEEE80211_FC1_DIR_FROMDS || 1035195784Srpaulo WHDIR(wh) == IEEE80211_FC1_DIR_DSTODS, 1036195784Srpaulo ("bogus dir, fc 0x%x:0x%x", wh->i_fc[0], wh->i_fc[1])); 1037195784Srpaulo 1038195784Srpaulo llc = (struct llc *)(mtod(m, caddr_t) + hdrlen); 1039195784Srpaulo if (llc->llc_dsap == LLC_SNAP_LSAP && llc->llc_ssap == LLC_SNAP_LSAP && 1040195784Srpaulo llc->llc_control == LLC_UI && llc->llc_snap.org_code[0] == 0 && 1041195784Srpaulo llc->llc_snap.org_code[1] == 0 && llc->llc_snap.org_code[2] == 0 && 1042195784Srpaulo /* NB: preserve AppleTalk frames that have a native SNAP hdr */ 1043195784Srpaulo !(llc->llc_snap.ether_type == htons(ETHERTYPE_AARP) || 1044195784Srpaulo llc->llc_snap.ether_type == htons(ETHERTYPE_IPX))) { 1045195784Srpaulo m_adj(m, hdrlen + sizeof(struct llc) - sizeof(*eh)); 1046195784Srpaulo llc = NULL; 1047195784Srpaulo } else { 1048195784Srpaulo m_adj(m, hdrlen - sizeof(*eh)); 1049195784Srpaulo } 1050195784Srpaulo eh = mtod(m, struct ether_header *); 1051234878Smonthadar ae = mc->mc_flags & IEEE80211_MESH_AE_MASK; 1052195784Srpaulo if (WHDIR(wh) == IEEE80211_FC1_DIR_FROMDS) { 1053195784Srpaulo IEEE80211_ADDR_COPY(eh->ether_dhost, wh->i_addr1); 1054234878Smonthadar if (ae == IEEE80211_MESH_AE_00) { 1055195784Srpaulo IEEE80211_ADDR_COPY(eh->ether_shost, wh->i_addr3); 1056234878Smonthadar } else if (ae == IEEE80211_MESH_AE_01) { 1057234878Smonthadar IEEE80211_ADDR_COPY(eh->ether_shost, 1058234878Smonthadar MC01(mc)->mc_addr4); 1059195784Srpaulo } else { 1060195784Srpaulo IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, 1061195784Srpaulo (const struct ieee80211_frame *)wh, NULL, 1062195784Srpaulo "bad AE %d", ae); 1063195784Srpaulo vap->iv_stats.is_mesh_badae++; 1064195784Srpaulo m_freem(m); 1065195784Srpaulo return NULL; 1066195784Srpaulo } 1067195784Srpaulo } else { 1068234878Smonthadar if (ae == IEEE80211_MESH_AE_00) { 1069195784Srpaulo IEEE80211_ADDR_COPY(eh->ether_dhost, wh->i_addr3); 1070195784Srpaulo IEEE80211_ADDR_COPY(eh->ether_shost, wh->i_addr4); 1071234878Smonthadar } else if (ae == IEEE80211_MESH_AE_10) { 1072234878Smonthadar IEEE80211_ADDR_COPY(eh->ether_dhost, mc->mc_addr5); 1073234878Smonthadar IEEE80211_ADDR_COPY(eh->ether_shost, mc->mc_addr6); 1074195784Srpaulo } else { 1075195784Srpaulo IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, 1076195784Srpaulo (const struct ieee80211_frame *)wh, NULL, 1077195784Srpaulo "bad AE %d", ae); 1078195784Srpaulo vap->iv_stats.is_mesh_badae++; 1079195784Srpaulo m_freem(m); 1080195784Srpaulo return NULL; 1081195784Srpaulo } 1082195784Srpaulo } 1083195784Srpaulo#ifdef ALIGNED_POINTER 1084195784Srpaulo if (!ALIGNED_POINTER(mtod(m, caddr_t) + sizeof(*eh), uint32_t)) { 1085195784Srpaulo m = ieee80211_realign(vap, m, sizeof(*eh)); 1086195784Srpaulo if (m == NULL) 1087195784Srpaulo return NULL; 1088195784Srpaulo } 1089195784Srpaulo#endif /* ALIGNED_POINTER */ 1090195784Srpaulo if (llc != NULL) { 1091195784Srpaulo eh = mtod(m, struct ether_header *); 1092195784Srpaulo eh->ether_type = htons(m->m_pkthdr.len - sizeof(*eh)); 1093195784Srpaulo } 1094195784Srpaulo return m; 1095234878Smonthadar#undef WDIR 1096234878Smonthadar#undef MC01 1097195784Srpaulo} 1098195784Srpaulo 1099195784Srpaulo/* 1100195784Srpaulo * Return non-zero if the unicast mesh data frame should be processed 1101195784Srpaulo * locally. Frames that are not proxy'd have our address, otherwise 1102195784Srpaulo * we need to consult the routing table to look for a proxy entry. 1103195784Srpaulo */ 1104195784Srpaulostatic __inline int 1105195784Srpaulomesh_isucastforme(struct ieee80211vap *vap, const struct ieee80211_frame *wh, 1106195784Srpaulo const struct ieee80211_meshcntl *mc) 1107195784Srpaulo{ 1108195784Srpaulo int ae = mc->mc_flags & 3; 1109195784Srpaulo 1110195784Srpaulo KASSERT((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS, 1111195784Srpaulo ("bad dir 0x%x:0x%x", wh->i_fc[0], wh->i_fc[1])); 1112234878Smonthadar KASSERT(ae == IEEE80211_MESH_AE_00 || ae == IEEE80211_MESH_AE_10, 1113234878Smonthadar ("bad AE %d", ae)); 1114234878Smonthadar if (ae == IEEE80211_MESH_AE_10) { /* ucast w/ proxy */ 1115195784Srpaulo const struct ieee80211_meshcntl_ae10 *mc10 = 1116195784Srpaulo (const struct ieee80211_meshcntl_ae10 *) mc; 1117195784Srpaulo struct ieee80211_mesh_route *rt = 1118234878Smonthadar ieee80211_mesh_rt_find(vap, mc10->mc_addr5); 1119195784Srpaulo /* check for proxy route to ourself */ 1120195784Srpaulo return (rt != NULL && 1121195784Srpaulo (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY)); 1122195784Srpaulo } else /* ucast w/o proxy */ 1123195784Srpaulo return IEEE80211_ADDR_EQ(wh->i_addr3, vap->iv_myaddr); 1124195784Srpaulo} 1125195784Srpaulo 1126234878Smonthadar/* 1127234878Smonthadar * Verifies transmitter, updates lifetime, precursor list and forwards data. 1128234878Smonthadar * > 0 means we have forwarded data and no need to process locally 1129234878Smonthadar * == 0 means we want to process locally (and we may have forwarded data 1130234878Smonthadar * < 0 means there was an error and data should be discarded 1131234878Smonthadar */ 1132195618Srpaulostatic int 1133234878Smonthadarmesh_recv_indiv_data_to_fwrd(struct ieee80211vap *vap, struct mbuf *m, 1134234878Smonthadar struct ieee80211_frame *wh, const struct ieee80211_meshcntl *mc) 1135234878Smonthadar{ 1136234879Smonthadar struct ieee80211_qosframe_addr4 *qwh; 1137234879Smonthadar struct ieee80211_mesh_state *ms = vap->iv_mesh; 1138234879Smonthadar struct ieee80211_mesh_route *rt_meshda, *rt_meshsa; 1139234878Smonthadar 1140234879Smonthadar qwh = (struct ieee80211_qosframe_addr4 *)wh; 1141234879Smonthadar 1142234878Smonthadar /* 1143234878Smonthadar * TODO: 1144234878Smonthadar * o verify addr2 is a legitimate transmitter 1145234878Smonthadar * o lifetime of precursor of addr3 (addr2) is max(init, curr) 1146234878Smonthadar * o lifetime of precursor of addr4 (nexthop) is max(init, curr) 1147234878Smonthadar */ 1148234878Smonthadar 1149234879Smonthadar /* set lifetime of addr3 (meshDA) to initial value */ 1150234879Smonthadar rt_meshda = ieee80211_mesh_rt_find(vap, qwh->i_addr3); 1151234880Smonthadar if (rt_meshda == NULL) { 1152234880Smonthadar IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, qwh->i_addr2, 1153234880Smonthadar "no route to meshDA(%6D)", qwh->i_addr3, ":"); 1154234880Smonthadar /* 1155234880Smonthadar * [Optional] any of the following three actions: 1156234880Smonthadar * o silently discard [X] 1157234880Smonthadar * o trigger a path discovery [ ] 1158234880Smonthadar * o inform TA that meshDA is unknown. [ ] 1159234880Smonthadar */ 1160234880Smonthadar /* XXX: stats */ 1161234880Smonthadar return (-1); 1162234880Smonthadar } 1163234880Smonthadar 1164234879Smonthadar ieee80211_mesh_rt_update(rt_meshda, ticks_to_msecs( 1165234879Smonthadar ms->ms_ppath->mpp_inact)); 1166234879Smonthadar 1167234879Smonthadar /* set lifetime of addr4 (meshSA) to initial value */ 1168234879Smonthadar rt_meshsa = ieee80211_mesh_rt_find(vap, qwh->i_addr4); 1169234879Smonthadar KASSERT(rt_meshsa != NULL, ("no route")); 1170234879Smonthadar ieee80211_mesh_rt_update(rt_meshsa, ticks_to_msecs( 1171234879Smonthadar ms->ms_ppath->mpp_inact)); 1172234879Smonthadar 1173234878Smonthadar mesh_forward(vap, m, mc); 1174234878Smonthadar return (1); /* dont process locally */ 1175234878Smonthadar} 1176234878Smonthadar 1177234878Smonthadar/* 1178234878Smonthadar * Verifies transmitter, updates lifetime, precursor list and process data 1179234878Smonthadar * locally, if data is is proxy with AE = 10 it could mean data should go 1180234878Smonthadar * on another mesh path or data should be forwarded to the DS. 1181234878Smonthadar * 1182234878Smonthadar * > 0 means we have forwarded data and no need to process locally 1183234878Smonthadar * == 0 means we want to process locally (and we may have forwarded data 1184234878Smonthadar * < 0 means there was an error and data should be discarded 1185234878Smonthadar */ 1186234878Smonthadarstatic int 1187234878Smonthadarmesh_recv_indiv_data_to_me(struct ieee80211vap *vap, struct mbuf *m, 1188234878Smonthadar struct ieee80211_frame *wh, const struct ieee80211_meshcntl *mc) 1189234878Smonthadar{ 1190234878Smonthadar struct ieee80211_qosframe_addr4 *qwh; 1191234878Smonthadar const struct ieee80211_meshcntl_ae10 *mc10; 1192234879Smonthadar struct ieee80211_mesh_state *ms = vap->iv_mesh; 1193234878Smonthadar struct ieee80211_mesh_route *rt; 1194234878Smonthadar int ae; 1195234878Smonthadar 1196234878Smonthadar qwh = (struct ieee80211_qosframe_addr4 *)wh; 1197234878Smonthadar mc10 = (const struct ieee80211_meshcntl_ae10 *)mc; 1198234878Smonthadar 1199234878Smonthadar /* 1200234878Smonthadar * TODO: 1201234878Smonthadar * o verify addr2 is a legitimate transmitter 1202234878Smonthadar * o lifetime of precursor entry is max(init, curr) 1203234878Smonthadar */ 1204234878Smonthadar 1205234879Smonthadar /* set lifetime of addr4 (meshSA) to initial value */ 1206234879Smonthadar rt = ieee80211_mesh_rt_find(vap, qwh->i_addr4); 1207234879Smonthadar KASSERT(rt != NULL, ("no route")); 1208234879Smonthadar ieee80211_mesh_rt_update(rt, ticks_to_msecs(ms->ms_ppath->mpp_inact)); 1209234879Smonthadar rt = NULL; 1210234879Smonthadar 1211234878Smonthadar ae = mc10->mc_flags & IEEE80211_MESH_AE_MASK; 1212234878Smonthadar KASSERT(ae == IEEE80211_MESH_AE_00 || 1213234878Smonthadar ae == IEEE80211_MESH_AE_10, ("bad AE %d", ae)); 1214234878Smonthadar if (ae == IEEE80211_MESH_AE_10) { 1215234878Smonthadar if (IEEE80211_ADDR_EQ(mc10->mc_addr5, qwh->i_addr3)) { 1216234878Smonthadar return (0); /* process locally */ 1217234878Smonthadar } 1218234878Smonthadar 1219234878Smonthadar rt = ieee80211_mesh_rt_find(vap, mc10->mc_addr5); 1220234878Smonthadar if (rt != NULL && 1221234878Smonthadar (rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) && 1222234878Smonthadar (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY) == 0) { 1223234878Smonthadar /* 1224234878Smonthadar * Forward on another mesh-path, according to 1225234878Smonthadar * amendment as specified in 9.32.4.1 1226234878Smonthadar */ 1227234878Smonthadar IEEE80211_ADDR_COPY(qwh->i_addr3, mc10->mc_addr5); 1228234878Smonthadar mesh_forward(vap, m, 1229234878Smonthadar (const struct ieee80211_meshcntl *)mc10); 1230234878Smonthadar return (1); /* dont process locally */ 1231234878Smonthadar } 1232234878Smonthadar /* 1233234878Smonthadar * All other cases: forward of MSDUs from the MBSS to DS indiv. 1234234878Smonthadar * addressed according to 13.11.3.2. 1235234878Smonthadar */ 1236234878Smonthadar } 1237234878Smonthadar return (0); /* process locally */ 1238234878Smonthadar} 1239234878Smonthadar 1240234878Smonthadar/* 1241234878Smonthadar * Try to forward the group addressed data on to other mesh STAs, and 1242234878Smonthadar * also to the DS. 1243234878Smonthadar * 1244234878Smonthadar * > 0 means we have forwarded data and no need to process locally 1245234878Smonthadar * == 0 means we want to process locally (and we may have forwarded data 1246234878Smonthadar * < 0 means there was an error and data should be discarded 1247234878Smonthadar */ 1248234878Smonthadarstatic int 1249234878Smonthadarmesh_recv_group_data(struct ieee80211vap *vap, struct mbuf *m, 1250234878Smonthadar struct ieee80211_frame *wh, const struct ieee80211_meshcntl *mc) 1251234878Smonthadar{ 1252234878Smonthadar#define MC01(mc) ((const struct ieee80211_meshcntl_ae01 *)mc) 1253234878Smonthadar struct ieee80211_mesh_state *ms = vap->iv_mesh; 1254234878Smonthadar 1255234878Smonthadar mesh_forward(vap, m, mc); 1256234878Smonthadar 1257234878Smonthadar if(mc->mc_ttl > 0) { 1258234878Smonthadar if (mc->mc_flags & IEEE80211_MESH_AE_01) { 1259234878Smonthadar /* 1260234878Smonthadar * Forward of MSDUs from the MBSS to DS group addressed 1261234878Smonthadar * (according to 13.11.3.2) 1262234878Smonthadar * This happens by delivering the packet, and a bridge 1263234878Smonthadar * will sent it on another port member. 1264234878Smonthadar */ 1265234878Smonthadar if (ms->ms_flags & IEEE80211_MESHFLAGS_PORTAL && 1266234878Smonthadar ms->ms_flags & IEEE80211_MESHFLAGS_FWD) 1267234878Smonthadar IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, 1268234878Smonthadar MC01(mc)->mc_addr4, "%s", 1269234878Smonthadar "forward from MBSS to the DS"); 1270234878Smonthadar } 1271234878Smonthadar } 1272234878Smonthadar return (0); /* process locally */ 1273234878Smonthadar#undef MC01 1274234878Smonthadar} 1275234878Smonthadar 1276234878Smonthadarstatic int 1277195618Srpaulomesh_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf) 1278195618Srpaulo{ 1279195618Srpaulo#define HAS_SEQ(type) ((type & 0x4) == 0) 1280234878Smonthadar#define MC01(mc) ((const struct ieee80211_meshcntl_ae01 *)mc) 1281234878Smonthadar#define MC10(mc) ((const struct ieee80211_meshcntl_ae10 *)mc) 1282195618Srpaulo struct ieee80211vap *vap = ni->ni_vap; 1283195618Srpaulo struct ieee80211com *ic = ni->ni_ic; 1284195618Srpaulo struct ifnet *ifp = vap->iv_ifp; 1285195618Srpaulo struct ieee80211_frame *wh; 1286195618Srpaulo const struct ieee80211_meshcntl *mc; 1287234878Smonthadar int hdrspace, meshdrlen, need_tap, error; 1288234878Smonthadar uint8_t dir, type, subtype, ae; 1289195618Srpaulo uint32_t seq; 1290234878Smonthadar const uint8_t *addr; 1291234878Smonthadar uint8_t qos[2]; 1292195618Srpaulo ieee80211_seq rxseq; 1293195618Srpaulo 1294195618Srpaulo KASSERT(ni != NULL, ("null node")); 1295195618Srpaulo ni->ni_inact = ni->ni_inact_reload; 1296195618Srpaulo 1297195618Srpaulo need_tap = 1; /* mbuf need to be tapped. */ 1298195618Srpaulo type = -1; /* undefined */ 1299195618Srpaulo 1300195618Srpaulo if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) { 1301195618Srpaulo IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, 1302195618Srpaulo ni->ni_macaddr, NULL, 1303195618Srpaulo "too short (1): len %u", m->m_pkthdr.len); 1304195618Srpaulo vap->iv_stats.is_rx_tooshort++; 1305195618Srpaulo goto out; 1306195618Srpaulo } 1307195618Srpaulo /* 1308195618Srpaulo * Bit of a cheat here, we use a pointer for a 3-address 1309195618Srpaulo * frame format but don't reference fields past outside 1310195618Srpaulo * ieee80211_frame_min w/o first validating the data is 1311195618Srpaulo * present. 1312195618Srpaulo */ 1313195618Srpaulo wh = mtod(m, struct ieee80211_frame *); 1314195618Srpaulo 1315195618Srpaulo if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) != 1316195618Srpaulo IEEE80211_FC0_VERSION_0) { 1317195618Srpaulo IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, 1318195618Srpaulo ni->ni_macaddr, NULL, "wrong version %x", wh->i_fc[0]); 1319195618Srpaulo vap->iv_stats.is_rx_badversion++; 1320195618Srpaulo goto err; 1321195618Srpaulo } 1322195618Srpaulo dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK; 1323195618Srpaulo type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; 1324195618Srpaulo subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; 1325195618Srpaulo if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) { 1326195618Srpaulo IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi); 1327195618Srpaulo ni->ni_noise = nf; 1328195618Srpaulo if (HAS_SEQ(type)) { 1329195618Srpaulo uint8_t tid = ieee80211_gettid(wh); 1330195618Srpaulo 1331195618Srpaulo if (IEEE80211_QOS_HAS_SEQ(wh) && 1332195618Srpaulo TID_TO_WME_AC(tid) >= WME_AC_VI) 1333195618Srpaulo ic->ic_wme.wme_hipri_traffic++; 1334195618Srpaulo rxseq = le16toh(*(uint16_t *)wh->i_seq); 1335221418Sadrian if (! ieee80211_check_rxseq(ni, wh)) { 1336195618Srpaulo /* duplicate, discard */ 1337195618Srpaulo IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, 1338195618Srpaulo wh->i_addr1, "duplicate", 1339195618Srpaulo "seqno <%u,%u> fragno <%u,%u> tid %u", 1340195618Srpaulo rxseq >> IEEE80211_SEQ_SEQ_SHIFT, 1341195618Srpaulo ni->ni_rxseqs[tid] >> 1342195618Srpaulo IEEE80211_SEQ_SEQ_SHIFT, 1343195618Srpaulo rxseq & IEEE80211_SEQ_FRAG_MASK, 1344195618Srpaulo ni->ni_rxseqs[tid] & 1345195618Srpaulo IEEE80211_SEQ_FRAG_MASK, 1346195618Srpaulo tid); 1347195618Srpaulo vap->iv_stats.is_rx_dup++; 1348195618Srpaulo IEEE80211_NODE_STAT(ni, rx_dup); 1349195618Srpaulo goto out; 1350195618Srpaulo } 1351195618Srpaulo ni->ni_rxseqs[tid] = rxseq; 1352195618Srpaulo } 1353195618Srpaulo } 1354195618Srpaulo#ifdef IEEE80211_DEBUG 1355195618Srpaulo /* 1356195618Srpaulo * It's easier, but too expensive, to simulate different mesh 1357195618Srpaulo * topologies by consulting the ACL policy very early, so do this 1358195618Srpaulo * only under DEBUG. 1359195618Srpaulo * 1360195618Srpaulo * NB: this check is also done upon peering link initiation. 1361195618Srpaulo */ 1362228622Sbschmidt if (vap->iv_acl != NULL && !vap->iv_acl->iac_check(vap, wh)) { 1363195618Srpaulo IEEE80211_DISCARD(vap, IEEE80211_MSG_ACL, 1364195618Srpaulo wh, NULL, "%s", "disallowed by ACL"); 1365195618Srpaulo vap->iv_stats.is_rx_acl++; 1366195618Srpaulo goto out; 1367195618Srpaulo } 1368195618Srpaulo#endif 1369195618Srpaulo switch (type) { 1370195618Srpaulo case IEEE80211_FC0_TYPE_DATA: 1371195618Srpaulo if (ni == vap->iv_bss) 1372195618Srpaulo goto out; 1373195618Srpaulo if (ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED) { 1374195618Srpaulo IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_MESH, 1375195618Srpaulo ni->ni_macaddr, NULL, 1376195618Srpaulo "peer link not yet established (%d)", 1377195618Srpaulo ni->ni_mlstate); 1378195618Srpaulo vap->iv_stats.is_mesh_nolink++; 1379195618Srpaulo goto out; 1380234878Smonthadar } 1381195618Srpaulo if (dir != IEEE80211_FC1_DIR_FROMDS && 1382195618Srpaulo dir != IEEE80211_FC1_DIR_DSTODS) { 1383195618Srpaulo IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 1384195618Srpaulo wh, "data", "incorrect dir 0x%x", dir); 1385195618Srpaulo vap->iv_stats.is_rx_wrongdir++; 1386195618Srpaulo goto err; 1387195618Srpaulo } 1388232480Sadrian 1389232480Sadrian /* All Mesh data frames are QoS subtype */ 1390232480Sadrian if (!HAS_SEQ(type)) { 1391232480Sadrian IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 1392232480Sadrian wh, "data", "incorrect subtype 0x%x", subtype); 1393232480Sadrian vap->iv_stats.is_rx_badsubtype++; 1394232480Sadrian goto err; 1395232480Sadrian } 1396232480Sadrian 1397232480Sadrian /* 1398232480Sadrian * Next up, any fragmentation. 1399232480Sadrian * XXX: we defrag before we even try to forward, 1400232480Sadrian * Mesh Control field is not present in sub-sequent 1401232480Sadrian * fragmented frames. This is in contrast to Draft 4.0. 1402232480Sadrian */ 1403232480Sadrian hdrspace = ieee80211_hdrspace(ic, wh); 1404232480Sadrian if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { 1405232480Sadrian m = ieee80211_defrag(ni, m, hdrspace); 1406232480Sadrian if (m == NULL) { 1407232480Sadrian /* Fragment dropped or frame not complete yet */ 1408232480Sadrian goto out; 1409232480Sadrian } 1410232480Sadrian } 1411232480Sadrian wh = mtod(m, struct ieee80211_frame *); /* NB: after defrag */ 1412232480Sadrian 1413232480Sadrian /* 1414232480Sadrian * Now we have a complete Mesh Data frame. 1415232480Sadrian */ 1416232480Sadrian 1417232480Sadrian /* 1418232480Sadrian * Only fromDStoDS data frames use 4 address qos frames 1419232480Sadrian * as specified in amendment. Otherwise addr4 is located 1420232480Sadrian * in the Mesh Control field and a 3 address qos frame 1421232480Sadrian * is used. 1422232480Sadrian */ 1423232480Sadrian if (IEEE80211_IS_DSTODS(wh)) 1424232480Sadrian *(uint16_t *)qos = *(uint16_t *) 1425232480Sadrian ((struct ieee80211_qosframe_addr4 *)wh)->i_qos; 1426232480Sadrian else 1427232480Sadrian *(uint16_t *)qos = *(uint16_t *) 1428232480Sadrian ((struct ieee80211_qosframe *)wh)->i_qos; 1429232480Sadrian 1430232480Sadrian /* 1431232480Sadrian * NB: The mesh STA sets the Mesh Control Present 1432232480Sadrian * subfield to 1 in the Mesh Data frame containing 1433232480Sadrian * an unfragmented MSDU, an A-MSDU, or the first 1434232480Sadrian * fragment of an MSDU. 1435232480Sadrian * After defrag it should always be present. 1436232480Sadrian */ 1437232480Sadrian if (!(qos[1] & IEEE80211_QOS_MC)) { 1438232480Sadrian IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_MESH, 1439232480Sadrian ni->ni_macaddr, NULL, 1440232480Sadrian "%s", "Mesh control field not present"); 1441232480Sadrian vap->iv_stats.is_rx_elem_missing++; /* XXX: kinda */ 1442232480Sadrian goto err; 1443232480Sadrian } 1444232480Sadrian 1445195618Srpaulo /* pull up enough to get to the mesh control */ 1446195618Srpaulo if (m->m_len < hdrspace + sizeof(struct ieee80211_meshcntl) && 1447195618Srpaulo (m = m_pullup(m, hdrspace + 1448195618Srpaulo sizeof(struct ieee80211_meshcntl))) == NULL) { 1449195618Srpaulo IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, 1450195618Srpaulo ni->ni_macaddr, NULL, 1451195618Srpaulo "data too short: expecting %u", hdrspace); 1452195618Srpaulo vap->iv_stats.is_rx_tooshort++; 1453195618Srpaulo goto out; /* XXX */ 1454195618Srpaulo } 1455195618Srpaulo /* 1456195618Srpaulo * Now calculate the full extent of the headers. Note 1457195784Srpaulo * mesh_decap will pull up anything we didn't get 1458195618Srpaulo * above when it strips the 802.11 headers. 1459195618Srpaulo */ 1460195618Srpaulo mc = (const struct ieee80211_meshcntl *) 1461195618Srpaulo (mtod(m, const uint8_t *) + hdrspace); 1462234878Smonthadar ae = mc->mc_flags & IEEE80211_MESH_AE_MASK; 1463195784Srpaulo meshdrlen = sizeof(struct ieee80211_meshcntl) + 1464234878Smonthadar ae * IEEE80211_ADDR_LEN; 1465195784Srpaulo hdrspace += meshdrlen; 1466234878Smonthadar 1467234878Smonthadar /* pull complete hdrspace = ieee80211_hdrspace + meshcontrol */ 1468234878Smonthadar if ((meshdrlen > sizeof(struct ieee80211_meshcntl)) && 1469234878Smonthadar (m->m_len < hdrspace) && 1470234878Smonthadar ((m = m_pullup(m, hdrspace)) == NULL)) { 1471234878Smonthadar IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, 1472234878Smonthadar ni->ni_macaddr, NULL, 1473234878Smonthadar "data too short: expecting %u", hdrspace); 1474234878Smonthadar vap->iv_stats.is_rx_tooshort++; 1475234878Smonthadar goto out; /* XXX */ 1476234878Smonthadar } 1477234878Smonthadar /* XXX: are we sure there is no reallocating after m_pullup? */ 1478234878Smonthadar 1479195618Srpaulo seq = LE_READ_4(mc->mc_seq); 1480195618Srpaulo if (IEEE80211_IS_MULTICAST(wh->i_addr1)) 1481195618Srpaulo addr = wh->i_addr3; 1482234878Smonthadar else if (ae == IEEE80211_MESH_AE_01) 1483234878Smonthadar addr = MC01(mc)->mc_addr4; 1484195618Srpaulo else 1485195618Srpaulo addr = ((struct ieee80211_qosframe_addr4 *)wh)->i_addr4; 1486195618Srpaulo if (IEEE80211_ADDR_EQ(vap->iv_myaddr, addr)) { 1487195618Srpaulo IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, 1488195618Srpaulo addr, "data", "%s", "not to me"); 1489195618Srpaulo vap->iv_stats.is_rx_wrongbss++; /* XXX kinda */ 1490195618Srpaulo goto out; 1491195618Srpaulo } 1492195618Srpaulo if (mesh_checkpseq(vap, addr, seq) != 0) { 1493195618Srpaulo vap->iv_stats.is_rx_dup++; 1494195618Srpaulo goto out; 1495195618Srpaulo } 1496195618Srpaulo 1497234878Smonthadar /* This code "routes" the frame to the right control path */ 1498234878Smonthadar if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { 1499234878Smonthadar if (IEEE80211_ADDR_EQ(vap->iv_myaddr, wh->i_addr3)) 1500234878Smonthadar error = 1501234878Smonthadar mesh_recv_indiv_data_to_me(vap, m, wh, mc); 1502234878Smonthadar else if (IEEE80211_IS_MULTICAST(wh->i_addr3)) 1503234878Smonthadar error = mesh_recv_group_data(vap, m, wh, mc); 1504234878Smonthadar else 1505234878Smonthadar error = mesh_recv_indiv_data_to_fwrd(vap, m, 1506234878Smonthadar wh, mc); 1507234878Smonthadar } else 1508234878Smonthadar error = mesh_recv_group_data(vap, m, wh, mc); 1509234878Smonthadar if (error < 0) 1510234878Smonthadar goto err; 1511234878Smonthadar else if (error > 0) 1512234878Smonthadar goto out; 1513195618Srpaulo 1514195618Srpaulo if (ieee80211_radiotap_active_vap(vap)) 1515195618Srpaulo ieee80211_radiotap_rx(vap, m); 1516195618Srpaulo need_tap = 0; 1517195618Srpaulo 1518195618Srpaulo /* 1519195618Srpaulo * Finally, strip the 802.11 header. 1520195618Srpaulo */ 1521195784Srpaulo m = mesh_decap(vap, m, hdrspace, meshdrlen); 1522195618Srpaulo if (m == NULL) { 1523195618Srpaulo /* XXX mask bit to check for both */ 1524195618Srpaulo /* don't count Null data frames as errors */ 1525195618Srpaulo if (subtype == IEEE80211_FC0_SUBTYPE_NODATA || 1526195618Srpaulo subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL) 1527195618Srpaulo goto out; 1528195618Srpaulo IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, 1529195618Srpaulo ni->ni_macaddr, "data", "%s", "decap error"); 1530195618Srpaulo vap->iv_stats.is_rx_decap++; 1531195618Srpaulo IEEE80211_NODE_STAT(ni, rx_decap); 1532195618Srpaulo goto err; 1533195618Srpaulo } 1534232480Sadrian if (qos[0] & IEEE80211_QOS_AMSDU) { 1535195618Srpaulo m = ieee80211_decap_amsdu(ni, m); 1536195618Srpaulo if (m == NULL) 1537195618Srpaulo return IEEE80211_FC0_TYPE_DATA; 1538195784Srpaulo } 1539195618Srpaulo ieee80211_deliver_data(vap, ni, m); 1540195618Srpaulo return type; 1541195618Srpaulo case IEEE80211_FC0_TYPE_MGT: 1542195618Srpaulo vap->iv_stats.is_rx_mgmt++; 1543195618Srpaulo IEEE80211_NODE_STAT(ni, rx_mgmt); 1544195618Srpaulo if (dir != IEEE80211_FC1_DIR_NODS) { 1545195618Srpaulo IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 1546195618Srpaulo wh, "mgt", "incorrect dir 0x%x", dir); 1547195618Srpaulo vap->iv_stats.is_rx_wrongdir++; 1548195618Srpaulo goto err; 1549195618Srpaulo } 1550195618Srpaulo if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) { 1551195618Srpaulo IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, 1552195618Srpaulo ni->ni_macaddr, "mgt", "too short: len %u", 1553195618Srpaulo m->m_pkthdr.len); 1554195618Srpaulo vap->iv_stats.is_rx_tooshort++; 1555195618Srpaulo goto out; 1556195618Srpaulo } 1557195618Srpaulo#ifdef IEEE80211_DEBUG 1558195618Srpaulo if ((ieee80211_msg_debug(vap) && 1559195618Srpaulo (vap->iv_ic->ic_flags & IEEE80211_F_SCAN)) || 1560195618Srpaulo ieee80211_msg_dumppkts(vap)) { 1561195618Srpaulo if_printf(ifp, "received %s from %s rssi %d\n", 1562195618Srpaulo ieee80211_mgt_subtype_name[subtype >> 1563195618Srpaulo IEEE80211_FC0_SUBTYPE_SHIFT], 1564195618Srpaulo ether_sprintf(wh->i_addr2), rssi); 1565195618Srpaulo } 1566195618Srpaulo#endif 1567195618Srpaulo if (wh->i_fc[1] & IEEE80211_FC1_WEP) { 1568195618Srpaulo IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 1569195618Srpaulo wh, NULL, "%s", "WEP set but not permitted"); 1570195618Srpaulo vap->iv_stats.is_rx_mgtdiscard++; /* XXX */ 1571195618Srpaulo goto out; 1572195618Srpaulo } 1573195618Srpaulo vap->iv_recv_mgmt(ni, m, subtype, rssi, nf); 1574195618Srpaulo goto out; 1575195618Srpaulo case IEEE80211_FC0_TYPE_CTL: 1576195618Srpaulo vap->iv_stats.is_rx_ctl++; 1577195618Srpaulo IEEE80211_NODE_STAT(ni, rx_ctrl); 1578195618Srpaulo goto out; 1579195618Srpaulo default: 1580195618Srpaulo IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, 1581195618Srpaulo wh, "bad", "frame type 0x%x", type); 1582195618Srpaulo /* should not come here */ 1583195618Srpaulo break; 1584195618Srpaulo } 1585195618Srpauloerr: 1586195618Srpaulo ifp->if_ierrors++; 1587195618Srpauloout: 1588195618Srpaulo if (m != NULL) { 1589195618Srpaulo if (need_tap && ieee80211_radiotap_active_vap(vap)) 1590195618Srpaulo ieee80211_radiotap_rx(vap, m); 1591195618Srpaulo m_freem(m); 1592195618Srpaulo } 1593195618Srpaulo return type; 1594234878Smonthadar#undef HAS_SEQ 1595234878Smonthadar#undef MC01 1596234878Smonthadar#undef MC10 1597195618Srpaulo} 1598195618Srpaulo 1599195618Srpaulostatic void 1600195618Srpaulomesh_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, int subtype, 1601195618Srpaulo int rssi, int nf) 1602195618Srpaulo{ 1603195618Srpaulo struct ieee80211vap *vap = ni->ni_vap; 1604195618Srpaulo struct ieee80211_mesh_state *ms = vap->iv_mesh; 1605195618Srpaulo struct ieee80211com *ic = ni->ni_ic; 1606195618Srpaulo struct ieee80211_frame *wh; 1607234877Smonthadar struct ieee80211_mesh_route *rt; 1608195618Srpaulo uint8_t *frm, *efrm; 1609195618Srpaulo 1610195618Srpaulo wh = mtod(m0, struct ieee80211_frame *); 1611195618Srpaulo frm = (uint8_t *)&wh[1]; 1612195618Srpaulo efrm = mtod(m0, uint8_t *) + m0->m_len; 1613195618Srpaulo switch (subtype) { 1614195618Srpaulo case IEEE80211_FC0_SUBTYPE_PROBE_RESP: 1615195618Srpaulo case IEEE80211_FC0_SUBTYPE_BEACON: 1616195618Srpaulo { 1617195618Srpaulo struct ieee80211_scanparams scan; 1618195618Srpaulo /* 1619195618Srpaulo * We process beacon/probe response 1620195618Srpaulo * frames to discover neighbors. 1621195618Srpaulo */ 1622195618Srpaulo if (ieee80211_parse_beacon(ni, m0, &scan) != 0) 1623195618Srpaulo return; 1624195618Srpaulo /* 1625195618Srpaulo * Count frame now that we know it's to be processed. 1626195618Srpaulo */ 1627195618Srpaulo if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) { 1628195618Srpaulo vap->iv_stats.is_rx_beacon++; /* XXX remove */ 1629195618Srpaulo IEEE80211_NODE_STAT(ni, rx_beacons); 1630195618Srpaulo } else 1631195618Srpaulo IEEE80211_NODE_STAT(ni, rx_proberesp); 1632195618Srpaulo /* 1633195618Srpaulo * If scanning, just pass information to the scan module. 1634195618Srpaulo */ 1635195618Srpaulo if (ic->ic_flags & IEEE80211_F_SCAN) { 1636195618Srpaulo if (ic->ic_flags_ext & IEEE80211_FEXT_PROBECHAN) { 1637195618Srpaulo /* 1638195618Srpaulo * Actively scanning a channel marked passive; 1639195618Srpaulo * send a probe request now that we know there 1640195618Srpaulo * is 802.11 traffic present. 1641195618Srpaulo * 1642195618Srpaulo * XXX check if the beacon we recv'd gives 1643195618Srpaulo * us what we need and suppress the probe req 1644195618Srpaulo */ 1645195618Srpaulo ieee80211_probe_curchan(vap, 1); 1646195618Srpaulo ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN; 1647195618Srpaulo } 1648195618Srpaulo ieee80211_add_scan(vap, &scan, wh, 1649195618Srpaulo subtype, rssi, nf); 1650195618Srpaulo return; 1651195618Srpaulo } 1652195618Srpaulo 1653195618Srpaulo /* The rest of this code assumes we are running */ 1654195618Srpaulo if (vap->iv_state != IEEE80211_S_RUN) 1655195618Srpaulo return; 1656195618Srpaulo /* 1657195618Srpaulo * Ignore non-mesh STAs. 1658195618Srpaulo */ 1659195618Srpaulo if ((scan.capinfo & 1660195618Srpaulo (IEEE80211_CAPINFO_ESS|IEEE80211_CAPINFO_IBSS)) || 1661195618Srpaulo scan.meshid == NULL || scan.meshconf == NULL) { 1662195618Srpaulo IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 1663195618Srpaulo wh, "beacon", "%s", "not a mesh sta"); 1664195618Srpaulo vap->iv_stats.is_mesh_wrongmesh++; 1665195618Srpaulo return; 1666195618Srpaulo } 1667195618Srpaulo /* 1668195618Srpaulo * Ignore STAs for other mesh networks. 1669195618Srpaulo */ 1670195618Srpaulo if (memcmp(scan.meshid+2, ms->ms_id, ms->ms_idlen) != 0 || 1671195618Srpaulo mesh_verify_meshconf(vap, scan.meshconf)) { 1672195618Srpaulo IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 1673195618Srpaulo wh, "beacon", "%s", "not for our mesh"); 1674195618Srpaulo vap->iv_stats.is_mesh_wrongmesh++; 1675195618Srpaulo return; 1676195618Srpaulo } 1677195618Srpaulo /* 1678195618Srpaulo * Peer only based on the current ACL policy. 1679195618Srpaulo */ 1680228622Sbschmidt if (vap->iv_acl != NULL && !vap->iv_acl->iac_check(vap, wh)) { 1681195618Srpaulo IEEE80211_DISCARD(vap, IEEE80211_MSG_ACL, 1682195618Srpaulo wh, NULL, "%s", "disallowed by ACL"); 1683195618Srpaulo vap->iv_stats.is_rx_acl++; 1684195618Srpaulo return; 1685195618Srpaulo } 1686195618Srpaulo /* 1687195618Srpaulo * Do neighbor discovery. 1688195618Srpaulo */ 1689195618Srpaulo if (!IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) { 1690195618Srpaulo /* 1691195618Srpaulo * Create a new entry in the neighbor table. 1692195618Srpaulo */ 1693195618Srpaulo ni = ieee80211_add_neighbor(vap, wh, &scan); 1694195618Srpaulo } 1695195618Srpaulo /* 1696195618Srpaulo * Automatically peer with discovered nodes if possible. 1697195618Srpaulo * XXX backoff on repeated failure 1698195618Srpaulo */ 1699195618Srpaulo if (ni != vap->iv_bss && 1700234877Smonthadar (ms->ms_flags & IEEE80211_MESHFLAGS_AP)) { 1701234877Smonthadar switch (ni->ni_mlstate) { 1702234877Smonthadar case IEEE80211_NODE_MESH_IDLE: 1703234877Smonthadar { 1704234877Smonthadar uint16_t args[1]; 1705195618Srpaulo 1706234877Smonthadar ni->ni_mlpid = mesh_generateid(vap); 1707234877Smonthadar if (ni->ni_mlpid == 0) 1708234877Smonthadar return; 1709234877Smonthadar mesh_linkchange(ni, IEEE80211_NODE_MESH_OPENSNT); 1710234877Smonthadar args[0] = ni->ni_mlpid; 1711234877Smonthadar ieee80211_send_action(ni, 1712234877Smonthadar IEEE80211_ACTION_CAT_SELF_PROT, 1713234877Smonthadar IEEE80211_ACTION_MESHPEERING_OPEN, args); 1714234877Smonthadar ni->ni_mlrcnt = 0; 1715234877Smonthadar mesh_peer_timeout_setup(ni); 1716234877Smonthadar break; 1717234877Smonthadar } 1718234877Smonthadar case IEEE80211_NODE_MESH_ESTABLISHED: 1719234877Smonthadar { 1720234877Smonthadar /* 1721234877Smonthadar * Valid beacon from a peer mesh STA 1722234877Smonthadar * bump TA lifetime 1723234877Smonthadar */ 1724234877Smonthadar rt = ieee80211_mesh_rt_find(vap, wh->i_addr2); 1725234877Smonthadar if(rt != NULL) { 1726234877Smonthadar ieee80211_mesh_rt_update(rt, 1727234879Smonthadar ticks_to_msecs( 1728234879Smonthadar ms->ms_ppath->mpp_inact)); 1729234877Smonthadar } 1730234877Smonthadar break; 1731234877Smonthadar } 1732234877Smonthadar default: 1733234877Smonthadar break; /* ignore */ 1734234877Smonthadar } 1735195618Srpaulo } 1736195618Srpaulo break; 1737195618Srpaulo } 1738195618Srpaulo case IEEE80211_FC0_SUBTYPE_PROBE_REQ: 1739195618Srpaulo { 1740195618Srpaulo uint8_t *ssid, *meshid, *rates, *xrates; 1741195618Srpaulo uint8_t *sfrm; 1742195618Srpaulo 1743195618Srpaulo if (vap->iv_state != IEEE80211_S_RUN) { 1744195618Srpaulo IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 1745195618Srpaulo wh, NULL, "wrong state %s", 1746195618Srpaulo ieee80211_state_name[vap->iv_state]); 1747195618Srpaulo vap->iv_stats.is_rx_mgtdiscard++; 1748195618Srpaulo return; 1749195618Srpaulo } 1750195618Srpaulo if (IEEE80211_IS_MULTICAST(wh->i_addr2)) { 1751195618Srpaulo /* frame must be directed */ 1752195618Srpaulo IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 1753195618Srpaulo wh, NULL, "%s", "not unicast"); 1754195618Srpaulo vap->iv_stats.is_rx_mgtdiscard++; /* XXX stat */ 1755195618Srpaulo return; 1756195618Srpaulo } 1757195618Srpaulo /* 1758195618Srpaulo * prreq frame format 1759195618Srpaulo * [tlv] ssid 1760195618Srpaulo * [tlv] supported rates 1761195618Srpaulo * [tlv] extended supported rates 1762195618Srpaulo * [tlv] mesh id 1763195618Srpaulo */ 1764195618Srpaulo ssid = meshid = rates = xrates = NULL; 1765195618Srpaulo sfrm = frm; 1766195618Srpaulo while (efrm - frm > 1) { 1767195618Srpaulo IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return); 1768195618Srpaulo switch (*frm) { 1769195618Srpaulo case IEEE80211_ELEMID_SSID: 1770195618Srpaulo ssid = frm; 1771195618Srpaulo break; 1772195618Srpaulo case IEEE80211_ELEMID_RATES: 1773195618Srpaulo rates = frm; 1774195618Srpaulo break; 1775195618Srpaulo case IEEE80211_ELEMID_XRATES: 1776195618Srpaulo xrates = frm; 1777195618Srpaulo break; 1778195618Srpaulo case IEEE80211_ELEMID_MESHID: 1779195618Srpaulo meshid = frm; 1780195618Srpaulo break; 1781195618Srpaulo } 1782217590Sbschmidt frm += frm[1] + 2; 1783195618Srpaulo } 1784195618Srpaulo IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN, return); 1785195618Srpaulo IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE, return); 1786195618Srpaulo if (xrates != NULL) 1787195618Srpaulo IEEE80211_VERIFY_ELEMENT(xrates, 1788195618Srpaulo IEEE80211_RATE_MAXSIZE - rates[1], return); 1789203423Srpaulo if (meshid != NULL) { 1790195618Srpaulo IEEE80211_VERIFY_ELEMENT(meshid, 1791195618Srpaulo IEEE80211_MESHID_LEN, return); 1792203423Srpaulo /* NB: meshid, not ssid */ 1793203423Srpaulo IEEE80211_VERIFY_SSID(vap->iv_bss, meshid, return); 1794203423Srpaulo } 1795195618Srpaulo 1796195618Srpaulo /* XXX find a better class or define it's own */ 1797195618Srpaulo IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_INPUT, wh->i_addr2, 1798195618Srpaulo "%s", "recv probe req"); 1799195618Srpaulo /* 1800195618Srpaulo * Some legacy 11b clients cannot hack a complete 1801195618Srpaulo * probe response frame. When the request includes 1802195618Srpaulo * only a bare-bones rate set, communicate this to 1803195618Srpaulo * the transmit side. 1804195618Srpaulo */ 1805195618Srpaulo ieee80211_send_proberesp(vap, wh->i_addr2, 0); 1806195618Srpaulo break; 1807195618Srpaulo } 1808218927Sbschmidt 1809195618Srpaulo case IEEE80211_FC0_SUBTYPE_ACTION: 1810218927Sbschmidt case IEEE80211_FC0_SUBTYPE_ACTION_NOACK: 1811195618Srpaulo if (ni == vap->iv_bss) { 1812218958Sbschmidt IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 1813195618Srpaulo wh, NULL, "%s", "unknown node"); 1814195618Srpaulo vap->iv_stats.is_rx_mgtdiscard++; 1815218958Sbschmidt } else if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, wh->i_addr1) && 1816195618Srpaulo !IEEE80211_IS_MULTICAST(wh->i_addr1)) { 1817218958Sbschmidt IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 1818218958Sbschmidt wh, NULL, "%s", "not for us"); 1819195618Srpaulo vap->iv_stats.is_rx_mgtdiscard++; 1820218958Sbschmidt } else if (vap->iv_state != IEEE80211_S_RUN) { 1821218927Sbschmidt IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 1822218927Sbschmidt wh, NULL, "wrong state %s", 1823218927Sbschmidt ieee80211_state_name[vap->iv_state]); 1824218927Sbschmidt vap->iv_stats.is_rx_mgtdiscard++; 1825218958Sbschmidt } else { 1826218958Sbschmidt if (ieee80211_parse_action(ni, m0) == 0) 1827218958Sbschmidt (void)ic->ic_recv_action(ni, wh, frm, efrm); 1828218927Sbschmidt } 1829195618Srpaulo break; 1830218927Sbschmidt 1831195618Srpaulo case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: 1832218927Sbschmidt case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: 1833195618Srpaulo case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: 1834195618Srpaulo case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: 1835218927Sbschmidt case IEEE80211_FC0_SUBTYPE_ATIM: 1836218927Sbschmidt case IEEE80211_FC0_SUBTYPE_DISASSOC: 1837218927Sbschmidt case IEEE80211_FC0_SUBTYPE_AUTH: 1838195618Srpaulo case IEEE80211_FC0_SUBTYPE_DEAUTH: 1839195618Srpaulo IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 1840195618Srpaulo wh, NULL, "%s", "not handled"); 1841195618Srpaulo vap->iv_stats.is_rx_mgtdiscard++; 1842218927Sbschmidt break; 1843218927Sbschmidt 1844195618Srpaulo default: 1845195618Srpaulo IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, 1846195618Srpaulo wh, "mgt", "subtype 0x%x not handled", subtype); 1847195618Srpaulo vap->iv_stats.is_rx_badsubtype++; 1848195618Srpaulo break; 1849195618Srpaulo } 1850195618Srpaulo} 1851195618Srpaulo 1852205277Srpaulostatic void 1853205277Srpaulomesh_recv_ctl(struct ieee80211_node *ni, struct mbuf *m, int subtype) 1854205277Srpaulo{ 1855205277Srpaulo 1856205277Srpaulo switch (subtype) { 1857205277Srpaulo case IEEE80211_FC0_SUBTYPE_BAR: 1858205277Srpaulo ieee80211_recv_bar(ni, m); 1859205277Srpaulo break; 1860205277Srpaulo } 1861205277Srpaulo} 1862205277Srpaulo 1863195618Srpaulo/* 1864234876Smonthadar * Parse meshpeering action ie's for MPM frames 1865195618Srpaulo */ 1866195618Srpaulostatic const struct ieee80211_meshpeer_ie * 1867195618Srpaulomesh_parse_meshpeering_action(struct ieee80211_node *ni, 1868195618Srpaulo const struct ieee80211_frame *wh, /* XXX for VERIFY_LENGTH */ 1869195618Srpaulo const uint8_t *frm, const uint8_t *efrm, 1870197413Srpaulo struct ieee80211_meshpeer_ie *mp, uint8_t subtype) 1871195618Srpaulo{ 1872195618Srpaulo struct ieee80211vap *vap = ni->ni_vap; 1873195618Srpaulo const struct ieee80211_meshpeer_ie *mpie; 1874234876Smonthadar uint16_t args[3]; 1875197413Srpaulo const uint8_t *meshid, *meshconf, *meshpeer; 1876234876Smonthadar uint8_t sendclose = 0; /* 1 = MPM frame rejected, close will be sent */ 1877195618Srpaulo 1878197413Srpaulo meshid = meshconf = meshpeer = NULL; 1879195618Srpaulo while (efrm - frm > 1) { 1880195618Srpaulo IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return NULL); 1881195618Srpaulo switch (*frm) { 1882195618Srpaulo case IEEE80211_ELEMID_MESHID: 1883195618Srpaulo meshid = frm; 1884195618Srpaulo break; 1885195618Srpaulo case IEEE80211_ELEMID_MESHCONF: 1886195618Srpaulo meshconf = frm; 1887195618Srpaulo break; 1888195618Srpaulo case IEEE80211_ELEMID_MESHPEER: 1889195618Srpaulo meshpeer = frm; 1890195618Srpaulo mpie = (const struct ieee80211_meshpeer_ie *) frm; 1891195618Srpaulo memset(mp, 0, sizeof(*mp)); 1892234876Smonthadar mp->peer_len = mpie->peer_len; 1893234875Smonthadar mp->peer_proto = LE_READ_2(&mpie->peer_proto); 1894195618Srpaulo mp->peer_llinkid = LE_READ_2(&mpie->peer_llinkid); 1895234875Smonthadar switch (subtype) { 1896234875Smonthadar case IEEE80211_ACTION_MESHPEERING_CONFIRM: 1897234875Smonthadar mp->peer_linkid = 1898234875Smonthadar LE_READ_2(&mpie->peer_linkid); 1899234875Smonthadar break; 1900234875Smonthadar case IEEE80211_ACTION_MESHPEERING_CLOSE: 1901234875Smonthadar /* NB: peer link ID is optional */ 1902234875Smonthadar if (mpie->peer_len == 1903234875Smonthadar (IEEE80211_MPM_BASE_SZ + 2)) { 1904234875Smonthadar mp->peer_linkid = 0; 1905234875Smonthadar mp->peer_rcode = 1906234875Smonthadar LE_READ_2(&mpie->peer_linkid); 1907234875Smonthadar } else { 1908234875Smonthadar mp->peer_linkid = 1909234875Smonthadar LE_READ_2(&mpie->peer_linkid); 1910234875Smonthadar mp->peer_rcode = 1911234875Smonthadar LE_READ_2(&mpie->peer_rcode); 1912234875Smonthadar } 1913234875Smonthadar break; 1914195618Srpaulo } 1915195618Srpaulo break; 1916195618Srpaulo } 1917195618Srpaulo frm += frm[1] + 2; 1918195618Srpaulo } 1919195618Srpaulo 1920195618Srpaulo /* 1921234876Smonthadar * Verify the contents of the frame. 1922234876Smonthadar * If it fails validation, close the peer link. 1923195618Srpaulo */ 1924234876Smonthadar if (mesh_verify_meshpeer(vap, subtype, (const uint8_t *)mp)) { 1925234876Smonthadar sendclose = 1; 1926234876Smonthadar IEEE80211_DISCARD(vap, 1927234876Smonthadar IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, 1928234876Smonthadar wh, NULL, "%s", "MPM validation failed"); 1929234876Smonthadar } 1930195618Srpaulo 1931234876Smonthadar /* If meshid is not the same reject any frames type. */ 1932234876Smonthadar if (sendclose == 0 && mesh_verify_meshid(vap, meshid)) { 1933234876Smonthadar sendclose = 1; 1934234876Smonthadar IEEE80211_DISCARD(vap, 1935234876Smonthadar IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, 1936234876Smonthadar wh, NULL, "%s", "not for our mesh"); 1937234876Smonthadar if (subtype == IEEE80211_ACTION_MESHPEERING_CLOSE) { 1938234876Smonthadar /* 1939234876Smonthadar * Standard not clear about this, if we dont ignore 1940234876Smonthadar * there will be an endless loop between nodes sending 1941234876Smonthadar * CLOSE frames between each other with wrong meshid. 1942234876Smonthadar * Discard and timers will bring FSM to IDLE state. 1943234876Smonthadar */ 1944234876Smonthadar return NULL; 1945234876Smonthadar } 1946234876Smonthadar } 1947234876Smonthadar 1948234876Smonthadar /* 1949234876Smonthadar * Close frames are accepted if meshid is the same. 1950234876Smonthadar * Verify the other two types. 1951234876Smonthadar */ 1952234876Smonthadar if (sendclose == 0 && subtype != IEEE80211_ACTION_MESHPEERING_CLOSE && 1953195618Srpaulo mesh_verify_meshconf(vap, meshconf)) { 1954234876Smonthadar sendclose = 1; 1955195618Srpaulo IEEE80211_DISCARD(vap, 1956195618Srpaulo IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, 1957234876Smonthadar wh, NULL, "%s", "configuration missmatch"); 1958234876Smonthadar } 1959234876Smonthadar 1960234876Smonthadar if (sendclose) { 1961195618Srpaulo vap->iv_stats.is_rx_mgtdiscard++; 1962195618Srpaulo switch (ni->ni_mlstate) { 1963195618Srpaulo case IEEE80211_NODE_MESH_IDLE: 1964195618Srpaulo case IEEE80211_NODE_MESH_ESTABLISHED: 1965195618Srpaulo case IEEE80211_NODE_MESH_HOLDING: 1966195618Srpaulo /* ignore */ 1967195618Srpaulo break; 1968195618Srpaulo case IEEE80211_NODE_MESH_OPENSNT: 1969195618Srpaulo case IEEE80211_NODE_MESH_OPENRCV: 1970195618Srpaulo case IEEE80211_NODE_MESH_CONFIRMRCV: 1971195618Srpaulo args[0] = ni->ni_mlpid; 1972195618Srpaulo args[1] = ni->ni_mllid; 1973234876Smonthadar /* Reason codes for rejection */ 1974234876Smonthadar switch (subtype) { 1975234876Smonthadar case IEEE80211_ACTION_MESHPEERING_OPEN: 1976234876Smonthadar args[2] = IEEE80211_REASON_MESH_CPVIOLATION; 1977234876Smonthadar break; 1978234876Smonthadar case IEEE80211_ACTION_MESHPEERING_CONFIRM: 1979234876Smonthadar args[2] = IEEE80211_REASON_MESH_INCONS_PARAMS; 1980234876Smonthadar break; 1981234876Smonthadar } 1982195618Srpaulo ieee80211_send_action(ni, 1983234874Smonthadar IEEE80211_ACTION_CAT_SELF_PROT, 1984195618Srpaulo IEEE80211_ACTION_MESHPEERING_CLOSE, 1985195618Srpaulo args); 1986195618Srpaulo mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING); 1987195618Srpaulo mesh_peer_timeout_setup(ni); 1988195618Srpaulo break; 1989195618Srpaulo } 1990195618Srpaulo return NULL; 1991195618Srpaulo } 1992234876Smonthadar 1993195618Srpaulo return (const struct ieee80211_meshpeer_ie *) mp; 1994195618Srpaulo} 1995195618Srpaulo 1996195618Srpaulostatic int 1997195618Srpaulomesh_recv_action_meshpeering_open(struct ieee80211_node *ni, 1998195618Srpaulo const struct ieee80211_frame *wh, 1999195618Srpaulo const uint8_t *frm, const uint8_t *efrm) 2000195618Srpaulo{ 2001195618Srpaulo struct ieee80211vap *vap = ni->ni_vap; 2002234876Smonthadar struct ieee80211_mesh_state *ms = vap->iv_mesh; 2003195618Srpaulo struct ieee80211_meshpeer_ie ie; 2004195618Srpaulo const struct ieee80211_meshpeer_ie *meshpeer; 2005195618Srpaulo uint16_t args[3]; 2006195618Srpaulo 2007195618Srpaulo /* +2+2 for action + code + capabilites */ 2008197413Srpaulo meshpeer = mesh_parse_meshpeering_action(ni, wh, frm+2+2, efrm, &ie, 2009197413Srpaulo IEEE80211_ACTION_MESHPEERING_OPEN); 2010195618Srpaulo if (meshpeer == NULL) { 2011195618Srpaulo return 0; 2012195618Srpaulo } 2013195618Srpaulo 2014195618Srpaulo /* XXX move up */ 2015195618Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, ni, 2016195618Srpaulo "recv PEER OPEN, lid 0x%x", meshpeer->peer_llinkid); 2017195618Srpaulo 2018195618Srpaulo switch (ni->ni_mlstate) { 2019195618Srpaulo case IEEE80211_NODE_MESH_IDLE: 2020234876Smonthadar /* Reject open request if reached our maximum neighbor count */ 2021234876Smonthadar if (ms->ms_neighbors >= IEEE80211_MESH_MAX_NEIGHBORS) { 2022234876Smonthadar args[0] = meshpeer->peer_llinkid; 2023234876Smonthadar args[1] = 0; 2024234876Smonthadar args[2] = IEEE80211_REASON_MESH_MAX_PEERS; 2025234876Smonthadar ieee80211_send_action(ni, 2026234876Smonthadar IEEE80211_ACTION_CAT_SELF_PROT, 2027234876Smonthadar IEEE80211_ACTION_MESHPEERING_CLOSE, 2028234876Smonthadar args); 2029234876Smonthadar /* stay in IDLE state */ 2030234876Smonthadar return (0); 2031234876Smonthadar } 2032234876Smonthadar /* Open frame accepted */ 2033195618Srpaulo mesh_linkchange(ni, IEEE80211_NODE_MESH_OPENRCV); 2034195618Srpaulo ni->ni_mllid = meshpeer->peer_llinkid; 2035195618Srpaulo ni->ni_mlpid = mesh_generateid(vap); 2036195618Srpaulo if (ni->ni_mlpid == 0) 2037195618Srpaulo return 0; /* XXX */ 2038195618Srpaulo args[0] = ni->ni_mlpid; 2039195618Srpaulo /* Announce we're open too... */ 2040195618Srpaulo ieee80211_send_action(ni, 2041234874Smonthadar IEEE80211_ACTION_CAT_SELF_PROT, 2042195618Srpaulo IEEE80211_ACTION_MESHPEERING_OPEN, args); 2043195618Srpaulo /* ...and confirm the link. */ 2044195618Srpaulo args[0] = ni->ni_mlpid; 2045195618Srpaulo args[1] = ni->ni_mllid; 2046195618Srpaulo ieee80211_send_action(ni, 2047234874Smonthadar IEEE80211_ACTION_CAT_SELF_PROT, 2048195618Srpaulo IEEE80211_ACTION_MESHPEERING_CONFIRM, 2049195618Srpaulo args); 2050195618Srpaulo mesh_peer_timeout_setup(ni); 2051195618Srpaulo break; 2052195618Srpaulo case IEEE80211_NODE_MESH_OPENRCV: 2053195618Srpaulo /* Wrong Link ID */ 2054195618Srpaulo if (ni->ni_mllid != meshpeer->peer_llinkid) { 2055195618Srpaulo args[0] = ni->ni_mllid; 2056195618Srpaulo args[1] = ni->ni_mlpid; 2057195618Srpaulo args[2] = IEEE80211_REASON_PEER_LINK_CANCELED; 2058195618Srpaulo ieee80211_send_action(ni, 2059234874Smonthadar IEEE80211_ACTION_CAT_SELF_PROT, 2060195618Srpaulo IEEE80211_ACTION_MESHPEERING_CLOSE, 2061195618Srpaulo args); 2062195618Srpaulo mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING); 2063195618Srpaulo mesh_peer_timeout_setup(ni); 2064195618Srpaulo break; 2065195618Srpaulo } 2066195618Srpaulo /* Duplicate open, confirm again. */ 2067195618Srpaulo args[0] = ni->ni_mlpid; 2068195618Srpaulo args[1] = ni->ni_mllid; 2069195618Srpaulo ieee80211_send_action(ni, 2070234874Smonthadar IEEE80211_ACTION_CAT_SELF_PROT, 2071195618Srpaulo IEEE80211_ACTION_MESHPEERING_CONFIRM, 2072195618Srpaulo args); 2073195618Srpaulo break; 2074195618Srpaulo case IEEE80211_NODE_MESH_OPENSNT: 2075195618Srpaulo ni->ni_mllid = meshpeer->peer_llinkid; 2076195618Srpaulo mesh_linkchange(ni, IEEE80211_NODE_MESH_OPENRCV); 2077195618Srpaulo args[0] = ni->ni_mlpid; 2078195618Srpaulo args[1] = ni->ni_mllid; 2079195618Srpaulo ieee80211_send_action(ni, 2080234874Smonthadar IEEE80211_ACTION_CAT_SELF_PROT, 2081195618Srpaulo IEEE80211_ACTION_MESHPEERING_CONFIRM, 2082195618Srpaulo args); 2083195618Srpaulo /* NB: don't setup/clear any timeout */ 2084195618Srpaulo break; 2085195618Srpaulo case IEEE80211_NODE_MESH_CONFIRMRCV: 2086195618Srpaulo if (ni->ni_mlpid != meshpeer->peer_linkid || 2087195618Srpaulo ni->ni_mllid != meshpeer->peer_llinkid) { 2088195618Srpaulo args[0] = ni->ni_mlpid; 2089195618Srpaulo args[1] = ni->ni_mllid; 2090195618Srpaulo args[2] = IEEE80211_REASON_PEER_LINK_CANCELED; 2091195618Srpaulo ieee80211_send_action(ni, 2092234874Smonthadar IEEE80211_ACTION_CAT_SELF_PROT, 2093195618Srpaulo IEEE80211_ACTION_MESHPEERING_CLOSE, 2094195618Srpaulo args); 2095195618Srpaulo mesh_linkchange(ni, 2096195618Srpaulo IEEE80211_NODE_MESH_HOLDING); 2097195618Srpaulo mesh_peer_timeout_setup(ni); 2098195618Srpaulo break; 2099195618Srpaulo } 2100195618Srpaulo mesh_linkchange(ni, IEEE80211_NODE_MESH_ESTABLISHED); 2101195618Srpaulo ni->ni_mllid = meshpeer->peer_llinkid; 2102195618Srpaulo args[0] = ni->ni_mlpid; 2103195618Srpaulo args[1] = ni->ni_mllid; 2104195618Srpaulo ieee80211_send_action(ni, 2105234874Smonthadar IEEE80211_ACTION_CAT_SELF_PROT, 2106195618Srpaulo IEEE80211_ACTION_MESHPEERING_CONFIRM, 2107195618Srpaulo args); 2108195618Srpaulo mesh_peer_timeout_stop(ni); 2109195618Srpaulo break; 2110195618Srpaulo case IEEE80211_NODE_MESH_ESTABLISHED: 2111195618Srpaulo if (ni->ni_mllid != meshpeer->peer_llinkid) { 2112195618Srpaulo args[0] = ni->ni_mllid; 2113195618Srpaulo args[1] = ni->ni_mlpid; 2114195618Srpaulo args[2] = IEEE80211_REASON_PEER_LINK_CANCELED; 2115195618Srpaulo ieee80211_send_action(ni, 2116234874Smonthadar IEEE80211_ACTION_CAT_SELF_PROT, 2117195618Srpaulo IEEE80211_ACTION_MESHPEERING_CLOSE, 2118195618Srpaulo args); 2119195618Srpaulo mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING); 2120195618Srpaulo mesh_peer_timeout_setup(ni); 2121195618Srpaulo break; 2122195618Srpaulo } 2123195618Srpaulo args[0] = ni->ni_mlpid; 2124195618Srpaulo args[1] = ni->ni_mllid; 2125195618Srpaulo ieee80211_send_action(ni, 2126234874Smonthadar IEEE80211_ACTION_CAT_SELF_PROT, 2127195618Srpaulo IEEE80211_ACTION_MESHPEERING_CONFIRM, 2128195618Srpaulo args); 2129195618Srpaulo break; 2130195618Srpaulo case IEEE80211_NODE_MESH_HOLDING: 2131195618Srpaulo args[0] = ni->ni_mlpid; 2132195618Srpaulo args[1] = meshpeer->peer_llinkid; 2133234876Smonthadar /* Standard not clear about what the reaason code should be */ 2134234876Smonthadar args[2] = IEEE80211_REASON_PEER_LINK_CANCELED; 2135195618Srpaulo ieee80211_send_action(ni, 2136234874Smonthadar IEEE80211_ACTION_CAT_SELF_PROT, 2137195618Srpaulo IEEE80211_ACTION_MESHPEERING_CLOSE, 2138195618Srpaulo args); 2139195618Srpaulo break; 2140195618Srpaulo } 2141195618Srpaulo return 0; 2142195618Srpaulo} 2143195618Srpaulo 2144195618Srpaulostatic int 2145195618Srpaulomesh_recv_action_meshpeering_confirm(struct ieee80211_node *ni, 2146195618Srpaulo const struct ieee80211_frame *wh, 2147195618Srpaulo const uint8_t *frm, const uint8_t *efrm) 2148195618Srpaulo{ 2149195618Srpaulo struct ieee80211vap *vap = ni->ni_vap; 2150195618Srpaulo struct ieee80211_meshpeer_ie ie; 2151195618Srpaulo const struct ieee80211_meshpeer_ie *meshpeer; 2152195618Srpaulo uint16_t args[3]; 2153195618Srpaulo 2154195618Srpaulo /* +2+2+2+2 for action + code + capabilites + status code + AID */ 2155197413Srpaulo meshpeer = mesh_parse_meshpeering_action(ni, wh, frm+2+2+2+2, efrm, &ie, 2156197413Srpaulo IEEE80211_ACTION_MESHPEERING_CONFIRM); 2157195618Srpaulo if (meshpeer == NULL) { 2158195618Srpaulo return 0; 2159195618Srpaulo } 2160195618Srpaulo 2161195618Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, ni, 2162195618Srpaulo "recv PEER CONFIRM, local id 0x%x, peer id 0x%x", 2163195618Srpaulo meshpeer->peer_llinkid, meshpeer->peer_linkid); 2164195618Srpaulo 2165195618Srpaulo switch (ni->ni_mlstate) { 2166195618Srpaulo case IEEE80211_NODE_MESH_OPENRCV: 2167195618Srpaulo mesh_linkchange(ni, IEEE80211_NODE_MESH_ESTABLISHED); 2168195618Srpaulo mesh_peer_timeout_stop(ni); 2169195618Srpaulo break; 2170195618Srpaulo case IEEE80211_NODE_MESH_OPENSNT: 2171195618Srpaulo mesh_linkchange(ni, IEEE80211_NODE_MESH_CONFIRMRCV); 2172234876Smonthadar mesh_peer_timeout_setup(ni); 2173195618Srpaulo break; 2174195618Srpaulo case IEEE80211_NODE_MESH_HOLDING: 2175195618Srpaulo args[0] = ni->ni_mlpid; 2176195618Srpaulo args[1] = meshpeer->peer_llinkid; 2177234876Smonthadar /* Standard not clear about what the reaason code should be */ 2178234876Smonthadar args[2] = IEEE80211_REASON_PEER_LINK_CANCELED; 2179195618Srpaulo ieee80211_send_action(ni, 2180234874Smonthadar IEEE80211_ACTION_CAT_SELF_PROT, 2181195618Srpaulo IEEE80211_ACTION_MESHPEERING_CLOSE, 2182195618Srpaulo args); 2183195618Srpaulo break; 2184195618Srpaulo case IEEE80211_NODE_MESH_CONFIRMRCV: 2185195618Srpaulo if (ni->ni_mllid != meshpeer->peer_llinkid) { 2186195618Srpaulo args[0] = ni->ni_mlpid; 2187195618Srpaulo args[1] = ni->ni_mllid; 2188195618Srpaulo args[2] = IEEE80211_REASON_PEER_LINK_CANCELED; 2189195618Srpaulo ieee80211_send_action(ni, 2190234874Smonthadar IEEE80211_ACTION_CAT_SELF_PROT, 2191195618Srpaulo IEEE80211_ACTION_MESHPEERING_CLOSE, 2192195618Srpaulo args); 2193195618Srpaulo mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING); 2194195618Srpaulo mesh_peer_timeout_setup(ni); 2195195618Srpaulo } 2196195618Srpaulo break; 2197195618Srpaulo default: 2198195618Srpaulo IEEE80211_DISCARD(vap, 2199195618Srpaulo IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, 2200195618Srpaulo wh, NULL, "received confirm in invalid state %d", 2201195618Srpaulo ni->ni_mlstate); 2202195618Srpaulo vap->iv_stats.is_rx_mgtdiscard++; 2203195618Srpaulo break; 2204195618Srpaulo } 2205195618Srpaulo return 0; 2206195618Srpaulo} 2207195618Srpaulo 2208195618Srpaulostatic int 2209195618Srpaulomesh_recv_action_meshpeering_close(struct ieee80211_node *ni, 2210195618Srpaulo const struct ieee80211_frame *wh, 2211195618Srpaulo const uint8_t *frm, const uint8_t *efrm) 2212195618Srpaulo{ 2213234876Smonthadar struct ieee80211_meshpeer_ie ie; 2214234876Smonthadar const struct ieee80211_meshpeer_ie *meshpeer; 2215195618Srpaulo uint16_t args[3]; 2216195618Srpaulo 2217234876Smonthadar /* +2 for action + code */ 2218234876Smonthadar meshpeer = mesh_parse_meshpeering_action(ni, wh, frm+2, efrm, &ie, 2219234876Smonthadar IEEE80211_ACTION_MESHPEERING_CLOSE); 2220234876Smonthadar if (meshpeer == NULL) { 2221234876Smonthadar return 0; 2222234876Smonthadar } 2223234876Smonthadar 2224234876Smonthadar /* 2225234876Smonthadar * XXX: check reason code, for example we could receive 2226234876Smonthadar * IEEE80211_REASON_MESH_MAX_PEERS then we should not attempt 2227234876Smonthadar * to peer again. 2228234876Smonthadar */ 2229234876Smonthadar 2230195618Srpaulo IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, 2231195618Srpaulo ni, "%s", "recv PEER CLOSE"); 2232195618Srpaulo 2233195618Srpaulo switch (ni->ni_mlstate) { 2234195618Srpaulo case IEEE80211_NODE_MESH_IDLE: 2235195618Srpaulo /* ignore */ 2236195618Srpaulo break; 2237195618Srpaulo case IEEE80211_NODE_MESH_OPENRCV: 2238195618Srpaulo case IEEE80211_NODE_MESH_OPENSNT: 2239195618Srpaulo case IEEE80211_NODE_MESH_CONFIRMRCV: 2240195618Srpaulo case IEEE80211_NODE_MESH_ESTABLISHED: 2241195618Srpaulo args[0] = ni->ni_mlpid; 2242195618Srpaulo args[1] = ni->ni_mllid; 2243195618Srpaulo args[2] = IEEE80211_REASON_MESH_CLOSE_RCVD; 2244195618Srpaulo ieee80211_send_action(ni, 2245234874Smonthadar IEEE80211_ACTION_CAT_SELF_PROT, 2246195618Srpaulo IEEE80211_ACTION_MESHPEERING_CLOSE, 2247195618Srpaulo args); 2248195618Srpaulo mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING); 2249195618Srpaulo mesh_peer_timeout_setup(ni); 2250195618Srpaulo break; 2251195618Srpaulo case IEEE80211_NODE_MESH_HOLDING: 2252195618Srpaulo mesh_linkchange(ni, IEEE80211_NODE_MESH_IDLE); 2253234876Smonthadar mesh_peer_timeout_stop(ni); 2254195618Srpaulo break; 2255195618Srpaulo } 2256195618Srpaulo return 0; 2257195618Srpaulo} 2258195618Srpaulo 2259195618Srpaulo/* 2260195618Srpaulo * Link Metric handling. 2261195618Srpaulo */ 2262195618Srpaulostatic int 2263232479Sadrianmesh_recv_action_meshlmetric(struct ieee80211_node *ni, 2264195618Srpaulo const struct ieee80211_frame *wh, 2265195618Srpaulo const uint8_t *frm, const uint8_t *efrm) 2266195618Srpaulo{ 2267232479Sadrian const struct ieee80211_meshlmetric_ie *ie = 2268232479Sadrian (const struct ieee80211_meshlmetric_ie *) 2269232479Sadrian (frm+2); /* action + code */ 2270232479Sadrian struct ieee80211_meshlmetric_ie lm_rep; 2271232479Sadrian 2272232479Sadrian if (ie->lm_flags & IEEE80211_MESH_LMETRIC_FLAGS_REQ) { 2273232479Sadrian lm_rep.lm_flags = 0; 2274232479Sadrian lm_rep.lm_metric = mesh_airtime_calc(ni); 2275232479Sadrian ieee80211_send_action(ni, 2276232479Sadrian IEEE80211_ACTION_CAT_MESH, 2277232479Sadrian IEEE80211_ACTION_MESH_LMETRIC, 2278232479Sadrian &lm_rep); 2279232479Sadrian } 2280232479Sadrian /* XXX: else do nothing for now */ 2281195618Srpaulo return 0; 2282195618Srpaulo} 2283195618Srpaulo 2284195618Srpaulostatic int 2285195618Srpaulomesh_send_action(struct ieee80211_node *ni, struct mbuf *m) 2286195618Srpaulo{ 2287195618Srpaulo struct ieee80211_bpf_params params; 2288195618Srpaulo 2289195618Srpaulo memset(¶ms, 0, sizeof(params)); 2290195618Srpaulo params.ibp_pri = WME_AC_VO; 2291195618Srpaulo params.ibp_rate0 = ni->ni_txparms->mgmtrate; 2292195618Srpaulo /* XXX ucast/mcast */ 2293195618Srpaulo params.ibp_try0 = ni->ni_txparms->maxretry; 2294195618Srpaulo params.ibp_power = ni->ni_txpower; 2295195618Srpaulo return ieee80211_mgmt_output(ni, m, IEEE80211_FC0_SUBTYPE_ACTION, 2296195618Srpaulo ¶ms); 2297195618Srpaulo} 2298195618Srpaulo 2299195618Srpaulo#define ADDSHORT(frm, v) do { \ 2300195618Srpaulo frm[0] = (v) & 0xff; \ 2301195618Srpaulo frm[1] = (v) >> 8; \ 2302195618Srpaulo frm += 2; \ 2303195618Srpaulo} while (0) 2304195618Srpaulo#define ADDWORD(frm, v) do { \ 2305195618Srpaulo frm[0] = (v) & 0xff; \ 2306195618Srpaulo frm[1] = ((v) >> 8) & 0xff; \ 2307195618Srpaulo frm[2] = ((v) >> 16) & 0xff; \ 2308195618Srpaulo frm[3] = ((v) >> 24) & 0xff; \ 2309195618Srpaulo frm += 4; \ 2310195618Srpaulo} while (0) 2311195618Srpaulo 2312195618Srpaulostatic int 2313195618Srpaulomesh_send_action_meshpeering_open(struct ieee80211_node *ni, 2314195618Srpaulo int category, int action, void *args0) 2315195618Srpaulo{ 2316195618Srpaulo struct ieee80211vap *vap = ni->ni_vap; 2317195618Srpaulo struct ieee80211com *ic = ni->ni_ic; 2318195618Srpaulo uint16_t *args = args0; 2319195618Srpaulo const struct ieee80211_rateset *rs; 2320195618Srpaulo struct mbuf *m; 2321195618Srpaulo uint8_t *frm; 2322195618Srpaulo 2323195618Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, ni, 2324195618Srpaulo "send PEER OPEN action: localid 0x%x", args[0]); 2325195618Srpaulo 2326195618Srpaulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, 2327195618Srpaulo "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__, 2328195618Srpaulo ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1); 2329195618Srpaulo ieee80211_ref_node(ni); 2330195618Srpaulo 2331195618Srpaulo m = ieee80211_getmgtframe(&frm, 2332195618Srpaulo ic->ic_headroom + sizeof(struct ieee80211_frame), 2333195618Srpaulo sizeof(uint16_t) /* action+category */ 2334195618Srpaulo + sizeof(uint16_t) /* capabilites */ 2335230926Srpaulo + 2 + IEEE80211_RATE_SIZE 2336230926Srpaulo + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) 2337195618Srpaulo + 2 + IEEE80211_MESHID_LEN 2338230926Srpaulo + sizeof(struct ieee80211_meshconf_ie) 2339195618Srpaulo + sizeof(struct ieee80211_meshpeer_ie) 2340195618Srpaulo ); 2341195618Srpaulo if (m != NULL) { 2342195618Srpaulo /* 2343195618Srpaulo * mesh peer open action frame format: 2344195618Srpaulo * [1] category 2345195618Srpaulo * [1] action 2346195618Srpaulo * [2] capabilities 2347195618Srpaulo * [tlv] rates 2348195618Srpaulo * [tlv] xrates 2349195618Srpaulo * [tlv] mesh id 2350195618Srpaulo * [tlv] mesh conf 2351195618Srpaulo * [tlv] mesh peer link mgmt 2352195618Srpaulo */ 2353195618Srpaulo *frm++ = category; 2354195618Srpaulo *frm++ = action; 2355195618Srpaulo ADDSHORT(frm, ieee80211_getcapinfo(vap, ni->ni_chan)); 2356195618Srpaulo rs = ieee80211_get_suprates(ic, ic->ic_curchan); 2357195618Srpaulo frm = ieee80211_add_rates(frm, rs); 2358195618Srpaulo frm = ieee80211_add_xrates(frm, rs); 2359195618Srpaulo frm = ieee80211_add_meshid(frm, vap); 2360195618Srpaulo frm = ieee80211_add_meshconf(frm, vap); 2361234874Smonthadar frm = ieee80211_add_meshpeer(frm, IEEE80211_ACTION_MESHPEERING_OPEN, 2362195618Srpaulo args[0], 0, 0); 2363195618Srpaulo m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); 2364195618Srpaulo return mesh_send_action(ni, m); 2365195618Srpaulo } else { 2366195618Srpaulo vap->iv_stats.is_tx_nobuf++; 2367195618Srpaulo ieee80211_free_node(ni); 2368195618Srpaulo return ENOMEM; 2369195618Srpaulo } 2370195618Srpaulo} 2371195618Srpaulo 2372195618Srpaulostatic int 2373195618Srpaulomesh_send_action_meshpeering_confirm(struct ieee80211_node *ni, 2374195618Srpaulo int category, int action, void *args0) 2375195618Srpaulo{ 2376195618Srpaulo struct ieee80211vap *vap = ni->ni_vap; 2377195618Srpaulo struct ieee80211com *ic = ni->ni_ic; 2378195618Srpaulo uint16_t *args = args0; 2379195618Srpaulo const struct ieee80211_rateset *rs; 2380195618Srpaulo struct mbuf *m; 2381195618Srpaulo uint8_t *frm; 2382195618Srpaulo 2383195618Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, ni, 2384195618Srpaulo "send PEER CONFIRM action: localid 0x%x, peerid 0x%x", 2385195618Srpaulo args[0], args[1]); 2386195618Srpaulo 2387195618Srpaulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, 2388195618Srpaulo "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__, 2389195618Srpaulo ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1); 2390195618Srpaulo ieee80211_ref_node(ni); 2391195618Srpaulo 2392195618Srpaulo m = ieee80211_getmgtframe(&frm, 2393195618Srpaulo ic->ic_headroom + sizeof(struct ieee80211_frame), 2394195618Srpaulo sizeof(uint16_t) /* action+category */ 2395195618Srpaulo + sizeof(uint16_t) /* capabilites */ 2396195618Srpaulo + sizeof(uint16_t) /* status code */ 2397195618Srpaulo + sizeof(uint16_t) /* AID */ 2398230926Srpaulo + 2 + IEEE80211_RATE_SIZE 2399230926Srpaulo + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) 2400195618Srpaulo + 2 + IEEE80211_MESHID_LEN 2401230926Srpaulo + sizeof(struct ieee80211_meshconf_ie) 2402195618Srpaulo + sizeof(struct ieee80211_meshpeer_ie) 2403195618Srpaulo ); 2404195618Srpaulo if (m != NULL) { 2405195618Srpaulo /* 2406195618Srpaulo * mesh peer confirm action frame format: 2407195618Srpaulo * [1] category 2408195618Srpaulo * [1] action 2409195618Srpaulo * [2] capabilities 2410195618Srpaulo * [2] status code 2411195618Srpaulo * [2] association id (peer ID) 2412195618Srpaulo * [tlv] rates 2413195618Srpaulo * [tlv] xrates 2414195618Srpaulo * [tlv] mesh id 2415195618Srpaulo * [tlv] mesh conf 2416195618Srpaulo * [tlv] mesh peer link mgmt 2417195618Srpaulo */ 2418195618Srpaulo *frm++ = category; 2419195618Srpaulo *frm++ = action; 2420195618Srpaulo ADDSHORT(frm, ieee80211_getcapinfo(vap, ni->ni_chan)); 2421195618Srpaulo ADDSHORT(frm, 0); /* status code */ 2422195618Srpaulo ADDSHORT(frm, args[1]); /* AID */ 2423195618Srpaulo rs = ieee80211_get_suprates(ic, ic->ic_curchan); 2424195618Srpaulo frm = ieee80211_add_rates(frm, rs); 2425195618Srpaulo frm = ieee80211_add_xrates(frm, rs); 2426195618Srpaulo frm = ieee80211_add_meshid(frm, vap); 2427195618Srpaulo frm = ieee80211_add_meshconf(frm, vap); 2428195618Srpaulo frm = ieee80211_add_meshpeer(frm, 2429234874Smonthadar IEEE80211_ACTION_MESHPEERING_CONFIRM, 2430195618Srpaulo args[0], args[1], 0); 2431195618Srpaulo m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); 2432195618Srpaulo return mesh_send_action(ni, m); 2433195618Srpaulo } else { 2434195618Srpaulo vap->iv_stats.is_tx_nobuf++; 2435195618Srpaulo ieee80211_free_node(ni); 2436195618Srpaulo return ENOMEM; 2437195618Srpaulo } 2438195618Srpaulo} 2439195618Srpaulo 2440195618Srpaulostatic int 2441195618Srpaulomesh_send_action_meshpeering_close(struct ieee80211_node *ni, 2442195618Srpaulo int category, int action, void *args0) 2443195618Srpaulo{ 2444195618Srpaulo struct ieee80211vap *vap = ni->ni_vap; 2445195618Srpaulo struct ieee80211com *ic = ni->ni_ic; 2446195618Srpaulo uint16_t *args = args0; 2447195618Srpaulo struct mbuf *m; 2448195618Srpaulo uint8_t *frm; 2449195618Srpaulo 2450195618Srpaulo IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, ni, 2451195618Srpaulo "send PEER CLOSE action: localid 0x%x, peerid 0x%x reason %d", 2452195618Srpaulo args[0], args[1], args[2]); 2453195618Srpaulo 2454195618Srpaulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, 2455195618Srpaulo "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__, 2456195618Srpaulo ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1); 2457195618Srpaulo ieee80211_ref_node(ni); 2458195618Srpaulo 2459195618Srpaulo m = ieee80211_getmgtframe(&frm, 2460195618Srpaulo ic->ic_headroom + sizeof(struct ieee80211_frame), 2461195618Srpaulo sizeof(uint16_t) /* action+category */ 2462195618Srpaulo + sizeof(uint16_t) /* reason code */ 2463195618Srpaulo + 2 + IEEE80211_MESHID_LEN 2464230926Srpaulo + sizeof(struct ieee80211_meshpeer_ie) 2465195618Srpaulo ); 2466195618Srpaulo if (m != NULL) { 2467195618Srpaulo /* 2468195618Srpaulo * mesh peer close action frame format: 2469195618Srpaulo * [1] category 2470195618Srpaulo * [1] action 2471195618Srpaulo * [tlv] mesh id 2472195618Srpaulo * [tlv] mesh peer link mgmt 2473195618Srpaulo */ 2474195618Srpaulo *frm++ = category; 2475195618Srpaulo *frm++ = action; 2476195618Srpaulo frm = ieee80211_add_meshid(frm, vap); 2477195618Srpaulo frm = ieee80211_add_meshpeer(frm, 2478234874Smonthadar IEEE80211_ACTION_MESHPEERING_CLOSE, 2479195618Srpaulo args[0], args[1], args[2]); 2480195618Srpaulo m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); 2481195618Srpaulo return mesh_send_action(ni, m); 2482195618Srpaulo } else { 2483195618Srpaulo vap->iv_stats.is_tx_nobuf++; 2484195618Srpaulo ieee80211_free_node(ni); 2485195618Srpaulo return ENOMEM; 2486195618Srpaulo } 2487195618Srpaulo} 2488195618Srpaulo 2489195618Srpaulostatic int 2490232479Sadrianmesh_send_action_meshlmetric(struct ieee80211_node *ni, 2491195618Srpaulo int category, int action, void *arg0) 2492195618Srpaulo{ 2493195618Srpaulo struct ieee80211vap *vap = ni->ni_vap; 2494195618Srpaulo struct ieee80211com *ic = ni->ni_ic; 2495232479Sadrian struct ieee80211_meshlmetric_ie *ie = arg0; 2496195618Srpaulo struct mbuf *m; 2497195618Srpaulo uint8_t *frm; 2498195618Srpaulo 2499232479Sadrian if (ie->lm_flags & IEEE80211_MESH_LMETRIC_FLAGS_REQ) { 2500232479Sadrian IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, 2501232479Sadrian ni, "%s", "send LINK METRIC REQUEST action"); 2502195618Srpaulo } else { 2503232479Sadrian IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, 2504232479Sadrian ni, "send LINK METRIC REPLY action: metric 0x%x", 2505232479Sadrian ie->lm_metric); 2506195618Srpaulo } 2507195618Srpaulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, 2508195618Srpaulo "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__, 2509195618Srpaulo ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1); 2510195618Srpaulo ieee80211_ref_node(ni); 2511195618Srpaulo 2512195618Srpaulo m = ieee80211_getmgtframe(&frm, 2513195618Srpaulo ic->ic_headroom + sizeof(struct ieee80211_frame), 2514232479Sadrian sizeof(uint16_t) + /* action+category */ 2515232479Sadrian sizeof(struct ieee80211_meshlmetric_ie) 2516195618Srpaulo ); 2517195618Srpaulo if (m != NULL) { 2518195618Srpaulo /* 2519232479Sadrian * mesh link metric 2520195618Srpaulo * [1] category 2521195618Srpaulo * [1] action 2522195618Srpaulo * [tlv] mesh link metric 2523195618Srpaulo */ 2524195618Srpaulo *frm++ = category; 2525195618Srpaulo *frm++ = action; 2526232479Sadrian frm = ieee80211_add_meshlmetric(frm, 2527232479Sadrian ie->lm_flags, ie->lm_metric); 2528195618Srpaulo m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); 2529195618Srpaulo return mesh_send_action(ni, m); 2530195618Srpaulo } else { 2531195618Srpaulo vap->iv_stats.is_tx_nobuf++; 2532195618Srpaulo ieee80211_free_node(ni); 2533195618Srpaulo return ENOMEM; 2534195618Srpaulo } 2535195618Srpaulo} 2536195618Srpaulo 2537195618Srpaulostatic void 2538195618Srpaulomesh_peer_timeout_setup(struct ieee80211_node *ni) 2539195618Srpaulo{ 2540195618Srpaulo switch (ni->ni_mlstate) { 2541195618Srpaulo case IEEE80211_NODE_MESH_HOLDING: 2542195618Srpaulo ni->ni_mltval = ieee80211_mesh_holdingtimeout; 2543195618Srpaulo break; 2544195618Srpaulo case IEEE80211_NODE_MESH_CONFIRMRCV: 2545195618Srpaulo ni->ni_mltval = ieee80211_mesh_confirmtimeout; 2546195618Srpaulo break; 2547195618Srpaulo case IEEE80211_NODE_MESH_IDLE: 2548195618Srpaulo ni->ni_mltval = 0; 2549195618Srpaulo break; 2550195618Srpaulo default: 2551195618Srpaulo ni->ni_mltval = ieee80211_mesh_retrytimeout; 2552195618Srpaulo break; 2553195618Srpaulo } 2554195618Srpaulo if (ni->ni_mltval) 2555195618Srpaulo callout_reset(&ni->ni_mltimer, ni->ni_mltval, 2556195618Srpaulo mesh_peer_timeout_cb, ni); 2557195618Srpaulo} 2558195618Srpaulo 2559195618Srpaulo/* 2560195618Srpaulo * Same as above but backoffs timer statisically 50%. 2561195618Srpaulo */ 2562195618Srpaulostatic void 2563195618Srpaulomesh_peer_timeout_backoff(struct ieee80211_node *ni) 2564195618Srpaulo{ 2565195618Srpaulo uint32_t r; 2566195618Srpaulo 2567195618Srpaulo r = arc4random(); 2568195618Srpaulo ni->ni_mltval += r % ni->ni_mltval; 2569195618Srpaulo callout_reset(&ni->ni_mltimer, ni->ni_mltval, mesh_peer_timeout_cb, 2570195618Srpaulo ni); 2571195618Srpaulo} 2572195618Srpaulo 2573195618Srpaulostatic __inline void 2574195618Srpaulomesh_peer_timeout_stop(struct ieee80211_node *ni) 2575195618Srpaulo{ 2576195784Srpaulo callout_drain(&ni->ni_mltimer); 2577195618Srpaulo} 2578195618Srpaulo 2579195618Srpaulo/* 2580195618Srpaulo * Mesh Peer Link Management FSM timeout handling. 2581195618Srpaulo */ 2582195618Srpaulostatic void 2583195618Srpaulomesh_peer_timeout_cb(void *arg) 2584195618Srpaulo{ 2585195618Srpaulo struct ieee80211_node *ni = (struct ieee80211_node *)arg; 2586195618Srpaulo uint16_t args[3]; 2587195618Srpaulo 2588195618Srpaulo IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_MESH, 2589195618Srpaulo ni, "mesh link timeout, state %d, retry counter %d", 2590195618Srpaulo ni->ni_mlstate, ni->ni_mlrcnt); 2591195618Srpaulo 2592195618Srpaulo switch (ni->ni_mlstate) { 2593195618Srpaulo case IEEE80211_NODE_MESH_IDLE: 2594195618Srpaulo case IEEE80211_NODE_MESH_ESTABLISHED: 2595195618Srpaulo break; 2596195618Srpaulo case IEEE80211_NODE_MESH_OPENSNT: 2597195618Srpaulo case IEEE80211_NODE_MESH_OPENRCV: 2598195618Srpaulo if (ni->ni_mlrcnt == ieee80211_mesh_maxretries) { 2599195618Srpaulo args[0] = ni->ni_mlpid; 2600195618Srpaulo args[2] = IEEE80211_REASON_MESH_MAX_RETRIES; 2601195618Srpaulo ieee80211_send_action(ni, 2602234874Smonthadar IEEE80211_ACTION_CAT_SELF_PROT, 2603195618Srpaulo IEEE80211_ACTION_MESHPEERING_CLOSE, args); 2604195618Srpaulo ni->ni_mlrcnt = 0; 2605195618Srpaulo mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING); 2606195618Srpaulo mesh_peer_timeout_setup(ni); 2607195618Srpaulo } else { 2608195618Srpaulo args[0] = ni->ni_mlpid; 2609195618Srpaulo ieee80211_send_action(ni, 2610234874Smonthadar IEEE80211_ACTION_CAT_SELF_PROT, 2611195618Srpaulo IEEE80211_ACTION_MESHPEERING_OPEN, args); 2612195618Srpaulo ni->ni_mlrcnt++; 2613195618Srpaulo mesh_peer_timeout_backoff(ni); 2614195618Srpaulo } 2615195618Srpaulo break; 2616195618Srpaulo case IEEE80211_NODE_MESH_CONFIRMRCV: 2617234876Smonthadar args[0] = ni->ni_mlpid; 2618234876Smonthadar args[2] = IEEE80211_REASON_MESH_CONFIRM_TIMEOUT; 2619234876Smonthadar ieee80211_send_action(ni, 2620234876Smonthadar IEEE80211_ACTION_CAT_SELF_PROT, 2621234876Smonthadar IEEE80211_ACTION_MESHPEERING_CLOSE, args); 2622234876Smonthadar mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING); 2623234876Smonthadar mesh_peer_timeout_setup(ni); 2624195618Srpaulo break; 2625195618Srpaulo case IEEE80211_NODE_MESH_HOLDING: 2626195618Srpaulo mesh_linkchange(ni, IEEE80211_NODE_MESH_IDLE); 2627195618Srpaulo break; 2628195618Srpaulo } 2629195618Srpaulo} 2630195618Srpaulo 2631195661Srpaulostatic int 2632195618Srpaulomesh_verify_meshid(struct ieee80211vap *vap, const uint8_t *ie) 2633195618Srpaulo{ 2634195618Srpaulo struct ieee80211_mesh_state *ms = vap->iv_mesh; 2635195618Srpaulo 2636195618Srpaulo if (ie == NULL || ie[1] != ms->ms_idlen) 2637195618Srpaulo return 1; 2638195618Srpaulo return memcmp(ms->ms_id, ie + 2, ms->ms_idlen); 2639195618Srpaulo} 2640195618Srpaulo 2641195618Srpaulo/* 2642195618Srpaulo * Check if we are using the same algorithms for this mesh. 2643195618Srpaulo */ 2644195618Srpaulostatic int 2645195618Srpaulomesh_verify_meshconf(struct ieee80211vap *vap, const uint8_t *ie) 2646195618Srpaulo{ 2647195618Srpaulo const struct ieee80211_meshconf_ie *meshconf = 2648195618Srpaulo (const struct ieee80211_meshconf_ie *) ie; 2649195618Srpaulo const struct ieee80211_mesh_state *ms = vap->iv_mesh; 2650195618Srpaulo 2651195618Srpaulo if (meshconf == NULL) 2652195618Srpaulo return 1; 2653197975Srpaulo if (meshconf->conf_pselid != ms->ms_ppath->mpp_ie) { 2654195618Srpaulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_MESH, 2655197975Srpaulo "unknown path selection algorithm: 0x%x\n", 2656197975Srpaulo meshconf->conf_pselid); 2657195618Srpaulo return 1; 2658195618Srpaulo } 2659197975Srpaulo if (meshconf->conf_pmetid != ms->ms_pmetric->mpm_ie) { 2660195618Srpaulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_MESH, 2661197975Srpaulo "unknown path metric algorithm: 0x%x\n", 2662197975Srpaulo meshconf->conf_pmetid); 2663195618Srpaulo return 1; 2664195618Srpaulo } 2665197975Srpaulo if (meshconf->conf_ccid != 0) { 2666195618Srpaulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_MESH, 2667197975Srpaulo "unknown congestion control algorithm: 0x%x\n", 2668197975Srpaulo meshconf->conf_ccid); 2669195618Srpaulo return 1; 2670195618Srpaulo } 2671197975Srpaulo if (meshconf->conf_syncid != IEEE80211_MESHCONF_SYNC_NEIGHOFF) { 2672195618Srpaulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_MESH, 2673197975Srpaulo "unknown sync algorithm: 0x%x\n", 2674197975Srpaulo meshconf->conf_syncid); 2675195618Srpaulo return 1; 2676195618Srpaulo } 2677197975Srpaulo if (meshconf->conf_authid != 0) { 2678195618Srpaulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_MESH, 2679197975Srpaulo "unknown auth auth algorithm: 0x%x\n", 2680197975Srpaulo meshconf->conf_pselid); 2681195618Srpaulo return 1; 2682195618Srpaulo } 2683195618Srpaulo /* Not accepting peers */ 2684231576Sadrian if (!(meshconf->conf_cap & IEEE80211_MESHCONF_CAP_AP)) { 2685195618Srpaulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_MESH, 2686195618Srpaulo "not accepting peers: 0x%x\n", meshconf->conf_cap); 2687195618Srpaulo return 1; 2688195618Srpaulo } 2689195618Srpaulo return 0; 2690195618Srpaulo} 2691195618Srpaulo 2692195618Srpaulostatic int 2693197413Srpaulomesh_verify_meshpeer(struct ieee80211vap *vap, uint8_t subtype, 2694197413Srpaulo const uint8_t *ie) 2695195618Srpaulo{ 2696195618Srpaulo const struct ieee80211_meshpeer_ie *meshpeer = 2697195618Srpaulo (const struct ieee80211_meshpeer_ie *) ie; 2698195618Srpaulo 2699234875Smonthadar if (meshpeer == NULL || 2700234875Smonthadar meshpeer->peer_len < IEEE80211_MPM_BASE_SZ || 2701234875Smonthadar meshpeer->peer_len > IEEE80211_MPM_MAX_SZ) 2702195618Srpaulo return 1; 2703234875Smonthadar if (meshpeer->peer_proto != IEEE80211_MPPID_MPM) { 2704234875Smonthadar IEEE80211_DPRINTF(vap, 2705234875Smonthadar IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, 2706234875Smonthadar "Only MPM protocol is supported (proto: 0x%02X)", 2707234875Smonthadar meshpeer->peer_proto); 2708234875Smonthadar return 1; 2709234875Smonthadar } 2710197413Srpaulo switch (subtype) { 2711234874Smonthadar case IEEE80211_ACTION_MESHPEERING_OPEN: 2712234875Smonthadar if (meshpeer->peer_len != IEEE80211_MPM_BASE_SZ) 2713195618Srpaulo return 1; 2714195618Srpaulo break; 2715234874Smonthadar case IEEE80211_ACTION_MESHPEERING_CONFIRM: 2716234875Smonthadar if (meshpeer->peer_len != IEEE80211_MPM_BASE_SZ + 2) 2717195618Srpaulo return 1; 2718195618Srpaulo break; 2719234874Smonthadar case IEEE80211_ACTION_MESHPEERING_CLOSE: 2720234875Smonthadar if (meshpeer->peer_len < IEEE80211_MPM_BASE_SZ + 2) 2721195618Srpaulo return 1; 2722234875Smonthadar if (meshpeer->peer_len == (IEEE80211_MPM_BASE_SZ + 2) && 2723234875Smonthadar meshpeer->peer_linkid != 0) 2724195618Srpaulo return 1; 2725195618Srpaulo if (meshpeer->peer_rcode == 0) 2726195618Srpaulo return 1; 2727195618Srpaulo break; 2728195618Srpaulo } 2729195618Srpaulo return 0; 2730195618Srpaulo} 2731195618Srpaulo 2732195618Srpaulo/* 2733195618Srpaulo * Add a Mesh ID IE to a frame. 2734195618Srpaulo */ 2735195618Srpaulouint8_t * 2736195618Srpauloieee80211_add_meshid(uint8_t *frm, struct ieee80211vap *vap) 2737195618Srpaulo{ 2738195618Srpaulo struct ieee80211_mesh_state *ms = vap->iv_mesh; 2739195618Srpaulo 2740195618Srpaulo KASSERT(vap->iv_opmode == IEEE80211_M_MBSS, ("not a mbss vap")); 2741195618Srpaulo 2742195618Srpaulo *frm++ = IEEE80211_ELEMID_MESHID; 2743195618Srpaulo *frm++ = ms->ms_idlen; 2744195618Srpaulo memcpy(frm, ms->ms_id, ms->ms_idlen); 2745195618Srpaulo return frm + ms->ms_idlen; 2746195618Srpaulo} 2747195618Srpaulo 2748195618Srpaulo/* 2749195618Srpaulo * Add a Mesh Configuration IE to a frame. 2750195618Srpaulo * For now just use HWMP routing, Airtime link metric, Null Congestion 2751195618Srpaulo * Signaling, Null Sync Protocol and Null Authentication. 2752195618Srpaulo */ 2753195618Srpaulouint8_t * 2754195618Srpauloieee80211_add_meshconf(uint8_t *frm, struct ieee80211vap *vap) 2755195618Srpaulo{ 2756195618Srpaulo const struct ieee80211_mesh_state *ms = vap->iv_mesh; 2757202178Srpaulo uint16_t caps; 2758195618Srpaulo 2759195618Srpaulo KASSERT(vap->iv_opmode == IEEE80211_M_MBSS, ("not a MBSS vap")); 2760195618Srpaulo 2761195618Srpaulo *frm++ = IEEE80211_ELEMID_MESHCONF; 2762231576Sadrian *frm++ = IEEE80211_MESH_CONF_SZ; 2763197975Srpaulo *frm++ = ms->ms_ppath->mpp_ie; /* path selection */ 2764197975Srpaulo *frm++ = ms->ms_pmetric->mpm_ie; /* link metric */ 2765197975Srpaulo *frm++ = IEEE80211_MESHCONF_CC_DISABLED; 2766197975Srpaulo *frm++ = IEEE80211_MESHCONF_SYNC_NEIGHOFF; 2767197975Srpaulo *frm++ = IEEE80211_MESHCONF_AUTH_DISABLED; 2768195618Srpaulo /* NB: set the number of neighbors before the rest */ 2769234876Smonthadar *frm = (ms->ms_neighbors > IEEE80211_MESH_MAX_NEIGHBORS ? 2770234876Smonthadar IEEE80211_MESH_MAX_NEIGHBORS : ms->ms_neighbors) << 1; 2771195618Srpaulo if (ms->ms_flags & IEEE80211_MESHFLAGS_PORTAL) 2772195618Srpaulo *frm |= IEEE80211_MESHCONF_FORM_MP; 2773195618Srpaulo frm += 1; 2774202178Srpaulo caps = 0; 2775195618Srpaulo if (ms->ms_flags & IEEE80211_MESHFLAGS_AP) 2776202178Srpaulo caps |= IEEE80211_MESHCONF_CAP_AP; 2777195618Srpaulo if (ms->ms_flags & IEEE80211_MESHFLAGS_FWD) 2778202178Srpaulo caps |= IEEE80211_MESHCONF_CAP_FWRD; 2779231576Sadrian *frm++ = caps; 2780195618Srpaulo return frm; 2781195618Srpaulo} 2782195618Srpaulo 2783195618Srpaulo/* 2784195618Srpaulo * Add a Mesh Peer Management IE to a frame. 2785195618Srpaulo */ 2786195618Srpaulouint8_t * 2787195618Srpauloieee80211_add_meshpeer(uint8_t *frm, uint8_t subtype, uint16_t localid, 2788195618Srpaulo uint16_t peerid, uint16_t reason) 2789195618Srpaulo{ 2790197413Srpaulo 2791195618Srpaulo KASSERT(localid != 0, ("localid == 0")); 2792195618Srpaulo 2793195618Srpaulo *frm++ = IEEE80211_ELEMID_MESHPEER; 2794195618Srpaulo switch (subtype) { 2795234874Smonthadar case IEEE80211_ACTION_MESHPEERING_OPEN: 2796234875Smonthadar *frm++ = IEEE80211_MPM_BASE_SZ; /* length */ 2797234875Smonthadar ADDSHORT(frm, IEEE80211_MPPID_MPM); /* proto */ 2798234875Smonthadar ADDSHORT(frm, localid); /* local ID */ 2799195618Srpaulo break; 2800234874Smonthadar case IEEE80211_ACTION_MESHPEERING_CONFIRM: 2801195618Srpaulo KASSERT(peerid != 0, ("sending peer confirm without peer id")); 2802234875Smonthadar *frm++ = IEEE80211_MPM_BASE_SZ + 2; /* length */ 2803234875Smonthadar ADDSHORT(frm, IEEE80211_MPPID_MPM); /* proto */ 2804234875Smonthadar ADDSHORT(frm, localid); /* local ID */ 2805234875Smonthadar ADDSHORT(frm, peerid); /* peer ID */ 2806195618Srpaulo break; 2807234874Smonthadar case IEEE80211_ACTION_MESHPEERING_CLOSE: 2808195618Srpaulo if (peerid) 2809234875Smonthadar *frm++ = IEEE80211_MPM_MAX_SZ; /* length */ 2810195618Srpaulo else 2811234875Smonthadar *frm++ = IEEE80211_MPM_BASE_SZ + 2; /* length */ 2812234875Smonthadar ADDSHORT(frm, IEEE80211_MPPID_MPM); /* proto */ 2813195618Srpaulo ADDSHORT(frm, localid); /* local ID */ 2814195618Srpaulo if (peerid) 2815195618Srpaulo ADDSHORT(frm, peerid); /* peer ID */ 2816195618Srpaulo ADDSHORT(frm, reason); 2817195618Srpaulo break; 2818195618Srpaulo } 2819195618Srpaulo return frm; 2820195618Srpaulo} 2821195618Srpaulo 2822195618Srpaulo/* 2823195618Srpaulo * Compute an Airtime Link Metric for the link with this node. 2824195618Srpaulo * 2825195618Srpaulo * Based on Draft 3.0 spec (11B.10, p.149). 2826195618Srpaulo */ 2827195618Srpaulo/* 2828195618Srpaulo * Max 802.11s overhead. 2829195618Srpaulo */ 2830195618Srpaulo#define IEEE80211_MESH_MAXOVERHEAD \ 2831195618Srpaulo (sizeof(struct ieee80211_qosframe_addr4) \ 2832234878Smonthadar + sizeof(struct ieee80211_meshcntl_ae10) \ 2833195618Srpaulo + sizeof(struct llc) \ 2834195618Srpaulo + IEEE80211_ADDR_LEN \ 2835195618Srpaulo + IEEE80211_WEP_IVLEN \ 2836195618Srpaulo + IEEE80211_WEP_KIDLEN \ 2837195618Srpaulo + IEEE80211_WEP_CRCLEN \ 2838195618Srpaulo + IEEE80211_WEP_MICLEN \ 2839195618Srpaulo + IEEE80211_CRC_LEN) 2840195618Srpaulouint32_t 2841195618Srpaulomesh_airtime_calc(struct ieee80211_node *ni) 2842195618Srpaulo{ 2843195618Srpaulo#define M_BITS 8 2844195618Srpaulo#define S_FACTOR (2 * M_BITS) 2845195618Srpaulo struct ieee80211com *ic = ni->ni_ic; 2846195618Srpaulo struct ifnet *ifp = ni->ni_vap->iv_ifp; 2847195618Srpaulo const static int nbits = 8192 << M_BITS; 2848195618Srpaulo uint32_t overhead, rate, errrate; 2849195618Srpaulo uint64_t res; 2850195618Srpaulo 2851195618Srpaulo /* Time to transmit a frame */ 2852195618Srpaulo rate = ni->ni_txrate; 2853195618Srpaulo overhead = ieee80211_compute_duration(ic->ic_rt, 2854195618Srpaulo ifp->if_mtu + IEEE80211_MESH_MAXOVERHEAD, rate, 0) << M_BITS; 2855195618Srpaulo /* Error rate in percentage */ 2856195618Srpaulo /* XXX assuming small failures are ok */ 2857195618Srpaulo errrate = (((ifp->if_oerrors + 2858195618Srpaulo ifp->if_ierrors) / 100) << M_BITS) / 100; 2859195618Srpaulo res = (overhead + (nbits / rate)) * 2860195618Srpaulo ((1 << S_FACTOR) / ((1 << M_BITS) - errrate)); 2861195618Srpaulo 2862195618Srpaulo return (uint32_t)(res >> S_FACTOR); 2863195618Srpaulo#undef M_BITS 2864195618Srpaulo#undef S_FACTOR 2865195618Srpaulo} 2866195618Srpaulo 2867195618Srpaulo/* 2868195618Srpaulo * Add a Mesh Link Metric report IE to a frame. 2869195618Srpaulo */ 2870195618Srpaulouint8_t * 2871232479Sadrianieee80211_add_meshlmetric(uint8_t *frm, uint8_t flags, uint32_t metric) 2872195618Srpaulo{ 2873195618Srpaulo *frm++ = IEEE80211_ELEMID_MESHLINK; 2874232479Sadrian *frm++ = 5; 2875232479Sadrian *frm++ = flags; 2876195618Srpaulo ADDWORD(frm, metric); 2877195618Srpaulo return frm; 2878195618Srpaulo} 2879195618Srpaulo#undef ADDSHORT 2880195618Srpaulo#undef ADDWORD 2881195618Srpaulo 2882195618Srpaulo/* 2883195618Srpaulo * Initialize any mesh-specific node state. 2884195618Srpaulo */ 2885195618Srpaulovoid 2886195618Srpauloieee80211_mesh_node_init(struct ieee80211vap *vap, struct ieee80211_node *ni) 2887195618Srpaulo{ 2888195618Srpaulo ni->ni_flags |= IEEE80211_NODE_QOS; 2889195618Srpaulo callout_init(&ni->ni_mltimer, CALLOUT_MPSAFE); 2890195618Srpaulo} 2891195618Srpaulo 2892195618Srpaulo/* 2893195618Srpaulo * Cleanup any mesh-specific node state. 2894195618Srpaulo */ 2895195618Srpaulovoid 2896195618Srpauloieee80211_mesh_node_cleanup(struct ieee80211_node *ni) 2897195618Srpaulo{ 2898195618Srpaulo struct ieee80211vap *vap = ni->ni_vap; 2899195618Srpaulo struct ieee80211_mesh_state *ms = vap->iv_mesh; 2900195618Srpaulo 2901195618Srpaulo callout_drain(&ni->ni_mltimer); 2902195618Srpaulo /* NB: short-circuit callbacks after mesh_vdetach */ 2903195618Srpaulo if (vap->iv_mesh != NULL) 2904195618Srpaulo ms->ms_ppath->mpp_peerdown(ni); 2905195618Srpaulo} 2906195618Srpaulo 2907195618Srpaulovoid 2908195618Srpauloieee80211_parse_meshid(struct ieee80211_node *ni, const uint8_t *ie) 2909195618Srpaulo{ 2910195618Srpaulo ni->ni_meshidlen = ie[1]; 2911195618Srpaulo memcpy(ni->ni_meshid, ie + 2, ie[1]); 2912195618Srpaulo} 2913195618Srpaulo 2914195618Srpaulo/* 2915195618Srpaulo * Setup mesh-specific node state on neighbor discovery. 2916195618Srpaulo */ 2917195618Srpaulovoid 2918195618Srpauloieee80211_mesh_init_neighbor(struct ieee80211_node *ni, 2919195618Srpaulo const struct ieee80211_frame *wh, 2920195618Srpaulo const struct ieee80211_scanparams *sp) 2921195618Srpaulo{ 2922195618Srpaulo ieee80211_parse_meshid(ni, sp->meshid); 2923195618Srpaulo} 2924195618Srpaulo 2925198242Srpaulovoid 2926198242Srpauloieee80211_mesh_update_beacon(struct ieee80211vap *vap, 2927198242Srpaulo struct ieee80211_beacon_offsets *bo) 2928198242Srpaulo{ 2929198242Srpaulo KASSERT(vap->iv_opmode == IEEE80211_M_MBSS, ("not a MBSS vap")); 2930198242Srpaulo 2931198242Srpaulo if (isset(bo->bo_flags, IEEE80211_BEACON_MESHCONF)) { 2932198242Srpaulo (void)ieee80211_add_meshconf(bo->bo_meshconf, vap); 2933198242Srpaulo clrbit(bo->bo_flags, IEEE80211_BEACON_MESHCONF); 2934198242Srpaulo } 2935198242Srpaulo} 2936198242Srpaulo 2937195618Srpaulostatic int 2938195618Srpaulomesh_ioctl_get80211(struct ieee80211vap *vap, struct ieee80211req *ireq) 2939195618Srpaulo{ 2940195618Srpaulo struct ieee80211_mesh_state *ms = vap->iv_mesh; 2941195618Srpaulo uint8_t tmpmeshid[IEEE80211_NWID_LEN]; 2942195618Srpaulo struct ieee80211_mesh_route *rt; 2943195618Srpaulo struct ieee80211req_mesh_route *imr; 2944195618Srpaulo size_t len, off; 2945195618Srpaulo uint8_t *p; 2946195618Srpaulo int error; 2947195618Srpaulo 2948195618Srpaulo if (vap->iv_opmode != IEEE80211_M_MBSS) 2949195618Srpaulo return ENOSYS; 2950195618Srpaulo 2951195618Srpaulo error = 0; 2952195618Srpaulo switch (ireq->i_type) { 2953195618Srpaulo case IEEE80211_IOC_MESH_ID: 2954195618Srpaulo ireq->i_len = ms->ms_idlen; 2955195618Srpaulo memcpy(tmpmeshid, ms->ms_id, ireq->i_len); 2956195618Srpaulo error = copyout(tmpmeshid, ireq->i_data, ireq->i_len); 2957195618Srpaulo break; 2958195618Srpaulo case IEEE80211_IOC_MESH_AP: 2959195618Srpaulo ireq->i_val = (ms->ms_flags & IEEE80211_MESHFLAGS_AP) != 0; 2960195618Srpaulo break; 2961195618Srpaulo case IEEE80211_IOC_MESH_FWRD: 2962195618Srpaulo ireq->i_val = (ms->ms_flags & IEEE80211_MESHFLAGS_FWD) != 0; 2963195618Srpaulo break; 2964195618Srpaulo case IEEE80211_IOC_MESH_TTL: 2965195618Srpaulo ireq->i_val = ms->ms_ttl; 2966195618Srpaulo break; 2967195618Srpaulo case IEEE80211_IOC_MESH_RTCMD: 2968195618Srpaulo switch (ireq->i_val) { 2969195618Srpaulo case IEEE80211_MESH_RTCMD_LIST: 2970195618Srpaulo len = 0; 2971195618Srpaulo MESH_RT_LOCK(ms); 2972195618Srpaulo TAILQ_FOREACH(rt, &ms->ms_routes, rt_next) { 2973195618Srpaulo len += sizeof(*imr); 2974195618Srpaulo } 2975195618Srpaulo MESH_RT_UNLOCK(ms); 2976195618Srpaulo if (len > ireq->i_len || ireq->i_len < sizeof(*imr)) { 2977195618Srpaulo ireq->i_len = len; 2978195618Srpaulo return ENOMEM; 2979195618Srpaulo } 2980195618Srpaulo ireq->i_len = len; 2981195784Srpaulo /* XXX M_WAIT? */ 2982195618Srpaulo p = malloc(len, M_TEMP, M_NOWAIT | M_ZERO); 2983195618Srpaulo if (p == NULL) 2984195618Srpaulo return ENOMEM; 2985195618Srpaulo off = 0; 2986195618Srpaulo MESH_RT_LOCK(ms); 2987195618Srpaulo TAILQ_FOREACH(rt, &ms->ms_routes, rt_next) { 2988195618Srpaulo if (off >= len) 2989195618Srpaulo break; 2990195618Srpaulo imr = (struct ieee80211req_mesh_route *) 2991195618Srpaulo (p + off); 2992195618Srpaulo IEEE80211_ADDR_COPY(imr->imr_dest, 2993195618Srpaulo rt->rt_dest); 2994195618Srpaulo IEEE80211_ADDR_COPY(imr->imr_nexthop, 2995195618Srpaulo rt->rt_nexthop); 2996195618Srpaulo imr->imr_metric = rt->rt_metric; 2997195618Srpaulo imr->imr_nhops = rt->rt_nhops; 2998234877Smonthadar imr->imr_lifetime = 2999234877Smonthadar ieee80211_mesh_rt_update(rt, 0); 3000195784Srpaulo imr->imr_lastmseq = rt->rt_lastmseq; 3001234877Smonthadar imr->imr_flags = rt->rt_flags; /* last */ 3002195618Srpaulo off += sizeof(*imr); 3003195618Srpaulo } 3004195618Srpaulo MESH_RT_UNLOCK(ms); 3005195618Srpaulo error = copyout(p, (uint8_t *)ireq->i_data, 3006195618Srpaulo ireq->i_len); 3007195618Srpaulo free(p, M_TEMP); 3008195618Srpaulo break; 3009195618Srpaulo case IEEE80211_MESH_RTCMD_FLUSH: 3010195618Srpaulo case IEEE80211_MESH_RTCMD_ADD: 3011195618Srpaulo case IEEE80211_MESH_RTCMD_DELETE: 3012195618Srpaulo return EINVAL; 3013195618Srpaulo default: 3014195618Srpaulo return ENOSYS; 3015195618Srpaulo } 3016195618Srpaulo break; 3017195618Srpaulo case IEEE80211_IOC_MESH_PR_METRIC: 3018195618Srpaulo len = strlen(ms->ms_pmetric->mpm_descr); 3019195618Srpaulo if (ireq->i_len < len) 3020195618Srpaulo return EINVAL; 3021195618Srpaulo ireq->i_len = len; 3022195618Srpaulo error = copyout(ms->ms_pmetric->mpm_descr, 3023195618Srpaulo (uint8_t *)ireq->i_data, len); 3024195618Srpaulo break; 3025195618Srpaulo case IEEE80211_IOC_MESH_PR_PATH: 3026195618Srpaulo len = strlen(ms->ms_ppath->mpp_descr); 3027195618Srpaulo if (ireq->i_len < len) 3028195618Srpaulo return EINVAL; 3029195618Srpaulo ireq->i_len = len; 3030195618Srpaulo error = copyout(ms->ms_ppath->mpp_descr, 3031195618Srpaulo (uint8_t *)ireq->i_data, len); 3032195618Srpaulo break; 3033195618Srpaulo default: 3034195618Srpaulo return ENOSYS; 3035195618Srpaulo } 3036195618Srpaulo 3037195618Srpaulo return error; 3038195618Srpaulo} 3039195618SrpauloIEEE80211_IOCTL_GET(mesh, mesh_ioctl_get80211); 3040195618Srpaulo 3041195618Srpaulostatic int 3042195618Srpaulomesh_ioctl_set80211(struct ieee80211vap *vap, struct ieee80211req *ireq) 3043195618Srpaulo{ 3044195618Srpaulo struct ieee80211_mesh_state *ms = vap->iv_mesh; 3045195618Srpaulo uint8_t tmpmeshid[IEEE80211_NWID_LEN]; 3046195618Srpaulo uint8_t tmpaddr[IEEE80211_ADDR_LEN]; 3047195618Srpaulo char tmpproto[IEEE80211_MESH_PROTO_DSZ]; 3048195618Srpaulo int error; 3049195618Srpaulo 3050195618Srpaulo if (vap->iv_opmode != IEEE80211_M_MBSS) 3051195618Srpaulo return ENOSYS; 3052195618Srpaulo 3053195618Srpaulo error = 0; 3054195618Srpaulo switch (ireq->i_type) { 3055195618Srpaulo case IEEE80211_IOC_MESH_ID: 3056195618Srpaulo if (ireq->i_val != 0 || ireq->i_len > IEEE80211_MESHID_LEN) 3057195618Srpaulo return EINVAL; 3058195618Srpaulo error = copyin(ireq->i_data, tmpmeshid, ireq->i_len); 3059195784Srpaulo if (error != 0) 3060195618Srpaulo break; 3061195618Srpaulo memset(ms->ms_id, 0, IEEE80211_NWID_LEN); 3062195618Srpaulo ms->ms_idlen = ireq->i_len; 3063195618Srpaulo memcpy(ms->ms_id, tmpmeshid, ireq->i_len); 3064195784Srpaulo error = ENETRESET; 3065195618Srpaulo break; 3066195618Srpaulo case IEEE80211_IOC_MESH_AP: 3067195618Srpaulo if (ireq->i_val) 3068195618Srpaulo ms->ms_flags |= IEEE80211_MESHFLAGS_AP; 3069195618Srpaulo else 3070195618Srpaulo ms->ms_flags &= ~IEEE80211_MESHFLAGS_AP; 3071195784Srpaulo error = ENETRESET; 3072195618Srpaulo break; 3073195618Srpaulo case IEEE80211_IOC_MESH_FWRD: 3074195618Srpaulo if (ireq->i_val) 3075195618Srpaulo ms->ms_flags |= IEEE80211_MESHFLAGS_FWD; 3076195618Srpaulo else 3077195618Srpaulo ms->ms_flags &= ~IEEE80211_MESHFLAGS_FWD; 3078195618Srpaulo break; 3079195618Srpaulo case IEEE80211_IOC_MESH_TTL: 3080195618Srpaulo ms->ms_ttl = (uint8_t) ireq->i_val; 3081195618Srpaulo break; 3082195618Srpaulo case IEEE80211_IOC_MESH_RTCMD: 3083195618Srpaulo switch (ireq->i_val) { 3084195618Srpaulo case IEEE80211_MESH_RTCMD_LIST: 3085195618Srpaulo return EINVAL; 3086195618Srpaulo case IEEE80211_MESH_RTCMD_FLUSH: 3087195618Srpaulo ieee80211_mesh_rt_flush(vap); 3088195618Srpaulo break; 3089195618Srpaulo case IEEE80211_MESH_RTCMD_ADD: 3090195618Srpaulo if (IEEE80211_ADDR_EQ(vap->iv_myaddr, ireq->i_data) || 3091195618Srpaulo IEEE80211_ADDR_EQ(broadcastaddr, ireq->i_data)) 3092195618Srpaulo return EINVAL; 3093195618Srpaulo error = copyin(ireq->i_data, &tmpaddr, 3094195618Srpaulo IEEE80211_ADDR_LEN); 3095195784Srpaulo if (error == 0) 3096195618Srpaulo ieee80211_mesh_discover(vap, tmpaddr, NULL); 3097195618Srpaulo break; 3098195618Srpaulo case IEEE80211_MESH_RTCMD_DELETE: 3099195618Srpaulo ieee80211_mesh_rt_del(vap, ireq->i_data); 3100195618Srpaulo break; 3101195618Srpaulo default: 3102195618Srpaulo return ENOSYS; 3103195618Srpaulo } 3104195618Srpaulo break; 3105195618Srpaulo case IEEE80211_IOC_MESH_PR_METRIC: 3106195618Srpaulo error = copyin(ireq->i_data, tmpproto, sizeof(tmpproto)); 3107195784Srpaulo if (error == 0) { 3108195784Srpaulo error = mesh_select_proto_metric(vap, tmpproto); 3109195784Srpaulo if (error == 0) 3110195784Srpaulo error = ENETRESET; 3111195784Srpaulo } 3112195618Srpaulo break; 3113195618Srpaulo case IEEE80211_IOC_MESH_PR_PATH: 3114195618Srpaulo error = copyin(ireq->i_data, tmpproto, sizeof(tmpproto)); 3115195784Srpaulo if (error == 0) { 3116195784Srpaulo error = mesh_select_proto_path(vap, tmpproto); 3117195784Srpaulo if (error == 0) 3118195784Srpaulo error = ENETRESET; 3119195784Srpaulo } 3120195618Srpaulo break; 3121195618Srpaulo default: 3122195618Srpaulo return ENOSYS; 3123195618Srpaulo } 3124195618Srpaulo return error; 3125195618Srpaulo} 3126195618SrpauloIEEE80211_IOCTL_SET(mesh, mesh_ioctl_set80211); 3127