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