1 /*- 2 * Copyright (c) 2009 The FreeBSD Foundation 3 * All rights reserved. 4 * 5 * This software was developed by Rui Paulo under sponsorship from the 6 * FreeBSD Foundation. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 #include <sys/cdefs.h> 30 #ifdef __FreeBSD__ 31 __FBSDID("$FreeBSD$"); 32 #endif 33 34 /* 35 * IEEE 802.11s Hybrid Wireless Mesh Protocol, HWMP. 36 * 37 * Based on March 2009, D3.0 802.11s draft spec. 38 */ 39 #include "opt_inet.h" 40 #include "opt_wlan.h" 41 42 #include <sys/param.h> 43 #include <sys/systm.h> 44 #include <sys/mbuf.h> 45 #include <sys/malloc.h> 46 #include <sys/kernel.h> 47 48 #include <sys/socket.h> 49 #include <sys/sockio.h> 50 #include <sys/endian.h> 51 #include <sys/errno.h> 52 #include <sys/proc.h> 53 #include <sys/sysctl.h> 54 55 #include <net/if.h> 56 #include <net/if_media.h> 57 #include <net/if_llc.h> 58 #include <net/ethernet.h> 59 60 #include <net/bpf.h> 61 62 #include <net80211/ieee80211_var.h> 63 #include <net80211/ieee80211_action.h> 64 #include <net80211/ieee80211_input.h> 65 #include <net80211/ieee80211_mesh.h> 66 67 static void hwmp_vattach(struct ieee80211vap *); 68 static void hwmp_vdetach(struct ieee80211vap *); 69 static int hwmp_newstate(struct ieee80211vap *, 70 enum ieee80211_state, int); 71 static int hwmp_send_action(struct ieee80211_node *, 72 const uint8_t [IEEE80211_ADDR_LEN], 73 const uint8_t [IEEE80211_ADDR_LEN], 74 uint8_t *, size_t); 75 static uint8_t * hwmp_add_meshpreq(uint8_t *, 76 const struct ieee80211_meshpreq_ie *); 77 static uint8_t * hwmp_add_meshprep(uint8_t *, 78 const struct ieee80211_meshprep_ie *); 79 static uint8_t * hwmp_add_meshperr(uint8_t *, 80 const struct ieee80211_meshperr_ie *); 81 static uint8_t * hwmp_add_meshrann(uint8_t *, 82 const struct ieee80211_meshrann_ie *); 83 static void hwmp_rootmode_setup(struct ieee80211vap *); 84 static void hwmp_rootmode_cb(void *); 85 static void hwmp_rootmode_rann_cb(void *); 86 static void hwmp_recv_preq(struct ieee80211vap *, struct ieee80211_node *, 87 const struct ieee80211_frame *, 88 const struct ieee80211_meshpreq_ie *); 89 static int hwmp_send_preq(struct ieee80211_node *, 90 const uint8_t [IEEE80211_ADDR_LEN], 91 const uint8_t [IEEE80211_ADDR_LEN], 92 struct ieee80211_meshpreq_ie *); 93 static void hwmp_recv_prep(struct ieee80211vap *, struct ieee80211_node *, 94 const struct ieee80211_frame *, 95 const struct ieee80211_meshprep_ie *); 96 static int hwmp_send_prep(struct ieee80211_node *, 97 const uint8_t [IEEE80211_ADDR_LEN], 98 const uint8_t [IEEE80211_ADDR_LEN], 99 struct ieee80211_meshprep_ie *); 100 static void hwmp_recv_perr(struct ieee80211vap *, struct ieee80211_node *, 101 const struct ieee80211_frame *, 102 const struct ieee80211_meshperr_ie *); 103 static int hwmp_send_perr(struct ieee80211_node *, 104 const uint8_t [IEEE80211_ADDR_LEN], 105 const uint8_t [IEEE80211_ADDR_LEN], 106 struct ieee80211_meshperr_ie *); 107 static void hwmp_recv_rann(struct ieee80211vap *, struct ieee80211_node *, 108 const struct ieee80211_frame *, 109 const struct ieee80211_meshrann_ie *); 110 static int hwmp_send_rann(struct ieee80211_node *, 111 const uint8_t [IEEE80211_ADDR_LEN], 112 const uint8_t [IEEE80211_ADDR_LEN], 113 struct ieee80211_meshrann_ie *); 114 static struct ieee80211_node * 115 hwmp_discover(struct ieee80211vap *, 116 const uint8_t [IEEE80211_ADDR_LEN], struct mbuf *); 117 static void hwmp_peerdown(struct ieee80211_node *); 118 119 static struct timeval ieee80211_hwmp_preqminint = { 0, 100000 }; 120 static struct timeval ieee80211_hwmp_perrminint = { 0, 100000 }; 121 122 /* unalligned little endian access */ 123 #define LE_WRITE_2(p, v) do { \ 124 ((uint8_t *)(p))[0] = (v) & 0xff; \ 125 ((uint8_t *)(p))[1] = ((v) >> 8) & 0xff; \ 126 } while (0) 127 #define LE_WRITE_4(p, v) do { \ 128 ((uint8_t *)(p))[0] = (v) & 0xff; \ 129 ((uint8_t *)(p))[1] = ((v) >> 8) & 0xff; \ 130 ((uint8_t *)(p))[2] = ((v) >> 16) & 0xff; \ 131 ((uint8_t *)(p))[3] = ((v) >> 24) & 0xff; \ 132 } while (0) 133 134 135 /* NB: the Target Address set in a Proactive PREQ is the broadcast address. */ 136 static const uint8_t broadcastaddr[IEEE80211_ADDR_LEN] = 137 { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; 138 139 typedef uint32_t ieee80211_hwmp_seq; 140 #define HWMP_SEQ_LT(a, b) ((int32_t)((a)-(b)) < 0) 141 #define HWMP_SEQ_LEQ(a, b) ((int32_t)((a)-(b)) <= 0) 142 #define HWMP_SEQ_EQ(a, b) ((int32_t)((a)-(b)) == 0) 143 #define HWMP_SEQ_GT(a, b) ((int32_t)((a)-(b)) > 0) 144 #define HWMP_SEQ_GEQ(a, b) ((int32_t)((a)-(b)) >= 0) 145 146 /* The longer one of the lifetime should be stored as new lifetime */ 147 #define MESH_ROUTE_LIFETIME_MAX(a, b) (a > b ? a : b) 148 149 /* 150 * Private extension of ieee80211_mesh_route. 151 */ 152 struct ieee80211_hwmp_route { 153 ieee80211_hwmp_seq hr_seq; /* last HWMP seq seen from dst*/ 154 ieee80211_hwmp_seq hr_preqid; /* last PREQ ID seen from dst */ 155 ieee80211_hwmp_seq hr_origseq; /* seq. no. on our latest PREQ*/ 156 int hr_preqretries; 157 }; 158 struct ieee80211_hwmp_state { 159 ieee80211_hwmp_seq hs_seq; /* next seq to be used */ 160 ieee80211_hwmp_seq hs_preqid; /* next PREQ ID to be used */ 161 struct timeval hs_lastpreq; /* last time we sent a PREQ */ 162 struct timeval hs_lastperr; /* last time we sent a PERR */ 163 int hs_rootmode; /* proactive HWMP */ 164 struct callout hs_roottimer; 165 uint8_t hs_maxhops; /* max hop count */ 166 }; 167 168 static SYSCTL_NODE(_net_wlan, OID_AUTO, hwmp, CTLFLAG_RD, 0, 169 "IEEE 802.11s HWMP parameters"); 170 static int ieee80211_hwmp_targetonly = 0; 171 SYSCTL_INT(_net_wlan_hwmp, OID_AUTO, targetonly, CTLTYPE_INT | CTLFLAG_RW, 172 &ieee80211_hwmp_targetonly, 0, "Set TO bit on generated PREQs"); 173 static int ieee80211_hwmp_replyforward = 1; 174 SYSCTL_INT(_net_wlan_hwmp, OID_AUTO, replyforward, CTLTYPE_INT | CTLFLAG_RW, 175 &ieee80211_hwmp_replyforward, 0, "Set RF bit on generated PREQs"); 176 static int ieee80211_hwmp_pathtimeout = -1; 177 SYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, pathlifetime, CTLTYPE_INT | CTLFLAG_RW, 178 &ieee80211_hwmp_pathtimeout, 0, ieee80211_sysctl_msecs_ticks, "I", 179 "path entry lifetime (ms)"); 180 static int ieee80211_hwmp_roottimeout = -1; 181 SYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, roottimeout, CTLTYPE_INT | CTLFLAG_RW, 182 &ieee80211_hwmp_roottimeout, 0, ieee80211_sysctl_msecs_ticks, "I", 183 "root PREQ timeout (ms)"); 184 static int ieee80211_hwmp_rootint = -1; 185 SYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, rootint, CTLTYPE_INT | CTLFLAG_RW, 186 &ieee80211_hwmp_rootint, 0, ieee80211_sysctl_msecs_ticks, "I", 187 "root interval (ms)"); 188 static int ieee80211_hwmp_rannint = -1; 189 SYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, rannint, CTLTYPE_INT | CTLFLAG_RW, 190 &ieee80211_hwmp_rannint, 0, ieee80211_sysctl_msecs_ticks, "I", 191 "root announcement interval (ms)"); 192 193 #define IEEE80211_HWMP_DEFAULT_MAXHOPS 31 194 195 static ieee80211_recv_action_func hwmp_recv_action_meshpath; 196 197 static struct ieee80211_mesh_proto_path mesh_proto_hwmp = { 198 .mpp_descr = "HWMP", 199 .mpp_ie = IEEE80211_MESHCONF_PATH_HWMP, 200 .mpp_discover = hwmp_discover, 201 .mpp_peerdown = hwmp_peerdown, 202 .mpp_vattach = hwmp_vattach, 203 .mpp_vdetach = hwmp_vdetach, 204 .mpp_newstate = hwmp_newstate, 205 .mpp_privlen = sizeof(struct ieee80211_hwmp_route), 206 }; 207 SYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, inact, CTLTYPE_INT | CTLFLAG_RW, 208 &mesh_proto_hwmp.mpp_inact, 0, ieee80211_sysctl_msecs_ticks, "I", 209 "mesh route inactivity timeout (ms)"); 210 211 212 static void 213 ieee80211_hwmp_init(void) 214 { 215 ieee80211_hwmp_pathtimeout = msecs_to_ticks(5*1000); 216 ieee80211_hwmp_roottimeout = msecs_to_ticks(5*1000); 217 ieee80211_hwmp_rootint = msecs_to_ticks(2*1000); 218 ieee80211_hwmp_rannint = msecs_to_ticks(1*1000); 219 220 /* 221 * Register action frame handler. 222 */ 223 ieee80211_recv_action_register(IEEE80211_ACTION_CAT_MESHPATH, 224 IEEE80211_ACTION_MESHPATH_SEL, hwmp_recv_action_meshpath); 225 226 /* NB: default is 5 secs per spec */ 227 mesh_proto_hwmp.mpp_inact = msecs_to_ticks(5*1000); 228 229 /* 230 * Register HWMP. 231 */ 232 ieee80211_mesh_register_proto_path(&mesh_proto_hwmp); 233 } 234 SYSINIT(wlan_hwmp, SI_SUB_DRIVERS, SI_ORDER_SECOND, ieee80211_hwmp_init, NULL); 235 236 void 237 hwmp_vattach(struct ieee80211vap *vap) 238 { 239 struct ieee80211_hwmp_state *hs; 240 241 KASSERT(vap->iv_opmode == IEEE80211_M_MBSS, 242 ("not a mesh vap, opmode %d", vap->iv_opmode)); 243 244 hs = malloc(sizeof(struct ieee80211_hwmp_state), M_80211_VAP, 245 M_NOWAIT | M_ZERO); 246 if (hs == NULL) { 247 printf("%s: couldn't alloc HWMP state\n", __func__); 248 return; 249 } 250 hs->hs_maxhops = IEEE80211_HWMP_DEFAULT_MAXHOPS; 251 callout_init(&hs->hs_roottimer, CALLOUT_MPSAFE); 252 vap->iv_hwmp = hs; 253 } 254 255 void 256 hwmp_vdetach(struct ieee80211vap *vap) 257 { 258 struct ieee80211_hwmp_state *hs = vap->iv_hwmp; 259 260 callout_drain(&hs->hs_roottimer); 261 free(vap->iv_hwmp, M_80211_VAP); 262 vap->iv_hwmp = NULL; 263 } 264 265 int 266 hwmp_newstate(struct ieee80211vap *vap, enum ieee80211_state ostate, int arg) 267 { 268 enum ieee80211_state nstate = vap->iv_state; 269 struct ieee80211_hwmp_state *hs = vap->iv_hwmp; 270 271 IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n", 272 __func__, ieee80211_state_name[ostate], 273 ieee80211_state_name[nstate], arg); 274 275 if (nstate != IEEE80211_S_RUN && ostate == IEEE80211_S_RUN) 276 callout_drain(&hs->hs_roottimer); 277 if (nstate == IEEE80211_S_RUN) 278 hwmp_rootmode_setup(vap); 279 return 0; 280 } 281 282 static int 283 hwmp_recv_action_meshpath(struct ieee80211_node *ni, 284 const struct ieee80211_frame *wh, 285 const uint8_t *frm, const uint8_t *efrm) 286 { 287 struct ieee80211vap *vap = ni->ni_vap; 288 struct ieee80211_meshpreq_ie preq; 289 struct ieee80211_meshprep_ie prep; 290 struct ieee80211_meshperr_ie perr; 291 struct ieee80211_meshrann_ie rann; 292 const uint8_t *iefrm = frm + 2; /* action + code */ 293 int found = 0; 294 295 while (efrm - iefrm > 1) { 296 IEEE80211_VERIFY_LENGTH(efrm - iefrm, iefrm[1] + 2, return 0); 297 switch (*iefrm) { 298 case IEEE80211_ELEMID_MESHPREQ: 299 { 300 const struct ieee80211_meshpreq_ie *mpreq = 301 (const struct ieee80211_meshpreq_ie *) iefrm; 302 /* XXX > 1 target */ 303 if (mpreq->preq_len != 304 sizeof(struct ieee80211_meshpreq_ie) - 2) { 305 IEEE80211_DISCARD(vap, 306 IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP, 307 wh, NULL, "%s", "PREQ with wrong len"); 308 vap->iv_stats.is_rx_mgtdiscard++; 309 break; 310 } 311 memcpy(&preq, mpreq, sizeof(preq)); 312 preq.preq_id = LE_READ_4(&mpreq->preq_id); 313 preq.preq_origseq = LE_READ_4(&mpreq->preq_origseq); 314 preq.preq_lifetime = LE_READ_4(&mpreq->preq_lifetime); 315 preq.preq_metric = LE_READ_4(&mpreq->preq_metric); 316 preq.preq_targets[0].target_seq = 317 LE_READ_4(&mpreq->preq_targets[0].target_seq); 318 hwmp_recv_preq(vap, ni, wh, &preq); 319 found++; 320 break; 321 } 322 case IEEE80211_ELEMID_MESHPREP: 323 { 324 const struct ieee80211_meshprep_ie *mprep = 325 (const struct ieee80211_meshprep_ie *) iefrm; 326 if (mprep->prep_len != 327 sizeof(struct ieee80211_meshprep_ie) - 2) { 328 IEEE80211_DISCARD(vap, 329 IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP, 330 wh, NULL, "%s", "PREP with wrong len"); 331 vap->iv_stats.is_rx_mgtdiscard++; 332 break; 333 } 334 memcpy(&prep, mprep, sizeof(prep)); 335 prep.prep_targetseq = LE_READ_4(&mprep->prep_targetseq); 336 prep.prep_lifetime = LE_READ_4(&mprep->prep_lifetime); 337 prep.prep_metric = LE_READ_4(&mprep->prep_metric); 338 prep.prep_origseq = LE_READ_4(&mprep->prep_origseq); 339 hwmp_recv_prep(vap, ni, wh, &prep); 340 found++; 341 break; 342 } 343 case IEEE80211_ELEMID_MESHPERR: 344 { 345 const struct ieee80211_meshperr_ie *mperr = 346 (const struct ieee80211_meshperr_ie *) iefrm; 347 /* XXX > 1 target */ 348 if (mperr->perr_len != 349 sizeof(struct ieee80211_meshperr_ie) - 2) { 350 IEEE80211_DISCARD(vap, 351 IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP, 352 wh, NULL, "%s", "PERR with wrong len"); 353 vap->iv_stats.is_rx_mgtdiscard++; 354 break; 355 } 356 memcpy(&perr, mperr, sizeof(perr)); 357 perr.perr_dests[0].dest_seq = 358 LE_READ_4(&mperr->perr_dests[0].dest_seq); 359 hwmp_recv_perr(vap, ni, wh, &perr); 360 found++; 361 break; 362 } 363 case IEEE80211_ELEMID_MESHRANN: 364 { 365 const struct ieee80211_meshrann_ie *mrann = 366 (const struct ieee80211_meshrann_ie *) iefrm; 367 if (mrann->rann_len != 368 sizeof(struct ieee80211_meshrann_ie) - 2) { 369 IEEE80211_DISCARD(vap, 370 IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP, 371 wh, NULL, "%s", "RAN with wrong len"); 372 vap->iv_stats.is_rx_mgtdiscard++; 373 return 1; 374 } 375 memcpy(&rann, mrann, sizeof(rann)); 376 rann.rann_seq = LE_READ_4(&mrann->rann_seq); 377 rann.rann_metric = LE_READ_4(&mrann->rann_metric); 378 hwmp_recv_rann(vap, ni, wh, &rann); 379 found++; 380 break; 381 } 382 } 383 iefrm += iefrm[1] + 2; 384 } 385 if (!found) { 386 IEEE80211_DISCARD(vap, 387 IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP, 388 wh, NULL, "%s", "PATH SEL action without IE"); 389 vap->iv_stats.is_rx_mgtdiscard++; 390 } 391 return 0; 392 } 393 394 static int 395 hwmp_send_action(struct ieee80211_node *ni, 396 const uint8_t sa[IEEE80211_ADDR_LEN], 397 const uint8_t da[IEEE80211_ADDR_LEN], 398 uint8_t *ie, size_t len) 399 { 400 struct ieee80211vap *vap = ni->ni_vap; 401 struct ieee80211com *ic = ni->ni_ic; 402 struct ieee80211_bpf_params params; 403 struct mbuf *m; 404 uint8_t *frm; 405 406 if (vap->iv_state == IEEE80211_S_CAC) { 407 IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, ni, 408 "block %s frame in CAC state", "HWMP action"); 409 vap->iv_stats.is_tx_badstate++; 410 return EIO; /* XXX */ 411 } 412 413 KASSERT(ni != NULL, ("null node")); 414 /* 415 * Hold a reference on the node so it doesn't go away until after 416 * the xmit is complete all the way in the driver. On error we 417 * will remove our reference. 418 */ 419 #ifdef IEEE80211_DEBUG_REFCNT 420 IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, 421 "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", 422 __func__, __LINE__, 423 ni, ether_sprintf(ni->ni_macaddr), 424 ieee80211_node_refcnt(ni)+1); 425 #endif 426 ieee80211_ref_node(ni); 427 428 m = ieee80211_getmgtframe(&frm, 429 ic->ic_headroom + sizeof(struct ieee80211_frame), 430 sizeof(struct ieee80211_action) + len 431 ); 432 if (m == NULL) { 433 ieee80211_free_node(ni); 434 vap->iv_stats.is_tx_nobuf++; 435 return ENOMEM; 436 } 437 *frm++ = IEEE80211_ACTION_CAT_MESHPATH; 438 *frm++ = IEEE80211_ACTION_MESHPATH_SEL; 439 switch (*ie) { 440 case IEEE80211_ELEMID_MESHPREQ: 441 frm = hwmp_add_meshpreq(frm, 442 (struct ieee80211_meshpreq_ie *)ie); 443 break; 444 case IEEE80211_ELEMID_MESHPREP: 445 frm = hwmp_add_meshprep(frm, 446 (struct ieee80211_meshprep_ie *)ie); 447 break; 448 case IEEE80211_ELEMID_MESHPERR: 449 frm = hwmp_add_meshperr(frm, 450 (struct ieee80211_meshperr_ie *)ie); 451 break; 452 case IEEE80211_ELEMID_MESHRANN: 453 frm = hwmp_add_meshrann(frm, 454 (struct ieee80211_meshrann_ie *)ie); 455 break; 456 } 457 458 m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); 459 M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT); 460 if (m == NULL) { 461 ieee80211_free_node(ni); 462 vap->iv_stats.is_tx_nobuf++; 463 return ENOMEM; 464 } 465 ieee80211_send_setup(ni, m, 466 IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_ACTION, 467 IEEE80211_NONQOS_TID, sa, da, sa); 468 469 m->m_flags |= M_ENCAP; /* mark encapsulated */ 470 IEEE80211_NODE_STAT(ni, tx_mgmt); 471 472 memset(¶ms, 0, sizeof(params)); 473 params.ibp_pri = WME_AC_VO; 474 params.ibp_rate0 = ni->ni_txparms->mgmtrate; 475 if (IEEE80211_IS_MULTICAST(da)) 476 params.ibp_try0 = 1; 477 else 478 params.ibp_try0 = ni->ni_txparms->maxretry; 479 params.ibp_power = ni->ni_txpower; 480 return ic->ic_raw_xmit(ni, m, ¶ms); 481 } 482 483 #define ADDSHORT(frm, v) do { \ 484 frm[0] = (v) & 0xff; \ 485 frm[1] = (v) >> 8; \ 486 frm += 2; \ 487 } while (0) 488 #define ADDWORD(frm, v) do { \ 489 LE_WRITE_4(frm, v); \ 490 frm += 4; \ 491 } while (0) 492 /* 493 * Add a Mesh Path Request IE to a frame. 494 */ 495 static uint8_t * 496 hwmp_add_meshpreq(uint8_t *frm, const struct ieee80211_meshpreq_ie *preq) 497 { 498 int i; 499 500 *frm++ = IEEE80211_ELEMID_MESHPREQ; 501 *frm++ = sizeof(struct ieee80211_meshpreq_ie) - 2 + 502 (preq->preq_tcount - 1) * sizeof(*preq->preq_targets); 503 *frm++ = preq->preq_flags; 504 *frm++ = preq->preq_hopcount; 505 *frm++ = preq->preq_ttl; 506 ADDWORD(frm, preq->preq_id); 507 IEEE80211_ADDR_COPY(frm, preq->preq_origaddr); frm += 6; 508 ADDWORD(frm, preq->preq_origseq); 509 ADDWORD(frm, preq->preq_lifetime); 510 ADDWORD(frm, preq->preq_metric); 511 *frm++ = preq->preq_tcount; 512 for (i = 0; i < preq->preq_tcount; i++) { 513 *frm++ = preq->preq_targets[i].target_flags; 514 IEEE80211_ADDR_COPY(frm, preq->preq_targets[i].target_addr); 515 frm += 6; 516 ADDWORD(frm, preq->preq_targets[i].target_seq); 517 } 518 return frm; 519 } 520 521 /* 522 * Add a Mesh Path Reply IE to a frame. 523 */ 524 static uint8_t * 525 hwmp_add_meshprep(uint8_t *frm, const struct ieee80211_meshprep_ie *prep) 526 { 527 *frm++ = IEEE80211_ELEMID_MESHPREP; 528 *frm++ = sizeof(struct ieee80211_meshprep_ie) - 2; 529 *frm++ = prep->prep_flags; 530 *frm++ = prep->prep_hopcount; 531 *frm++ = prep->prep_ttl; 532 IEEE80211_ADDR_COPY(frm, prep->prep_targetaddr); frm += 6; 533 ADDWORD(frm, prep->prep_targetseq); 534 ADDWORD(frm, prep->prep_lifetime); 535 ADDWORD(frm, prep->prep_metric); 536 IEEE80211_ADDR_COPY(frm, prep->prep_origaddr); frm += 6; 537 ADDWORD(frm, prep->prep_origseq); 538 return frm; 539 } 540 541 /* 542 * Add a Mesh Path Error IE to a frame. 543 */ 544 static uint8_t * 545 hwmp_add_meshperr(uint8_t *frm, const struct ieee80211_meshperr_ie *perr) 546 { 547 int i; 548 549 *frm++ = IEEE80211_ELEMID_MESHPERR; 550 *frm++ = sizeof(struct ieee80211_meshperr_ie) - 2 + 551 (perr->perr_ndests - 1) * sizeof(*perr->perr_dests); 552 *frm++ = perr->perr_ttl; 553 *frm++ = perr->perr_ndests; 554 for (i = 0; i < perr->perr_ndests; i++) { 555 *frm++ = perr->perr_dests[i].dest_flags; 556 IEEE80211_ADDR_COPY(frm, perr->perr_dests[i].dest_addr); 557 frm += 6; 558 ADDWORD(frm, perr->perr_dests[i].dest_seq); 559 ADDSHORT(frm, perr->perr_dests[i].dest_rcode); 560 } 561 return frm; 562 } 563 564 /* 565 * Add a Root Annoucement IE to a frame. 566 */ 567 static uint8_t * 568 hwmp_add_meshrann(uint8_t *frm, const struct ieee80211_meshrann_ie *rann) 569 { 570 *frm++ = IEEE80211_ELEMID_MESHRANN; 571 *frm++ = sizeof(struct ieee80211_meshrann_ie) - 2; 572 *frm++ = rann->rann_flags; 573 *frm++ = rann->rann_hopcount; 574 *frm++ = rann->rann_ttl; 575 IEEE80211_ADDR_COPY(frm, rann->rann_addr); frm += 6; 576 ADDWORD(frm, rann->rann_seq); 577 ADDWORD(frm, rann->rann_metric); 578 return frm; 579 } 580 581 static void 582 hwmp_rootmode_setup(struct ieee80211vap *vap) 583 { 584 struct ieee80211_hwmp_state *hs = vap->iv_hwmp; 585 586 switch (hs->hs_rootmode) { 587 case IEEE80211_HWMP_ROOTMODE_DISABLED: 588 callout_drain(&hs->hs_roottimer); 589 break; 590 case IEEE80211_HWMP_ROOTMODE_NORMAL: 591 case IEEE80211_HWMP_ROOTMODE_PROACTIVE: 592 callout_reset(&hs->hs_roottimer, ieee80211_hwmp_rootint, 593 hwmp_rootmode_cb, vap); 594 break; 595 case IEEE80211_HWMP_ROOTMODE_RANN: 596 callout_reset(&hs->hs_roottimer, ieee80211_hwmp_rannint, 597 hwmp_rootmode_rann_cb, vap); 598 break; 599 } 600 } 601 602 /* 603 * Send a broadcast Path Request to find all nodes on the mesh. We are 604 * called when the vap is configured as a HWMP root node. 605 */ 606 #define PREQ_TFLAGS(n) preq.preq_targets[n].target_flags 607 #define PREQ_TADDR(n) preq.preq_targets[n].target_addr 608 #define PREQ_TSEQ(n) preq.preq_targets[n].target_seq 609 static void 610 hwmp_rootmode_cb(void *arg) 611 { 612 struct ieee80211vap *vap = (struct ieee80211vap *)arg; 613 struct ieee80211_hwmp_state *hs = vap->iv_hwmp; 614 struct ieee80211_mesh_state *ms = vap->iv_mesh; 615 struct ieee80211_meshpreq_ie preq; 616 617 IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, vap->iv_bss, 618 "%s", "send broadcast PREQ"); 619 620 preq.preq_flags = IEEE80211_MESHPREQ_FLAGS_AM; 621 if (ms->ms_flags & IEEE80211_MESHFLAGS_PORTAL) 622 preq.preq_flags |= IEEE80211_MESHPREQ_FLAGS_PR; 623 if (hs->hs_rootmode == IEEE80211_HWMP_ROOTMODE_PROACTIVE) 624 preq.preq_flags |= IEEE80211_MESHPREQ_FLAGS_PP; 625 preq.preq_hopcount = 0; 626 preq.preq_ttl = ms->ms_ttl; 627 preq.preq_id = ++hs->hs_preqid; 628 IEEE80211_ADDR_COPY(preq.preq_origaddr, vap->iv_myaddr); 629 preq.preq_origseq = ++hs->hs_seq; 630 preq.preq_lifetime = ticks_to_msecs(ieee80211_hwmp_roottimeout); 631 preq.preq_metric = IEEE80211_MESHLMETRIC_INITIALVAL; 632 preq.preq_tcount = 1; 633 IEEE80211_ADDR_COPY(PREQ_TADDR(0), broadcastaddr); 634 PREQ_TFLAGS(0) = IEEE80211_MESHPREQ_TFLAGS_TO | 635 IEEE80211_MESHPREQ_TFLAGS_RF; 636 PREQ_TSEQ(0) = 0; 637 vap->iv_stats.is_hwmp_rootreqs++; 638 hwmp_send_preq(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &preq); 639 hwmp_rootmode_setup(vap); 640 } 641 #undef PREQ_TFLAGS 642 #undef PREQ_TADDR 643 #undef PREQ_TSEQ 644 645 /* 646 * Send a Root Annoucement (RANN) to find all the nodes on the mesh. We are 647 * called when the vap is configured as a HWMP RANN root node. 648 */ 649 static void 650 hwmp_rootmode_rann_cb(void *arg) 651 { 652 struct ieee80211vap *vap = (struct ieee80211vap *)arg; 653 struct ieee80211_hwmp_state *hs = vap->iv_hwmp; 654 struct ieee80211_mesh_state *ms = vap->iv_mesh; 655 struct ieee80211_meshrann_ie rann; 656 657 IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, vap->iv_bss, 658 "%s", "send broadcast RANN"); 659 660 rann.rann_flags = 0; 661 if (ms->ms_flags & IEEE80211_MESHFLAGS_PORTAL) 662 rann.rann_flags |= IEEE80211_MESHRANN_FLAGS_PR; 663 rann.rann_hopcount = 0; 664 rann.rann_ttl = ms->ms_ttl; 665 IEEE80211_ADDR_COPY(rann.rann_addr, vap->iv_myaddr); 666 rann.rann_seq = ++hs->hs_seq; 667 rann.rann_metric = IEEE80211_MESHLMETRIC_INITIALVAL; 668 669 vap->iv_stats.is_hwmp_rootrann++; 670 hwmp_send_rann(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &rann); 671 hwmp_rootmode_setup(vap); 672 } 673 674 #define PREQ_TFLAGS(n) preq->preq_targets[n].target_flags 675 #define PREQ_TADDR(n) preq->preq_targets[n].target_addr 676 #define PREQ_TSEQ(n) preq->preq_targets[n].target_seq 677 static void 678 hwmp_recv_preq(struct ieee80211vap *vap, struct ieee80211_node *ni, 679 const struct ieee80211_frame *wh, const struct ieee80211_meshpreq_ie *preq) 680 { 681 struct ieee80211_mesh_state *ms = vap->iv_mesh; 682 struct ieee80211_mesh_route *rt = NULL; 683 struct ieee80211_mesh_route *rtorig = NULL; 684 struct ieee80211_mesh_route *rttarg = NULL; 685 struct ieee80211_hwmp_route *hrorig = NULL; 686 struct ieee80211_hwmp_route *hrtarg = NULL; 687 struct ieee80211_hwmp_state *hs = vap->iv_hwmp; 688 struct ieee80211_meshprep_ie prep; 689 690 if (ni == vap->iv_bss || 691 ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED) 692 return; 693 /* 694 * Ignore PREQs from us. Could happen because someone forward it 695 * back to us. 696 */ 697 if (IEEE80211_ADDR_EQ(vap->iv_myaddr, preq->preq_origaddr)) 698 return; 699 700 IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 701 "received PREQ, source %6D", preq->preq_origaddr, ":"); 702 703 /* 704 * Acceptance criteria: if the PREQ is not for us or not broadcast 705 * AND forwarding is disabled, discard this PREQ. 706 * XXX: need to check PROXY 707 */ 708 if ((!IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0)) || 709 !IEEE80211_IS_MULTICAST(PREQ_TADDR(0))) && 710 !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) { 711 IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_HWMP, 712 preq->preq_origaddr, NULL, "%s", "not accepting PREQ"); 713 return; 714 } 715 /* 716 * Acceptance criteria: if unicast addressed 717 * AND no valid forwarding for Target of PREQ, discard this PREQ. 718 */ 719 rttarg = ieee80211_mesh_rt_find(vap, PREQ_TADDR(0)); 720 if(rttarg != NULL) 721 hrtarg = IEEE80211_MESH_ROUTE_PRIV(rttarg, 722 struct ieee80211_hwmp_route); 723 /* Address mode: ucast */ 724 if((preq->preq_flags & IEEE80211_MESHPREQ_FLAGS_AM) == 0 && 725 rttarg == NULL && 726 !IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0))) { 727 IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_HWMP, 728 preq->preq_origaddr, NULL, 729 "unicast addressed PREQ of unknown target %6D", 730 PREQ_TADDR(0), ":"); 731 return; 732 } 733 734 /* PREQ ACCEPTED */ 735 736 rtorig = ieee80211_mesh_rt_find(vap, preq->preq_origaddr); 737 if (rtorig == NULL) { 738 rtorig = ieee80211_mesh_rt_add(vap, preq->preq_origaddr); 739 IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 740 "adding originator %6D", preq->preq_origaddr, ":"); 741 } 742 if (rtorig == NULL) { 743 /* XXX stat */ 744 return; 745 } 746 hrorig = IEEE80211_MESH_ROUTE_PRIV(rtorig, struct ieee80211_hwmp_route); 747 748 /* Data creation and update of forwarding information 749 * according to Table 11C-8 for originator mesh STA. 750 */ 751 if(HWMP_SEQ_GT(preq->preq_origseq, hrorig->hr_seq) || 752 (HWMP_SEQ_EQ(preq->preq_origseq, hrorig->hr_seq) && 753 preq->preq_metric < rtorig->rt_metric)) { 754 hrorig->hr_seq = preq->preq_origseq; 755 IEEE80211_ADDR_COPY(rtorig->rt_nexthop, wh->i_addr2); 756 rtorig->rt_metric = preq->preq_metric + 757 ms->ms_pmetric->mpm_metric(ni); 758 rtorig->rt_nhops = preq->preq_hopcount + 1; 759 rtorig->rt_lifetime = MESH_ROUTE_LIFETIME_MAX( 760 preq->preq_lifetime, rtorig->rt_lifetime); 761 /* path to orig is valid now */ 762 rtorig->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID; 763 }else if(hrtarg != NULL && 764 HWMP_SEQ_EQ(hrtarg->hr_seq, PREQ_TSEQ(0)) && 765 (rtorig->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0) { 766 IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 767 "discard PREQ from %6D, old seq no %u <= %u", 768 preq->preq_origaddr, ":", 769 preq->preq_origseq, hrorig->hr_seq); 770 return; 771 } 772 773 /* 774 * Forwarding information for transmitter mesh STA 775 * [OPTIONAL: if metric improved] 776 */ 777 778 /* 779 * Check if the PREQ is addressed to us. 780 */ 781 if (IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0))) { 782 IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 783 "reply to %6D", preq->preq_origaddr, ":"); 784 /* 785 * Build and send a PREP frame. 786 */ 787 prep.prep_flags = 0; 788 prep.prep_hopcount = 0; 789 prep.prep_ttl = ms->ms_ttl; 790 IEEE80211_ADDR_COPY(prep.prep_targetaddr, vap->iv_myaddr); 791 prep.prep_targetseq = ++hs->hs_seq; 792 prep.prep_lifetime = preq->preq_lifetime; 793 prep.prep_metric = IEEE80211_MESHLMETRIC_INITIALVAL; 794 IEEE80211_ADDR_COPY(prep.prep_origaddr, preq->preq_origaddr); 795 prep.prep_origseq = preq->preq_origseq; 796 hwmp_send_prep(ni, vap->iv_myaddr, wh->i_addr2, &prep); 797 /* 798 * Build the reverse path, if we don't have it already. 799 */ 800 rt = ieee80211_mesh_rt_find(vap, preq->preq_origaddr); 801 if (rt == NULL) 802 hwmp_discover(vap, preq->preq_origaddr, NULL); 803 else if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0) 804 hwmp_discover(vap, rt->rt_dest, NULL); 805 return; 806 } 807 /* 808 * Proactive PREQ: reply with a proactive PREP to the 809 * root STA if requested. 810 */ 811 if (IEEE80211_ADDR_EQ(PREQ_TADDR(0), broadcastaddr) && 812 (PREQ_TFLAGS(0) & 813 ((IEEE80211_MESHPREQ_TFLAGS_TO|IEEE80211_MESHPREQ_TFLAGS_RF) == 814 (IEEE80211_MESHPREQ_TFLAGS_TO|IEEE80211_MESHPREQ_TFLAGS_RF)))) { 815 uint8_t rootmac[IEEE80211_ADDR_LEN]; 816 817 IEEE80211_ADDR_COPY(rootmac, preq->preq_origaddr); 818 rt = ieee80211_mesh_rt_find(vap, rootmac); 819 if (rt == NULL) { 820 rt = ieee80211_mesh_rt_add(vap, rootmac); 821 if (rt == NULL) { 822 IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 823 "unable to add root mesh path to %6D", 824 rootmac, ":"); 825 vap->iv_stats.is_mesh_rtaddfailed++; 826 return; 827 } 828 } 829 IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 830 "root mesh station @ %6D", rootmac, ":"); 831 832 /* 833 * Reply with a PREP if we don't have a path to the root 834 * or if the root sent us a proactive PREQ. 835 */ 836 if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0 || 837 (preq->preq_flags & IEEE80211_MESHPREQ_FLAGS_PP)) { 838 prep.prep_flags = 0; 839 prep.prep_hopcount = 0; 840 prep.prep_ttl = ms->ms_ttl; 841 IEEE80211_ADDR_COPY(prep.prep_origaddr, rootmac); 842 prep.prep_origseq = preq->preq_origseq; 843 prep.prep_lifetime = preq->preq_lifetime; 844 prep.prep_metric = IEEE80211_MESHLMETRIC_INITIALVAL; 845 IEEE80211_ADDR_COPY(prep.prep_targetaddr, 846 vap->iv_myaddr); 847 prep.prep_targetseq = ++hs->hs_seq; 848 hwmp_send_prep(vap->iv_bss, vap->iv_myaddr, 849 broadcastaddr, &prep); 850 } 851 hwmp_discover(vap, rootmac, NULL); 852 return; 853 } 854 rt = ieee80211_mesh_rt_find(vap, PREQ_TADDR(0)); 855 856 /* 857 * Forwarding and Intermediate reply for PREQs with 1 target. 858 */ 859 if (preq->preq_tcount == 1) { 860 struct ieee80211_meshpreq_ie ppreq; /* propagated PREQ */ 861 862 memcpy(&ppreq, preq, sizeof(ppreq)); 863 /* 864 * We have a valid route to this node. 865 */ 866 if (rt != NULL && 867 (rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID)) { 868 if (preq->preq_ttl > 1 && 869 preq->preq_hopcount < hs->hs_maxhops) { 870 IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 871 "forward PREQ from %6D", 872 preq->preq_origaddr, ":"); 873 /* 874 * Propagate the original PREQ. 875 * PREQ is unicast now to rt->rt_nexthop 876 */ 877 ppreq.preq_flags &= 878 ~IEEE80211_MESHPREQ_FLAGS_AM; 879 ppreq.preq_hopcount += 1; 880 ppreq.preq_ttl -= 1; 881 ppreq.preq_metric += 882 ms->ms_pmetric->mpm_metric(ni); 883 /* 884 * Set TO and unset RF bits because we are 885 * going to send a PREP next. 886 */ 887 ppreq.preq_targets[0].target_flags |= 888 IEEE80211_MESHPREQ_TFLAGS_TO; 889 ppreq.preq_targets[0].target_flags &= 890 ~IEEE80211_MESHPREQ_TFLAGS_RF; 891 hwmp_send_preq(ni, vap->iv_myaddr, 892 rt->rt_nexthop, &ppreq); 893 } 894 /* 895 * Check if we can send an intermediate Path Reply, 896 * i.e., Target Only bit is not set. 897 */ 898 if (!(PREQ_TFLAGS(0) & IEEE80211_MESHPREQ_TFLAGS_TO)) { 899 struct ieee80211_meshprep_ie prep; 900 901 IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 902 "intermediate reply for PREQ from %6D", 903 preq->preq_origaddr, ":"); 904 prep.prep_flags = 0; 905 prep.prep_hopcount = rt->rt_nhops + 1; 906 prep.prep_ttl = ms->ms_ttl; 907 IEEE80211_ADDR_COPY(&prep.prep_targetaddr, 908 PREQ_TADDR(0)); 909 prep.prep_targetseq = hrorig->hr_seq; 910 prep.prep_lifetime = preq->preq_lifetime; 911 prep.prep_metric = rt->rt_metric + 912 ms->ms_pmetric->mpm_metric(ni); 913 IEEE80211_ADDR_COPY(&prep.prep_origaddr, 914 preq->preq_origaddr); 915 prep.prep_origseq = hrorig->hr_seq; 916 hwmp_send_prep(ni, vap->iv_myaddr, 917 broadcastaddr, &prep); 918 } 919 /* 920 * We have no information about this path, 921 * propagate the PREQ. 922 */ 923 } else if (preq->preq_ttl > 1 && 924 preq->preq_hopcount < hs->hs_maxhops) { 925 if (rt == NULL) { 926 rt = ieee80211_mesh_rt_add(vap, PREQ_TADDR(0)); 927 if (rt == NULL) { 928 IEEE80211_NOTE(vap, 929 IEEE80211_MSG_HWMP, ni, 930 "unable to add PREQ path to %6D", 931 PREQ_TADDR(0), ":"); 932 vap->iv_stats.is_mesh_rtaddfailed++; 933 return; 934 } 935 } 936 rt->rt_metric = preq->preq_metric; 937 rt->rt_lifetime = preq->preq_lifetime; 938 hrorig = IEEE80211_MESH_ROUTE_PRIV(rt, 939 struct ieee80211_hwmp_route); 940 hrorig->hr_seq = preq->preq_origseq; 941 hrorig->hr_preqid = preq->preq_id; 942 943 IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 944 "forward PREQ from %6D", 945 preq->preq_origaddr, ":"); 946 ppreq.preq_hopcount += 1; 947 ppreq.preq_ttl -= 1; 948 ppreq.preq_metric += ms->ms_pmetric->mpm_metric(ni); 949 hwmp_send_preq(ni, vap->iv_myaddr, broadcastaddr, 950 &ppreq); 951 } 952 } 953 } 954 #undef PREQ_TFLAGS 955 #undef PREQ_TADDR 956 #undef PREQ_TSEQ 957 958 static int 959 hwmp_send_preq(struct ieee80211_node *ni, 960 const uint8_t sa[IEEE80211_ADDR_LEN], 961 const uint8_t da[IEEE80211_ADDR_LEN], 962 struct ieee80211_meshpreq_ie *preq) 963 { 964 struct ieee80211_hwmp_state *hs = ni->ni_vap->iv_hwmp; 965 966 /* 967 * Enforce PREQ interval. 968 */ 969 if (ratecheck(&hs->hs_lastpreq, &ieee80211_hwmp_preqminint) == 0) 970 return EALREADY; 971 getmicrouptime(&hs->hs_lastpreq); 972 973 /* 974 * mesh preq action frame format 975 * [6] da 976 * [6] sa 977 * [6] addr3 = sa 978 * [1] action 979 * [1] category 980 * [tlv] mesh path request 981 */ 982 preq->preq_ie = IEEE80211_ELEMID_MESHPREQ; 983 return hwmp_send_action(ni, sa, da, (uint8_t *)preq, 984 sizeof(struct ieee80211_meshpreq_ie)); 985 } 986 987 static void 988 hwmp_recv_prep(struct ieee80211vap *vap, struct ieee80211_node *ni, 989 const struct ieee80211_frame *wh, const struct ieee80211_meshprep_ie *prep) 990 { 991 struct ieee80211_mesh_state *ms = vap->iv_mesh; 992 struct ieee80211_hwmp_state *hs = vap->iv_hwmp; 993 struct ieee80211_mesh_route *rt = NULL; 994 struct ieee80211_hwmp_route *hr; 995 struct ieee80211com *ic = vap->iv_ic; 996 struct ifnet *ifp = vap->iv_ifp; 997 struct mbuf *m, *next; 998 uint32_t metric = 0; 999 1000 /* 1001 * Acceptance criteria: if the corresponding PREQ was not generated 1002 * by us and forwarding is disabled, discard this PREP. 1003 */ 1004 if (ni == vap->iv_bss || 1005 ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED) 1006 return; 1007 if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, prep->prep_origaddr) && 1008 !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) 1009 return; 1010 1011 IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 1012 "received PREP from %6D", prep->prep_targetaddr, ":"); 1013 1014 rt = ieee80211_mesh_rt_find(vap, prep->prep_targetaddr); 1015 if (rt == NULL) { 1016 /* 1017 * If we have no entry this could be a reply to a root PREQ. 1018 * XXX: not true anymore cause we dont create entry for target 1019 * when propagating PREQs like the old code did. 1020 */ 1021 if (hs->hs_rootmode != IEEE80211_HWMP_ROOTMODE_DISABLED) { 1022 rt = ieee80211_mesh_rt_add(vap, prep->prep_targetaddr); 1023 if (rt == NULL) { 1024 IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, 1025 ni, "unable to add PREP path to %6D", 1026 prep->prep_targetaddr, ":"); 1027 vap->iv_stats.is_mesh_rtaddfailed++; 1028 return; 1029 } 1030 IEEE80211_ADDR_COPY(rt->rt_nexthop, wh->i_addr2); 1031 rt->rt_nhops = prep->prep_hopcount; 1032 rt->rt_lifetime = prep->prep_lifetime; 1033 rt->rt_metric = prep->prep_metric; 1034 rt->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID; 1035 IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 1036 "add root path to %6D nhops %d metric %lu (PREP)", 1037 prep->prep_targetaddr, ":", 1038 rt->rt_nhops, rt->rt_metric); 1039 return; 1040 } 1041 return; 1042 } 1043 /* 1044 * Sequence number validation. 1045 */ 1046 hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route); 1047 if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID)) { 1048 if (HWMP_SEQ_LT(prep->prep_targetseq, hr->hr_seq)) { 1049 IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 1050 "discard PREP from %6D, old seq no %u < %u", 1051 prep->prep_targetaddr, ":", 1052 prep->prep_targetseq, hr->hr_seq); 1053 return; 1054 } else if (HWMP_SEQ_LEQ(prep->prep_targetseq, hr->hr_seq) && 1055 prep->prep_metric > rt->rt_metric) { 1056 IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 1057 "discard PREP from %6D, new metric %u > %u", 1058 prep->prep_targetaddr, ":", 1059 prep->prep_metric, rt->rt_metric); 1060 return; 1061 } 1062 } 1063 1064 hr->hr_seq = prep->prep_targetseq; 1065 /* 1066 * If it's NOT for us, propagate the PREP. 1067 */ 1068 if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, prep->prep_origaddr) && 1069 prep->prep_ttl > 1 && prep->prep_hopcount < hs->hs_maxhops) { 1070 struct ieee80211_meshprep_ie pprep; /* propagated PREP */ 1071 IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 1072 "propagate PREP from %6D", 1073 prep->prep_targetaddr, ":"); 1074 1075 memcpy(&pprep, prep, sizeof(pprep)); 1076 pprep.prep_hopcount += 1; 1077 pprep.prep_ttl -= 1; 1078 pprep.prep_metric += ms->ms_pmetric->mpm_metric(ni); 1079 hwmp_send_prep(ni, vap->iv_myaddr, broadcastaddr, &pprep); 1080 } 1081 hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route); 1082 if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY) { 1083 /* NB: never clobber a proxy entry */; 1084 IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 1085 "discard PREP for %6D, route is marked PROXY", 1086 prep->prep_targetaddr, ":"); 1087 vap->iv_stats.is_hwmp_proxy++; 1088 /* NB: first path discovery always fails */ 1089 } else if (hr->hr_origseq == 0 || 1090 prep->prep_origseq == hr->hr_origseq) { 1091 /* 1092 * Check if we already have a path to this node. 1093 * If we do, check if this path reply contains a 1094 * better route. 1095 */ 1096 if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0 || 1097 (prep->prep_hopcount < rt->rt_nhops || 1098 prep->prep_metric < rt->rt_metric)) { 1099 hr->hr_origseq = prep->prep_origseq; 1100 metric = prep->prep_metric + 1101 ms->ms_pmetric->mpm_metric(ni); 1102 IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 1103 "%s path to %6D, hopcount %d:%d metric %d:%d", 1104 rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID ? 1105 "prefer" : "update", 1106 prep->prep_origaddr, ":", 1107 rt->rt_nhops, prep->prep_hopcount, 1108 rt->rt_metric, prep->prep_metric); 1109 IEEE80211_ADDR_COPY(rt->rt_nexthop, wh->i_addr2); 1110 rt->rt_nhops = prep->prep_hopcount + 1; 1111 rt->rt_lifetime = prep->prep_lifetime; 1112 rt->rt_metric = metric; 1113 rt->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID; 1114 } else { 1115 IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 1116 "ignore PREP for %6D, hopcount %d:%d metric %d:%d", 1117 prep->prep_targetaddr, ":", 1118 rt->rt_nhops, prep->prep_hopcount, 1119 rt->rt_metric, prep->prep_metric); 1120 } 1121 } else { 1122 IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 1123 "discard PREP for %6D, wrong orig seqno %u != %u", 1124 prep->prep_targetaddr, ":", prep->prep_origseq, 1125 hr->hr_origseq); 1126 vap->iv_stats.is_hwmp_wrongseq++; 1127 } 1128 /* 1129 * Check for frames queued awaiting path discovery. 1130 * XXX probably can tell exactly and avoid remove call 1131 * NB: hash may have false matches, if so they will get 1132 * stuck back on the stageq because there won't be 1133 * a path. 1134 */ 1135 m = ieee80211_ageq_remove(&ic->ic_stageq, 1136 (struct ieee80211_node *)(uintptr_t) 1137 ieee80211_mac_hash(ic, rt->rt_dest)); 1138 for (; m != NULL; m = next) { 1139 next = m->m_nextpkt; 1140 m->m_nextpkt = NULL; 1141 IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 1142 "flush queued frame %p len %d", m, m->m_pkthdr.len); 1143 ifp->if_transmit(ifp, m); 1144 } 1145 } 1146 1147 static int 1148 hwmp_send_prep(struct ieee80211_node *ni, 1149 const uint8_t sa[IEEE80211_ADDR_LEN], 1150 const uint8_t da[IEEE80211_ADDR_LEN], 1151 struct ieee80211_meshprep_ie *prep) 1152 { 1153 /* NB: there's no PREP minimum interval. */ 1154 1155 /* 1156 * mesh prep action frame format 1157 * [6] da 1158 * [6] sa 1159 * [6] addr3 = sa 1160 * [1] action 1161 * [1] category 1162 * [tlv] mesh path reply 1163 */ 1164 prep->prep_ie = IEEE80211_ELEMID_MESHPREP; 1165 return hwmp_send_action(ni, sa, da, (uint8_t *)prep, 1166 sizeof(struct ieee80211_meshprep_ie)); 1167 } 1168 1169 #define PERR_DFLAGS(n) perr.perr_dests[n].dest_flags 1170 #define PERR_DADDR(n) perr.perr_dests[n].dest_addr 1171 #define PERR_DSEQ(n) perr.perr_dests[n].dest_seq 1172 #define PERR_DRCODE(n) perr.perr_dests[n].dest_rcode 1173 static void 1174 hwmp_peerdown(struct ieee80211_node *ni) 1175 { 1176 struct ieee80211vap *vap = ni->ni_vap; 1177 struct ieee80211_mesh_state *ms = vap->iv_mesh; 1178 struct ieee80211_meshperr_ie perr; 1179 struct ieee80211_mesh_route *rt; 1180 struct ieee80211_hwmp_route *hr; 1181 1182 rt = ieee80211_mesh_rt_find(vap, ni->ni_macaddr); 1183 if (rt == NULL) 1184 return; 1185 hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route); 1186 IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 1187 "%s", "delete route entry"); 1188 perr.perr_ttl = ms->ms_ttl; 1189 perr.perr_ndests = 1; 1190 PERR_DFLAGS(0) = 0; 1191 if (hr->hr_seq == 0) 1192 PERR_DFLAGS(0) |= IEEE80211_MESHPERR_DFLAGS_USN; 1193 PERR_DFLAGS(0) |= IEEE80211_MESHPERR_DFLAGS_RC; 1194 IEEE80211_ADDR_COPY(PERR_DADDR(0), rt->rt_dest); 1195 PERR_DSEQ(0) = hr->hr_seq; 1196 PERR_DRCODE(0) = IEEE80211_REASON_MESH_PERR_DEST_UNREACH; 1197 /* NB: flush everything passing through peer */ 1198 ieee80211_mesh_rt_flush_peer(vap, ni->ni_macaddr); 1199 hwmp_send_perr(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &perr); 1200 } 1201 #undef PERR_DFLAGS 1202 #undef PERR_DADDR 1203 #undef PERR_DSEQ 1204 #undef PERR_DRCODE 1205 1206 #define PERR_DFLAGS(n) perr->perr_dests[n].dest_flags 1207 #define PERR_DADDR(n) perr->perr_dests[n].dest_addr 1208 #define PERR_DSEQ(n) perr->perr_dests[n].dest_seq 1209 #define PERR_DRCODE(n) perr->perr_dests[n].dest_rcode 1210 static void 1211 hwmp_recv_perr(struct ieee80211vap *vap, struct ieee80211_node *ni, 1212 const struct ieee80211_frame *wh, const struct ieee80211_meshperr_ie *perr) 1213 { 1214 struct ieee80211_mesh_state *ms = vap->iv_mesh; 1215 struct ieee80211_mesh_route *rt = NULL; 1216 struct ieee80211_hwmp_route *hr; 1217 struct ieee80211_meshperr_ie pperr; 1218 int i, forward = 0; 1219 1220 /* 1221 * Acceptance criteria: check if we received a PERR from a 1222 * neighbor and forwarding is enabled. 1223 */ 1224 if (ni == vap->iv_bss || 1225 ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED || 1226 !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) 1227 return; 1228 /* 1229 * Find all routing entries that match and delete them. 1230 */ 1231 for (i = 0; i < perr->perr_ndests; i++) { 1232 rt = ieee80211_mesh_rt_find(vap, PERR_DADDR(i)); 1233 if (rt == NULL) 1234 continue; 1235 hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route); 1236 if (!(PERR_DFLAGS(0) & IEEE80211_MESHPERR_DFLAGS_USN) && 1237 HWMP_SEQ_GEQ(PERR_DSEQ(i), hr->hr_seq)) { 1238 ieee80211_mesh_rt_del(vap, rt->rt_dest); 1239 ieee80211_mesh_rt_flush_peer(vap, rt->rt_dest); 1240 rt = NULL; 1241 forward = 1; 1242 } 1243 } 1244 /* 1245 * Propagate the PERR if we previously found it on our routing table. 1246 * XXX handle ndest > 1 1247 */ 1248 if (forward && perr->perr_ttl > 1) { 1249 IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, 1250 "propagate PERR from %6D", wh->i_addr2, ":"); 1251 memcpy(&pperr, perr, sizeof(*perr)); 1252 pperr.perr_ttl--; 1253 hwmp_send_perr(vap->iv_bss, vap->iv_myaddr, broadcastaddr, 1254 &pperr); 1255 } 1256 } 1257 #undef PEER_DADDR 1258 #undef PERR_DSEQ 1259 1260 static int 1261 hwmp_send_perr(struct ieee80211_node *ni, 1262 const uint8_t sa[IEEE80211_ADDR_LEN], 1263 const uint8_t da[IEEE80211_ADDR_LEN], 1264 struct ieee80211_meshperr_ie *perr) 1265 { 1266 struct ieee80211_hwmp_state *hs = ni->ni_vap->iv_hwmp; 1267 1268 /* 1269 * Enforce PERR interval. 1270 */ 1271 if (ratecheck(&hs->hs_lastperr, &ieee80211_hwmp_perrminint) == 0) 1272 return EALREADY; 1273 getmicrouptime(&hs->hs_lastperr); 1274 1275 /* 1276 * mesh perr action frame format 1277 * [6] da 1278 * [6] sa 1279 * [6] addr3 = sa 1280 * [1] action 1281 * [1] category 1282 * [tlv] mesh path error 1283 */ 1284 perr->perr_ie = IEEE80211_ELEMID_MESHPERR; 1285 return hwmp_send_action(ni, sa, da, (uint8_t *)perr, 1286 sizeof(struct ieee80211_meshperr_ie)); 1287 } 1288 1289 static void 1290 hwmp_recv_rann(struct ieee80211vap *vap, struct ieee80211_node *ni, 1291 const struct ieee80211_frame *wh, const struct ieee80211_meshrann_ie *rann) 1292 { 1293 struct ieee80211_mesh_state *ms = vap->iv_mesh; 1294 struct ieee80211_hwmp_state *hs = vap->iv_hwmp; 1295 struct ieee80211_mesh_route *rt = NULL; 1296 struct ieee80211_hwmp_route *hr; 1297 struct ieee80211_meshrann_ie prann; 1298 1299 if (ni == vap->iv_bss || 1300 ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED || 1301 IEEE80211_ADDR_EQ(rann->rann_addr, vap->iv_myaddr)) 1302 return; 1303 1304 rt = ieee80211_mesh_rt_find(vap, rann->rann_addr); 1305 /* 1306 * Discover the path to the root mesh STA. 1307 * If we already know it, propagate the RANN element. 1308 */ 1309 if (rt == NULL) { 1310 hwmp_discover(vap, rann->rann_addr, NULL); 1311 return; 1312 } 1313 hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route); 1314 if (HWMP_SEQ_GT(rann->rann_seq, hr->hr_seq)) { 1315 hr->hr_seq = rann->rann_seq; 1316 if (rann->rann_ttl > 1 && 1317 rann->rann_hopcount < hs->hs_maxhops && 1318 (ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) { 1319 memcpy(&prann, rann, sizeof(prann)); 1320 prann.rann_hopcount += 1; 1321 prann.rann_ttl -= 1; 1322 prann.rann_metric += ms->ms_pmetric->mpm_metric(ni); 1323 hwmp_send_rann(vap->iv_bss, vap->iv_myaddr, 1324 broadcastaddr, &prann); 1325 } 1326 } 1327 } 1328 1329 static int 1330 hwmp_send_rann(struct ieee80211_node *ni, 1331 const uint8_t sa[IEEE80211_ADDR_LEN], 1332 const uint8_t da[IEEE80211_ADDR_LEN], 1333 struct ieee80211_meshrann_ie *rann) 1334 { 1335 /* 1336 * mesh rann action frame format 1337 * [6] da 1338 * [6] sa 1339 * [6] addr3 = sa 1340 * [1] action 1341 * [1] category 1342 * [tlv] root annoucement 1343 */ 1344 rann->rann_ie = IEEE80211_ELEMID_MESHRANN; 1345 return hwmp_send_action(ni, sa, da, (uint8_t *)rann, 1346 sizeof(struct ieee80211_meshrann_ie)); 1347 } 1348 1349 #define PREQ_TFLAGS(n) preq.preq_targets[n].target_flags 1350 #define PREQ_TADDR(n) preq.preq_targets[n].target_addr 1351 #define PREQ_TSEQ(n) preq.preq_targets[n].target_seq 1352 static struct ieee80211_node * 1353 hwmp_discover(struct ieee80211vap *vap, 1354 const uint8_t dest[IEEE80211_ADDR_LEN], struct mbuf *m) 1355 { 1356 struct ieee80211_hwmp_state *hs = vap->iv_hwmp; 1357 struct ieee80211_mesh_state *ms = vap->iv_mesh; 1358 struct ieee80211_mesh_route *rt = NULL; 1359 struct ieee80211_hwmp_route *hr; 1360 struct ieee80211_meshpreq_ie preq; 1361 struct ieee80211_node *ni; 1362 int sendpreq = 0; 1363 1364 KASSERT(vap->iv_opmode == IEEE80211_M_MBSS, 1365 ("not a mesh vap, opmode %d", vap->iv_opmode)); 1366 1367 KASSERT(!IEEE80211_ADDR_EQ(vap->iv_myaddr, dest), 1368 ("%s: discovering self!", __func__)); 1369 1370 ni = NULL; 1371 if (!IEEE80211_IS_MULTICAST(dest)) { 1372 rt = ieee80211_mesh_rt_find(vap, dest); 1373 if (rt == NULL) { 1374 rt = ieee80211_mesh_rt_add(vap, dest); 1375 if (rt == NULL) { 1376 IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, 1377 ni, "unable to add discovery path to %6D", 1378 dest, ":"); 1379 vap->iv_stats.is_mesh_rtaddfailed++; 1380 goto done; 1381 } 1382 } 1383 hr = IEEE80211_MESH_ROUTE_PRIV(rt, 1384 struct ieee80211_hwmp_route); 1385 if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0) { 1386 if (hr->hr_origseq == 0) 1387 hr->hr_origseq = ++hs->hs_seq; 1388 rt->rt_metric = IEEE80211_MESHLMETRIC_INITIALVAL; 1389 rt->rt_lifetime = 1390 ticks_to_msecs(ieee80211_hwmp_pathtimeout); 1391 /* XXX check preq retries */ 1392 sendpreq = 1; 1393 IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP, dest, 1394 "start path discovery (src %s), target seq %u", 1395 m == NULL ? "<none>" : ether_sprintf( 1396 mtod(m, struct ether_header *)->ether_shost), 1397 hr->hr_seq); 1398 /* 1399 * Try to discover the path for this node. 1400 * Group addressed PREQ Case A 1401 */ 1402 preq.preq_flags = IEEE80211_MESHPREQ_FLAGS_AM; 1403 preq.preq_hopcount = 0; 1404 preq.preq_ttl = ms->ms_ttl; 1405 preq.preq_id = ++hs->hs_preqid; 1406 IEEE80211_ADDR_COPY(preq.preq_origaddr, vap->iv_myaddr); 1407 preq.preq_origseq = hr->hr_origseq; 1408 preq.preq_lifetime = rt->rt_lifetime; 1409 preq.preq_metric = rt->rt_metric; 1410 preq.preq_tcount = 1; 1411 IEEE80211_ADDR_COPY(PREQ_TADDR(0), dest); 1412 PREQ_TFLAGS(0) = 0; 1413 if (ieee80211_hwmp_targetonly) 1414 PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_TO; 1415 if (ieee80211_hwmp_replyforward) 1416 PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_RF; 1417 PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_USN; 1418 PREQ_TSEQ(0) = hr->hr_seq; 1419 /* XXX check return value */ 1420 hwmp_send_preq(vap->iv_bss, vap->iv_myaddr, 1421 broadcastaddr, &preq); 1422 } 1423 if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) 1424 ni = ieee80211_find_txnode(vap, rt->rt_nexthop); 1425 } else { 1426 ni = ieee80211_find_txnode(vap, dest); 1427 /* NB: if null then we leak mbuf */ 1428 KASSERT(ni != NULL, ("leak mcast frame")); 1429 return ni; 1430 } 1431 done: 1432 if (ni == NULL && m != NULL) { 1433 if (sendpreq) { 1434 struct ieee80211com *ic = vap->iv_ic; 1435 /* 1436 * Queue packet for transmit when path discovery 1437 * completes. If discovery never completes the 1438 * frame will be flushed by way of the aging timer. 1439 */ 1440 IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP, dest, 1441 "%s", "queue frame until path found"); 1442 m->m_pkthdr.rcvif = (void *)(uintptr_t) 1443 ieee80211_mac_hash(ic, dest); 1444 /* XXX age chosen randomly */ 1445 ieee80211_ageq_append(&ic->ic_stageq, m, 1446 IEEE80211_INACT_WAIT); 1447 } else { 1448 IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_HWMP, 1449 dest, NULL, "%s", "no valid path to this node"); 1450 m_freem(m); 1451 } 1452 } 1453 return ni; 1454 } 1455 #undef PREQ_TFLAGS 1456 #undef PREQ_TADDR 1457 #undef PREQ_TSEQ 1458 1459 static int 1460 hwmp_ioctl_get80211(struct ieee80211vap *vap, struct ieee80211req *ireq) 1461 { 1462 struct ieee80211_hwmp_state *hs = vap->iv_hwmp; 1463 int error; 1464 1465 if (vap->iv_opmode != IEEE80211_M_MBSS) 1466 return ENOSYS; 1467 error = 0; 1468 switch (ireq->i_type) { 1469 case IEEE80211_IOC_HWMP_ROOTMODE: 1470 ireq->i_val = hs->hs_rootmode; 1471 break; 1472 case IEEE80211_IOC_HWMP_MAXHOPS: 1473 ireq->i_val = hs->hs_maxhops; 1474 break; 1475 default: 1476 return ENOSYS; 1477 } 1478 return error; 1479 } 1480 IEEE80211_IOCTL_GET(hwmp, hwmp_ioctl_get80211); 1481 1482 static int 1483 hwmp_ioctl_set80211(struct ieee80211vap *vap, struct ieee80211req *ireq) 1484 { 1485 struct ieee80211_hwmp_state *hs = vap->iv_hwmp; 1486 int error; 1487 1488 if (vap->iv_opmode != IEEE80211_M_MBSS) 1489 return ENOSYS; 1490 error = 0; 1491 switch (ireq->i_type) { 1492 case IEEE80211_IOC_HWMP_ROOTMODE: 1493 if (ireq->i_val < 0 || ireq->i_val > 3) 1494 return EINVAL; 1495 hs->hs_rootmode = ireq->i_val; 1496 hwmp_rootmode_setup(vap); 1497 break; 1498 case IEEE80211_IOC_HWMP_MAXHOPS: 1499 if (ireq->i_val <= 0 || ireq->i_val > 255) 1500 return EINVAL; 1501 hs->hs_maxhops = ireq->i_val; 1502 break; 1503 default: 1504 return ENOSYS; 1505 } 1506 return error; 1507 } 1508 IEEE80211_IOCTL_SET(hwmp, hwmp_ioctl_set80211); 1509