ieee80211_hwmp.c revision 197413
1195618Srpaulo/*-
2195618Srpaulo * Copyright (c) 2009 The FreeBSD Foundation
3195618Srpaulo * All rights reserved.
4195618Srpaulo *
5195618Srpaulo * This software was developed by Rui Paulo under sponsorship from the
6195618Srpaulo * FreeBSD Foundation.
7195618Srpaulo *
8195618Srpaulo * Redistribution and use in source and binary forms, with or without
9195618Srpaulo * modification, are permitted provided that the following conditions
10195618Srpaulo * are met:
11195618Srpaulo * 1. Redistributions of source code must retain the above copyright
12195618Srpaulo *    notice, this list of conditions and the following disclaimer.
13195618Srpaulo * 2. Redistributions in binary form must reproduce the above copyright
14195618Srpaulo *    notice, this list of conditions and the following disclaimer in the
15195618Srpaulo *    documentation and/or other materials provided with the distribution.
16195618Srpaulo *
17195618Srpaulo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18195618Srpaulo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19195618Srpaulo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20195618Srpaulo * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21195618Srpaulo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22195618Srpaulo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23195618Srpaulo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24195618Srpaulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25195618Srpaulo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26195618Srpaulo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27195618Srpaulo * SUCH DAMAGE.
28195618Srpaulo */
29195618Srpaulo#include <sys/cdefs.h>
30195618Srpaulo#ifdef __FreeBSD__
31195618Srpaulo__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_hwmp.c 197413 2009-09-22 18:18:14Z rpaulo $");
32195618Srpaulo#endif
33195618Srpaulo
34195618Srpaulo/*
35195618Srpaulo * IEEE 802.11s Hybrid Wireless Mesh Protocol, HWMP.
36195618Srpaulo *
37195618Srpaulo * Based on March 2009, D3.0 802.11s draft spec.
38195618Srpaulo */
39195618Srpaulo#include "opt_inet.h"
40195618Srpaulo#include "opt_wlan.h"
41195618Srpaulo
42195618Srpaulo#include <sys/param.h>
43195618Srpaulo#include <sys/systm.h>
44195618Srpaulo#include <sys/mbuf.h>
45195618Srpaulo#include <sys/malloc.h>
46195618Srpaulo#include <sys/kernel.h>
47195618Srpaulo
48195618Srpaulo#include <sys/socket.h>
49195618Srpaulo#include <sys/sockio.h>
50195618Srpaulo#include <sys/endian.h>
51195618Srpaulo#include <sys/errno.h>
52195618Srpaulo#include <sys/proc.h>
53195618Srpaulo#include <sys/sysctl.h>
54195618Srpaulo
55195618Srpaulo#include <net/if.h>
56195618Srpaulo#include <net/if_media.h>
57195618Srpaulo#include <net/if_llc.h>
58195618Srpaulo#include <net/ethernet.h>
59195618Srpaulo
60195618Srpaulo#include <net/bpf.h>
61195618Srpaulo
62195618Srpaulo#include <net80211/ieee80211_var.h>
63195618Srpaulo#include <net80211/ieee80211_action.h>
64195618Srpaulo#include <net80211/ieee80211_input.h>
65195618Srpaulo#include <net80211/ieee80211_mesh.h>
66195618Srpaulo
67195618Srpaulostatic void	hwmp_vattach(struct ieee80211vap *);
68195618Srpaulostatic void	hwmp_vdetach(struct ieee80211vap *);
69195618Srpaulostatic int	hwmp_newstate(struct ieee80211vap *,
70195618Srpaulo		    enum ieee80211_state, int);
71195618Srpaulostatic int	hwmp_send_action(struct ieee80211_node *,
72195618Srpaulo		    const uint8_t [IEEE80211_ADDR_LEN],
73195618Srpaulo		    const uint8_t [IEEE80211_ADDR_LEN],
74195618Srpaulo		    uint8_t *, size_t);
75195618Srpaulostatic uint8_t * hwmp_add_meshpreq(uint8_t *,
76195618Srpaulo		    const struct ieee80211_meshpreq_ie *);
77195618Srpaulostatic uint8_t * hwmp_add_meshprep(uint8_t *,
78195618Srpaulo		    const struct ieee80211_meshprep_ie *);
79195618Srpaulostatic uint8_t * hwmp_add_meshperr(uint8_t *,
80195618Srpaulo		    const struct ieee80211_meshperr_ie *);
81195618Srpaulostatic uint8_t * hwmp_add_meshrann(uint8_t *,
82195618Srpaulo		    const struct ieee80211_meshrann_ie *);
83195618Srpaulostatic void	hwmp_rootmode_setup(struct ieee80211vap *);
84195618Srpaulostatic void	hwmp_rootmode_cb(void *);
85195618Srpaulostatic void	hwmp_rootmode_rann_cb(void *);
86195618Srpaulostatic void	hwmp_recv_preq(struct ieee80211vap *, struct ieee80211_node *,
87195618Srpaulo		    const struct ieee80211_frame *,
88195618Srpaulo		    const struct ieee80211_meshpreq_ie *);
89195618Srpaulostatic int	hwmp_send_preq(struct ieee80211_node *,
90195618Srpaulo		    const uint8_t [IEEE80211_ADDR_LEN],
91195618Srpaulo		    const uint8_t [IEEE80211_ADDR_LEN],
92195618Srpaulo		    struct ieee80211_meshpreq_ie *);
93195618Srpaulostatic void	hwmp_recv_prep(struct ieee80211vap *, struct ieee80211_node *,
94195618Srpaulo		    const struct ieee80211_frame *,
95195618Srpaulo		    const struct ieee80211_meshprep_ie *);
96195618Srpaulostatic int	hwmp_send_prep(struct ieee80211_node *,
97195618Srpaulo		    const uint8_t [IEEE80211_ADDR_LEN],
98195618Srpaulo		    const uint8_t [IEEE80211_ADDR_LEN],
99195618Srpaulo		    struct ieee80211_meshprep_ie *);
100195618Srpaulostatic void	hwmp_recv_perr(struct ieee80211vap *, struct ieee80211_node *,
101195618Srpaulo		    const struct ieee80211_frame *,
102195618Srpaulo		    const struct ieee80211_meshperr_ie *);
103195618Srpaulostatic int	hwmp_send_perr(struct ieee80211_node *,
104195618Srpaulo		    const uint8_t [IEEE80211_ADDR_LEN],
105195618Srpaulo		    const uint8_t [IEEE80211_ADDR_LEN],
106195618Srpaulo		    struct ieee80211_meshperr_ie *);
107195618Srpaulostatic void	hwmp_recv_rann(struct ieee80211vap *, struct ieee80211_node *,
108195618Srpaulo		   const struct ieee80211_frame *,
109195618Srpaulo		   const struct ieee80211_meshrann_ie *);
110195618Srpaulostatic int	hwmp_send_rann(struct ieee80211_node *,
111195618Srpaulo		    const uint8_t [IEEE80211_ADDR_LEN],
112195618Srpaulo		    const uint8_t [IEEE80211_ADDR_LEN],
113195618Srpaulo		    struct ieee80211_meshrann_ie *);
114195618Srpaulostatic struct ieee80211_node *
115195618Srpaulo		hwmp_discover(struct ieee80211vap *,
116195618Srpaulo		    const uint8_t [IEEE80211_ADDR_LEN], struct mbuf *);
117195618Srpaulostatic void	hwmp_peerdown(struct ieee80211_node *);
118195618Srpaulo
119195813Ssamstatic struct timeval ieee80211_hwmp_preqminint = { 0, 100000 };
120195813Ssamstatic struct timeval ieee80211_hwmp_perrminint = { 0, 100000 };
121195618Srpaulo
122195618Srpaulo/* unalligned little endian access */
123195618Srpaulo#define LE_WRITE_2(p, v) do {				\
124195618Srpaulo	((uint8_t *)(p))[0] = (v) & 0xff;		\
125195618Srpaulo	((uint8_t *)(p))[1] = ((v) >> 8) & 0xff;	\
126195618Srpaulo} while (0)
127195618Srpaulo#define LE_WRITE_4(p, v) do {				\
128195618Srpaulo	((uint8_t *)(p))[0] = (v) & 0xff;		\
129195618Srpaulo	((uint8_t *)(p))[1] = ((v) >> 8) & 0xff;	\
130195618Srpaulo	((uint8_t *)(p))[2] = ((v) >> 16) & 0xff;	\
131195618Srpaulo	((uint8_t *)(p))[3] = ((v) >> 24) & 0xff;	\
132195618Srpaulo} while (0)
133195618Srpaulo
134195618Srpaulo
135195618Srpaulo/* NB: the Target Address set in a Proactive PREQ is the broadcast address. */
136195618Srpaulostatic const uint8_t	broadcastaddr[IEEE80211_ADDR_LEN] =
137195618Srpaulo	{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
138195618Srpaulo
139195618Srpaulotypedef uint32_t ieee80211_hwmp_seq;
140195908Srpaulo#define	HWMP_SEQ_LT(a, b)	((int32_t)((a)-(b)) < 0)
141195908Srpaulo#define	HWMP_SEQ_LEQ(a, b)	((int32_t)((a)-(b)) <= 0)
142195908Srpaulo#define	HWMP_SEQ_GT(a, b)	((int32_t)((a)-(b)) > 0)
143195908Srpaulo#define	HWMP_SEQ_GEQ(a, b)	((int32_t)((a)-(b)) >= 0)
144195618Srpaulo
145195618Srpaulo/*
146195618Srpaulo * Private extension of ieee80211_mesh_route.
147195618Srpaulo */
148195618Srpaulostruct ieee80211_hwmp_route {
149195908Srpaulo	ieee80211_hwmp_seq	hr_seq;		/* last HWMP seq seen from dst*/
150195908Srpaulo	ieee80211_hwmp_seq	hr_preqid;	/* last PREQ ID seen from dst */
151195908Srpaulo	ieee80211_hwmp_seq	hr_targetseq;	/* seq. no. on our latest PREQ*/
152195618Srpaulo	int			hr_preqretries;
153195618Srpaulo};
154195618Srpaulostruct ieee80211_hwmp_state {
155195618Srpaulo	ieee80211_hwmp_seq	hs_seq;		/* next seq to be used */
156195618Srpaulo	ieee80211_hwmp_seq	hs_preqid;	/* next PREQ ID to be used */
157195618Srpaulo	struct timeval		hs_lastpreq;	/* last time we sent a PREQ */
158195618Srpaulo	struct timeval		hs_lastperr;	/* last time we sent a PERR */
159195618Srpaulo	int			hs_rootmode;	/* proactive HWMP */
160195618Srpaulo	struct callout		hs_roottimer;
161195618Srpaulo	uint8_t			hs_maxhops;	/* max hop count */
162195618Srpaulo};
163195618Srpaulo
164195618SrpauloSYSCTL_NODE(_net_wlan, OID_AUTO, hwmp, CTLFLAG_RD, 0,
165195618Srpaulo    "IEEE 802.11s HWMP parameters");
166195813Ssamstatic int	ieee80211_hwmp_targetonly = 0;
167195618SrpauloSYSCTL_INT(_net_wlan_hwmp, OID_AUTO, targetonly, CTLTYPE_INT | CTLFLAG_RW,
168195618Srpaulo    &ieee80211_hwmp_targetonly, 0, "Set TO bit on generated PREQs");
169195813Ssamstatic int	ieee80211_hwmp_replyforward = 1;
170195618SrpauloSYSCTL_INT(_net_wlan_hwmp, OID_AUTO, replyforward, CTLTYPE_INT | CTLFLAG_RW,
171195618Srpaulo    &ieee80211_hwmp_replyforward, 0, "Set RF bit on generated PREQs");
172195813Ssamstatic int	ieee80211_hwmp_pathtimeout = -1;
173195813SsamSYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, pathlifetime, CTLTYPE_INT | CTLFLAG_RW,
174195813Ssam    &ieee80211_hwmp_pathtimeout, 0, ieee80211_sysctl_msecs_ticks, "I",
175195813Ssam    "path entry lifetime (ms)");
176195813Ssamstatic int	ieee80211_hwmp_roottimeout = -1;
177195813SsamSYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, roottimeout, CTLTYPE_INT | CTLFLAG_RW,
178195813Ssam    &ieee80211_hwmp_roottimeout, 0, ieee80211_sysctl_msecs_ticks, "I",
179195813Ssam    "root PREQ timeout (ms)");
180195813Ssamstatic int	ieee80211_hwmp_rootint = -1;
181195813SsamSYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, rootint, CTLTYPE_INT | CTLFLAG_RW,
182195813Ssam    &ieee80211_hwmp_rootint, 0, ieee80211_sysctl_msecs_ticks, "I",
183195813Ssam    "root interval (ms)");
184195813Ssamstatic int	ieee80211_hwmp_rannint = -1;
185195813SsamSYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, rannint, CTLTYPE_INT | CTLFLAG_RW,
186195813Ssam    &ieee80211_hwmp_rannint, 0, ieee80211_sysctl_msecs_ticks, "I",
187195813Ssam    "root announcement interval (ms)");
188195618Srpaulo
189195618Srpaulo#define	IEEE80211_HWMP_DEFAULT_MAXHOPS	31
190195618Srpaulo
191197413Srpaulostatic	ieee80211_recv_action_func hwmp_recv_action_meshpath;
192195618Srpaulo
193195813Ssamstatic struct ieee80211_mesh_proto_path mesh_proto_hwmp = {
194195618Srpaulo	.mpp_descr	= "HWMP",
195195618Srpaulo	.mpp_ie		= IEEE80211_MESHCONF_HWMP,
196195618Srpaulo	.mpp_discover	= hwmp_discover,
197195618Srpaulo	.mpp_peerdown	= hwmp_peerdown,
198195618Srpaulo	.mpp_vattach	= hwmp_vattach,
199195618Srpaulo	.mpp_vdetach	= hwmp_vdetach,
200195618Srpaulo	.mpp_newstate	= hwmp_newstate,
201195618Srpaulo	.mpp_privlen	= sizeof(struct ieee80211_hwmp_route),
202195618Srpaulo};
203195813SsamSYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, inact, CTLTYPE_INT | CTLFLAG_RW,
204195813Ssam	&mesh_proto_hwmp.mpp_inact, 0, ieee80211_sysctl_msecs_ticks, "I",
205195813Ssam	"mesh route inactivity timeout (ms)");
206195618Srpaulo
207195618Srpaulo
208195618Srpaulostatic void
209195618Srpauloieee80211_hwmp_init(void)
210195618Srpaulo{
211195813Ssam	ieee80211_hwmp_pathtimeout = msecs_to_ticks(5*1000);
212195813Ssam	ieee80211_hwmp_roottimeout = msecs_to_ticks(5*1000);
213195813Ssam	ieee80211_hwmp_rootint = msecs_to_ticks(2*1000);
214195813Ssam	ieee80211_hwmp_rannint = msecs_to_ticks(1*1000);
215195813Ssam
216195618Srpaulo	/*
217197413Srpaulo	 * Register action frame handler.
218195618Srpaulo	 */
219195618Srpaulo	ieee80211_recv_action_register(IEEE80211_ACTION_CAT_MESHPATH,
220197413Srpaulo	    IEEE80211_ACTION_MESHPATH_SEL, hwmp_recv_action_meshpath);
221195618Srpaulo
222195813Ssam	/* NB: default is 5 secs per spec */
223195813Ssam	mesh_proto_hwmp.mpp_inact = msecs_to_ticks(5*1000);
224195813Ssam
225195618Srpaulo	/*
226195618Srpaulo	 * Register HWMP.
227195618Srpaulo	 */
228195618Srpaulo	ieee80211_mesh_register_proto_path(&mesh_proto_hwmp);
229195618Srpaulo}
230195618SrpauloSYSINIT(wlan_hwmp, SI_SUB_DRIVERS, SI_ORDER_SECOND, ieee80211_hwmp_init, NULL);
231195618Srpaulo
232195618Srpaulovoid
233195618Srpaulohwmp_vattach(struct ieee80211vap *vap)
234195618Srpaulo{
235195618Srpaulo	struct ieee80211_hwmp_state *hs;
236195618Srpaulo
237195618Srpaulo	KASSERT(vap->iv_opmode == IEEE80211_M_MBSS,
238195618Srpaulo	    ("not a mesh vap, opmode %d", vap->iv_opmode));
239195618Srpaulo
240195618Srpaulo	hs = malloc(sizeof(struct ieee80211_hwmp_state), M_80211_VAP,
241195618Srpaulo	    M_NOWAIT | M_ZERO);
242195618Srpaulo	if (hs == NULL) {
243195618Srpaulo		printf("%s: couldn't alloc HWMP state\n", __func__);
244195618Srpaulo		return;
245195618Srpaulo	}
246195618Srpaulo	hs->hs_maxhops = IEEE80211_HWMP_DEFAULT_MAXHOPS;
247195618Srpaulo	callout_init(&hs->hs_roottimer, CALLOUT_MPSAFE);
248195618Srpaulo	vap->iv_hwmp = hs;
249195618Srpaulo}
250195618Srpaulo
251195618Srpaulovoid
252195618Srpaulohwmp_vdetach(struct ieee80211vap *vap)
253195618Srpaulo{
254195618Srpaulo	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
255195618Srpaulo
256195784Srpaulo	callout_drain(&hs->hs_roottimer);
257195618Srpaulo	free(vap->iv_hwmp, M_80211_VAP);
258195618Srpaulo	vap->iv_hwmp = NULL;
259195618Srpaulo}
260195618Srpaulo
261195618Srpauloint
262195618Srpaulohwmp_newstate(struct ieee80211vap *vap, enum ieee80211_state ostate, int arg)
263195618Srpaulo{
264195618Srpaulo	enum ieee80211_state nstate = vap->iv_state;
265195618Srpaulo	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
266195618Srpaulo
267195618Srpaulo	IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n",
268195618Srpaulo	    __func__, ieee80211_state_name[ostate],
269195618Srpaulo	    ieee80211_state_name[nstate], arg);
270195618Srpaulo
271195618Srpaulo	if (nstate != IEEE80211_S_RUN && ostate == IEEE80211_S_RUN)
272195618Srpaulo		callout_drain(&hs->hs_roottimer);
273195784Srpaulo	if (nstate == IEEE80211_S_RUN)
274195784Srpaulo		hwmp_rootmode_setup(vap);
275195618Srpaulo	return 0;
276195618Srpaulo}
277195618Srpaulo
278195618Srpaulostatic int
279197413Srpaulohwmp_recv_action_meshpath(struct ieee80211_node *ni,
280195618Srpaulo	const struct ieee80211_frame *wh,
281195618Srpaulo	const uint8_t *frm, const uint8_t *efrm)
282195618Srpaulo{
283195618Srpaulo	struct ieee80211vap *vap = ni->ni_vap;
284195618Srpaulo	struct ieee80211_meshpreq_ie preq;
285197413Srpaulo	struct ieee80211_meshprep_ie prep;
286197413Srpaulo	struct ieee80211_meshperr_ie perr;
287197413Srpaulo	struct ieee80211_meshrann_ie rann;
288195618Srpaulo	const uint8_t *iefrm = frm + 2; /* action + code */
289197413Srpaulo	int found = 0;
290195618Srpaulo
291195618Srpaulo	while (efrm - iefrm > 1) {
292195618Srpaulo		IEEE80211_VERIFY_LENGTH(efrm - iefrm, iefrm[1] + 2, return 0);
293197413Srpaulo		switch (*iefrm) {
294197413Srpaulo		case IEEE80211_ELEMID_MESHPREQ:
295197413Srpaulo		{
296195618Srpaulo			const struct ieee80211_meshpreq_ie *mpreq =
297195618Srpaulo			    (const struct ieee80211_meshpreq_ie *) iefrm;
298195618Srpaulo			/* XXX > 1 target */
299195618Srpaulo			if (mpreq->preq_len !=
300195618Srpaulo			    sizeof(struct ieee80211_meshpreq_ie) - 2) {
301195618Srpaulo				IEEE80211_DISCARD(vap,
302195618Srpaulo				    IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
303195618Srpaulo				    wh, NULL, "%s", "PREQ with wrong len");
304195618Srpaulo				vap->iv_stats.is_rx_mgtdiscard++;
305197413Srpaulo				break;
306195618Srpaulo			}
307195618Srpaulo			memcpy(&preq, mpreq, sizeof(preq));
308195618Srpaulo			preq.preq_id = LE_READ_4(&mpreq->preq_id);
309195618Srpaulo			preq.preq_origseq = LE_READ_4(&mpreq->preq_origseq);
310195618Srpaulo			preq.preq_lifetime = LE_READ_4(&mpreq->preq_lifetime);
311195618Srpaulo			preq.preq_metric = LE_READ_4(&mpreq->preq_metric);
312195618Srpaulo			preq.preq_targets[0].target_seq =
313195618Srpaulo			    LE_READ_4(&mpreq->preq_targets[0].target_seq);
314195618Srpaulo			hwmp_recv_preq(vap, ni, wh, &preq);
315197413Srpaulo			found++;
316197413Srpaulo			break;
317195618Srpaulo		}
318197413Srpaulo		case IEEE80211_ELEMID_MESHPREP:
319197413Srpaulo		{
320195618Srpaulo			const struct ieee80211_meshprep_ie *mprep =
321195618Srpaulo			    (const struct ieee80211_meshprep_ie *) iefrm;
322195618Srpaulo			if (mprep->prep_len !=
323195618Srpaulo			    sizeof(struct ieee80211_meshprep_ie) - 2) {
324195618Srpaulo				IEEE80211_DISCARD(vap,
325195618Srpaulo				    IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
326195618Srpaulo				    wh, NULL, "%s", "PREP with wrong len");
327195618Srpaulo				vap->iv_stats.is_rx_mgtdiscard++;
328197413Srpaulo				break;
329195618Srpaulo			}
330195618Srpaulo			memcpy(&prep, mprep, sizeof(prep));
331195618Srpaulo			prep.prep_targetseq = LE_READ_4(&mprep->prep_targetseq);
332195618Srpaulo			prep.prep_lifetime = LE_READ_4(&mprep->prep_lifetime);
333195618Srpaulo			prep.prep_metric = LE_READ_4(&mprep->prep_metric);
334195618Srpaulo			prep.prep_origseq = LE_READ_4(&mprep->prep_origseq);
335195618Srpaulo			hwmp_recv_prep(vap, ni, wh, &prep);
336197413Srpaulo			found++;
337197413Srpaulo			break;
338195618Srpaulo		}
339197413Srpaulo		case IEEE80211_ELEMID_MESHPERR:
340197413Srpaulo		{
341195618Srpaulo			const struct ieee80211_meshperr_ie *mperr =
342195618Srpaulo			    (const struct ieee80211_meshperr_ie *) iefrm;
343195618Srpaulo			/* XXX > 1 target */
344195618Srpaulo			if (mperr->perr_len !=
345195618Srpaulo			    sizeof(struct ieee80211_meshperr_ie) - 2) {
346195618Srpaulo				IEEE80211_DISCARD(vap,
347195618Srpaulo				    IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
348195618Srpaulo				    wh, NULL, "%s", "PERR with wrong len");
349195618Srpaulo				vap->iv_stats.is_rx_mgtdiscard++;
350197413Srpaulo				break;
351195618Srpaulo			}
352195618Srpaulo			memcpy(&perr, mperr, sizeof(perr));
353195618Srpaulo			perr.perr_dests[0].dest_seq =
354195618Srpaulo			    LE_READ_4(&mperr->perr_dests[0].dest_seq);
355195618Srpaulo			hwmp_recv_perr(vap, ni, wh, &perr);
356197413Srpaulo			found++;
357197413Srpaulo			break;
358195618Srpaulo		}
359197413Srpaulo		case IEEE80211_ELEMID_MESHRANN:
360197413Srpaulo		{
361195618Srpaulo			const struct ieee80211_meshrann_ie *mrann =
362195618Srpaulo			    (const struct ieee80211_meshrann_ie *) iefrm;
363195618Srpaulo			if (mrann->rann_len !=
364195618Srpaulo			    sizeof(struct ieee80211_meshrann_ie) - 2) {
365195618Srpaulo				IEEE80211_DISCARD(vap,
366195618Srpaulo				    IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
367195618Srpaulo				    wh, NULL, "%s", "RAN with wrong len");
368195618Srpaulo				vap->iv_stats.is_rx_mgtdiscard++;
369195618Srpaulo				return 1;
370195618Srpaulo			}
371195618Srpaulo			memcpy(&rann, mrann, sizeof(rann));
372195618Srpaulo			rann.rann_seq = LE_READ_4(&mrann->rann_seq);
373195618Srpaulo			rann.rann_metric = LE_READ_4(&mrann->rann_metric);
374195618Srpaulo			hwmp_recv_rann(vap, ni, wh, &rann);
375197413Srpaulo			found++;
376197413Srpaulo			break;
377195618Srpaulo		}
378197413Srpaulo		}
379195618Srpaulo		iefrm += iefrm[1] + 2;
380195618Srpaulo	}
381197413Srpaulo	if (!found) {
382197413Srpaulo		IEEE80211_DISCARD(vap,
383197413Srpaulo		    IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
384197413Srpaulo		    wh, NULL, "%s", "PATH SEL action without IE");
385197413Srpaulo		vap->iv_stats.is_rx_mgtdiscard++;
386197413Srpaulo	}
387195618Srpaulo	return 0;
388195618Srpaulo}
389195618Srpaulo
390195618Srpaulostatic int
391195618Srpaulohwmp_send_action(struct ieee80211_node *ni,
392195618Srpaulo    const uint8_t sa[IEEE80211_ADDR_LEN],
393195618Srpaulo    const uint8_t da[IEEE80211_ADDR_LEN],
394195618Srpaulo    uint8_t *ie, size_t len)
395195618Srpaulo{
396195618Srpaulo	struct ieee80211vap *vap = ni->ni_vap;
397195618Srpaulo	struct ieee80211com *ic = ni->ni_ic;
398195618Srpaulo	struct ieee80211_bpf_params params;
399195618Srpaulo	struct mbuf *m;
400195618Srpaulo	uint8_t *frm;
401195618Srpaulo
402195618Srpaulo	if (vap->iv_state == IEEE80211_S_CAC) {
403195618Srpaulo		IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, ni,
404195618Srpaulo		    "block %s frame in CAC state", "HWMP action");
405195618Srpaulo		vap->iv_stats.is_tx_badstate++;
406195618Srpaulo		return EIO;	/* XXX */
407195618Srpaulo	}
408195618Srpaulo
409195618Srpaulo	KASSERT(ni != NULL, ("null node"));
410195618Srpaulo	/*
411195618Srpaulo	 * Hold a reference on the node so it doesn't go away until after
412195618Srpaulo	 * the xmit is complete all the way in the driver.  On error we
413195618Srpaulo	 * will remove our reference.
414195618Srpaulo	 */
415195618Srpaulo#ifdef IEEE80211_DEBUG_REFCNT
416195618Srpaulo	IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
417195618Srpaulo	    "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n",
418195618Srpaulo	    __func__, __LINE__,
419195618Srpaulo	    ni, ether_sprintf(ni->ni_macaddr),
420195618Srpaulo	    ieee80211_node_refcnt(ni)+1);
421195618Srpaulo#endif
422195618Srpaulo	ieee80211_ref_node(ni);
423195618Srpaulo
424195618Srpaulo	m = ieee80211_getmgtframe(&frm,
425195618Srpaulo	    ic->ic_headroom + sizeof(struct ieee80211_frame),
426195618Srpaulo	    sizeof(struct ieee80211_action) + len
427195618Srpaulo	);
428195618Srpaulo	if (m == NULL) {
429195618Srpaulo		ieee80211_free_node(ni);
430195618Srpaulo		vap->iv_stats.is_tx_nobuf++;
431195618Srpaulo		return ENOMEM;
432195618Srpaulo	}
433195618Srpaulo	*frm++ = IEEE80211_ACTION_CAT_MESHPATH;
434197413Srpaulo	*frm++ = IEEE80211_ACTION_MESHPATH_SEL;
435195618Srpaulo	switch (*ie) {
436195618Srpaulo	case IEEE80211_ELEMID_MESHPREQ:
437195618Srpaulo		frm = hwmp_add_meshpreq(frm,
438195618Srpaulo		    (struct ieee80211_meshpreq_ie *)ie);
439195618Srpaulo		break;
440195618Srpaulo	case IEEE80211_ELEMID_MESHPREP:
441195618Srpaulo		frm = hwmp_add_meshprep(frm,
442195618Srpaulo		    (struct ieee80211_meshprep_ie *)ie);
443195618Srpaulo		break;
444195618Srpaulo	case IEEE80211_ELEMID_MESHPERR:
445195618Srpaulo		frm = hwmp_add_meshperr(frm,
446195618Srpaulo		    (struct ieee80211_meshperr_ie *)ie);
447195618Srpaulo		break;
448195618Srpaulo	case IEEE80211_ELEMID_MESHRANN:
449195618Srpaulo		frm = hwmp_add_meshrann(frm,
450195618Srpaulo		    (struct ieee80211_meshrann_ie *)ie);
451195618Srpaulo		break;
452195618Srpaulo	}
453195618Srpaulo
454195618Srpaulo	m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
455195618Srpaulo	M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT);
456195618Srpaulo	if (m == NULL) {
457195618Srpaulo		ieee80211_free_node(ni);
458195618Srpaulo		vap->iv_stats.is_tx_nobuf++;
459195618Srpaulo		return ENOMEM;
460195618Srpaulo	}
461195618Srpaulo	ieee80211_send_setup(ni, m,
462195618Srpaulo	    IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_ACTION,
463195618Srpaulo	    IEEE80211_NONQOS_TID, sa, da, sa);
464195618Srpaulo
465195618Srpaulo	m->m_flags |= M_ENCAP;		/* mark encapsulated */
466195618Srpaulo	IEEE80211_NODE_STAT(ni, tx_mgmt);
467195618Srpaulo
468195618Srpaulo	memset(&params, 0, sizeof(params));
469195618Srpaulo	params.ibp_pri = WME_AC_VO;
470195618Srpaulo	params.ibp_rate0 = ni->ni_txparms->mgmtrate;
471195618Srpaulo	if (IEEE80211_IS_MULTICAST(da))
472195618Srpaulo		params.ibp_try0 = 1;
473195618Srpaulo	else
474195618Srpaulo		params.ibp_try0 = ni->ni_txparms->maxretry;
475195618Srpaulo	params.ibp_power = ni->ni_txpower;
476195618Srpaulo	return ic->ic_raw_xmit(ni, m, &params);
477195618Srpaulo}
478195618Srpaulo
479197413Srpaulo#define ADDSHORT(frm, v) do {		\
480197413Srpaulo	frm[0] = (v) & 0xff;		\
481197413Srpaulo	frm[1] = (v) >> 8;		\
482197413Srpaulo	frm += 2;			\
483197413Srpaulo} while (0)
484195618Srpaulo#define ADDWORD(frm, v) do {		\
485195618Srpaulo	LE_WRITE_4(frm, v);		\
486195618Srpaulo	frm += 4;			\
487195618Srpaulo} while (0)
488195618Srpaulo/*
489195618Srpaulo * Add a Mesh Path Request IE to a frame.
490195618Srpaulo */
491195618Srpaulostatic uint8_t *
492195618Srpaulohwmp_add_meshpreq(uint8_t *frm, const struct ieee80211_meshpreq_ie *preq)
493195618Srpaulo{
494195618Srpaulo	int i;
495195618Srpaulo
496195618Srpaulo	*frm++ = IEEE80211_ELEMID_MESHPREQ;
497195618Srpaulo	*frm++ = sizeof(struct ieee80211_meshpreq_ie) - 2 +
498195618Srpaulo	    (preq->preq_tcount - 1) * sizeof(*preq->preq_targets);
499195618Srpaulo	*frm++ = preq->preq_flags;
500195618Srpaulo	*frm++ = preq->preq_hopcount;
501195618Srpaulo	*frm++ = preq->preq_ttl;
502195618Srpaulo	ADDWORD(frm, preq->preq_id);
503195618Srpaulo	IEEE80211_ADDR_COPY(frm, preq->preq_origaddr); frm += 6;
504195618Srpaulo	ADDWORD(frm, preq->preq_origseq);
505195618Srpaulo	ADDWORD(frm, preq->preq_lifetime);
506195618Srpaulo	ADDWORD(frm, preq->preq_metric);
507195618Srpaulo	*frm++ = preq->preq_tcount;
508195618Srpaulo	for (i = 0; i < preq->preq_tcount; i++) {
509195618Srpaulo		*frm++ = preq->preq_targets[i].target_flags;
510195618Srpaulo		IEEE80211_ADDR_COPY(frm, preq->preq_targets[i].target_addr);
511195618Srpaulo		frm += 6;
512195618Srpaulo		ADDWORD(frm, preq->preq_targets[i].target_seq);
513195618Srpaulo	}
514195618Srpaulo	return frm;
515195618Srpaulo}
516195618Srpaulo
517195618Srpaulo/*
518195618Srpaulo * Add a Mesh Path Reply IE to a frame.
519195618Srpaulo */
520195618Srpaulostatic uint8_t *
521195618Srpaulohwmp_add_meshprep(uint8_t *frm, const struct ieee80211_meshprep_ie *prep)
522195618Srpaulo{
523195618Srpaulo	*frm++ = IEEE80211_ELEMID_MESHPREP;
524195618Srpaulo	*frm++ = sizeof(struct ieee80211_meshprep_ie) - 2;
525195618Srpaulo	*frm++ = prep->prep_flags;
526195618Srpaulo	*frm++ = prep->prep_hopcount;
527195618Srpaulo	*frm++ = prep->prep_ttl;
528195618Srpaulo	IEEE80211_ADDR_COPY(frm, prep->prep_targetaddr); frm += 6;
529195618Srpaulo	ADDWORD(frm, prep->prep_targetseq);
530195618Srpaulo	ADDWORD(frm, prep->prep_lifetime);
531195618Srpaulo	ADDWORD(frm, prep->prep_metric);
532195618Srpaulo	IEEE80211_ADDR_COPY(frm, prep->prep_origaddr); frm += 6;
533195618Srpaulo	ADDWORD(frm, prep->prep_origseq);
534195618Srpaulo	return frm;
535195618Srpaulo}
536195618Srpaulo
537195618Srpaulo/*
538195618Srpaulo * Add a Mesh Path Error IE to a frame.
539195618Srpaulo */
540195618Srpaulostatic uint8_t *
541195618Srpaulohwmp_add_meshperr(uint8_t *frm, const struct ieee80211_meshperr_ie *perr)
542195618Srpaulo{
543195618Srpaulo	int i;
544195618Srpaulo
545195618Srpaulo	*frm++ = IEEE80211_ELEMID_MESHPERR;
546195618Srpaulo	*frm++ = sizeof(struct ieee80211_meshperr_ie) - 2 +
547195618Srpaulo	    (perr->perr_ndests - 1) * sizeof(*perr->perr_dests);
548197413Srpaulo	*frm++ = perr->perr_ttl;
549195618Srpaulo	*frm++ = perr->perr_ndests;
550195618Srpaulo	for (i = 0; i < perr->perr_ndests; i++) {
551197413Srpaulo		*frm += perr->perr_dests[i].dest_flags;
552195618Srpaulo		IEEE80211_ADDR_COPY(frm, perr->perr_dests[i].dest_addr);
553195618Srpaulo		frm += 6;
554195618Srpaulo		ADDWORD(frm, perr->perr_dests[i].dest_seq);
555197413Srpaulo		ADDSHORT(frm, perr->perr_dests[i].dest_rcode);
556195618Srpaulo	}
557195618Srpaulo	return frm;
558195618Srpaulo}
559195618Srpaulo
560195618Srpaulo/*
561195618Srpaulo * Add a Root Annoucement IE to a frame.
562195618Srpaulo */
563195618Srpaulostatic uint8_t *
564195618Srpaulohwmp_add_meshrann(uint8_t *frm, const struct ieee80211_meshrann_ie *rann)
565195618Srpaulo{
566195618Srpaulo	*frm++ = IEEE80211_ELEMID_MESHRANN;
567195618Srpaulo	*frm++ = sizeof(struct ieee80211_meshrann_ie) - 2;
568195618Srpaulo	*frm++ = rann->rann_flags;
569195618Srpaulo	*frm++ = rann->rann_hopcount;
570195618Srpaulo	*frm++ = rann->rann_ttl;
571195618Srpaulo	IEEE80211_ADDR_COPY(frm, rann->rann_addr); frm += 6;
572195618Srpaulo	ADDWORD(frm, rann->rann_seq);
573195618Srpaulo	ADDWORD(frm, rann->rann_metric);
574195618Srpaulo	return frm;
575195618Srpaulo}
576195618Srpaulo
577195618Srpaulostatic void
578195618Srpaulohwmp_rootmode_setup(struct ieee80211vap *vap)
579195618Srpaulo{
580195618Srpaulo	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
581195618Srpaulo
582195618Srpaulo	switch (hs->hs_rootmode) {
583195618Srpaulo	case IEEE80211_HWMP_ROOTMODE_DISABLED:
584195618Srpaulo		callout_drain(&hs->hs_roottimer);
585195618Srpaulo		break;
586195618Srpaulo	case IEEE80211_HWMP_ROOTMODE_NORMAL:
587195618Srpaulo	case IEEE80211_HWMP_ROOTMODE_PROACTIVE:
588195813Ssam		callout_reset(&hs->hs_roottimer, ieee80211_hwmp_rootint,
589195618Srpaulo		    hwmp_rootmode_cb, vap);
590195618Srpaulo		break;
591195618Srpaulo	case IEEE80211_HWMP_ROOTMODE_RANN:
592195813Ssam		callout_reset(&hs->hs_roottimer, ieee80211_hwmp_rannint,
593195618Srpaulo		    hwmp_rootmode_rann_cb, vap);
594195618Srpaulo		break;
595195618Srpaulo	}
596195618Srpaulo}
597195618Srpaulo
598195618Srpaulo/*
599195618Srpaulo * Send a broadcast Path Request to find all nodes on the mesh. We are
600195618Srpaulo * called when the vap is configured as a HWMP root node.
601195618Srpaulo */
602195618Srpaulo#define	PREQ_TFLAGS(n)	preq.preq_targets[n].target_flags
603195618Srpaulo#define	PREQ_TADDR(n)	preq.preq_targets[n].target_addr
604195618Srpaulo#define	PREQ_TSEQ(n)	preq.preq_targets[n].target_seq
605195618Srpaulostatic void
606195618Srpaulohwmp_rootmode_cb(void *arg)
607195618Srpaulo{
608195618Srpaulo	struct ieee80211vap *vap = (struct ieee80211vap *)arg;
609195618Srpaulo	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
610195618Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
611195618Srpaulo	struct ieee80211_meshpreq_ie preq;
612195618Srpaulo
613195618Srpaulo	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, vap->iv_bss,
614195784Srpaulo	    "%s", "send broadcast PREQ");
615195618Srpaulo
616195618Srpaulo	preq.preq_flags = IEEE80211_MESHPREQ_FLAGS_AM;
617195908Srpaulo	if (ms->ms_flags & IEEE80211_MESHFLAGS_PORTAL)
618195908Srpaulo		preq.preq_flags |= IEEE80211_MESHPREQ_FLAGS_PR;
619195618Srpaulo	if (hs->hs_rootmode == IEEE80211_HWMP_ROOTMODE_PROACTIVE)
620195618Srpaulo		preq.preq_flags |= IEEE80211_MESHPREQ_FLAGS_PP;
621195618Srpaulo	preq.preq_hopcount = 0;
622195618Srpaulo	preq.preq_ttl = ms->ms_ttl;
623195618Srpaulo	preq.preq_id = ++hs->hs_preqid;
624195618Srpaulo	IEEE80211_ADDR_COPY(preq.preq_origaddr, vap->iv_myaddr);
625195618Srpaulo	preq.preq_origseq = ++hs->hs_seq;
626195813Ssam	preq.preq_lifetime = ticks_to_msecs(ieee80211_hwmp_roottimeout);
627195618Srpaulo	preq.preq_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
628195618Srpaulo	preq.preq_tcount = 1;
629195618Srpaulo	IEEE80211_ADDR_COPY(PREQ_TADDR(0), broadcastaddr);
630195618Srpaulo	PREQ_TFLAGS(0) = IEEE80211_MESHPREQ_TFLAGS_TO |
631195618Srpaulo	    IEEE80211_MESHPREQ_TFLAGS_RF;
632195618Srpaulo	PREQ_TSEQ(0) = 0;
633195618Srpaulo	vap->iv_stats.is_hwmp_rootreqs++;
634195618Srpaulo	hwmp_send_preq(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &preq);
635195618Srpaulo	hwmp_rootmode_setup(vap);
636195618Srpaulo}
637195618Srpaulo#undef	PREQ_TFLAGS
638195618Srpaulo#undef	PREQ_TADDR
639195618Srpaulo#undef	PREQ_TSEQ
640195618Srpaulo
641195618Srpaulo/*
642195618Srpaulo * Send a Root Annoucement (RANN) to find all the nodes on the mesh. We are
643195618Srpaulo * called when the vap is configured as a HWMP RANN root node.
644195618Srpaulo */
645195618Srpaulostatic void
646195618Srpaulohwmp_rootmode_rann_cb(void *arg)
647195618Srpaulo{
648195618Srpaulo	struct ieee80211vap *vap = (struct ieee80211vap *)arg;
649195618Srpaulo	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
650195618Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
651195618Srpaulo	struct ieee80211_meshrann_ie rann;
652195618Srpaulo
653195618Srpaulo	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, vap->iv_bss,
654195784Srpaulo	    "%s", "send broadcast RANN");
655195618Srpaulo
656195908Srpaulo	if (ms->ms_flags & IEEE80211_MESHFLAGS_PORTAL)
657195908Srpaulo		rann.rann_flags |= IEEE80211_MESHRANN_FLAGS_PR;
658195618Srpaulo	rann.rann_hopcount = 0;
659195618Srpaulo	rann.rann_ttl = ms->ms_ttl;
660195618Srpaulo	IEEE80211_ADDR_COPY(rann.rann_addr, vap->iv_myaddr);
661195618Srpaulo	rann.rann_seq = ++hs->hs_seq;
662195618Srpaulo	rann.rann_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
663195618Srpaulo
664195618Srpaulo	vap->iv_stats.is_hwmp_rootrann++;
665195618Srpaulo	hwmp_send_rann(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &rann);
666195618Srpaulo	hwmp_rootmode_setup(vap);
667195618Srpaulo}
668195618Srpaulo
669195618Srpaulo#define	PREQ_TFLAGS(n)	preq->preq_targets[n].target_flags
670195618Srpaulo#define	PREQ_TADDR(n)	preq->preq_targets[n].target_addr
671195618Srpaulo#define	PREQ_TSEQ(n)	preq->preq_targets[n].target_seq
672195618Srpaulostatic void
673195618Srpaulohwmp_recv_preq(struct ieee80211vap *vap, struct ieee80211_node *ni,
674195618Srpaulo    const struct ieee80211_frame *wh, const struct ieee80211_meshpreq_ie *preq)
675195618Srpaulo{
676195618Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
677195618Srpaulo	struct ieee80211_mesh_route *rt = NULL;
678195908Srpaulo	struct ieee80211_mesh_route *rtorig = NULL;
679195908Srpaulo	struct ieee80211_hwmp_route *hrorig;
680195618Srpaulo	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
681195618Srpaulo	struct ieee80211_meshprep_ie prep;
682195618Srpaulo
683195618Srpaulo	if (ni == vap->iv_bss ||
684195618Srpaulo	    ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED)
685195618Srpaulo		return;
686195618Srpaulo	/*
687195618Srpaulo	 * Ignore PREQs from us. Could happen because someone forward it
688195618Srpaulo	 * back to us.
689195618Srpaulo	 */
690195618Srpaulo	if (IEEE80211_ADDR_EQ(vap->iv_myaddr, preq->preq_origaddr))
691195618Srpaulo		return;
692195618Srpaulo
693195618Srpaulo	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
694195618Srpaulo	    "received PREQ, source %s", ether_sprintf(preq->preq_origaddr));
695195618Srpaulo
696195618Srpaulo	/*
697195618Srpaulo	 * Acceptance criteria: if the PREQ is not for us and
698195618Srpaulo	 * forwarding is disabled, discard this PREQ.
699195618Srpaulo	 */
700195618Srpaulo	if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0)) &&
701195618Srpaulo	    !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) {
702195618Srpaulo		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_HWMP,
703195618Srpaulo		    preq->preq_origaddr, NULL, "%s", "not accepting PREQ");
704195618Srpaulo		return;
705195618Srpaulo	}
706195908Srpaulo	rtorig = ieee80211_mesh_rt_find(vap, preq->preq_origaddr);
707195908Srpaulo	if (rtorig == NULL)
708195908Srpaulo		rtorig = ieee80211_mesh_rt_add(vap, preq->preq_origaddr);
709195908Srpaulo	hrorig = IEEE80211_MESH_ROUTE_PRIV(rtorig, struct ieee80211_hwmp_route);
710195618Srpaulo	/*
711195908Srpaulo	 * Sequence number validation.
712195908Srpaulo	 */
713195908Srpaulo	if (HWMP_SEQ_LEQ(preq->preq_id, hrorig->hr_preqid) &&
714195908Srpaulo	    HWMP_SEQ_LEQ(preq->preq_origseq, hrorig->hr_seq)) {
715195908Srpaulo		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
716195908Srpaulo		    "discard PREQ from %s, old seq no %u <= %u",
717195908Srpaulo		    ether_sprintf(preq->preq_origaddr),
718195908Srpaulo		    preq->preq_origseq, hrorig->hr_seq);
719195908Srpaulo		return;
720195908Srpaulo	}
721195908Srpaulo	hrorig->hr_preqid = preq->preq_id;
722195908Srpaulo	hrorig->hr_seq = preq->preq_origseq;
723195908Srpaulo
724195908Srpaulo	/*
725195618Srpaulo	 * Check if the PREQ is addressed to us.
726195618Srpaulo	 */
727195618Srpaulo	if (IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0))) {
728195618Srpaulo		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
729195784Srpaulo		    "reply to %s", ether_sprintf(preq->preq_origaddr));
730195618Srpaulo		/*
731195618Srpaulo		 * Build and send a PREP frame.
732195618Srpaulo		 */
733195618Srpaulo		prep.prep_flags = 0;
734195618Srpaulo		prep.prep_hopcount = 0;
735195618Srpaulo		prep.prep_ttl = ms->ms_ttl;
736195618Srpaulo		IEEE80211_ADDR_COPY(prep.prep_targetaddr, preq->preq_origaddr);
737195618Srpaulo		prep.prep_targetseq = preq->preq_origseq;
738195618Srpaulo		prep.prep_lifetime = preq->preq_lifetime;
739195618Srpaulo		prep.prep_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
740195618Srpaulo		IEEE80211_ADDR_COPY(prep.prep_origaddr, vap->iv_myaddr);
741195618Srpaulo		prep.prep_origseq = ++hs->hs_seq;
742195618Srpaulo		hwmp_send_prep(ni, vap->iv_myaddr, wh->i_addr2, &prep);
743195618Srpaulo		/*
744195618Srpaulo		 * Build the reverse path, if we don't have it already.
745195618Srpaulo		 */
746195618Srpaulo		rt = ieee80211_mesh_rt_find(vap, preq->preq_origaddr);
747195618Srpaulo		if (rt == NULL)
748195618Srpaulo			hwmp_discover(vap, preq->preq_origaddr, NULL);
749195784Srpaulo		else if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0)
750195618Srpaulo			hwmp_discover(vap, rt->rt_dest, NULL);
751195618Srpaulo		return;
752195618Srpaulo	}
753195618Srpaulo	/*
754195618Srpaulo	 * Proactive PREQ: reply with a proactive PREP to the
755195618Srpaulo	 * root STA if requested.
756195618Srpaulo	 */
757195618Srpaulo	if (IEEE80211_ADDR_EQ(PREQ_TADDR(0), broadcastaddr) &&
758195618Srpaulo	    (PREQ_TFLAGS(0) &
759195618Srpaulo	    ((IEEE80211_MESHPREQ_TFLAGS_TO|IEEE80211_MESHPREQ_TFLAGS_RF) ==
760195618Srpaulo	    (IEEE80211_MESHPREQ_TFLAGS_TO|IEEE80211_MESHPREQ_TFLAGS_RF)))) {
761195618Srpaulo		uint8_t rootmac[IEEE80211_ADDR_LEN];
762195618Srpaulo
763195618Srpaulo		IEEE80211_ADDR_COPY(rootmac, preq->preq_origaddr);
764195618Srpaulo		rt = ieee80211_mesh_rt_find(vap, rootmac);
765195784Srpaulo		if (rt == NULL) {
766195618Srpaulo			rt = ieee80211_mesh_rt_add(vap, rootmac);
767195784Srpaulo			if (rt == NULL) {
768195784Srpaulo				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
769195784Srpaulo				    "unable to add root mesh path to %s",
770195784Srpaulo				    ether_sprintf(rootmac));
771195784Srpaulo				vap->iv_stats.is_mesh_rtaddfailed++;
772195784Srpaulo				return;
773195784Srpaulo			}
774195784Srpaulo		}
775195784Srpaulo		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
776195784Srpaulo		    "root mesh station @ %s", ether_sprintf(rootmac));
777195784Srpaulo
778195618Srpaulo		/*
779195618Srpaulo		 * Reply with a PREP if we don't have a path to the root
780195618Srpaulo		 * or if the root sent us a proactive PREQ.
781195618Srpaulo		 */
782195784Srpaulo		if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0 ||
783195618Srpaulo		    (preq->preq_flags & IEEE80211_MESHPREQ_FLAGS_PP)) {
784195618Srpaulo			prep.prep_flags = 0;
785195618Srpaulo			prep.prep_hopcount = 0;
786195618Srpaulo			prep.prep_ttl = ms->ms_ttl;
787195784Srpaulo			IEEE80211_ADDR_COPY(prep.prep_origaddr, vap->iv_myaddr);
788195618Srpaulo			prep.prep_origseq = preq->preq_origseq;
789195618Srpaulo			prep.prep_targetseq = ++hs->hs_seq;
790195618Srpaulo			prep.prep_lifetime = preq->preq_lifetime;
791195618Srpaulo			prep.prep_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
792195784Srpaulo			IEEE80211_ADDR_COPY(prep.prep_targetaddr, rootmac);
793195618Srpaulo			prep.prep_targetseq = PREQ_TSEQ(0);
794195618Srpaulo			hwmp_send_prep(vap->iv_bss, vap->iv_myaddr,
795195618Srpaulo			    broadcastaddr, &prep);
796195618Srpaulo		}
797195618Srpaulo		hwmp_discover(vap, rootmac, NULL);
798195618Srpaulo		return;
799195618Srpaulo	}
800195618Srpaulo	rt = ieee80211_mesh_rt_find(vap, PREQ_TADDR(0));
801195618Srpaulo
802195618Srpaulo	/*
803195618Srpaulo	 * Forwarding and Intermediate reply for PREQs with 1 target.
804195618Srpaulo	 */
805195618Srpaulo	if (preq->preq_tcount == 1) {
806195618Srpaulo		struct ieee80211_meshpreq_ie ppreq; /* propagated PREQ */
807195618Srpaulo
808195618Srpaulo		memcpy(&ppreq, preq, sizeof(ppreq));
809195618Srpaulo		/*
810195618Srpaulo		 * We have a valid route to this node.
811195618Srpaulo		 */
812195618Srpaulo		if (rt != NULL &&
813195784Srpaulo		    (rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID)) {
814195618Srpaulo			if (preq->preq_ttl > 1 &&
815195618Srpaulo			    preq->preq_hopcount < hs->hs_maxhops) {
816195618Srpaulo				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
817195784Srpaulo				    "forward PREQ from %s",
818195618Srpaulo				    ether_sprintf(preq->preq_origaddr));
819195618Srpaulo				/*
820195618Srpaulo				 * Propagate the original PREQ.
821195618Srpaulo				 */
822195618Srpaulo				ppreq.preq_hopcount += 1;
823195618Srpaulo				ppreq.preq_ttl -= 1;
824195618Srpaulo				ppreq.preq_metric +=
825195618Srpaulo				    ms->ms_pmetric->mpm_metric(ni);
826195618Srpaulo				/*
827195618Srpaulo				 * Set TO and unset RF bits because we are going
828195618Srpaulo				 * to send a PREP next.
829195618Srpaulo				 */
830195618Srpaulo				ppreq.preq_targets[0].target_flags |=
831195618Srpaulo				    IEEE80211_MESHPREQ_TFLAGS_TO;
832195618Srpaulo				ppreq.preq_targets[0].target_flags &=
833195618Srpaulo				    ~IEEE80211_MESHPREQ_TFLAGS_RF;
834195618Srpaulo				hwmp_send_preq(ni, vap->iv_myaddr,
835195618Srpaulo				    broadcastaddr, &ppreq);
836195618Srpaulo			}
837195618Srpaulo			/*
838195618Srpaulo			 * Check if we can send an intermediate Path Reply,
839195618Srpaulo			 * i.e., Target Only bit is not set.
840195618Srpaulo			 */
841195618Srpaulo	    		if (!(PREQ_TFLAGS(0) & IEEE80211_MESHPREQ_TFLAGS_TO)) {
842195618Srpaulo				struct ieee80211_meshprep_ie prep;
843195618Srpaulo
844195618Srpaulo				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
845195618Srpaulo				    "intermediate reply for PREQ from %s",
846195618Srpaulo				    ether_sprintf(preq->preq_origaddr));
847195618Srpaulo				prep.prep_flags = 0;
848195618Srpaulo				prep.prep_hopcount = rt->rt_nhops + 1;
849195618Srpaulo				prep.prep_ttl = ms->ms_ttl;
850195618Srpaulo				IEEE80211_ADDR_COPY(&prep.prep_targetaddr,
851195618Srpaulo				    preq->preq_origaddr);
852195908Srpaulo				prep.prep_targetseq = hrorig->hr_seq;
853195618Srpaulo				prep.prep_lifetime = preq->preq_lifetime;
854195618Srpaulo				prep.prep_metric = rt->rt_metric +
855195618Srpaulo				    ms->ms_pmetric->mpm_metric(ni);
856195618Srpaulo				IEEE80211_ADDR_COPY(&prep.prep_origaddr,
857195618Srpaulo				    PREQ_TADDR(0));
858195908Srpaulo				prep.prep_origseq = hrorig->hr_seq;
859195618Srpaulo				hwmp_send_prep(ni, vap->iv_myaddr,
860195618Srpaulo				    broadcastaddr, &prep);
861195618Srpaulo			}
862195618Srpaulo		/*
863195618Srpaulo		 * We have no information about this path,
864195618Srpaulo		 * propagate the PREQ.
865195618Srpaulo		 */
866195618Srpaulo		} else if (preq->preq_ttl > 1 &&
867195618Srpaulo		    preq->preq_hopcount < hs->hs_maxhops) {
868195784Srpaulo			if (rt == NULL) {
869195618Srpaulo				rt = ieee80211_mesh_rt_add(vap, PREQ_TADDR(0));
870195784Srpaulo				if (rt == NULL) {
871195784Srpaulo					IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP,
872195784Srpaulo					    ni, "unable to add PREQ path to %s",
873195784Srpaulo					    ether_sprintf(PREQ_TADDR(0)));
874195784Srpaulo					vap->iv_stats.is_mesh_rtaddfailed++;
875195784Srpaulo					return;
876195784Srpaulo				}
877195784Srpaulo			}
878195618Srpaulo			rt->rt_metric = preq->preq_metric;
879195618Srpaulo			rt->rt_lifetime = preq->preq_lifetime;
880195908Srpaulo			hrorig = IEEE80211_MESH_ROUTE_PRIV(rt,
881195908Srpaulo			    struct ieee80211_hwmp_route);
882195908Srpaulo			hrorig->hr_seq = preq->preq_origseq;
883195908Srpaulo			hrorig->hr_preqid = preq->preq_id;
884195618Srpaulo
885195618Srpaulo			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
886195784Srpaulo			    "forward PREQ from %s",
887195618Srpaulo			    ether_sprintf(preq->preq_origaddr));
888195618Srpaulo			ppreq.preq_hopcount += 1;
889195618Srpaulo			ppreq.preq_ttl -= 1;
890195618Srpaulo			ppreq.preq_metric += ms->ms_pmetric->mpm_metric(ni);
891195618Srpaulo			hwmp_send_preq(ni, vap->iv_myaddr, broadcastaddr,
892195618Srpaulo			    &ppreq);
893195618Srpaulo		}
894195618Srpaulo	}
895195618Srpaulo
896195618Srpaulo}
897195618Srpaulo#undef	PREQ_TFLAGS
898195618Srpaulo#undef	PREQ_TADDR
899195618Srpaulo#undef	PREQ_TSEQ
900195618Srpaulo
901195618Srpaulostatic int
902195618Srpaulohwmp_send_preq(struct ieee80211_node *ni,
903195618Srpaulo    const uint8_t sa[IEEE80211_ADDR_LEN],
904195618Srpaulo    const uint8_t da[IEEE80211_ADDR_LEN],
905195618Srpaulo    struct ieee80211_meshpreq_ie *preq)
906195618Srpaulo{
907195618Srpaulo	struct ieee80211_hwmp_state *hs = ni->ni_vap->iv_hwmp;
908195618Srpaulo
909195618Srpaulo	/*
910195618Srpaulo	 * Enforce PREQ interval.
911195618Srpaulo	 */
912195618Srpaulo	if (ratecheck(&hs->hs_lastpreq, &ieee80211_hwmp_preqminint) == 0)
913195618Srpaulo		return EALREADY;
914195618Srpaulo	getmicrouptime(&hs->hs_lastpreq);
915195618Srpaulo
916195618Srpaulo	/*
917195618Srpaulo	 * mesh preq action frame format
918195618Srpaulo	 *     [6] da
919195618Srpaulo	 *     [6] sa
920195618Srpaulo	 *     [6] addr3 = sa
921195618Srpaulo	 *     [1] action
922195618Srpaulo	 *     [1] category
923195618Srpaulo	 *     [tlv] mesh path request
924195618Srpaulo	 */
925195618Srpaulo	preq->preq_ie = IEEE80211_ELEMID_MESHPREQ;
926195618Srpaulo	return hwmp_send_action(ni, sa, da, (uint8_t *)preq,
927195618Srpaulo	    sizeof(struct ieee80211_meshpreq_ie));
928195618Srpaulo}
929195618Srpaulo
930195618Srpaulostatic void
931195618Srpaulohwmp_recv_prep(struct ieee80211vap *vap, struct ieee80211_node *ni,
932195618Srpaulo    const struct ieee80211_frame *wh, const struct ieee80211_meshprep_ie *prep)
933195618Srpaulo{
934195618Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
935195618Srpaulo	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
936195618Srpaulo	struct ieee80211_mesh_route *rt = NULL;
937195618Srpaulo	struct ieee80211_hwmp_route *hr;
938195618Srpaulo	struct ieee80211com *ic = vap->iv_ic;
939195618Srpaulo	struct ifnet *ifp = vap->iv_ifp;
940195618Srpaulo	struct mbuf *m, *next;
941195618Srpaulo
942195618Srpaulo	/*
943195618Srpaulo	 * Acceptance criteria: if the corresponding PREQ was not generated
944195618Srpaulo	 * by us and forwarding is disabled, discard this PREP.
945195618Srpaulo	 */
946195618Srpaulo	if (ni == vap->iv_bss ||
947195618Srpaulo	    ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED)
948195618Srpaulo		return;
949195618Srpaulo	if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, prep->prep_origaddr) &&
950195618Srpaulo	    !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD))
951195618Srpaulo		return;
952195618Srpaulo
953195618Srpaulo	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
954195618Srpaulo	    "received PREP from %s", ether_sprintf(prep->prep_origaddr));
955195618Srpaulo
956195618Srpaulo	rt = ieee80211_mesh_rt_find(vap, prep->prep_origaddr);
957195618Srpaulo	if (rt == NULL) {
958195618Srpaulo		/*
959195618Srpaulo		 * If we have no entry this could be a reply to a root PREQ.
960195618Srpaulo		 */
961195618Srpaulo		if (hs->hs_rootmode != IEEE80211_HWMP_ROOTMODE_DISABLED) {
962195618Srpaulo			rt = ieee80211_mesh_rt_add(vap, prep->prep_origaddr);
963195784Srpaulo			if (rt == NULL) {
964195784Srpaulo				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP,
965195784Srpaulo				    ni, "unable to add PREP path to %s",
966195784Srpaulo				    ether_sprintf(prep->prep_origaddr));
967195784Srpaulo				vap->iv_stats.is_mesh_rtaddfailed++;
968195784Srpaulo				return;
969195784Srpaulo			}
970195618Srpaulo			IEEE80211_ADDR_COPY(rt->rt_nexthop, wh->i_addr2);
971195618Srpaulo			rt->rt_nhops = prep->prep_hopcount;
972195618Srpaulo			rt->rt_lifetime = prep->prep_lifetime;
973195618Srpaulo			rt->rt_metric = prep->prep_metric;
974195784Srpaulo			rt->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID;
975195908Srpaulo			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
976195908Srpaulo			    "add root path to %s nhops %d metric %d (PREP)",
977195908Srpaulo			    ether_sprintf(prep->prep_origaddr),
978195908Srpaulo			    rt->rt_nhops, rt->rt_metric);
979195618Srpaulo			return;
980195618Srpaulo		}
981195618Srpaulo		return;
982195618Srpaulo	}
983195908Srpaulo	/*
984195908Srpaulo	 * Sequence number validation.
985195908Srpaulo	 */
986195618Srpaulo	hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
987195908Srpaulo	if (HWMP_SEQ_LEQ(prep->prep_origseq, hr->hr_seq)) {
988195908Srpaulo		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
989195908Srpaulo		    "discard PREP from %s, old seq no %u <= %u",
990195908Srpaulo		    ether_sprintf(prep->prep_origaddr),
991195908Srpaulo		    prep->prep_origseq, hr->hr_seq);
992195908Srpaulo		return;
993195908Srpaulo	}
994195908Srpaulo	hr->hr_seq = prep->prep_origseq;
995195908Srpaulo	/*
996195908Srpaulo	 * If it's NOT for us, propagate the PREP.
997195908Srpaulo	 */
998195908Srpaulo	if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, prep->prep_targetaddr) &&
999195908Srpaulo	    prep->prep_ttl > 1 && prep->prep_hopcount < hs->hs_maxhops) {
1000195908Srpaulo		struct ieee80211_meshprep_ie pprep; /* propagated PREP */
1001195908Srpaulo
1002195908Srpaulo		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1003195908Srpaulo		    "propagate PREP from %s",
1004195908Srpaulo		    ether_sprintf(prep->prep_origaddr));
1005195908Srpaulo
1006195908Srpaulo		memcpy(&pprep, prep, sizeof(pprep));
1007195908Srpaulo		pprep.prep_hopcount += 1;
1008195908Srpaulo		pprep.prep_ttl -= 1;
1009195908Srpaulo		pprep.prep_metric += ms->ms_pmetric->mpm_metric(ni);
1010195908Srpaulo		IEEE80211_ADDR_COPY(pprep.prep_origaddr, vap->iv_myaddr);
1011195908Srpaulo		hwmp_send_prep(ni, vap->iv_myaddr, broadcastaddr, &pprep);
1012195908Srpaulo	}
1013195908Srpaulo	hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
1014195908Srpaulo	if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY) {
1015195908Srpaulo		/* NB: never clobber a proxy entry */;
1016195908Srpaulo		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1017195908Srpaulo		    "discard PREP for %s, route is marked PROXY",
1018195908Srpaulo		    ether_sprintf(prep->prep_origaddr));
1019195908Srpaulo		vap->iv_stats.is_hwmp_proxy++;
1020195908Srpaulo	} else if (prep->prep_targetseq == hr->hr_targetseq) {
1021195618Srpaulo		/*
1022195618Srpaulo		 * Check if we already have a path to this node.
1023195618Srpaulo		 * If we do, check if this path reply contains a
1024195618Srpaulo		 * better route.
1025195618Srpaulo		 */
1026195908Srpaulo		if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0 ||
1027195908Srpaulo		    (prep->prep_hopcount < rt->rt_nhops ||
1028195908Srpaulo		     prep->prep_metric < rt->rt_metric)) {
1029195908Srpaulo			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1030195908Srpaulo			    "%s path to %s, hopcount %d:%d metric %d:%d",
1031195908Srpaulo			    rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID ?
1032195908Srpaulo				"prefer" : "update",
1033195908Srpaulo			    ether_sprintf(prep->prep_origaddr),
1034195908Srpaulo			    rt->rt_nhops, prep->prep_hopcount,
1035195908Srpaulo			    rt->rt_metric, prep->prep_metric);
1036195618Srpaulo			IEEE80211_ADDR_COPY(rt->rt_nexthop, wh->i_addr2);
1037195618Srpaulo			rt->rt_nhops = prep->prep_hopcount;
1038195618Srpaulo			rt->rt_lifetime = prep->prep_lifetime;
1039195618Srpaulo			rt->rt_metric = prep->prep_metric;
1040195784Srpaulo			rt->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID;
1041195908Srpaulo		} else {
1042195908Srpaulo			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1043195908Srpaulo			    "ignore PREP for %s, hopcount %d:%d metric %d:%d",
1044195908Srpaulo			    ether_sprintf(prep->prep_origaddr),
1045195908Srpaulo			    rt->rt_nhops, prep->prep_hopcount,
1046195908Srpaulo			    rt->rt_metric, prep->prep_metric);
1047195618Srpaulo		}
1048195618Srpaulo	} else {
1049195618Srpaulo		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1050195908Srpaulo		    "discard PREP for %s, wrong seqno %u != %u",
1051195618Srpaulo		    ether_sprintf(prep->prep_origaddr), prep->prep_targetseq,
1052195618Srpaulo		    hr->hr_seq);
1053195618Srpaulo		vap->iv_stats.is_hwmp_wrongseq++;
1054195618Srpaulo	}
1055195618Srpaulo	/*
1056195618Srpaulo	 * Check for frames queued awaiting path discovery.
1057195618Srpaulo	 * XXX probably can tell exactly and avoid remove call
1058195618Srpaulo	 * NB: hash may have false matches, if so they will get
1059195618Srpaulo	 *     stuck back on the stageq because there won't be
1060195618Srpaulo	 *     a path.
1061195618Srpaulo	 */
1062195618Srpaulo	m = ieee80211_ageq_remove(&ic->ic_stageq,
1063195618Srpaulo	    (struct ieee80211_node *)(uintptr_t)
1064195618Srpaulo		ieee80211_mac_hash(ic, rt->rt_dest));
1065195618Srpaulo	for (; m != NULL; m = next) {
1066195618Srpaulo		next = m->m_nextpkt;
1067195618Srpaulo		m->m_nextpkt = NULL;
1068195784Srpaulo		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1069195784Srpaulo		    "flush queued frame %p len %d", m, m->m_pkthdr.len);
1070195618Srpaulo		ifp->if_transmit(ifp, m);
1071195618Srpaulo	}
1072195618Srpaulo}
1073195618Srpaulo
1074195618Srpaulostatic int
1075195618Srpaulohwmp_send_prep(struct ieee80211_node *ni,
1076195618Srpaulo    const uint8_t sa[IEEE80211_ADDR_LEN],
1077195618Srpaulo    const uint8_t da[IEEE80211_ADDR_LEN],
1078195618Srpaulo    struct ieee80211_meshprep_ie *prep)
1079195618Srpaulo{
1080195784Srpaulo	/* NB: there's no PREP minimum interval. */
1081195618Srpaulo
1082195618Srpaulo	/*
1083195618Srpaulo	 * mesh prep action frame format
1084195618Srpaulo	 *     [6] da
1085195618Srpaulo	 *     [6] sa
1086195618Srpaulo	 *     [6] addr3 = sa
1087195618Srpaulo	 *     [1] action
1088195618Srpaulo	 *     [1] category
1089195618Srpaulo	 *     [tlv] mesh path reply
1090195618Srpaulo	 */
1091195618Srpaulo	prep->prep_ie = IEEE80211_ELEMID_MESHPREP;
1092195618Srpaulo	return hwmp_send_action(ni, sa, da, (uint8_t *)prep,
1093195618Srpaulo	    sizeof(struct ieee80211_meshprep_ie));
1094195618Srpaulo}
1095195618Srpaulo
1096197413Srpaulo#define	PERR_DFLAGS(n)	perr.perr_dests[n].dest_flags
1097195618Srpaulo#define	PERR_DADDR(n)	perr.perr_dests[n].dest_addr
1098195618Srpaulo#define	PERR_DSEQ(n)	perr.perr_dests[n].dest_seq
1099197413Srpaulo#define	PERR_DRCODE(n)	perr.perr_dests[n].dest_rcode
1100195618Srpaulostatic void
1101195618Srpaulohwmp_peerdown(struct ieee80211_node *ni)
1102195618Srpaulo{
1103195618Srpaulo	struct ieee80211vap *vap = ni->ni_vap;
1104197413Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
1105195618Srpaulo	struct ieee80211_meshperr_ie perr;
1106195618Srpaulo	struct ieee80211_mesh_route *rt;
1107195618Srpaulo	struct ieee80211_hwmp_route *hr;
1108195618Srpaulo
1109195618Srpaulo	rt = ieee80211_mesh_rt_find(vap, ni->ni_macaddr);
1110195618Srpaulo	if (rt == NULL)
1111195618Srpaulo		return;
1112195618Srpaulo	hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
1113195618Srpaulo	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1114195784Srpaulo	    "%s", "delete route entry");
1115197413Srpaulo	perr.perr_ttl = ms->ms_ttl;
1116195618Srpaulo	perr.perr_ndests = 1;
1117197413Srpaulo	if (hr->hr_seq == 0)
1118197413Srpaulo		PERR_DFLAGS(0) |= IEEE80211_MESHPERR_DFLAGS_USN;
1119197413Srpaulo	PERR_DFLAGS(0) |= IEEE80211_MESHPERR_DFLAGS_RC;
1120195618Srpaulo	IEEE80211_ADDR_COPY(PERR_DADDR(0), rt->rt_dest);
1121195618Srpaulo	PERR_DSEQ(0) = hr->hr_seq;
1122197413Srpaulo	PERR_DRCODE(0) = IEEE80211_REASON_MESH_PERR_DEST_UNREACH;
1123195908Srpaulo	/* NB: flush everything passing through peer */
1124195908Srpaulo	ieee80211_mesh_rt_flush_peer(vap, ni->ni_macaddr);
1125195618Srpaulo	hwmp_send_perr(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &perr);
1126195618Srpaulo}
1127197413Srpaulo#undef	PERR_DFLAGS
1128195618Srpaulo#undef	PERR_DADDR
1129195618Srpaulo#undef	PERR_DSEQ
1130197413Srpaulo#undef	PERR_DRCODE
1131195618Srpaulo
1132197413Srpaulo#define	PERR_DFLAGS(n)	perr->perr_dests[n].dest_flags
1133195618Srpaulo#define	PERR_DADDR(n)	perr->perr_dests[n].dest_addr
1134195618Srpaulo#define	PERR_DSEQ(n)	perr->perr_dests[n].dest_seq
1135197413Srpaulo#define	PERR_DRCODE(n)	perr->perr_dests[n].dest_rcode
1136195618Srpaulostatic void
1137195618Srpaulohwmp_recv_perr(struct ieee80211vap *vap, struct ieee80211_node *ni,
1138195618Srpaulo    const struct ieee80211_frame *wh, const struct ieee80211_meshperr_ie *perr)
1139195618Srpaulo{
1140195618Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
1141195618Srpaulo	struct ieee80211_mesh_route *rt = NULL;
1142195618Srpaulo	struct ieee80211_hwmp_route *hr;
1143195618Srpaulo 	struct ieee80211_meshperr_ie pperr;
1144195618Srpaulo	int i, forward = 0;
1145195618Srpaulo
1146195618Srpaulo	/*
1147195618Srpaulo	 * Acceptance criteria: check if we received a PERR from a
1148195618Srpaulo	 * neighbor and forwarding is enabled.
1149195618Srpaulo	 */
1150195618Srpaulo	if (ni == vap->iv_bss ||
1151195618Srpaulo	    ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED ||
1152195618Srpaulo	    !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD))
1153195618Srpaulo		return;
1154195618Srpaulo	/*
1155195618Srpaulo	 * Find all routing entries that match and delete them.
1156195618Srpaulo	 */
1157195618Srpaulo	for (i = 0; i < perr->perr_ndests; i++) {
1158195618Srpaulo		rt = ieee80211_mesh_rt_find(vap, PERR_DADDR(i));
1159195618Srpaulo		if (rt == NULL)
1160195618Srpaulo			continue;
1161197413Srpaulo		hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
1162197413Srpaulo		if (!(PERR_DFLAGS(0) & IEEE80211_MESHPERR_DFLAGS_USN) &&
1163197413Srpaulo		    HWMP_SEQ_GEQ(PERR_DSEQ(i), hr->hr_seq)) {
1164195618Srpaulo			ieee80211_mesh_rt_del(vap, rt->rt_dest);
1165195908Srpaulo			ieee80211_mesh_rt_flush_peer(vap, rt->rt_dest);
1166195618Srpaulo			rt = NULL;
1167195618Srpaulo			forward = 1;
1168195618Srpaulo		}
1169195618Srpaulo	}
1170195618Srpaulo	/*
1171195618Srpaulo	 * Propagate the PERR if we previously found it on our routing table.
1172195618Srpaulo	 * XXX handle ndest > 1
1173195618Srpaulo	 */
1174197413Srpaulo	if (forward && perr->perr_ttl > 1) {
1175195618Srpaulo		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1176195784Srpaulo		    "propagate PERR from %s", ether_sprintf(wh->i_addr2));
1177195618Srpaulo		memcpy(&pperr, perr, sizeof(*perr));
1178197413Srpaulo		pperr.perr_ttl--;
1179195908Srpaulo		hwmp_send_perr(vap->iv_bss, vap->iv_myaddr, broadcastaddr,
1180195908Srpaulo		    &pperr);
1181195618Srpaulo	}
1182195618Srpaulo}
1183195618Srpaulo#undef	PEER_DADDR
1184195618Srpaulo#undef	PERR_DSEQ
1185195618Srpaulo
1186195618Srpaulostatic int
1187195618Srpaulohwmp_send_perr(struct ieee80211_node *ni,
1188195618Srpaulo    const uint8_t sa[IEEE80211_ADDR_LEN],
1189195618Srpaulo    const uint8_t da[IEEE80211_ADDR_LEN],
1190195618Srpaulo    struct ieee80211_meshperr_ie *perr)
1191195618Srpaulo{
1192195618Srpaulo	struct ieee80211_hwmp_state *hs = ni->ni_vap->iv_hwmp;
1193195618Srpaulo
1194195618Srpaulo	/*
1195195618Srpaulo	 * Enforce PERR interval.
1196195618Srpaulo	 */
1197195618Srpaulo	if (ratecheck(&hs->hs_lastperr, &ieee80211_hwmp_perrminint) == 0)
1198195618Srpaulo		return EALREADY;
1199195618Srpaulo	getmicrouptime(&hs->hs_lastperr);
1200195618Srpaulo
1201195618Srpaulo	/*
1202195618Srpaulo	 * mesh perr action frame format
1203195618Srpaulo	 *     [6] da
1204195618Srpaulo	 *     [6] sa
1205195618Srpaulo	 *     [6] addr3 = sa
1206195618Srpaulo	 *     [1] action
1207195618Srpaulo	 *     [1] category
1208195618Srpaulo	 *     [tlv] mesh path error
1209195618Srpaulo	 */
1210195618Srpaulo	perr->perr_ie = IEEE80211_ELEMID_MESHPERR;
1211195618Srpaulo	return hwmp_send_action(ni, sa, da, (uint8_t *)perr,
1212195618Srpaulo	    sizeof(struct ieee80211_meshperr_ie));
1213195618Srpaulo}
1214195618Srpaulo
1215195618Srpaulostatic void
1216195618Srpaulohwmp_recv_rann(struct ieee80211vap *vap, struct ieee80211_node *ni,
1217195618Srpaulo    const struct ieee80211_frame *wh, const struct ieee80211_meshrann_ie *rann)
1218195618Srpaulo{
1219195618Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
1220195618Srpaulo	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
1221195618Srpaulo	struct ieee80211_mesh_route *rt = NULL;
1222195618Srpaulo	struct ieee80211_hwmp_route *hr;
1223195618Srpaulo	struct ieee80211_meshrann_ie prann;
1224195618Srpaulo
1225195618Srpaulo	if (ni == vap->iv_bss ||
1226195618Srpaulo	    ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED)
1227195618Srpaulo		return;
1228195618Srpaulo
1229195618Srpaulo	rt = ieee80211_mesh_rt_find(vap, rann->rann_addr);
1230195618Srpaulo	/*
1231195618Srpaulo	 * Discover the path to the root mesh STA.
1232195618Srpaulo	 * If we already know it, propagate the RANN element.
1233195618Srpaulo	 */
1234195618Srpaulo	if (rt == NULL) {
1235195618Srpaulo		hwmp_discover(vap, rann->rann_addr, NULL);
1236195618Srpaulo		return;
1237195618Srpaulo	}
1238195618Srpaulo	hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
1239195908Srpaulo	if (HWMP_SEQ_GT(rann->rann_seq, hr->hr_seq) && rann->rann_ttl > 1 &&
1240195618Srpaulo	    rann->rann_hopcount < hs->hs_maxhops &&
1241195618Srpaulo	    (ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) {
1242195618Srpaulo		memcpy(&prann, rann, sizeof(prann));
1243195618Srpaulo		prann.rann_hopcount += 1;
1244195618Srpaulo		prann.rann_ttl -= 1;
1245195618Srpaulo		prann.rann_metric += ms->ms_pmetric->mpm_metric(ni);
1246195618Srpaulo		hwmp_send_rann(vap->iv_bss, vap->iv_myaddr, broadcastaddr,
1247195618Srpaulo		    &prann);
1248195618Srpaulo	}
1249195618Srpaulo}
1250195618Srpaulo
1251195618Srpaulostatic int
1252195618Srpaulohwmp_send_rann(struct ieee80211_node *ni,
1253195618Srpaulo    const uint8_t sa[IEEE80211_ADDR_LEN],
1254195618Srpaulo    const uint8_t da[IEEE80211_ADDR_LEN],
1255195618Srpaulo    struct ieee80211_meshrann_ie *rann)
1256195618Srpaulo{
1257195618Srpaulo	/*
1258195618Srpaulo	 * mesh rann action frame format
1259195618Srpaulo	 *     [6] da
1260195618Srpaulo	 *     [6] sa
1261195618Srpaulo	 *     [6] addr3 = sa
1262195618Srpaulo	 *     [1] action
1263195618Srpaulo	 *     [1] category
1264195618Srpaulo	 *     [tlv] root annoucement
1265195618Srpaulo	 */
1266195618Srpaulo	rann->rann_ie = IEEE80211_ELEMID_MESHRANN;
1267195618Srpaulo	return hwmp_send_action(ni, sa, da, (uint8_t *)rann,
1268195618Srpaulo	    sizeof(struct ieee80211_meshrann_ie));
1269195618Srpaulo}
1270195618Srpaulo
1271195618Srpaulo#define	PREQ_TFLAGS(n)	preq.preq_targets[n].target_flags
1272195618Srpaulo#define	PREQ_TADDR(n)	preq.preq_targets[n].target_addr
1273195618Srpaulo#define	PREQ_TSEQ(n)	preq.preq_targets[n].target_seq
1274195618Srpaulostatic struct ieee80211_node *
1275195618Srpaulohwmp_discover(struct ieee80211vap *vap,
1276195618Srpaulo    const uint8_t dest[IEEE80211_ADDR_LEN], struct mbuf *m)
1277195618Srpaulo{
1278195618Srpaulo	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
1279195618Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
1280195618Srpaulo	struct ieee80211_mesh_route *rt = NULL;
1281195618Srpaulo	struct ieee80211_hwmp_route *hr;
1282195618Srpaulo	struct ieee80211_meshpreq_ie preq;
1283195618Srpaulo	struct ieee80211_node *ni;
1284195618Srpaulo	int sendpreq = 0;
1285195618Srpaulo
1286195618Srpaulo	KASSERT(vap->iv_opmode == IEEE80211_M_MBSS,
1287195618Srpaulo	    ("not a mesh vap, opmode %d", vap->iv_opmode));
1288195618Srpaulo
1289195618Srpaulo	KASSERT(!IEEE80211_ADDR_EQ(vap->iv_myaddr, dest),
1290195618Srpaulo	    ("%s: discovering self!", __func__));
1291195618Srpaulo
1292195618Srpaulo	ni = NULL;
1293195618Srpaulo	if (!IEEE80211_IS_MULTICAST(dest)) {
1294195618Srpaulo		rt = ieee80211_mesh_rt_find(vap, dest);
1295195618Srpaulo		if (rt == NULL) {
1296195618Srpaulo			rt = ieee80211_mesh_rt_add(vap, dest);
1297195618Srpaulo			if (rt == NULL) {
1298195784Srpaulo				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP,
1299195784Srpaulo				    ni, "unable to add discovery path to %s",
1300195784Srpaulo				    ether_sprintf(dest));
1301195784Srpaulo				vap->iv_stats.is_mesh_rtaddfailed++;
1302195618Srpaulo				goto done;
1303195618Srpaulo			}
1304195618Srpaulo		}
1305195618Srpaulo		hr = IEEE80211_MESH_ROUTE_PRIV(rt,
1306195618Srpaulo		    struct ieee80211_hwmp_route);
1307195784Srpaulo		if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0) {
1308195908Srpaulo			if (hr->hr_targetseq == 0)
1309195908Srpaulo				hr->hr_targetseq = ++hs->hs_seq;
1310195618Srpaulo			rt->rt_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
1311195618Srpaulo			rt->rt_lifetime =
1312195813Ssam			    ticks_to_msecs(ieee80211_hwmp_pathtimeout);
1313195618Srpaulo			/* XXX check preq retries */
1314195618Srpaulo			sendpreq = 1;
1315195618Srpaulo			IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP, dest,
1316195784Srpaulo			    "start path discovery (src %s)",
1317195784Srpaulo			    m == NULL ? "<none>" : ether_sprintf(
1318195784Srpaulo				mtod(m, struct ether_header *)->ether_shost));
1319195618Srpaulo			/*
1320195618Srpaulo			 * Try to discover the path for this node.
1321195618Srpaulo			 */
1322195618Srpaulo			preq.preq_flags = 0;
1323195618Srpaulo			preq.preq_hopcount = 0;
1324195618Srpaulo			preq.preq_ttl = ms->ms_ttl;
1325195908Srpaulo			preq.preq_id = ++hs->hs_preqid;
1326195618Srpaulo			IEEE80211_ADDR_COPY(preq.preq_origaddr, vap->iv_myaddr);
1327195908Srpaulo			preq.preq_origseq = hr->hr_targetseq;
1328195618Srpaulo			preq.preq_lifetime = rt->rt_lifetime;
1329195618Srpaulo			preq.preq_metric = rt->rt_metric;
1330195618Srpaulo			preq.preq_tcount = 1;
1331195618Srpaulo			IEEE80211_ADDR_COPY(PREQ_TADDR(0), dest);
1332195618Srpaulo			PREQ_TFLAGS(0) = 0;
1333195618Srpaulo			if (ieee80211_hwmp_targetonly)
1334195618Srpaulo				PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_TO;
1335195618Srpaulo			if (ieee80211_hwmp_replyforward)
1336195618Srpaulo				PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_RF;
1337195618Srpaulo			PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_USN;
1338195618Srpaulo			PREQ_TSEQ(0) = 0;
1339195618Srpaulo			/* XXX check return value */
1340195618Srpaulo			hwmp_send_preq(vap->iv_bss, vap->iv_myaddr,
1341195618Srpaulo			    broadcastaddr, &preq);
1342195618Srpaulo		}
1343195784Srpaulo		if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID)
1344195618Srpaulo			ni = ieee80211_find_txnode(vap, rt->rt_nexthop);
1345195618Srpaulo	} else {
1346195618Srpaulo		ni = ieee80211_find_txnode(vap, dest);
1347195784Srpaulo		/* NB: if null then we leak mbuf */
1348195784Srpaulo		KASSERT(ni != NULL, ("leak mcast frame"));
1349195618Srpaulo		return ni;
1350195618Srpaulo	}
1351195618Srpaulodone:
1352195618Srpaulo	if (ni == NULL && m != NULL) {
1353195618Srpaulo		if (sendpreq) {
1354195618Srpaulo			struct ieee80211com *ic = vap->iv_ic;
1355195618Srpaulo			/*
1356195618Srpaulo			 * Queue packet for transmit when path discovery
1357195618Srpaulo			 * completes.  If discovery never completes the
1358195618Srpaulo			 * frame will be flushed by way of the aging timer.
1359195618Srpaulo			 */
1360195784Srpaulo			IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP, dest,
1361195784Srpaulo			    "%s", "queue frame until path found");
1362195618Srpaulo			m->m_pkthdr.rcvif = (void *)(uintptr_t)
1363195618Srpaulo			    ieee80211_mac_hash(ic, dest);
1364195618Srpaulo			/* XXX age chosen randomly */
1365195618Srpaulo			ieee80211_ageq_append(&ic->ic_stageq, m,
1366195618Srpaulo			    IEEE80211_INACT_WAIT);
1367195784Srpaulo		} else {
1368195784Srpaulo			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_HWMP,
1369195784Srpaulo			    dest, NULL, "%s", "no valid path to this node");
1370195618Srpaulo			m_freem(m);
1371195784Srpaulo		}
1372195618Srpaulo	}
1373195618Srpaulo	return ni;
1374195618Srpaulo}
1375195618Srpaulo#undef	PREQ_TFLAGS
1376195618Srpaulo#undef	PREQ_TADDR
1377195618Srpaulo#undef	PREQ_TSEQ
1378195618Srpaulo
1379195618Srpaulostatic int
1380195618Srpaulohwmp_ioctl_get80211(struct ieee80211vap *vap, struct ieee80211req *ireq)
1381195618Srpaulo{
1382195618Srpaulo	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
1383195618Srpaulo	int error;
1384195618Srpaulo
1385195618Srpaulo	if (vap->iv_opmode != IEEE80211_M_MBSS)
1386195618Srpaulo		return ENOSYS;
1387195618Srpaulo	error = 0;
1388195618Srpaulo	switch (ireq->i_type) {
1389195618Srpaulo	case IEEE80211_IOC_HWMP_ROOTMODE:
1390195618Srpaulo		ireq->i_val = hs->hs_rootmode;
1391195618Srpaulo		break;
1392195618Srpaulo	case IEEE80211_IOC_HWMP_MAXHOPS:
1393195618Srpaulo		ireq->i_val = hs->hs_maxhops;
1394195618Srpaulo		break;
1395195618Srpaulo	default:
1396195618Srpaulo		return ENOSYS;
1397195618Srpaulo	}
1398195618Srpaulo	return error;
1399195618Srpaulo}
1400195618SrpauloIEEE80211_IOCTL_GET(hwmp, hwmp_ioctl_get80211);
1401195618Srpaulo
1402195618Srpaulostatic int
1403195618Srpaulohwmp_ioctl_set80211(struct ieee80211vap *vap, struct ieee80211req *ireq)
1404195618Srpaulo{
1405195618Srpaulo	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
1406195618Srpaulo	int error;
1407195618Srpaulo
1408195618Srpaulo	if (vap->iv_opmode != IEEE80211_M_MBSS)
1409195618Srpaulo		return ENOSYS;
1410195618Srpaulo	error = 0;
1411195618Srpaulo	switch (ireq->i_type) {
1412195618Srpaulo	case IEEE80211_IOC_HWMP_ROOTMODE:
1413195618Srpaulo		if (ireq->i_val < 0 || ireq->i_val > 3)
1414195618Srpaulo			return EINVAL;
1415195618Srpaulo		hs->hs_rootmode = ireq->i_val;
1416195618Srpaulo		hwmp_rootmode_setup(vap);
1417195618Srpaulo		break;
1418195618Srpaulo	case IEEE80211_IOC_HWMP_MAXHOPS:
1419195618Srpaulo		if (ireq->i_val <= 0 || ireq->i_val > 255)
1420195618Srpaulo			return EINVAL;
1421195618Srpaulo		hs->hs_maxhops = ireq->i_val;
1422195618Srpaulo		break;
1423195618Srpaulo	default:
1424195618Srpaulo		return ENOSYS;
1425195618Srpaulo	}
1426195618Srpaulo	return error;
1427195618Srpaulo}
1428195618SrpauloIEEE80211_IOCTL_SET(hwmp, hwmp_ioctl_set80211);
1429