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