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