ieee80211_hwmp.c revision 232625
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: head/sys/net80211/ieee80211_hwmp.c 232625 2012-03-06 21:20:16Z adrian $");
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
67static void	hwmp_vattach(struct ieee80211vap *);
68static void	hwmp_vdetach(struct ieee80211vap *);
69static int	hwmp_newstate(struct ieee80211vap *,
70		    enum ieee80211_state, int);
71static 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);
75static uint8_t * hwmp_add_meshpreq(uint8_t *,
76		    const struct ieee80211_meshpreq_ie *);
77static uint8_t * hwmp_add_meshprep(uint8_t *,
78		    const struct ieee80211_meshprep_ie *);
79static uint8_t * hwmp_add_meshperr(uint8_t *,
80		    const struct ieee80211_meshperr_ie *);
81static uint8_t * hwmp_add_meshrann(uint8_t *,
82		    const struct ieee80211_meshrann_ie *);
83static void	hwmp_rootmode_setup(struct ieee80211vap *);
84static void	hwmp_rootmode_cb(void *);
85static void	hwmp_rootmode_rann_cb(void *);
86static void	hwmp_recv_preq(struct ieee80211vap *, struct ieee80211_node *,
87		    const struct ieee80211_frame *,
88		    const struct ieee80211_meshpreq_ie *);
89static 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 *);
93static void	hwmp_recv_prep(struct ieee80211vap *, struct ieee80211_node *,
94		    const struct ieee80211_frame *,
95		    const struct ieee80211_meshprep_ie *);
96static 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 *);
100static void	hwmp_recv_perr(struct ieee80211vap *, struct ieee80211_node *,
101		    const struct ieee80211_frame *,
102		    const struct ieee80211_meshperr_ie *);
103static 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 *);
107static void	hwmp_recv_rann(struct ieee80211vap *, struct ieee80211_node *,
108		   const struct ieee80211_frame *,
109		   const struct ieee80211_meshrann_ie *);
110static 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 *);
114static struct ieee80211_node *
115		hwmp_discover(struct ieee80211vap *,
116		    const uint8_t [IEEE80211_ADDR_LEN], struct mbuf *);
117static void	hwmp_peerdown(struct ieee80211_node *);
118
119static struct timeval ieee80211_hwmp_preqminint = { 0, 100000 };
120static 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. */
136static const uint8_t	broadcastaddr[IEEE80211_ADDR_LEN] =
137	{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
138
139typedef 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 */
152struct 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};
158struct 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
168static SYSCTL_NODE(_net_wlan, OID_AUTO, hwmp, CTLFLAG_RD, 0,
169    "IEEE 802.11s HWMP parameters");
170static int	ieee80211_hwmp_targetonly = 0;
171SYSCTL_INT(_net_wlan_hwmp, OID_AUTO, targetonly, CTLTYPE_INT | CTLFLAG_RW,
172    &ieee80211_hwmp_targetonly, 0, "Set TO bit on generated PREQs");
173static int	ieee80211_hwmp_replyforward = 1;
174SYSCTL_INT(_net_wlan_hwmp, OID_AUTO, replyforward, CTLTYPE_INT | CTLFLAG_RW,
175    &ieee80211_hwmp_replyforward, 0, "Set RF bit on generated PREQs");
176static int	ieee80211_hwmp_pathtimeout = -1;
177SYSCTL_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)");
180static int	ieee80211_hwmp_roottimeout = -1;
181SYSCTL_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)");
184static int	ieee80211_hwmp_rootint = -1;
185SYSCTL_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)");
188static int	ieee80211_hwmp_rannint = -1;
189SYSCTL_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
195static	ieee80211_recv_action_func hwmp_recv_action_meshpath;
196
197static 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};
207SYSCTL_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
212static void
213ieee80211_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_MESH,
224	    IEEE80211_ACTION_MESH_HWMP, 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}
234SYSINIT(wlan_hwmp, SI_SUB_DRIVERS, SI_ORDER_SECOND, ieee80211_hwmp_init, NULL);
235
236void
237hwmp_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
255void
256hwmp_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
265int
266hwmp_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/*
283 * Verify the length of an HWMP PREQ and return the number
284 * of destinations >= 1, if verification fails -1 is returned.
285 */
286static int
287verify_mesh_preq_len(struct ieee80211vap *vap,
288    const struct ieee80211_frame *wh, const uint8_t *iefrm)
289{
290	int alloc_sz = -1;
291	int ndest = -1;
292	if (iefrm[2] & IEEE80211_MESHPREQ_FLAGS_AE) {
293		/* Originator External Address  present */
294		alloc_sz =  IEEE80211_MESHPREQ_BASE_SZ_AE;
295		ndest = iefrm[IEEE80211_MESHPREQ_TCNT_OFFSET_AE];
296	} else {
297		/* w/o Originator External Address */
298		alloc_sz =  IEEE80211_MESHPREQ_BASE_SZ;
299		ndest = iefrm[IEEE80211_MESHPREQ_TCNT_OFFSET];
300	}
301	alloc_sz += ndest * IEEE80211_MESHPREQ_TRGT_SZ;
302
303	if(iefrm[1] != (alloc_sz)) {
304		IEEE80211_DISCARD(vap,
305		    IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
306		    wh, NULL, "PREQ (AE=%s) with wrong len",
307		    iefrm[2] & IEEE80211_MESHPREQ_FLAGS_AE ? "1" : "0");
308		return (-1);
309	}
310	return ndest;
311}
312
313/*
314 * Verify the length of an HWMP PREP and returns 1 on success,
315 * otherwise -1.
316 */
317static int
318verify_mesh_prep_len(struct ieee80211vap *vap,
319    const struct ieee80211_frame *wh, const uint8_t *iefrm)
320{
321	int alloc_sz = -1;
322	if (iefrm[2] & IEEE80211_MESHPREP_FLAGS_AE) {
323		if (iefrm[1] == IEEE80211_MESHPREP_BASE_SZ_AE)
324			alloc_sz = IEEE80211_MESHPREP_BASE_SZ_AE;
325	} else if (iefrm[1] == IEEE80211_MESHPREP_BASE_SZ)
326		alloc_sz = IEEE80211_MESHPREP_BASE_SZ;
327	if(alloc_sz < 0) {
328		IEEE80211_DISCARD(vap,
329		    IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
330		    wh, NULL, "PREP (AE=%s) with wrong len",
331		    iefrm[2] & IEEE80211_MESHPREP_FLAGS_AE ? "1" : "0");
332		return (-1);
333	}
334	return (1);
335}
336
337/*
338 * Verify the length of an HWMP PERR and return the number
339 * of destinations >= 1, if verification fails -1 is returned.
340 */
341static int
342verify_mesh_perr_len(struct ieee80211vap *vap,
343    const struct ieee80211_frame *wh, const uint8_t *iefrm)
344{
345	int alloc_sz = -1;
346	const uint8_t *iefrm_t = iefrm;
347	uint8_t ndest = iefrm_t[IEEE80211_MESHPERR_NDEST_OFFSET];
348	int i;
349
350	if(ndest > IEEE80211_MESHPERR_MAXDEST) {
351		IEEE80211_DISCARD(vap,
352		    IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
353		    wh, NULL, "PERR with wrong number of destionat (>19), %u",
354		    ndest);
355		return (-1);
356	}
357
358	iefrm_t += IEEE80211_MESHPERR_NDEST_OFFSET + 1; /* flag is next field */
359	/* We need to check each destionation flag to know size */
360	for(i = 0; i<ndest; i++) {
361		if ((*iefrm_t) & IEEE80211_MESHPERR_FLAGS_AE)
362			iefrm_t += IEEE80211_MESHPERR_DEST_SZ_AE;
363		else
364			iefrm_t += IEEE80211_MESHPERR_DEST_SZ;
365	}
366
367	alloc_sz = (iefrm_t - iefrm) - 2; /* action + code */
368	if(alloc_sz !=  iefrm[1]) {
369		IEEE80211_DISCARD(vap,
370		    IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
371		    wh, NULL, "%s", "PERR with wrong len");
372		return (-1);
373	}
374	return ndest;
375}
376
377static int
378hwmp_recv_action_meshpath(struct ieee80211_node *ni,
379	const struct ieee80211_frame *wh,
380	const uint8_t *frm, const uint8_t *efrm)
381{
382	struct ieee80211vap *vap = ni->ni_vap;
383	struct ieee80211_meshpreq_ie *preq;
384	struct ieee80211_meshprep_ie *prep;
385	struct ieee80211_meshperr_ie *perr;
386	struct ieee80211_meshrann_ie rann;
387	const uint8_t *iefrm = frm + 2; /* action + code */
388	const uint8_t *iefrm_t = iefrm; /* temporary pointer */
389	int ndest = -1;
390	int found = 0;
391
392	while (efrm - iefrm > 1) {
393		IEEE80211_VERIFY_LENGTH(efrm - iefrm, iefrm[1] + 2, return 0);
394		switch (*iefrm) {
395		case IEEE80211_ELEMID_MESHPREQ:
396		{
397			int i = 0;
398
399			iefrm_t = iefrm;
400			ndest = verify_mesh_preq_len(vap, wh, iefrm_t);
401			if (ndest < 0) {
402				vap->iv_stats.is_rx_mgtdiscard++;
403				break;
404			}
405			preq = malloc(sizeof(*preq) +
406			    (ndest - 1) * sizeof(*preq->preq_targets),
407			    M_80211_MESH_PREQ, M_NOWAIT | M_ZERO);
408			KASSERT(preq != NULL, ("preq == NULL"));
409
410			preq->preq_ie = *iefrm_t++;
411			preq->preq_len = *iefrm_t++;
412			preq->preq_flags = *iefrm_t++;
413			preq->preq_hopcount = *iefrm_t++;
414			preq->preq_ttl = *iefrm_t++;
415			preq->preq_id = LE_READ_4(iefrm_t); iefrm_t += 4;
416			IEEE80211_ADDR_COPY(preq->preq_origaddr, iefrm_t);
417			iefrm_t += 6;
418			preq->preq_origseq = LE_READ_4(iefrm_t); iefrm_t += 4;
419			/* NB: may have Originator Proxied Address */
420			if (preq->preq_flags & IEEE80211_MESHPREQ_FLAGS_AE)  {
421				IEEE80211_ADDR_COPY(
422				    preq->preq_orig_ext_addr, iefrm_t);
423				iefrm_t += 6;
424			}
425			preq->preq_lifetime = LE_READ_4(iefrm_t); iefrm_t += 4;
426			preq->preq_metric = LE_READ_4(iefrm_t); iefrm_t += 4;
427			preq->preq_tcount = *iefrm_t++;
428
429			for (i = 0; i < preq->preq_tcount; i++) {
430				preq->preq_targets[i].target_flags = *iefrm_t++;
431				IEEE80211_ADDR_COPY(
432				    preq->preq_targets[i].target_addr, iefrm_t);
433				iefrm_t += 6;
434				preq->preq_targets[i].target_seq =
435				    LE_READ_4(iefrm_t);
436				iefrm_t += 4;
437			}
438
439			hwmp_recv_preq(vap, ni, wh, preq);
440			free(preq, M_80211_MESH_PREQ);
441			found++;
442			break;
443		}
444		case IEEE80211_ELEMID_MESHPREP:
445		{
446			iefrm_t = iefrm;
447			ndest = verify_mesh_prep_len(vap, wh, iefrm_t);
448			if (ndest < 0) {
449				vap->iv_stats.is_rx_mgtdiscard++;
450				break;
451			}
452			prep = malloc(sizeof(*prep),
453			    M_80211_MESH_PREP, M_NOWAIT | M_ZERO);
454			KASSERT(prep != NULL, ("prep == NULL"));
455
456			prep->prep_ie = *iefrm_t++;
457			prep->prep_len = *iefrm_t++;
458			prep->prep_flags = *iefrm_t++;
459			prep->prep_hopcount = *iefrm_t++;
460			prep->prep_ttl = *iefrm_t++;
461			IEEE80211_ADDR_COPY(prep->prep_targetaddr, iefrm_t);
462			iefrm_t += 6;
463			prep->prep_targetseq = LE_READ_4(iefrm_t); iefrm_t += 4;
464			/* NB: May have Target Proxied Address */
465			if (prep->prep_flags & IEEE80211_MESHPREP_FLAGS_AE)  {
466				IEEE80211_ADDR_COPY(
467				    prep->prep_target_ext_addr, iefrm_t);
468				iefrm_t += 6;
469			}
470			prep->prep_lifetime = LE_READ_4(iefrm_t); iefrm_t += 4;
471			prep->prep_metric = LE_READ_4(iefrm_t); iefrm_t += 4;
472			IEEE80211_ADDR_COPY(prep->prep_origaddr, iefrm_t);
473			iefrm_t += 6;
474			prep->prep_origseq = LE_READ_4(iefrm_t); iefrm_t += 4;
475
476			hwmp_recv_prep(vap, ni, wh, prep);
477			free(prep, M_80211_MESH_PREP);
478			found++;
479			break;
480		}
481		case IEEE80211_ELEMID_MESHPERR:
482		{
483			int i = 0;
484
485			iefrm_t = iefrm;
486			ndest = verify_mesh_perr_len(vap, wh, iefrm_t);
487			if (ndest < 0) {
488				vap->iv_stats.is_rx_mgtdiscard++;
489				break;
490			}
491			perr = malloc(sizeof(*perr) +
492			    (ndest - 1) * sizeof(*perr->perr_dests),
493			    M_80211_MESH_PERR, M_NOWAIT | M_ZERO);
494			KASSERT(perr != NULL, ("perr == NULL"));
495
496			perr->perr_ie = *iefrm_t++;
497			perr->perr_len = *iefrm_t++;
498			perr->perr_ttl = *iefrm_t++;
499			perr->perr_ndests = *iefrm_t++;
500
501			for (i = 0; i<perr->perr_ndests; i++) {
502				perr->perr_dests[i].dest_flags = *iefrm_t++;
503				IEEE80211_ADDR_COPY(
504				    perr->perr_dests[i].dest_addr, iefrm_t);
505				iefrm_t += 6;
506				perr->perr_dests[i].dest_seq = LE_READ_4(iefrm_t);
507				iefrm_t += 4;
508				/* NB: May have Target Proxied Address */
509				if (perr->perr_dests[i].dest_flags &
510				    IEEE80211_MESHPERR_FLAGS_AE) {
511					IEEE80211_ADDR_COPY(
512					    perr->perr_dests[i].dest_ext_addr,
513					    iefrm_t);
514					iefrm_t += 6;
515				}
516				perr->perr_dests[i].dest_rcode =
517				    LE_READ_2(iefrm_t);
518				iefrm_t += 2;
519			}
520
521			hwmp_recv_perr(vap, ni, wh, perr);
522			free(perr, M_80211_MESH_PERR);
523			found++;
524			break;
525		}
526		case IEEE80211_ELEMID_MESHRANN:
527		{
528			const struct ieee80211_meshrann_ie *mrann =
529			    (const struct ieee80211_meshrann_ie *) iefrm;
530			if (mrann->rann_len !=
531			    sizeof(struct ieee80211_meshrann_ie) - 2) {
532				IEEE80211_DISCARD(vap,
533				    IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
534				    wh, NULL, "%s", "RAN with wrong len");
535				    vap->iv_stats.is_rx_mgtdiscard++;
536				return 1;
537			}
538			memcpy(&rann, mrann, sizeof(rann));
539			rann.rann_seq = LE_READ_4(&mrann->rann_seq);
540			rann.rann_metric = LE_READ_4(&mrann->rann_metric);
541			hwmp_recv_rann(vap, ni, wh, &rann);
542			found++;
543			break;
544		}
545		}
546		iefrm += iefrm[1] + 2;
547	}
548	if (!found) {
549		IEEE80211_DISCARD(vap,
550		    IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
551		    wh, NULL, "%s", "PATH SEL action without IE");
552		vap->iv_stats.is_rx_mgtdiscard++;
553	}
554	return 0;
555}
556
557static int
558hwmp_send_action(struct ieee80211_node *ni,
559    const uint8_t sa[IEEE80211_ADDR_LEN],
560    const uint8_t da[IEEE80211_ADDR_LEN],
561    uint8_t *ie, size_t len)
562{
563	struct ieee80211vap *vap = ni->ni_vap;
564	struct ieee80211com *ic = ni->ni_ic;
565	struct ieee80211_bpf_params params;
566	struct mbuf *m;
567	uint8_t *frm;
568
569	if (vap->iv_state == IEEE80211_S_CAC) {
570		IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, ni,
571		    "block %s frame in CAC state", "HWMP action");
572		vap->iv_stats.is_tx_badstate++;
573		return EIO;	/* XXX */
574	}
575
576	KASSERT(ni != NULL, ("null node"));
577	/*
578	 * Hold a reference on the node so it doesn't go away until after
579	 * the xmit is complete all the way in the driver.  On error we
580	 * will remove our reference.
581	 */
582#ifdef IEEE80211_DEBUG_REFCNT
583	IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
584	    "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n",
585	    __func__, __LINE__,
586	    ni, ether_sprintf(ni->ni_macaddr),
587	    ieee80211_node_refcnt(ni)+1);
588#endif
589	ieee80211_ref_node(ni);
590
591	m = ieee80211_getmgtframe(&frm,
592	    ic->ic_headroom + sizeof(struct ieee80211_frame),
593	    sizeof(struct ieee80211_action) + len
594	);
595	if (m == NULL) {
596		ieee80211_free_node(ni);
597		vap->iv_stats.is_tx_nobuf++;
598		return ENOMEM;
599	}
600	*frm++ = IEEE80211_ACTION_CAT_MESH;
601	*frm++ = IEEE80211_ACTION_MESH_HWMP;
602	switch (*ie) {
603	case IEEE80211_ELEMID_MESHPREQ:
604		frm = hwmp_add_meshpreq(frm,
605		    (struct ieee80211_meshpreq_ie *)ie);
606		break;
607	case IEEE80211_ELEMID_MESHPREP:
608		frm = hwmp_add_meshprep(frm,
609		    (struct ieee80211_meshprep_ie *)ie);
610		break;
611	case IEEE80211_ELEMID_MESHPERR:
612		frm = hwmp_add_meshperr(frm,
613		    (struct ieee80211_meshperr_ie *)ie);
614		break;
615	case IEEE80211_ELEMID_MESHRANN:
616		frm = hwmp_add_meshrann(frm,
617		    (struct ieee80211_meshrann_ie *)ie);
618		break;
619	}
620
621	m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
622	M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT);
623	if (m == NULL) {
624		ieee80211_free_node(ni);
625		vap->iv_stats.is_tx_nobuf++;
626		return ENOMEM;
627	}
628	ieee80211_send_setup(ni, m,
629	    IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_ACTION,
630	    IEEE80211_NONQOS_TID, sa, da, sa);
631
632	m->m_flags |= M_ENCAP;		/* mark encapsulated */
633	IEEE80211_NODE_STAT(ni, tx_mgmt);
634
635	memset(&params, 0, sizeof(params));
636	params.ibp_pri = WME_AC_VO;
637	params.ibp_rate0 = ni->ni_txparms->mgmtrate;
638	if (IEEE80211_IS_MULTICAST(da))
639		params.ibp_try0 = 1;
640	else
641		params.ibp_try0 = ni->ni_txparms->maxretry;
642	params.ibp_power = ni->ni_txpower;
643	return ic->ic_raw_xmit(ni, m, &params);
644}
645
646#define ADDSHORT(frm, v) do {		\
647	frm[0] = (v) & 0xff;		\
648	frm[1] = (v) >> 8;		\
649	frm += 2;			\
650} while (0)
651#define ADDWORD(frm, v) do {		\
652	LE_WRITE_4(frm, v);		\
653	frm += 4;			\
654} while (0)
655/*
656 * Add a Mesh Path Request IE to a frame.
657 */
658#define	PREQ_TFLAGS(n)	preq->preq_targets[n].target_flags
659#define	PREQ_TADDR(n)	preq->preq_targets[n].target_addr
660#define	PREQ_TSEQ(n)	preq->preq_targets[n].target_seq
661static uint8_t *
662hwmp_add_meshpreq(uint8_t *frm, const struct ieee80211_meshpreq_ie *preq)
663{
664	int i;
665
666	*frm++ = IEEE80211_ELEMID_MESHPREQ;
667	*frm++ = preq->preq_len;	/* len already calculated */
668	*frm++ = preq->preq_flags;
669	*frm++ = preq->preq_hopcount;
670	*frm++ = preq->preq_ttl;
671	ADDWORD(frm, preq->preq_id);
672	IEEE80211_ADDR_COPY(frm, preq->preq_origaddr); frm += 6;
673	ADDWORD(frm, preq->preq_origseq);
674	if (preq->preq_flags & IEEE80211_MESHPREQ_FLAGS_AE) {
675		IEEE80211_ADDR_COPY(frm, preq->preq_orig_ext_addr);
676		frm += 6;
677	}
678	ADDWORD(frm, preq->preq_lifetime);
679	ADDWORD(frm, preq->preq_metric);
680	*frm++ = preq->preq_tcount;
681	for (i = 0; i < preq->preq_tcount; i++) {
682		*frm++ = PREQ_TFLAGS(i);
683		IEEE80211_ADDR_COPY(frm, PREQ_TADDR(i));
684		frm += 6;
685		ADDWORD(frm, PREQ_TSEQ(i));
686	}
687	return frm;
688}
689#undef	PREQ_TFLAGS
690#undef	PREQ_TADDR
691#undef	PREQ_TSEQ
692
693/*
694 * Add a Mesh Path Reply IE to a frame.
695 */
696static uint8_t *
697hwmp_add_meshprep(uint8_t *frm, const struct ieee80211_meshprep_ie *prep)
698{
699	*frm++ = IEEE80211_ELEMID_MESHPREP;
700	*frm++ = prep->prep_len;	/* len already calculated */
701	*frm++ = prep->prep_flags;
702	*frm++ = prep->prep_hopcount;
703	*frm++ = prep->prep_ttl;
704	IEEE80211_ADDR_COPY(frm, prep->prep_targetaddr); frm += 6;
705	ADDWORD(frm, prep->prep_targetseq);
706	if (prep->prep_flags & IEEE80211_MESHPREP_FLAGS_AE) {
707		IEEE80211_ADDR_COPY(frm, prep->prep_target_ext_addr);
708		frm += 6;
709	}
710	ADDWORD(frm, prep->prep_lifetime);
711	ADDWORD(frm, prep->prep_metric);
712	IEEE80211_ADDR_COPY(frm, prep->prep_origaddr); frm += 6;
713	ADDWORD(frm, prep->prep_origseq);
714	return frm;
715}
716
717/*
718 * Add a Mesh Path Error IE to a frame.
719 */
720#define	PERR_DFLAGS(n)	perr->perr_dests[n].dest_flags
721#define	PERR_DADDR(n)	perr->perr_dests[n].dest_addr
722#define	PERR_DSEQ(n)	perr->perr_dests[n].dest_seq
723#define	PERR_EXTADDR(n)	perr->perr_dests[n].dest_ext_addr
724#define	PERR_DRCODE(n)	perr->perr_dests[n].dest_rcode
725static uint8_t *
726hwmp_add_meshperr(uint8_t *frm, const struct ieee80211_meshperr_ie *perr)
727{
728	int i;
729
730	*frm++ = IEEE80211_ELEMID_MESHPERR;
731	*frm++ = perr->perr_len;	/* len already calculated */
732	*frm++ = perr->perr_ttl;
733	*frm++ = perr->perr_ndests;
734	for (i = 0; i < perr->perr_ndests; i++) {
735		*frm++ = PERR_DFLAGS(i);
736		IEEE80211_ADDR_COPY(frm, PERR_DADDR(i));
737		frm += 6;
738		ADDWORD(frm, PERR_DSEQ(i));
739		if (PERR_DFLAGS(i) & IEEE80211_MESHPERR_FLAGS_AE) {
740			IEEE80211_ADDR_COPY(frm, PERR_EXTADDR(i));
741			frm += 6;
742		}
743		ADDSHORT(frm, PERR_DRCODE(i));
744	}
745	return frm;
746}
747#undef	PERR_DFLAGS
748#undef	PERR_DADDR
749#undef	PERR_DSEQ
750#undef	PERR_EXTADDR
751#undef	PERR_DRCODE
752
753/*
754 * Add a Root Annoucement IE to a frame.
755 */
756static uint8_t *
757hwmp_add_meshrann(uint8_t *frm, const struct ieee80211_meshrann_ie *rann)
758{
759	*frm++ = IEEE80211_ELEMID_MESHRANN;
760	*frm++ = rann->rann_len;
761	*frm++ = rann->rann_flags;
762	*frm++ = rann->rann_hopcount;
763	*frm++ = rann->rann_ttl;
764	IEEE80211_ADDR_COPY(frm, rann->rann_addr); frm += 6;
765	ADDWORD(frm, rann->rann_seq);
766	ADDWORD(frm, rann->rann_interval);
767	ADDWORD(frm, rann->rann_metric);
768	return frm;
769}
770
771static void
772hwmp_rootmode_setup(struct ieee80211vap *vap)
773{
774	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
775
776	switch (hs->hs_rootmode) {
777	case IEEE80211_HWMP_ROOTMODE_DISABLED:
778		callout_drain(&hs->hs_roottimer);
779		break;
780	case IEEE80211_HWMP_ROOTMODE_NORMAL:
781	case IEEE80211_HWMP_ROOTMODE_PROACTIVE:
782		callout_reset(&hs->hs_roottimer, ieee80211_hwmp_rootint,
783		    hwmp_rootmode_cb, vap);
784		break;
785	case IEEE80211_HWMP_ROOTMODE_RANN:
786		callout_reset(&hs->hs_roottimer, ieee80211_hwmp_rannint,
787		    hwmp_rootmode_rann_cb, vap);
788		break;
789	}
790}
791
792/*
793 * Send a broadcast Path Request to find all nodes on the mesh. We are
794 * called when the vap is configured as a HWMP root node.
795 */
796#define	PREQ_TFLAGS(n)	preq.preq_targets[n].target_flags
797#define	PREQ_TADDR(n)	preq.preq_targets[n].target_addr
798#define	PREQ_TSEQ(n)	preq.preq_targets[n].target_seq
799static void
800hwmp_rootmode_cb(void *arg)
801{
802	struct ieee80211vap *vap = (struct ieee80211vap *)arg;
803	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
804	struct ieee80211_mesh_state *ms = vap->iv_mesh;
805	struct ieee80211_meshpreq_ie preq;
806
807	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, vap->iv_bss,
808	    "%s", "send broadcast PREQ");
809
810	preq.preq_flags = IEEE80211_MESHPREQ_FLAGS_AM;
811	if (ms->ms_flags & IEEE80211_MESHFLAGS_PORTAL)
812		preq.preq_flags |= IEEE80211_MESHPREQ_FLAGS_PR;
813	if (hs->hs_rootmode == IEEE80211_HWMP_ROOTMODE_PROACTIVE)
814		preq.preq_flags |= IEEE80211_MESHPREQ_FLAGS_PP;
815	preq.preq_hopcount = 0;
816	preq.preq_ttl = ms->ms_ttl;
817	preq.preq_id = ++hs->hs_preqid;
818	IEEE80211_ADDR_COPY(preq.preq_origaddr, vap->iv_myaddr);
819	preq.preq_origseq = ++hs->hs_seq;
820	preq.preq_lifetime = ticks_to_msecs(ieee80211_hwmp_roottimeout);
821	preq.preq_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
822	preq.preq_tcount = 1;
823	IEEE80211_ADDR_COPY(PREQ_TADDR(0), broadcastaddr);
824	PREQ_TFLAGS(0) = IEEE80211_MESHPREQ_TFLAGS_TO |
825	    IEEE80211_MESHPREQ_TFLAGS_RF;
826	PREQ_TSEQ(0) = 0;
827	vap->iv_stats.is_hwmp_rootreqs++;
828	hwmp_send_preq(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &preq);
829	hwmp_rootmode_setup(vap);
830}
831#undef	PREQ_TFLAGS
832#undef	PREQ_TADDR
833#undef	PREQ_TSEQ
834
835/*
836 * Send a Root Annoucement (RANN) to find all the nodes on the mesh. We are
837 * called when the vap is configured as a HWMP RANN root node.
838 */
839static void
840hwmp_rootmode_rann_cb(void *arg)
841{
842	struct ieee80211vap *vap = (struct ieee80211vap *)arg;
843	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
844	struct ieee80211_mesh_state *ms = vap->iv_mesh;
845	struct ieee80211_meshrann_ie rann;
846
847	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, vap->iv_bss,
848	    "%s", "send broadcast RANN");
849
850	rann.rann_flags = 0;
851	if (ms->ms_flags & IEEE80211_MESHFLAGS_PORTAL)
852		rann.rann_flags |= IEEE80211_MESHRANN_FLAGS_PR;
853	rann.rann_hopcount = 0;
854	rann.rann_ttl = ms->ms_ttl;
855	IEEE80211_ADDR_COPY(rann.rann_addr, vap->iv_myaddr);
856	rann.rann_seq = ++hs->hs_seq;
857	rann.rann_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
858
859	vap->iv_stats.is_hwmp_rootrann++;
860	hwmp_send_rann(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &rann);
861	hwmp_rootmode_setup(vap);
862}
863
864#define	PREQ_TFLAGS(n)	preq->preq_targets[n].target_flags
865#define	PREQ_TADDR(n)	preq->preq_targets[n].target_addr
866#define	PREQ_TSEQ(n)	preq->preq_targets[n].target_seq
867static void
868hwmp_recv_preq(struct ieee80211vap *vap, struct ieee80211_node *ni,
869    const struct ieee80211_frame *wh, const struct ieee80211_meshpreq_ie *preq)
870{
871	struct ieee80211_mesh_state *ms = vap->iv_mesh;
872	struct ieee80211_mesh_route *rt = NULL;
873	struct ieee80211_mesh_route *rtorig = NULL;
874	struct ieee80211_mesh_route *rttarg = NULL;
875	struct ieee80211_hwmp_route *hrorig = NULL;
876	struct ieee80211_hwmp_route *hrtarg = NULL;
877	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
878	struct ieee80211_meshprep_ie prep;
879
880	if (ni == vap->iv_bss ||
881	    ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED)
882		return;
883	/*
884	 * Ignore PREQs from us. Could happen because someone forward it
885	 * back to us.
886	 */
887	if (IEEE80211_ADDR_EQ(vap->iv_myaddr, preq->preq_origaddr))
888		return;
889
890	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
891	    "received PREQ, source %6D", preq->preq_origaddr, ":");
892
893	/*
894	 * Acceptance criteria: if the PREQ is not for us or not broadcast
895	 * AND forwarding is disabled, discard this PREQ.
896	 * XXX: need to check PROXY
897	 */
898	if ((!IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0)) ||
899	    !IEEE80211_IS_MULTICAST(PREQ_TADDR(0))) &&
900	    !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) {
901		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_HWMP,
902		    preq->preq_origaddr, NULL, "%s", "not accepting PREQ");
903		return;
904	}
905	/*
906	 * Acceptance criteria: if unicast addressed
907	 * AND no valid forwarding for Target of PREQ, discard this PREQ.
908	 */
909	rttarg = ieee80211_mesh_rt_find(vap, PREQ_TADDR(0));
910	if(rttarg != NULL)
911		hrtarg = IEEE80211_MESH_ROUTE_PRIV(rttarg,
912		    struct ieee80211_hwmp_route);
913	/* Address mode: ucast */
914	if((preq->preq_flags & IEEE80211_MESHPREQ_FLAGS_AM) == 0 &&
915	    rttarg == NULL &&
916	    !IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0))) {
917		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_HWMP,
918		    preq->preq_origaddr, NULL,
919		    "unicast addressed PREQ of unknown target %6D",
920		    PREQ_TADDR(0), ":");
921		return;
922	}
923
924	/* PREQ ACCEPTED */
925
926	rtorig = ieee80211_mesh_rt_find(vap, preq->preq_origaddr);
927	if (rtorig == NULL) {
928		rtorig = ieee80211_mesh_rt_add(vap, preq->preq_origaddr);
929		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
930		    "adding originator %6D", preq->preq_origaddr, ":");
931	}
932	if (rtorig == NULL) {
933		/* XXX stat */
934		return;
935	}
936	hrorig = IEEE80211_MESH_ROUTE_PRIV(rtorig, struct ieee80211_hwmp_route);
937
938	/* Data creation and update of forwarding information
939	 * according to Table 11C-8 for originator mesh STA.
940	 */
941	if(HWMP_SEQ_GT(preq->preq_origseq, hrorig->hr_seq) ||
942	    (HWMP_SEQ_EQ(preq->preq_origseq, hrorig->hr_seq) &&
943	    preq->preq_metric < rtorig->rt_metric)) {
944		hrorig->hr_seq = preq->preq_origseq;
945		IEEE80211_ADDR_COPY(rtorig->rt_nexthop, wh->i_addr2);
946		rtorig->rt_metric = preq->preq_metric +
947			ms->ms_pmetric->mpm_metric(ni);
948		rtorig->rt_nhops  = preq->preq_hopcount + 1;
949		rtorig->rt_lifetime  = MESH_ROUTE_LIFETIME_MAX(
950		    preq->preq_lifetime, rtorig->rt_lifetime);
951		/* path to orig is valid now */
952		rtorig->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID;
953	}else if(hrtarg != NULL &&
954		HWMP_SEQ_EQ(hrtarg->hr_seq, PREQ_TSEQ(0)) &&
955		(rtorig->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0) {
956		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
957		    "discard PREQ from %6D, old seq no %u <= %u",
958		    preq->preq_origaddr, ":",
959		    preq->preq_origseq, hrorig->hr_seq);
960		return;
961	}
962
963	/*
964	 * Forwarding information for transmitter mesh STA
965	 * [OPTIONAL: if metric improved]
966	 */
967
968	/*
969	 * Check if the PREQ is addressed to us.
970	 */
971	if (IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0))) {
972		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
973		    "reply to %6D", preq->preq_origaddr, ":");
974		/*
975		 * Build and send a PREP frame.
976		 */
977		prep.prep_flags = 0;
978		prep.prep_hopcount = 0;
979		prep.prep_ttl = ms->ms_ttl;
980		IEEE80211_ADDR_COPY(prep.prep_targetaddr, vap->iv_myaddr);
981		prep.prep_targetseq = ++hs->hs_seq;
982		prep.prep_lifetime = preq->preq_lifetime;
983		prep.prep_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
984		IEEE80211_ADDR_COPY(prep.prep_origaddr, preq->preq_origaddr);
985		prep.prep_origseq = preq->preq_origseq;
986		hwmp_send_prep(ni, vap->iv_myaddr, wh->i_addr2, &prep);
987		/*
988		 * Build the reverse path, if we don't have it already.
989		 */
990		rt = ieee80211_mesh_rt_find(vap, preq->preq_origaddr);
991		if (rt == NULL)
992			hwmp_discover(vap, preq->preq_origaddr, NULL);
993		else if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0)
994			hwmp_discover(vap, rt->rt_dest, NULL);
995		return;
996	}
997	/*
998	 * Proactive PREQ: reply with a proactive PREP to the
999	 * root STA if requested.
1000	 */
1001	if (IEEE80211_ADDR_EQ(PREQ_TADDR(0), broadcastaddr) &&
1002	    (PREQ_TFLAGS(0) &
1003	    ((IEEE80211_MESHPREQ_TFLAGS_TO|IEEE80211_MESHPREQ_TFLAGS_RF) ==
1004	    (IEEE80211_MESHPREQ_TFLAGS_TO|IEEE80211_MESHPREQ_TFLAGS_RF)))) {
1005		uint8_t rootmac[IEEE80211_ADDR_LEN];
1006
1007		IEEE80211_ADDR_COPY(rootmac, preq->preq_origaddr);
1008		rt = ieee80211_mesh_rt_find(vap, rootmac);
1009		if (rt == NULL) {
1010			rt = ieee80211_mesh_rt_add(vap, rootmac);
1011			if (rt == NULL) {
1012				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1013				    "unable to add root mesh path to %6D",
1014				    rootmac, ":");
1015				vap->iv_stats.is_mesh_rtaddfailed++;
1016				return;
1017			}
1018		}
1019		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1020		    "root mesh station @ %6D", rootmac, ":");
1021
1022		/*
1023		 * Reply with a PREP if we don't have a path to the root
1024		 * or if the root sent us a proactive PREQ.
1025		 */
1026		if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0 ||
1027		    (preq->preq_flags & IEEE80211_MESHPREQ_FLAGS_PP)) {
1028			prep.prep_flags = 0;
1029			prep.prep_hopcount = 0;
1030			prep.prep_ttl = ms->ms_ttl;
1031			IEEE80211_ADDR_COPY(prep.prep_origaddr, rootmac);
1032			prep.prep_origseq = preq->preq_origseq;
1033			prep.prep_lifetime = preq->preq_lifetime;
1034			prep.prep_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
1035			IEEE80211_ADDR_COPY(prep.prep_targetaddr,
1036			    vap->iv_myaddr);
1037			prep.prep_targetseq = ++hs->hs_seq;
1038			hwmp_send_prep(vap->iv_bss, vap->iv_myaddr,
1039			    broadcastaddr, &prep);
1040		}
1041		hwmp_discover(vap, rootmac, NULL);
1042		return;
1043	}
1044	rt = ieee80211_mesh_rt_find(vap, PREQ_TADDR(0));
1045
1046	/*
1047	 * Forwarding and Intermediate reply for PREQs with 1 target.
1048	 */
1049	if (preq->preq_tcount == 1) {
1050		struct ieee80211_meshpreq_ie ppreq; /* propagated PREQ */
1051
1052		memcpy(&ppreq, preq, sizeof(ppreq));
1053		/*
1054		 * We have a valid route to this node.
1055		 */
1056		if (rt != NULL &&
1057		    (rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID)) {
1058			if (preq->preq_ttl > 1 &&
1059			    preq->preq_hopcount < hs->hs_maxhops) {
1060				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1061				    "forward PREQ from %6D",
1062				    preq->preq_origaddr, ":");
1063				/*
1064				 * Propagate the original PREQ.
1065				 * PREQ is unicast now to rt->rt_nexthop
1066				 */
1067				ppreq.preq_flags &=
1068				    ~IEEE80211_MESHPREQ_FLAGS_AM;
1069				ppreq.preq_hopcount += 1;
1070				ppreq.preq_ttl -= 1;
1071				ppreq.preq_metric +=
1072				    ms->ms_pmetric->mpm_metric(ni);
1073				/*
1074				 * Set TO and unset RF bits because we are
1075				 * going to send a PREP next.
1076				 */
1077				ppreq.preq_targets[0].target_flags |=
1078				    IEEE80211_MESHPREQ_TFLAGS_TO;
1079				ppreq.preq_targets[0].target_flags &=
1080				    ~IEEE80211_MESHPREQ_TFLAGS_RF;
1081				hwmp_send_preq(ni, vap->iv_myaddr,
1082				    rt->rt_nexthop, &ppreq);
1083			}
1084			/*
1085			 * Check if we can send an intermediate Path Reply,
1086			 * i.e., Target Only bit is not set.
1087			 */
1088	    		if (!(PREQ_TFLAGS(0) & IEEE80211_MESHPREQ_TFLAGS_TO)) {
1089				struct ieee80211_meshprep_ie prep;
1090
1091				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1092				    "intermediate reply for PREQ from %6D",
1093				    preq->preq_origaddr, ":");
1094				prep.prep_flags = 0;
1095				prep.prep_hopcount = rt->rt_nhops + 1;
1096				prep.prep_ttl = ms->ms_ttl;
1097				IEEE80211_ADDR_COPY(&prep.prep_targetaddr,
1098				    PREQ_TADDR(0));
1099				prep.prep_targetseq = hrorig->hr_seq;
1100				prep.prep_lifetime = preq->preq_lifetime;
1101				prep.prep_metric = rt->rt_metric +
1102				    ms->ms_pmetric->mpm_metric(ni);
1103				IEEE80211_ADDR_COPY(&prep.prep_origaddr,
1104				    preq->preq_origaddr);
1105				prep.prep_origseq = hrorig->hr_seq;
1106				hwmp_send_prep(ni, vap->iv_myaddr,
1107				    broadcastaddr, &prep);
1108			}
1109		/*
1110		 * We have no information about this path,
1111		 * propagate the PREQ.
1112		 */
1113		} else if (preq->preq_ttl > 1 &&
1114		    preq->preq_hopcount < hs->hs_maxhops) {
1115			if (rt == NULL) {
1116				rt = ieee80211_mesh_rt_add(vap, PREQ_TADDR(0));
1117				if (rt == NULL) {
1118					IEEE80211_NOTE(vap,
1119					    IEEE80211_MSG_HWMP, ni,
1120					    "unable to add PREQ path to %6D",
1121					    PREQ_TADDR(0), ":");
1122					vap->iv_stats.is_mesh_rtaddfailed++;
1123					return;
1124				}
1125			}
1126			rt->rt_metric = preq->preq_metric;
1127			rt->rt_lifetime = preq->preq_lifetime;
1128			hrorig = IEEE80211_MESH_ROUTE_PRIV(rt,
1129			    struct ieee80211_hwmp_route);
1130			hrorig->hr_seq = preq->preq_origseq;
1131			hrorig->hr_preqid = preq->preq_id;
1132
1133			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1134			    "forward PREQ from %6D",
1135			    preq->preq_origaddr, ":");
1136			ppreq.preq_hopcount += 1;
1137			ppreq.preq_ttl -= 1;
1138			ppreq.preq_metric += ms->ms_pmetric->mpm_metric(ni);
1139			hwmp_send_preq(ni, vap->iv_myaddr, broadcastaddr,
1140			    &ppreq);
1141		}
1142	}
1143}
1144#undef	PREQ_TFLAGS
1145#undef	PREQ_TADDR
1146#undef	PREQ_TSEQ
1147
1148static int
1149hwmp_send_preq(struct ieee80211_node *ni,
1150    const uint8_t sa[IEEE80211_ADDR_LEN],
1151    const uint8_t da[IEEE80211_ADDR_LEN],
1152    struct ieee80211_meshpreq_ie *preq)
1153{
1154	struct ieee80211_hwmp_state *hs = ni->ni_vap->iv_hwmp;
1155
1156	/*
1157	 * Enforce PREQ interval.
1158	 */
1159	if (ratecheck(&hs->hs_lastpreq, &ieee80211_hwmp_preqminint) == 0)
1160		return EALREADY;
1161	getmicrouptime(&hs->hs_lastpreq);
1162
1163	/*
1164	 * mesh preq action frame format
1165	 *     [6] da
1166	 *     [6] sa
1167	 *     [6] addr3 = sa
1168	 *     [1] action
1169	 *     [1] category
1170	 *     [tlv] mesh path request
1171	 */
1172	preq->preq_ie = IEEE80211_ELEMID_MESHPREQ;
1173	preq->preq_len = (preq->preq_flags & IEEE80211_MESHPREQ_FLAGS_AE ?
1174	    IEEE80211_MESHPREQ_BASE_SZ_AE : IEEE80211_MESHPREQ_BASE_SZ) +
1175	    preq->preq_tcount * IEEE80211_MESHPREQ_TRGT_SZ;
1176	return hwmp_send_action(ni, sa, da, (uint8_t *)preq, preq->preq_len+2);
1177}
1178
1179static void
1180hwmp_recv_prep(struct ieee80211vap *vap, struct ieee80211_node *ni,
1181    const struct ieee80211_frame *wh, const struct ieee80211_meshprep_ie *prep)
1182{
1183	struct ieee80211_mesh_state *ms = vap->iv_mesh;
1184	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
1185	struct ieee80211_mesh_route *rt = NULL;
1186	struct ieee80211_hwmp_route *hr;
1187	struct ieee80211com *ic = vap->iv_ic;
1188	struct ifnet *ifp = vap->iv_ifp;
1189	struct mbuf *m, *next;
1190	uint32_t metric = 0;
1191
1192	/*
1193	 * Acceptance criteria: if the corresponding PREQ was not generated
1194	 * by us and forwarding is disabled, discard this PREP.
1195	 */
1196	if (ni == vap->iv_bss ||
1197	    ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED)
1198		return;
1199	if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, prep->prep_origaddr) &&
1200	    !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD))
1201		return;
1202
1203	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1204	    "received PREP from %6D", prep->prep_targetaddr, ":");
1205
1206	rt = ieee80211_mesh_rt_find(vap, prep->prep_targetaddr);
1207	if (rt == NULL) {
1208		/*
1209		 * If we have no entry this could be a reply to a root PREQ.
1210		 * XXX: not true anymore cause we dont create entry for target
1211		 *  when propagating PREQs like the old code did.
1212		 */
1213		if (hs->hs_rootmode != IEEE80211_HWMP_ROOTMODE_DISABLED) {
1214			rt = ieee80211_mesh_rt_add(vap, prep->prep_targetaddr);
1215			if (rt == NULL) {
1216				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP,
1217				    ni, "unable to add PREP path to %6D",
1218				    prep->prep_targetaddr, ":");
1219				vap->iv_stats.is_mesh_rtaddfailed++;
1220				return;
1221			}
1222			IEEE80211_ADDR_COPY(rt->rt_nexthop, wh->i_addr2);
1223			rt->rt_nhops = prep->prep_hopcount;
1224			rt->rt_lifetime = prep->prep_lifetime;
1225			rt->rt_metric = prep->prep_metric;
1226			rt->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID;
1227			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1228			    "add root path to %6D nhops %d metric %lu (PREP)",
1229			    prep->prep_targetaddr, ":",
1230			    rt->rt_nhops, rt->rt_metric);
1231			return;
1232		}
1233		return;
1234	}
1235	/*
1236	 * Sequence number validation.
1237	 */
1238	hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
1239	if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID)) {
1240		if (HWMP_SEQ_LT(prep->prep_targetseq, hr->hr_seq)) {
1241			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1242			    "discard PREP from %6D, old seq no %u < %u",
1243			    prep->prep_targetaddr, ":",
1244			    prep->prep_targetseq, hr->hr_seq);
1245			return;
1246		} else if (HWMP_SEQ_LEQ(prep->prep_targetseq, hr->hr_seq) &&
1247		    prep->prep_metric > rt->rt_metric) {
1248			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1249			    "discard PREP from %6D, new metric %u > %u",
1250			    prep->prep_targetaddr, ":",
1251			    prep->prep_metric, rt->rt_metric);
1252			return;
1253		}
1254	}
1255
1256	hr->hr_seq = prep->prep_targetseq;
1257	/*
1258	 * If it's NOT for us, propagate the PREP.
1259	 */
1260	if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, prep->prep_origaddr) &&
1261	    prep->prep_ttl > 1 && prep->prep_hopcount < hs->hs_maxhops) {
1262		struct ieee80211_meshprep_ie pprep; /* propagated PREP */
1263		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1264		    "propagate PREP from %6D",
1265		    prep->prep_targetaddr, ":");
1266
1267		memcpy(&pprep, prep, sizeof(pprep));
1268		pprep.prep_hopcount += 1;
1269		pprep.prep_ttl -= 1;
1270		pprep.prep_metric += ms->ms_pmetric->mpm_metric(ni);
1271		hwmp_send_prep(ni, vap->iv_myaddr, broadcastaddr, &pprep);
1272	}
1273	hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
1274	if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY) {
1275		/* NB: never clobber a proxy entry */;
1276		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1277		    "discard PREP for %6D, route is marked PROXY",
1278		    prep->prep_targetaddr, ":");
1279		vap->iv_stats.is_hwmp_proxy++;
1280	/* NB: first path discovery always fails */
1281	} else if (hr->hr_origseq == 0 ||
1282	    prep->prep_origseq == hr->hr_origseq) {
1283		/*
1284		 * Check if we already have a path to this node.
1285		 * If we do, check if this path reply contains a
1286		 * better route.
1287		 */
1288		if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0 ||
1289		    (prep->prep_hopcount < rt->rt_nhops ||
1290		     prep->prep_metric < rt->rt_metric)) {
1291			hr->hr_origseq = prep->prep_origseq;
1292			metric = prep->prep_metric +
1293			    ms->ms_pmetric->mpm_metric(ni);
1294			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1295			    "%s path to %6D, hopcount %d:%d metric %d:%d",
1296			    rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID ?
1297				"prefer" : "update",
1298			    prep->prep_origaddr, ":",
1299			    rt->rt_nhops, prep->prep_hopcount,
1300			    rt->rt_metric, prep->prep_metric);
1301			IEEE80211_ADDR_COPY(rt->rt_nexthop, wh->i_addr2);
1302			rt->rt_nhops = prep->prep_hopcount + 1;
1303			rt->rt_lifetime = prep->prep_lifetime;
1304			rt->rt_metric = metric;
1305			rt->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID;
1306		} else {
1307			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1308			    "ignore PREP for %6D, hopcount %d:%d metric %d:%d",
1309			    prep->prep_targetaddr, ":",
1310			    rt->rt_nhops, prep->prep_hopcount,
1311			    rt->rt_metric, prep->prep_metric);
1312		}
1313	} else {
1314		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1315		    "discard PREP for %6D, wrong orig seqno %u != %u",
1316		    prep->prep_targetaddr, ":", prep->prep_origseq,
1317		    hr->hr_origseq);
1318		vap->iv_stats.is_hwmp_wrongseq++;
1319	}
1320	/*
1321	 * Check for frames queued awaiting path discovery.
1322	 * XXX probably can tell exactly and avoid remove call
1323	 * NB: hash may have false matches, if so they will get
1324	 *     stuck back on the stageq because there won't be
1325	 *     a path.
1326	 */
1327	m = ieee80211_ageq_remove(&ic->ic_stageq,
1328	    (struct ieee80211_node *)(uintptr_t)
1329	    ieee80211_mac_hash(ic, rt->rt_dest));
1330	for (; m != NULL; m = next) {
1331		next = m->m_nextpkt;
1332		m->m_nextpkt = NULL;
1333		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1334		    "flush queued frame %p len %d", m, m->m_pkthdr.len);
1335		ifp->if_transmit(ifp, m);
1336	}
1337}
1338
1339static int
1340hwmp_send_prep(struct ieee80211_node *ni,
1341    const uint8_t sa[IEEE80211_ADDR_LEN],
1342    const uint8_t da[IEEE80211_ADDR_LEN],
1343    struct ieee80211_meshprep_ie *prep)
1344{
1345	/* NB: there's no PREP minimum interval. */
1346
1347	/*
1348	 * mesh prep action frame format
1349	 *     [6] da
1350	 *     [6] sa
1351	 *     [6] addr3 = sa
1352	 *     [1] action
1353	 *     [1] category
1354	 *     [tlv] mesh path reply
1355	 */
1356	prep->prep_ie = IEEE80211_ELEMID_MESHPREP;
1357	prep->prep_len = prep->prep_flags & IEEE80211_MESHPREP_FLAGS_AE ?
1358	    IEEE80211_MESHPREP_BASE_SZ_AE : IEEE80211_MESHPREP_BASE_SZ;
1359	return hwmp_send_action(ni, sa, da, (uint8_t *)prep,
1360	    prep->prep_len + 2);
1361}
1362
1363#define	PERR_DFLAGS(n)	perr.perr_dests[n].dest_flags
1364#define	PERR_DADDR(n)	perr.perr_dests[n].dest_addr
1365#define	PERR_DSEQ(n)	perr.perr_dests[n].dest_seq
1366#define	PERR_DRCODE(n)	perr.perr_dests[n].dest_rcode
1367static void
1368hwmp_peerdown(struct ieee80211_node *ni)
1369{
1370	struct ieee80211vap *vap = ni->ni_vap;
1371	struct ieee80211_mesh_state *ms = vap->iv_mesh;
1372	struct ieee80211_meshperr_ie perr;
1373	struct ieee80211_mesh_route *rt;
1374	struct ieee80211_hwmp_route *hr;
1375
1376	rt = ieee80211_mesh_rt_find(vap, ni->ni_macaddr);
1377	if (rt == NULL)
1378		return;
1379	hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
1380	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1381	    "%s", "delete route entry");
1382	perr.perr_ttl = ms->ms_ttl;
1383	perr.perr_ndests = 1;
1384	PERR_DFLAGS(0) = 0;
1385	if (hr->hr_seq == 0)
1386		PERR_DFLAGS(0) |= IEEE80211_MESHPERR_DFLAGS_USN;
1387	PERR_DFLAGS(0) |= IEEE80211_MESHPERR_DFLAGS_RC;
1388	IEEE80211_ADDR_COPY(PERR_DADDR(0), rt->rt_dest);
1389	PERR_DSEQ(0) = hr->hr_seq;
1390	PERR_DRCODE(0) = IEEE80211_REASON_MESH_PERR_DEST_UNREACH;
1391	/* NB: flush everything passing through peer */
1392	ieee80211_mesh_rt_flush_peer(vap, ni->ni_macaddr);
1393	hwmp_send_perr(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &perr);
1394}
1395#undef	PERR_DFLAGS
1396#undef	PERR_DADDR
1397#undef	PERR_DSEQ
1398#undef	PERR_DRCODE
1399
1400#define	PERR_DFLAGS(n)	perr->perr_dests[n].dest_flags
1401#define	PERR_DADDR(n)	perr->perr_dests[n].dest_addr
1402#define	PERR_DSEQ(n)	perr->perr_dests[n].dest_seq
1403#define	PERR_DRCODE(n)	perr->perr_dests[n].dest_rcode
1404static void
1405hwmp_recv_perr(struct ieee80211vap *vap, struct ieee80211_node *ni,
1406    const struct ieee80211_frame *wh, const struct ieee80211_meshperr_ie *perr)
1407{
1408	struct ieee80211_mesh_state *ms = vap->iv_mesh;
1409	struct ieee80211_mesh_route *rt = NULL;
1410	struct ieee80211_hwmp_route *hr;
1411	struct ieee80211_meshperr_ie pperr;
1412	int i, forward = 0;
1413
1414	/*
1415	 * Acceptance criteria: check if we received a PERR from a
1416	 * neighbor and forwarding is enabled.
1417	 */
1418	if (ni == vap->iv_bss ||
1419	    ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED ||
1420	    !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD))
1421		return;
1422	/*
1423	 * Find all routing entries that match and delete them.
1424	 */
1425	for (i = 0; i < perr->perr_ndests; i++) {
1426		rt = ieee80211_mesh_rt_find(vap, PERR_DADDR(i));
1427		if (rt == NULL)
1428			continue;
1429		hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
1430		if (!(PERR_DFLAGS(0) & IEEE80211_MESHPERR_DFLAGS_USN) &&
1431		    HWMP_SEQ_GEQ(PERR_DSEQ(i), hr->hr_seq)) {
1432			ieee80211_mesh_rt_del(vap, rt->rt_dest);
1433			ieee80211_mesh_rt_flush_peer(vap, rt->rt_dest);
1434			rt = NULL;
1435			forward = 1;
1436		}
1437	}
1438	/*
1439	 * Propagate the PERR if we previously found it on our routing table.
1440	 * XXX handle ndest > 1
1441	 */
1442	if (forward && perr->perr_ttl > 1) {
1443		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1444		    "propagate PERR from %6D", wh->i_addr2, ":");
1445		memcpy(&pperr, perr, sizeof(*perr));
1446		pperr.perr_ttl--;
1447		hwmp_send_perr(vap->iv_bss, vap->iv_myaddr, broadcastaddr,
1448		    &pperr);
1449	}
1450}
1451#undef	PERR_DFLAGS
1452#undef	PEER_DADDR
1453#undef	PERR_DSEQ
1454#undef	PERR_DRCODE
1455
1456static int
1457hwmp_send_perr(struct ieee80211_node *ni,
1458    const uint8_t sa[IEEE80211_ADDR_LEN],
1459    const uint8_t da[IEEE80211_ADDR_LEN],
1460    struct ieee80211_meshperr_ie *perr)
1461{
1462	struct ieee80211_hwmp_state *hs = ni->ni_vap->iv_hwmp;
1463	int i;
1464	uint8_t length = 0;
1465
1466	/*
1467	 * Enforce PERR interval.
1468	 */
1469	if (ratecheck(&hs->hs_lastperr, &ieee80211_hwmp_perrminint) == 0)
1470		return EALREADY;
1471	getmicrouptime(&hs->hs_lastperr);
1472
1473	/*
1474	 * mesh perr action frame format
1475	 *     [6] da
1476	 *     [6] sa
1477	 *     [6] addr3 = sa
1478	 *     [1] action
1479	 *     [1] category
1480	 *     [tlv] mesh path error
1481	 */
1482	perr->perr_ie = IEEE80211_ELEMID_MESHPERR;
1483	length = IEEE80211_MESHPERR_BASE_SZ;
1484	for (i = 0; i<perr->perr_ndests; i++) {
1485		if (perr->perr_dests[i].dest_flags &
1486		    IEEE80211_MESHPERR_FLAGS_AE) {
1487			length += IEEE80211_MESHPERR_DEST_SZ_AE;
1488			continue ;
1489		}
1490		length += IEEE80211_MESHPERR_DEST_SZ;
1491	}
1492	perr->perr_len =length;
1493	return hwmp_send_action(ni, sa, da, (uint8_t *)perr, perr->perr_len+2);
1494}
1495
1496static void
1497hwmp_recv_rann(struct ieee80211vap *vap, struct ieee80211_node *ni,
1498    const struct ieee80211_frame *wh, const struct ieee80211_meshrann_ie *rann)
1499{
1500	struct ieee80211_mesh_state *ms = vap->iv_mesh;
1501	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
1502	struct ieee80211_mesh_route *rt = NULL;
1503	struct ieee80211_hwmp_route *hr;
1504	struct ieee80211_meshrann_ie prann;
1505
1506	if (ni == vap->iv_bss ||
1507	    ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED ||
1508	    IEEE80211_ADDR_EQ(rann->rann_addr, vap->iv_myaddr))
1509		return;
1510
1511	rt = ieee80211_mesh_rt_find(vap, rann->rann_addr);
1512	/*
1513	 * Discover the path to the root mesh STA.
1514	 * If we already know it, propagate the RANN element.
1515	 */
1516	if (rt == NULL) {
1517		hwmp_discover(vap, rann->rann_addr, NULL);
1518		return;
1519	}
1520	hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
1521	if (HWMP_SEQ_GT(rann->rann_seq, hr->hr_seq)) {
1522		hr->hr_seq = rann->rann_seq;
1523		if (rann->rann_ttl > 1 &&
1524		    rann->rann_hopcount < hs->hs_maxhops &&
1525		    (ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) {
1526			memcpy(&prann, rann, sizeof(prann));
1527			prann.rann_hopcount += 1;
1528			prann.rann_ttl -= 1;
1529			prann.rann_metric += ms->ms_pmetric->mpm_metric(ni);
1530			hwmp_send_rann(vap->iv_bss, vap->iv_myaddr,
1531			    broadcastaddr, &prann);
1532		}
1533	}
1534}
1535
1536static int
1537hwmp_send_rann(struct ieee80211_node *ni,
1538    const uint8_t sa[IEEE80211_ADDR_LEN],
1539    const uint8_t da[IEEE80211_ADDR_LEN],
1540    struct ieee80211_meshrann_ie *rann)
1541{
1542	/*
1543	 * mesh rann action frame format
1544	 *     [6] da
1545	 *     [6] sa
1546	 *     [6] addr3 = sa
1547	 *     [1] action
1548	 *     [1] category
1549	 *     [tlv] root annoucement
1550	 */
1551	rann->rann_ie = IEEE80211_ELEMID_MESHRANN;
1552	rann->rann_len = IEEE80211_MESHRANN_BASE_SZ;
1553	return hwmp_send_action(ni, sa, da, (uint8_t *)rann,
1554	    rann->rann_len + 2);
1555}
1556
1557#define	PREQ_TFLAGS(n)	preq.preq_targets[n].target_flags
1558#define	PREQ_TADDR(n)	preq.preq_targets[n].target_addr
1559#define	PREQ_TSEQ(n)	preq.preq_targets[n].target_seq
1560static struct ieee80211_node *
1561hwmp_discover(struct ieee80211vap *vap,
1562    const uint8_t dest[IEEE80211_ADDR_LEN], struct mbuf *m)
1563{
1564	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
1565	struct ieee80211_mesh_state *ms = vap->iv_mesh;
1566	struct ieee80211_mesh_route *rt = NULL;
1567	struct ieee80211_hwmp_route *hr;
1568	struct ieee80211_meshpreq_ie preq;
1569	struct ieee80211_node *ni;
1570	int sendpreq = 0;
1571
1572	KASSERT(vap->iv_opmode == IEEE80211_M_MBSS,
1573	    ("not a mesh vap, opmode %d", vap->iv_opmode));
1574
1575	KASSERT(!IEEE80211_ADDR_EQ(vap->iv_myaddr, dest),
1576	    ("%s: discovering self!", __func__));
1577
1578	ni = NULL;
1579	if (!IEEE80211_IS_MULTICAST(dest)) {
1580		rt = ieee80211_mesh_rt_find(vap, dest);
1581		if (rt == NULL) {
1582			rt = ieee80211_mesh_rt_add(vap, dest);
1583			if (rt == NULL) {
1584				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP,
1585				    ni, "unable to add discovery path to %6D",
1586				    dest, ":");
1587				vap->iv_stats.is_mesh_rtaddfailed++;
1588				goto done;
1589			}
1590		}
1591		hr = IEEE80211_MESH_ROUTE_PRIV(rt,
1592		    struct ieee80211_hwmp_route);
1593		if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0) {
1594			if (hr->hr_origseq == 0)
1595				hr->hr_origseq = ++hs->hs_seq;
1596			rt->rt_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
1597			rt->rt_lifetime =
1598			    ticks_to_msecs(ieee80211_hwmp_pathtimeout);
1599			/* XXX check preq retries */
1600			sendpreq = 1;
1601			IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP, dest,
1602			    "start path discovery (src %s), target seq %u",
1603			    m == NULL ? "<none>" : ether_sprintf(
1604			    mtod(m, struct ether_header *)->ether_shost),
1605			    hr->hr_seq);
1606			/*
1607			 * Try to discover the path for this node.
1608			 * Group addressed PREQ Case A
1609			 */
1610			preq.preq_flags = IEEE80211_MESHPREQ_FLAGS_AM;
1611			preq.preq_hopcount = 0;
1612			preq.preq_ttl = ms->ms_ttl;
1613			preq.preq_id = ++hs->hs_preqid;
1614			IEEE80211_ADDR_COPY(preq.preq_origaddr, vap->iv_myaddr);
1615			preq.preq_origseq = hr->hr_origseq;
1616			preq.preq_lifetime = rt->rt_lifetime;
1617			preq.preq_metric = rt->rt_metric;
1618			preq.preq_tcount = 1;
1619			IEEE80211_ADDR_COPY(PREQ_TADDR(0), dest);
1620			PREQ_TFLAGS(0) = 0;
1621			if (ieee80211_hwmp_targetonly)
1622				PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_TO;
1623			if (ieee80211_hwmp_replyforward)
1624				PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_RF;
1625			PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_USN;
1626			PREQ_TSEQ(0) = hr->hr_seq;
1627			/* XXX check return value */
1628			hwmp_send_preq(vap->iv_bss, vap->iv_myaddr,
1629			    broadcastaddr, &preq);
1630		}
1631		if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID)
1632			ni = ieee80211_find_txnode(vap, rt->rt_nexthop);
1633	} else {
1634		ni = ieee80211_find_txnode(vap, dest);
1635		/* NB: if null then we leak mbuf */
1636		KASSERT(ni != NULL, ("leak mcast frame"));
1637		return ni;
1638	}
1639done:
1640	if (ni == NULL && m != NULL) {
1641		if (sendpreq) {
1642			struct ieee80211com *ic = vap->iv_ic;
1643			/*
1644			 * Queue packet for transmit when path discovery
1645			 * completes.  If discovery never completes the
1646			 * frame will be flushed by way of the aging timer.
1647			 */
1648			IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP, dest,
1649			    "%s", "queue frame until path found");
1650			m->m_pkthdr.rcvif = (void *)(uintptr_t)
1651			    ieee80211_mac_hash(ic, dest);
1652			/* XXX age chosen randomly */
1653			ieee80211_ageq_append(&ic->ic_stageq, m,
1654			    IEEE80211_INACT_WAIT);
1655		} else {
1656			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_HWMP,
1657			    dest, NULL, "%s", "no valid path to this node");
1658			m_freem(m);
1659		}
1660	}
1661	return ni;
1662}
1663#undef	PREQ_TFLAGS
1664#undef	PREQ_TADDR
1665#undef	PREQ_TSEQ
1666
1667static int
1668hwmp_ioctl_get80211(struct ieee80211vap *vap, struct ieee80211req *ireq)
1669{
1670	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
1671	int error;
1672
1673	if (vap->iv_opmode != IEEE80211_M_MBSS)
1674		return ENOSYS;
1675	error = 0;
1676	switch (ireq->i_type) {
1677	case IEEE80211_IOC_HWMP_ROOTMODE:
1678		ireq->i_val = hs->hs_rootmode;
1679		break;
1680	case IEEE80211_IOC_HWMP_MAXHOPS:
1681		ireq->i_val = hs->hs_maxhops;
1682		break;
1683	default:
1684		return ENOSYS;
1685	}
1686	return error;
1687}
1688IEEE80211_IOCTL_GET(hwmp, hwmp_ioctl_get80211);
1689
1690static int
1691hwmp_ioctl_set80211(struct ieee80211vap *vap, struct ieee80211req *ireq)
1692{
1693	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
1694	int error;
1695
1696	if (vap->iv_opmode != IEEE80211_M_MBSS)
1697		return ENOSYS;
1698	error = 0;
1699	switch (ireq->i_type) {
1700	case IEEE80211_IOC_HWMP_ROOTMODE:
1701		if (ireq->i_val < 0 || ireq->i_val > 3)
1702			return EINVAL;
1703		hs->hs_rootmode = ireq->i_val;
1704		hwmp_rootmode_setup(vap);
1705		break;
1706	case IEEE80211_IOC_HWMP_MAXHOPS:
1707		if (ireq->i_val <= 0 || ireq->i_val > 255)
1708			return EINVAL;
1709		hs->hs_maxhops = ireq->i_val;
1710		break;
1711	default:
1712		return ENOSYS;
1713	}
1714	return error;
1715}
1716IEEE80211_IOCTL_SET(hwmp, hwmp_ioctl_set80211);
1717