ieee80211_hwmp.c revision 198581
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 198581 2009-10-29 12:19:10Z 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 */
151198230Srpaulo	ieee80211_hwmp_seq	hr_origseq;	/* 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",
195197975Srpaulo	.mpp_ie		= IEEE80211_MESHCONF_PATH_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++) {
551198260Srpaulo		*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
656198369Srpaulo	rann.rann_flags = 0;
657195908Srpaulo	if (ms->ms_flags & IEEE80211_MESHFLAGS_PORTAL)
658195908Srpaulo		rann.rann_flags |= IEEE80211_MESHRANN_FLAGS_PR;
659195618Srpaulo	rann.rann_hopcount = 0;
660195618Srpaulo	rann.rann_ttl = ms->ms_ttl;
661195618Srpaulo	IEEE80211_ADDR_COPY(rann.rann_addr, vap->iv_myaddr);
662195618Srpaulo	rann.rann_seq = ++hs->hs_seq;
663195618Srpaulo	rann.rann_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
664195618Srpaulo
665195618Srpaulo	vap->iv_stats.is_hwmp_rootrann++;
666195618Srpaulo	hwmp_send_rann(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &rann);
667195618Srpaulo	hwmp_rootmode_setup(vap);
668195618Srpaulo}
669195618Srpaulo
670195618Srpaulo#define	PREQ_TFLAGS(n)	preq->preq_targets[n].target_flags
671195618Srpaulo#define	PREQ_TADDR(n)	preq->preq_targets[n].target_addr
672195618Srpaulo#define	PREQ_TSEQ(n)	preq->preq_targets[n].target_seq
673195618Srpaulostatic void
674195618Srpaulohwmp_recv_preq(struct ieee80211vap *vap, struct ieee80211_node *ni,
675195618Srpaulo    const struct ieee80211_frame *wh, const struct ieee80211_meshpreq_ie *preq)
676195618Srpaulo{
677195618Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
678195618Srpaulo	struct ieee80211_mesh_route *rt = NULL;
679195908Srpaulo	struct ieee80211_mesh_route *rtorig = NULL;
680195908Srpaulo	struct ieee80211_hwmp_route *hrorig;
681195618Srpaulo	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
682195618Srpaulo	struct ieee80211_meshprep_ie prep;
683195618Srpaulo
684195618Srpaulo	if (ni == vap->iv_bss ||
685195618Srpaulo	    ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED)
686195618Srpaulo		return;
687195618Srpaulo	/*
688195618Srpaulo	 * Ignore PREQs from us. Could happen because someone forward it
689195618Srpaulo	 * back to us.
690195618Srpaulo	 */
691195618Srpaulo	if (IEEE80211_ADDR_EQ(vap->iv_myaddr, preq->preq_origaddr))
692195618Srpaulo		return;
693195618Srpaulo
694195618Srpaulo	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
695195618Srpaulo	    "received PREQ, source %s", ether_sprintf(preq->preq_origaddr));
696195618Srpaulo
697195618Srpaulo	/*
698195618Srpaulo	 * Acceptance criteria: if the PREQ is not for us and
699195618Srpaulo	 * forwarding is disabled, discard this PREQ.
700195618Srpaulo	 */
701195618Srpaulo	if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0)) &&
702195618Srpaulo	    !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) {
703195618Srpaulo		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_HWMP,
704195618Srpaulo		    preq->preq_origaddr, NULL, "%s", "not accepting PREQ");
705195618Srpaulo		return;
706195618Srpaulo	}
707195908Srpaulo	rtorig = ieee80211_mesh_rt_find(vap, preq->preq_origaddr);
708195908Srpaulo	if (rtorig == NULL)
709195908Srpaulo		rtorig = ieee80211_mesh_rt_add(vap, preq->preq_origaddr);
710195908Srpaulo	hrorig = IEEE80211_MESH_ROUTE_PRIV(rtorig, struct ieee80211_hwmp_route);
711195618Srpaulo	/*
712195908Srpaulo	 * Sequence number validation.
713195908Srpaulo	 */
714195908Srpaulo	if (HWMP_SEQ_LEQ(preq->preq_id, hrorig->hr_preqid) &&
715195908Srpaulo	    HWMP_SEQ_LEQ(preq->preq_origseq, hrorig->hr_seq)) {
716195908Srpaulo		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
717195908Srpaulo		    "discard PREQ from %s, old seq no %u <= %u",
718195908Srpaulo		    ether_sprintf(preq->preq_origaddr),
719195908Srpaulo		    preq->preq_origseq, hrorig->hr_seq);
720195908Srpaulo		return;
721195908Srpaulo	}
722195908Srpaulo	hrorig->hr_preqid = preq->preq_id;
723195908Srpaulo	hrorig->hr_seq = preq->preq_origseq;
724195908Srpaulo
725195908Srpaulo	/*
726195618Srpaulo	 * Check if the PREQ is addressed to us.
727195618Srpaulo	 */
728195618Srpaulo	if (IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0))) {
729195618Srpaulo		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
730195784Srpaulo		    "reply to %s", ether_sprintf(preq->preq_origaddr));
731195618Srpaulo		/*
732195618Srpaulo		 * Build and send a PREP frame.
733195618Srpaulo		 */
734195618Srpaulo		prep.prep_flags = 0;
735195618Srpaulo		prep.prep_hopcount = 0;
736195618Srpaulo		prep.prep_ttl = ms->ms_ttl;
737198230Srpaulo		IEEE80211_ADDR_COPY(prep.prep_targetaddr, vap->iv_myaddr);
738198230Srpaulo		prep.prep_targetseq = ++hs->hs_seq;
739195618Srpaulo		prep.prep_lifetime = preq->preq_lifetime;
740195618Srpaulo		prep.prep_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
741198230Srpaulo		IEEE80211_ADDR_COPY(prep.prep_origaddr, preq->preq_origaddr);
742198230Srpaulo		prep.prep_origseq = preq->preq_origseq;
743195618Srpaulo		hwmp_send_prep(ni, vap->iv_myaddr, wh->i_addr2, &prep);
744195618Srpaulo		/*
745195618Srpaulo		 * Build the reverse path, if we don't have it already.
746195618Srpaulo		 */
747195618Srpaulo		rt = ieee80211_mesh_rt_find(vap, preq->preq_origaddr);
748195618Srpaulo		if (rt == NULL)
749195618Srpaulo			hwmp_discover(vap, preq->preq_origaddr, NULL);
750195784Srpaulo		else if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0)
751195618Srpaulo			hwmp_discover(vap, rt->rt_dest, NULL);
752195618Srpaulo		return;
753195618Srpaulo	}
754195618Srpaulo	/*
755195618Srpaulo	 * Proactive PREQ: reply with a proactive PREP to the
756195618Srpaulo	 * root STA if requested.
757195618Srpaulo	 */
758195618Srpaulo	if (IEEE80211_ADDR_EQ(PREQ_TADDR(0), broadcastaddr) &&
759195618Srpaulo	    (PREQ_TFLAGS(0) &
760195618Srpaulo	    ((IEEE80211_MESHPREQ_TFLAGS_TO|IEEE80211_MESHPREQ_TFLAGS_RF) ==
761195618Srpaulo	    (IEEE80211_MESHPREQ_TFLAGS_TO|IEEE80211_MESHPREQ_TFLAGS_RF)))) {
762195618Srpaulo		uint8_t rootmac[IEEE80211_ADDR_LEN];
763195618Srpaulo
764195618Srpaulo		IEEE80211_ADDR_COPY(rootmac, preq->preq_origaddr);
765195618Srpaulo		rt = ieee80211_mesh_rt_find(vap, rootmac);
766195784Srpaulo		if (rt == NULL) {
767195618Srpaulo			rt = ieee80211_mesh_rt_add(vap, rootmac);
768195784Srpaulo			if (rt == NULL) {
769195784Srpaulo				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
770195784Srpaulo				    "unable to add root mesh path to %s",
771195784Srpaulo				    ether_sprintf(rootmac));
772195784Srpaulo				vap->iv_stats.is_mesh_rtaddfailed++;
773195784Srpaulo				return;
774195784Srpaulo			}
775195784Srpaulo		}
776195784Srpaulo		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
777195784Srpaulo		    "root mesh station @ %s", ether_sprintf(rootmac));
778195784Srpaulo
779195618Srpaulo		/*
780195618Srpaulo		 * Reply with a PREP if we don't have a path to the root
781195618Srpaulo		 * or if the root sent us a proactive PREQ.
782195618Srpaulo		 */
783195784Srpaulo		if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0 ||
784195618Srpaulo		    (preq->preq_flags & IEEE80211_MESHPREQ_FLAGS_PP)) {
785195618Srpaulo			prep.prep_flags = 0;
786195618Srpaulo			prep.prep_hopcount = 0;
787195618Srpaulo			prep.prep_ttl = ms->ms_ttl;
788198230Srpaulo			IEEE80211_ADDR_COPY(prep.prep_origaddr, rootmac);
789195618Srpaulo			prep.prep_origseq = preq->preq_origseq;
790195618Srpaulo			prep.prep_lifetime = preq->preq_lifetime;
791195618Srpaulo			prep.prep_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
792198230Srpaulo			IEEE80211_ADDR_COPY(prep.prep_targetaddr,
793198230Srpaulo			    vap->iv_myaddr);
794198230Srpaulo			prep.prep_targetseq = ++hs->hs_seq;
795195618Srpaulo			hwmp_send_prep(vap->iv_bss, vap->iv_myaddr,
796195618Srpaulo			    broadcastaddr, &prep);
797195618Srpaulo		}
798195618Srpaulo		hwmp_discover(vap, rootmac, NULL);
799195618Srpaulo		return;
800195618Srpaulo	}
801195618Srpaulo	rt = ieee80211_mesh_rt_find(vap, PREQ_TADDR(0));
802195618Srpaulo
803195618Srpaulo	/*
804195618Srpaulo	 * Forwarding and Intermediate reply for PREQs with 1 target.
805195618Srpaulo	 */
806195618Srpaulo	if (preq->preq_tcount == 1) {
807195618Srpaulo		struct ieee80211_meshpreq_ie ppreq; /* propagated PREQ */
808195618Srpaulo
809195618Srpaulo		memcpy(&ppreq, preq, sizeof(ppreq));
810195618Srpaulo		/*
811195618Srpaulo		 * We have a valid route to this node.
812195618Srpaulo		 */
813195618Srpaulo		if (rt != NULL &&
814195784Srpaulo		    (rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID)) {
815195618Srpaulo			if (preq->preq_ttl > 1 &&
816195618Srpaulo			    preq->preq_hopcount < hs->hs_maxhops) {
817195618Srpaulo				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
818195784Srpaulo				    "forward PREQ from %s",
819195618Srpaulo				    ether_sprintf(preq->preq_origaddr));
820195618Srpaulo				/*
821195618Srpaulo				 * Propagate the original PREQ.
822195618Srpaulo				 */
823195618Srpaulo				ppreq.preq_hopcount += 1;
824195618Srpaulo				ppreq.preq_ttl -= 1;
825195618Srpaulo				ppreq.preq_metric +=
826195618Srpaulo				    ms->ms_pmetric->mpm_metric(ni);
827195618Srpaulo				/*
828195618Srpaulo				 * Set TO and unset RF bits because we are going
829195618Srpaulo				 * to send a PREP next.
830195618Srpaulo				 */
831195618Srpaulo				ppreq.preq_targets[0].target_flags |=
832195618Srpaulo				    IEEE80211_MESHPREQ_TFLAGS_TO;
833195618Srpaulo				ppreq.preq_targets[0].target_flags &=
834195618Srpaulo				    ~IEEE80211_MESHPREQ_TFLAGS_RF;
835195618Srpaulo				hwmp_send_preq(ni, vap->iv_myaddr,
836195618Srpaulo				    broadcastaddr, &ppreq);
837195618Srpaulo			}
838195618Srpaulo			/*
839195618Srpaulo			 * Check if we can send an intermediate Path Reply,
840195618Srpaulo			 * i.e., Target Only bit is not set.
841195618Srpaulo			 */
842195618Srpaulo	    		if (!(PREQ_TFLAGS(0) & IEEE80211_MESHPREQ_TFLAGS_TO)) {
843195618Srpaulo				struct ieee80211_meshprep_ie prep;
844195618Srpaulo
845195618Srpaulo				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
846195618Srpaulo				    "intermediate reply for PREQ from %s",
847195618Srpaulo				    ether_sprintf(preq->preq_origaddr));
848195618Srpaulo				prep.prep_flags = 0;
849195618Srpaulo				prep.prep_hopcount = rt->rt_nhops + 1;
850195618Srpaulo				prep.prep_ttl = ms->ms_ttl;
851195618Srpaulo				IEEE80211_ADDR_COPY(&prep.prep_targetaddr,
852198230Srpaulo				    PREQ_TADDR(0));
853195908Srpaulo				prep.prep_targetseq = hrorig->hr_seq;
854195618Srpaulo				prep.prep_lifetime = preq->preq_lifetime;
855195618Srpaulo				prep.prep_metric = rt->rt_metric +
856195618Srpaulo				    ms->ms_pmetric->mpm_metric(ni);
857195618Srpaulo				IEEE80211_ADDR_COPY(&prep.prep_origaddr,
858198230Srpaulo				    preq->preq_origaddr);
859195908Srpaulo				prep.prep_origseq = hrorig->hr_seq;
860195618Srpaulo				hwmp_send_prep(ni, vap->iv_myaddr,
861195618Srpaulo				    broadcastaddr, &prep);
862195618Srpaulo			}
863195618Srpaulo		/*
864195618Srpaulo		 * We have no information about this path,
865195618Srpaulo		 * propagate the PREQ.
866195618Srpaulo		 */
867195618Srpaulo		} else if (preq->preq_ttl > 1 &&
868195618Srpaulo		    preq->preq_hopcount < hs->hs_maxhops) {
869195784Srpaulo			if (rt == NULL) {
870195618Srpaulo				rt = ieee80211_mesh_rt_add(vap, PREQ_TADDR(0));
871195784Srpaulo				if (rt == NULL) {
872195784Srpaulo					IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP,
873195784Srpaulo					    ni, "unable to add PREQ path to %s",
874195784Srpaulo					    ether_sprintf(PREQ_TADDR(0)));
875195784Srpaulo					vap->iv_stats.is_mesh_rtaddfailed++;
876195784Srpaulo					return;
877195784Srpaulo				}
878195784Srpaulo			}
879195618Srpaulo			rt->rt_metric = preq->preq_metric;
880195618Srpaulo			rt->rt_lifetime = preq->preq_lifetime;
881195908Srpaulo			hrorig = IEEE80211_MESH_ROUTE_PRIV(rt,
882195908Srpaulo			    struct ieee80211_hwmp_route);
883195908Srpaulo			hrorig->hr_seq = preq->preq_origseq;
884195908Srpaulo			hrorig->hr_preqid = preq->preq_id;
885195618Srpaulo
886195618Srpaulo			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
887195784Srpaulo			    "forward PREQ from %s",
888195618Srpaulo			    ether_sprintf(preq->preq_origaddr));
889195618Srpaulo			ppreq.preq_hopcount += 1;
890195618Srpaulo			ppreq.preq_ttl -= 1;
891195618Srpaulo			ppreq.preq_metric += ms->ms_pmetric->mpm_metric(ni);
892195618Srpaulo			hwmp_send_preq(ni, vap->iv_myaddr, broadcastaddr,
893195618Srpaulo			    &ppreq);
894195618Srpaulo		}
895195618Srpaulo	}
896195618Srpaulo
897195618Srpaulo}
898195618Srpaulo#undef	PREQ_TFLAGS
899195618Srpaulo#undef	PREQ_TADDR
900195618Srpaulo#undef	PREQ_TSEQ
901195618Srpaulo
902195618Srpaulostatic int
903195618Srpaulohwmp_send_preq(struct ieee80211_node *ni,
904195618Srpaulo    const uint8_t sa[IEEE80211_ADDR_LEN],
905195618Srpaulo    const uint8_t da[IEEE80211_ADDR_LEN],
906195618Srpaulo    struct ieee80211_meshpreq_ie *preq)
907195618Srpaulo{
908195618Srpaulo	struct ieee80211_hwmp_state *hs = ni->ni_vap->iv_hwmp;
909195618Srpaulo
910195618Srpaulo	/*
911195618Srpaulo	 * Enforce PREQ interval.
912195618Srpaulo	 */
913195618Srpaulo	if (ratecheck(&hs->hs_lastpreq, &ieee80211_hwmp_preqminint) == 0)
914195618Srpaulo		return EALREADY;
915195618Srpaulo	getmicrouptime(&hs->hs_lastpreq);
916195618Srpaulo
917195618Srpaulo	/*
918195618Srpaulo	 * mesh preq action frame format
919195618Srpaulo	 *     [6] da
920195618Srpaulo	 *     [6] sa
921195618Srpaulo	 *     [6] addr3 = sa
922195618Srpaulo	 *     [1] action
923195618Srpaulo	 *     [1] category
924195618Srpaulo	 *     [tlv] mesh path request
925195618Srpaulo	 */
926195618Srpaulo	preq->preq_ie = IEEE80211_ELEMID_MESHPREQ;
927195618Srpaulo	return hwmp_send_action(ni, sa, da, (uint8_t *)preq,
928195618Srpaulo	    sizeof(struct ieee80211_meshpreq_ie));
929195618Srpaulo}
930195618Srpaulo
931195618Srpaulostatic void
932195618Srpaulohwmp_recv_prep(struct ieee80211vap *vap, struct ieee80211_node *ni,
933195618Srpaulo    const struct ieee80211_frame *wh, const struct ieee80211_meshprep_ie *prep)
934195618Srpaulo{
935195618Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
936195618Srpaulo	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
937195618Srpaulo	struct ieee80211_mesh_route *rt = NULL;
938195618Srpaulo	struct ieee80211_hwmp_route *hr;
939195618Srpaulo	struct ieee80211com *ic = vap->iv_ic;
940195618Srpaulo	struct ifnet *ifp = vap->iv_ifp;
941195618Srpaulo	struct mbuf *m, *next;
942195618Srpaulo
943195618Srpaulo	/*
944195618Srpaulo	 * Acceptance criteria: if the corresponding PREQ was not generated
945195618Srpaulo	 * by us and forwarding is disabled, discard this PREP.
946195618Srpaulo	 */
947195618Srpaulo	if (ni == vap->iv_bss ||
948195618Srpaulo	    ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED)
949195618Srpaulo		return;
950195618Srpaulo	if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, prep->prep_origaddr) &&
951195618Srpaulo	    !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD))
952195618Srpaulo		return;
953195618Srpaulo
954195618Srpaulo	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
955198230Srpaulo	    "received PREP from %s", ether_sprintf(prep->prep_targetaddr));
956195618Srpaulo
957198230Srpaulo	rt = ieee80211_mesh_rt_find(vap, prep->prep_targetaddr);
958195618Srpaulo	if (rt == NULL) {
959195618Srpaulo		/*
960195618Srpaulo		 * If we have no entry this could be a reply to a root PREQ.
961195618Srpaulo		 */
962195618Srpaulo		if (hs->hs_rootmode != IEEE80211_HWMP_ROOTMODE_DISABLED) {
963198230Srpaulo			rt = ieee80211_mesh_rt_add(vap, prep->prep_targetaddr);
964195784Srpaulo			if (rt == NULL) {
965195784Srpaulo				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP,
966195784Srpaulo				    ni, "unable to add PREP path to %s",
967198230Srpaulo				    ether_sprintf(prep->prep_targetaddr));
968195784Srpaulo				vap->iv_stats.is_mesh_rtaddfailed++;
969195784Srpaulo				return;
970195784Srpaulo			}
971195618Srpaulo			IEEE80211_ADDR_COPY(rt->rt_nexthop, wh->i_addr2);
972195618Srpaulo			rt->rt_nhops = prep->prep_hopcount;
973195618Srpaulo			rt->rt_lifetime = prep->prep_lifetime;
974195618Srpaulo			rt->rt_metric = prep->prep_metric;
975195784Srpaulo			rt->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID;
976195908Srpaulo			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
977195908Srpaulo			    "add root path to %s nhops %d metric %d (PREP)",
978198230Srpaulo			    ether_sprintf(prep->prep_targetaddr),
979195908Srpaulo			    rt->rt_nhops, rt->rt_metric);
980195618Srpaulo			return;
981195618Srpaulo		}
982195618Srpaulo		return;
983195618Srpaulo	}
984195908Srpaulo	/*
985195908Srpaulo	 * Sequence number validation.
986195908Srpaulo	 */
987195618Srpaulo	hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
988198230Srpaulo	if (HWMP_SEQ_LEQ(prep->prep_targetseq, hr->hr_seq)) {
989195908Srpaulo		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
990195908Srpaulo		    "discard PREP from %s, old seq no %u <= %u",
991198230Srpaulo		    ether_sprintf(prep->prep_targetaddr),
992198230Srpaulo		    prep->prep_targetseq, hr->hr_seq);
993195908Srpaulo		return;
994195908Srpaulo	}
995198230Srpaulo	hr->hr_seq = prep->prep_targetseq;
996195908Srpaulo	/*
997195908Srpaulo	 * If it's NOT for us, propagate the PREP.
998195908Srpaulo	 */
999198230Srpaulo	if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, prep->prep_origaddr) &&
1000195908Srpaulo	    prep->prep_ttl > 1 && prep->prep_hopcount < hs->hs_maxhops) {
1001195908Srpaulo		struct ieee80211_meshprep_ie pprep; /* propagated PREP */
1002195908Srpaulo
1003195908Srpaulo		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1004195908Srpaulo		    "propagate PREP from %s",
1005198230Srpaulo		    ether_sprintf(prep->prep_targetaddr));
1006195908Srpaulo
1007195908Srpaulo		memcpy(&pprep, prep, sizeof(pprep));
1008195908Srpaulo		pprep.prep_hopcount += 1;
1009195908Srpaulo		pprep.prep_ttl -= 1;
1010195908Srpaulo		pprep.prep_metric += ms->ms_pmetric->mpm_metric(ni);
1011198230Srpaulo		IEEE80211_ADDR_COPY(pprep.prep_targetaddr, vap->iv_myaddr);
1012195908Srpaulo		hwmp_send_prep(ni, vap->iv_myaddr, broadcastaddr, &pprep);
1013195908Srpaulo	}
1014195908Srpaulo	hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
1015195908Srpaulo	if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY) {
1016195908Srpaulo		/* NB: never clobber a proxy entry */;
1017195908Srpaulo		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1018195908Srpaulo		    "discard PREP for %s, route is marked PROXY",
1019198230Srpaulo		    ether_sprintf(prep->prep_targetaddr));
1020195908Srpaulo		vap->iv_stats.is_hwmp_proxy++;
1021198230Srpaulo	} else if (prep->prep_origseq == hr->hr_origseq) {
1022195618Srpaulo		/*
1023195618Srpaulo		 * Check if we already have a path to this node.
1024195618Srpaulo		 * If we do, check if this path reply contains a
1025195618Srpaulo		 * better route.
1026195618Srpaulo		 */
1027195908Srpaulo		if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0 ||
1028195908Srpaulo		    (prep->prep_hopcount < rt->rt_nhops ||
1029195908Srpaulo		     prep->prep_metric < rt->rt_metric)) {
1030195908Srpaulo			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1031195908Srpaulo			    "%s path to %s, hopcount %d:%d metric %d:%d",
1032195908Srpaulo			    rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID ?
1033195908Srpaulo				"prefer" : "update",
1034195908Srpaulo			    ether_sprintf(prep->prep_origaddr),
1035195908Srpaulo			    rt->rt_nhops, prep->prep_hopcount,
1036195908Srpaulo			    rt->rt_metric, prep->prep_metric);
1037195618Srpaulo			IEEE80211_ADDR_COPY(rt->rt_nexthop, wh->i_addr2);
1038195618Srpaulo			rt->rt_nhops = prep->prep_hopcount;
1039195618Srpaulo			rt->rt_lifetime = prep->prep_lifetime;
1040195618Srpaulo			rt->rt_metric = prep->prep_metric;
1041195784Srpaulo			rt->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID;
1042195908Srpaulo		} else {
1043195908Srpaulo			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1044195908Srpaulo			    "ignore PREP for %s, hopcount %d:%d metric %d:%d",
1045198230Srpaulo			    ether_sprintf(prep->prep_targetaddr),
1046195908Srpaulo			    rt->rt_nhops, prep->prep_hopcount,
1047195908Srpaulo			    rt->rt_metric, prep->prep_metric);
1048195618Srpaulo		}
1049195618Srpaulo	} else {
1050195618Srpaulo		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1051195908Srpaulo		    "discard PREP for %s, wrong seqno %u != %u",
1052198230Srpaulo		    ether_sprintf(prep->prep_targetaddr), prep->prep_origseq,
1053195618Srpaulo		    hr->hr_seq);
1054195618Srpaulo		vap->iv_stats.is_hwmp_wrongseq++;
1055195618Srpaulo	}
1056195618Srpaulo	/*
1057195618Srpaulo	 * Check for frames queued awaiting path discovery.
1058195618Srpaulo	 * XXX probably can tell exactly and avoid remove call
1059195618Srpaulo	 * NB: hash may have false matches, if so they will get
1060195618Srpaulo	 *     stuck back on the stageq because there won't be
1061195618Srpaulo	 *     a path.
1062195618Srpaulo	 */
1063195618Srpaulo	m = ieee80211_ageq_remove(&ic->ic_stageq,
1064195618Srpaulo	    (struct ieee80211_node *)(uintptr_t)
1065195618Srpaulo		ieee80211_mac_hash(ic, rt->rt_dest));
1066195618Srpaulo	for (; m != NULL; m = next) {
1067195618Srpaulo		next = m->m_nextpkt;
1068195618Srpaulo		m->m_nextpkt = NULL;
1069195784Srpaulo		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1070195784Srpaulo		    "flush queued frame %p len %d", m, m->m_pkthdr.len);
1071195618Srpaulo		ifp->if_transmit(ifp, m);
1072195618Srpaulo	}
1073195618Srpaulo}
1074195618Srpaulo
1075195618Srpaulostatic int
1076195618Srpaulohwmp_send_prep(struct ieee80211_node *ni,
1077195618Srpaulo    const uint8_t sa[IEEE80211_ADDR_LEN],
1078195618Srpaulo    const uint8_t da[IEEE80211_ADDR_LEN],
1079195618Srpaulo    struct ieee80211_meshprep_ie *prep)
1080195618Srpaulo{
1081195784Srpaulo	/* NB: there's no PREP minimum interval. */
1082195618Srpaulo
1083195618Srpaulo	/*
1084195618Srpaulo	 * mesh prep action frame format
1085195618Srpaulo	 *     [6] da
1086195618Srpaulo	 *     [6] sa
1087195618Srpaulo	 *     [6] addr3 = sa
1088195618Srpaulo	 *     [1] action
1089195618Srpaulo	 *     [1] category
1090195618Srpaulo	 *     [tlv] mesh path reply
1091195618Srpaulo	 */
1092195618Srpaulo	prep->prep_ie = IEEE80211_ELEMID_MESHPREP;
1093195618Srpaulo	return hwmp_send_action(ni, sa, da, (uint8_t *)prep,
1094195618Srpaulo	    sizeof(struct ieee80211_meshprep_ie));
1095195618Srpaulo}
1096195618Srpaulo
1097197413Srpaulo#define	PERR_DFLAGS(n)	perr.perr_dests[n].dest_flags
1098195618Srpaulo#define	PERR_DADDR(n)	perr.perr_dests[n].dest_addr
1099195618Srpaulo#define	PERR_DSEQ(n)	perr.perr_dests[n].dest_seq
1100197413Srpaulo#define	PERR_DRCODE(n)	perr.perr_dests[n].dest_rcode
1101195618Srpaulostatic void
1102195618Srpaulohwmp_peerdown(struct ieee80211_node *ni)
1103195618Srpaulo{
1104195618Srpaulo	struct ieee80211vap *vap = ni->ni_vap;
1105197413Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
1106195618Srpaulo	struct ieee80211_meshperr_ie perr;
1107195618Srpaulo	struct ieee80211_mesh_route *rt;
1108195618Srpaulo	struct ieee80211_hwmp_route *hr;
1109195618Srpaulo
1110195618Srpaulo	rt = ieee80211_mesh_rt_find(vap, ni->ni_macaddr);
1111195618Srpaulo	if (rt == NULL)
1112195618Srpaulo		return;
1113195618Srpaulo	hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
1114195618Srpaulo	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1115195784Srpaulo	    "%s", "delete route entry");
1116197413Srpaulo	perr.perr_ttl = ms->ms_ttl;
1117195618Srpaulo	perr.perr_ndests = 1;
1118198260Srpaulo	PERR_DFLAGS(0) = 0;
1119197413Srpaulo	if (hr->hr_seq == 0)
1120197413Srpaulo		PERR_DFLAGS(0) |= IEEE80211_MESHPERR_DFLAGS_USN;
1121197413Srpaulo	PERR_DFLAGS(0) |= IEEE80211_MESHPERR_DFLAGS_RC;
1122195618Srpaulo	IEEE80211_ADDR_COPY(PERR_DADDR(0), rt->rt_dest);
1123195618Srpaulo	PERR_DSEQ(0) = hr->hr_seq;
1124197413Srpaulo	PERR_DRCODE(0) = IEEE80211_REASON_MESH_PERR_DEST_UNREACH;
1125195908Srpaulo	/* NB: flush everything passing through peer */
1126195908Srpaulo	ieee80211_mesh_rt_flush_peer(vap, ni->ni_macaddr);
1127195618Srpaulo	hwmp_send_perr(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &perr);
1128195618Srpaulo}
1129197413Srpaulo#undef	PERR_DFLAGS
1130195618Srpaulo#undef	PERR_DADDR
1131195618Srpaulo#undef	PERR_DSEQ
1132197413Srpaulo#undef	PERR_DRCODE
1133195618Srpaulo
1134197413Srpaulo#define	PERR_DFLAGS(n)	perr->perr_dests[n].dest_flags
1135195618Srpaulo#define	PERR_DADDR(n)	perr->perr_dests[n].dest_addr
1136195618Srpaulo#define	PERR_DSEQ(n)	perr->perr_dests[n].dest_seq
1137197413Srpaulo#define	PERR_DRCODE(n)	perr->perr_dests[n].dest_rcode
1138195618Srpaulostatic void
1139195618Srpaulohwmp_recv_perr(struct ieee80211vap *vap, struct ieee80211_node *ni,
1140195618Srpaulo    const struct ieee80211_frame *wh, const struct ieee80211_meshperr_ie *perr)
1141195618Srpaulo{
1142195618Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
1143195618Srpaulo	struct ieee80211_mesh_route *rt = NULL;
1144195618Srpaulo	struct ieee80211_hwmp_route *hr;
1145195618Srpaulo 	struct ieee80211_meshperr_ie pperr;
1146195618Srpaulo	int i, forward = 0;
1147195618Srpaulo
1148195618Srpaulo	/*
1149195618Srpaulo	 * Acceptance criteria: check if we received a PERR from a
1150195618Srpaulo	 * neighbor and forwarding is enabled.
1151195618Srpaulo	 */
1152195618Srpaulo	if (ni == vap->iv_bss ||
1153195618Srpaulo	    ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED ||
1154195618Srpaulo	    !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD))
1155195618Srpaulo		return;
1156195618Srpaulo	/*
1157195618Srpaulo	 * Find all routing entries that match and delete them.
1158195618Srpaulo	 */
1159195618Srpaulo	for (i = 0; i < perr->perr_ndests; i++) {
1160195618Srpaulo		rt = ieee80211_mesh_rt_find(vap, PERR_DADDR(i));
1161195618Srpaulo		if (rt == NULL)
1162195618Srpaulo			continue;
1163197413Srpaulo		hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
1164197413Srpaulo		if (!(PERR_DFLAGS(0) & IEEE80211_MESHPERR_DFLAGS_USN) &&
1165197413Srpaulo		    HWMP_SEQ_GEQ(PERR_DSEQ(i), hr->hr_seq)) {
1166195618Srpaulo			ieee80211_mesh_rt_del(vap, rt->rt_dest);
1167195908Srpaulo			ieee80211_mesh_rt_flush_peer(vap, rt->rt_dest);
1168195618Srpaulo			rt = NULL;
1169195618Srpaulo			forward = 1;
1170195618Srpaulo		}
1171195618Srpaulo	}
1172195618Srpaulo	/*
1173195618Srpaulo	 * Propagate the PERR if we previously found it on our routing table.
1174195618Srpaulo	 * XXX handle ndest > 1
1175195618Srpaulo	 */
1176197413Srpaulo	if (forward && perr->perr_ttl > 1) {
1177195618Srpaulo		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1178195784Srpaulo		    "propagate PERR from %s", ether_sprintf(wh->i_addr2));
1179195618Srpaulo		memcpy(&pperr, perr, sizeof(*perr));
1180197413Srpaulo		pperr.perr_ttl--;
1181195908Srpaulo		hwmp_send_perr(vap->iv_bss, vap->iv_myaddr, broadcastaddr,
1182195908Srpaulo		    &pperr);
1183195618Srpaulo	}
1184195618Srpaulo}
1185195618Srpaulo#undef	PEER_DADDR
1186195618Srpaulo#undef	PERR_DSEQ
1187195618Srpaulo
1188195618Srpaulostatic int
1189195618Srpaulohwmp_send_perr(struct ieee80211_node *ni,
1190195618Srpaulo    const uint8_t sa[IEEE80211_ADDR_LEN],
1191195618Srpaulo    const uint8_t da[IEEE80211_ADDR_LEN],
1192195618Srpaulo    struct ieee80211_meshperr_ie *perr)
1193195618Srpaulo{
1194195618Srpaulo	struct ieee80211_hwmp_state *hs = ni->ni_vap->iv_hwmp;
1195195618Srpaulo
1196195618Srpaulo	/*
1197195618Srpaulo	 * Enforce PERR interval.
1198195618Srpaulo	 */
1199195618Srpaulo	if (ratecheck(&hs->hs_lastperr, &ieee80211_hwmp_perrminint) == 0)
1200195618Srpaulo		return EALREADY;
1201195618Srpaulo	getmicrouptime(&hs->hs_lastperr);
1202195618Srpaulo
1203195618Srpaulo	/*
1204195618Srpaulo	 * mesh perr action frame format
1205195618Srpaulo	 *     [6] da
1206195618Srpaulo	 *     [6] sa
1207195618Srpaulo	 *     [6] addr3 = sa
1208195618Srpaulo	 *     [1] action
1209195618Srpaulo	 *     [1] category
1210195618Srpaulo	 *     [tlv] mesh path error
1211195618Srpaulo	 */
1212195618Srpaulo	perr->perr_ie = IEEE80211_ELEMID_MESHPERR;
1213195618Srpaulo	return hwmp_send_action(ni, sa, da, (uint8_t *)perr,
1214195618Srpaulo	    sizeof(struct ieee80211_meshperr_ie));
1215195618Srpaulo}
1216195618Srpaulo
1217195618Srpaulostatic void
1218195618Srpaulohwmp_recv_rann(struct ieee80211vap *vap, struct ieee80211_node *ni,
1219195618Srpaulo    const struct ieee80211_frame *wh, const struct ieee80211_meshrann_ie *rann)
1220195618Srpaulo{
1221195618Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
1222195618Srpaulo	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
1223195618Srpaulo	struct ieee80211_mesh_route *rt = NULL;
1224195618Srpaulo	struct ieee80211_hwmp_route *hr;
1225195618Srpaulo	struct ieee80211_meshrann_ie prann;
1226195618Srpaulo
1227195618Srpaulo	if (ni == vap->iv_bss ||
1228198230Srpaulo	    ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED ||
1229198230Srpaulo	    IEEE80211_ADDR_EQ(rann->rann_addr, vap->iv_myaddr))
1230195618Srpaulo		return;
1231195618Srpaulo
1232195618Srpaulo	rt = ieee80211_mesh_rt_find(vap, rann->rann_addr);
1233195618Srpaulo	/*
1234195618Srpaulo	 * Discover the path to the root mesh STA.
1235195618Srpaulo	 * If we already know it, propagate the RANN element.
1236195618Srpaulo	 */
1237195618Srpaulo	if (rt == NULL) {
1238195618Srpaulo		hwmp_discover(vap, rann->rann_addr, NULL);
1239195618Srpaulo		return;
1240195618Srpaulo	}
1241195618Srpaulo	hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
1242198581Srpaulo	if (HWMP_SEQ_GT(rann->rann_seq, hr->hr_seq)) {
1243198581Srpaulo		hr->hr_seq = rann->rann_seq;
1244198581Srpaulo		if (rann->rann_ttl > 1 &&
1245198581Srpaulo		    rann->rann_hopcount < hs->hs_maxhops &&
1246198581Srpaulo		    (ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) {
1247198581Srpaulo			memcpy(&prann, rann, sizeof(prann));
1248198581Srpaulo			prann.rann_hopcount += 1;
1249198581Srpaulo			prann.rann_ttl -= 1;
1250198581Srpaulo			prann.rann_metric += ms->ms_pmetric->mpm_metric(ni);
1251198581Srpaulo			hwmp_send_rann(vap->iv_bss, vap->iv_myaddr,
1252198581Srpaulo			    broadcastaddr, &prann);
1253198581Srpaulo		}
1254195618Srpaulo	}
1255195618Srpaulo}
1256195618Srpaulo
1257195618Srpaulostatic int
1258195618Srpaulohwmp_send_rann(struct ieee80211_node *ni,
1259195618Srpaulo    const uint8_t sa[IEEE80211_ADDR_LEN],
1260195618Srpaulo    const uint8_t da[IEEE80211_ADDR_LEN],
1261195618Srpaulo    struct ieee80211_meshrann_ie *rann)
1262195618Srpaulo{
1263195618Srpaulo	/*
1264195618Srpaulo	 * mesh rann action frame format
1265195618Srpaulo	 *     [6] da
1266195618Srpaulo	 *     [6] sa
1267195618Srpaulo	 *     [6] addr3 = sa
1268195618Srpaulo	 *     [1] action
1269195618Srpaulo	 *     [1] category
1270195618Srpaulo	 *     [tlv] root annoucement
1271195618Srpaulo	 */
1272195618Srpaulo	rann->rann_ie = IEEE80211_ELEMID_MESHRANN;
1273195618Srpaulo	return hwmp_send_action(ni, sa, da, (uint8_t *)rann,
1274195618Srpaulo	    sizeof(struct ieee80211_meshrann_ie));
1275195618Srpaulo}
1276195618Srpaulo
1277195618Srpaulo#define	PREQ_TFLAGS(n)	preq.preq_targets[n].target_flags
1278195618Srpaulo#define	PREQ_TADDR(n)	preq.preq_targets[n].target_addr
1279195618Srpaulo#define	PREQ_TSEQ(n)	preq.preq_targets[n].target_seq
1280195618Srpaulostatic struct ieee80211_node *
1281195618Srpaulohwmp_discover(struct ieee80211vap *vap,
1282195618Srpaulo    const uint8_t dest[IEEE80211_ADDR_LEN], struct mbuf *m)
1283195618Srpaulo{
1284195618Srpaulo	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
1285195618Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
1286195618Srpaulo	struct ieee80211_mesh_route *rt = NULL;
1287195618Srpaulo	struct ieee80211_hwmp_route *hr;
1288195618Srpaulo	struct ieee80211_meshpreq_ie preq;
1289195618Srpaulo	struct ieee80211_node *ni;
1290195618Srpaulo	int sendpreq = 0;
1291195618Srpaulo
1292195618Srpaulo	KASSERT(vap->iv_opmode == IEEE80211_M_MBSS,
1293195618Srpaulo	    ("not a mesh vap, opmode %d", vap->iv_opmode));
1294195618Srpaulo
1295195618Srpaulo	KASSERT(!IEEE80211_ADDR_EQ(vap->iv_myaddr, dest),
1296195618Srpaulo	    ("%s: discovering self!", __func__));
1297195618Srpaulo
1298195618Srpaulo	ni = NULL;
1299195618Srpaulo	if (!IEEE80211_IS_MULTICAST(dest)) {
1300195618Srpaulo		rt = ieee80211_mesh_rt_find(vap, dest);
1301195618Srpaulo		if (rt == NULL) {
1302195618Srpaulo			rt = ieee80211_mesh_rt_add(vap, dest);
1303195618Srpaulo			if (rt == NULL) {
1304195784Srpaulo				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP,
1305195784Srpaulo				    ni, "unable to add discovery path to %s",
1306195784Srpaulo				    ether_sprintf(dest));
1307195784Srpaulo				vap->iv_stats.is_mesh_rtaddfailed++;
1308195618Srpaulo				goto done;
1309195618Srpaulo			}
1310195618Srpaulo		}
1311195618Srpaulo		hr = IEEE80211_MESH_ROUTE_PRIV(rt,
1312195618Srpaulo		    struct ieee80211_hwmp_route);
1313195784Srpaulo		if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0) {
1314198230Srpaulo			if (hr->hr_origseq == 0)
1315198230Srpaulo				hr->hr_origseq = ++hs->hs_seq;
1316195618Srpaulo			rt->rt_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
1317195618Srpaulo			rt->rt_lifetime =
1318195813Ssam			    ticks_to_msecs(ieee80211_hwmp_pathtimeout);
1319195618Srpaulo			/* XXX check preq retries */
1320195618Srpaulo			sendpreq = 1;
1321195618Srpaulo			IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP, dest,
1322195784Srpaulo			    "start path discovery (src %s)",
1323195784Srpaulo			    m == NULL ? "<none>" : ether_sprintf(
1324195784Srpaulo				mtod(m, struct ether_header *)->ether_shost));
1325195618Srpaulo			/*
1326195618Srpaulo			 * Try to discover the path for this node.
1327195618Srpaulo			 */
1328195618Srpaulo			preq.preq_flags = 0;
1329195618Srpaulo			preq.preq_hopcount = 0;
1330195618Srpaulo			preq.preq_ttl = ms->ms_ttl;
1331195908Srpaulo			preq.preq_id = ++hs->hs_preqid;
1332195618Srpaulo			IEEE80211_ADDR_COPY(preq.preq_origaddr, vap->iv_myaddr);
1333198230Srpaulo			preq.preq_origseq = hr->hr_origseq;
1334195618Srpaulo			preq.preq_lifetime = rt->rt_lifetime;
1335195618Srpaulo			preq.preq_metric = rt->rt_metric;
1336195618Srpaulo			preq.preq_tcount = 1;
1337195618Srpaulo			IEEE80211_ADDR_COPY(PREQ_TADDR(0), dest);
1338195618Srpaulo			PREQ_TFLAGS(0) = 0;
1339195618Srpaulo			if (ieee80211_hwmp_targetonly)
1340195618Srpaulo				PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_TO;
1341195618Srpaulo			if (ieee80211_hwmp_replyforward)
1342195618Srpaulo				PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_RF;
1343195618Srpaulo			PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_USN;
1344195618Srpaulo			PREQ_TSEQ(0) = 0;
1345195618Srpaulo			/* XXX check return value */
1346195618Srpaulo			hwmp_send_preq(vap->iv_bss, vap->iv_myaddr,
1347195618Srpaulo			    broadcastaddr, &preq);
1348195618Srpaulo		}
1349195784Srpaulo		if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID)
1350195618Srpaulo			ni = ieee80211_find_txnode(vap, rt->rt_nexthop);
1351195618Srpaulo	} else {
1352195618Srpaulo		ni = ieee80211_find_txnode(vap, dest);
1353195784Srpaulo		/* NB: if null then we leak mbuf */
1354195784Srpaulo		KASSERT(ni != NULL, ("leak mcast frame"));
1355195618Srpaulo		return ni;
1356195618Srpaulo	}
1357195618Srpaulodone:
1358195618Srpaulo	if (ni == NULL && m != NULL) {
1359195618Srpaulo		if (sendpreq) {
1360195618Srpaulo			struct ieee80211com *ic = vap->iv_ic;
1361195618Srpaulo			/*
1362195618Srpaulo			 * Queue packet for transmit when path discovery
1363195618Srpaulo			 * completes.  If discovery never completes the
1364195618Srpaulo			 * frame will be flushed by way of the aging timer.
1365195618Srpaulo			 */
1366195784Srpaulo			IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP, dest,
1367195784Srpaulo			    "%s", "queue frame until path found");
1368195618Srpaulo			m->m_pkthdr.rcvif = (void *)(uintptr_t)
1369195618Srpaulo			    ieee80211_mac_hash(ic, dest);
1370195618Srpaulo			/* XXX age chosen randomly */
1371195618Srpaulo			ieee80211_ageq_append(&ic->ic_stageq, m,
1372195618Srpaulo			    IEEE80211_INACT_WAIT);
1373195784Srpaulo		} else {
1374195784Srpaulo			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_HWMP,
1375195784Srpaulo			    dest, NULL, "%s", "no valid path to this node");
1376195618Srpaulo			m_freem(m);
1377195784Srpaulo		}
1378195618Srpaulo	}
1379195618Srpaulo	return ni;
1380195618Srpaulo}
1381195618Srpaulo#undef	PREQ_TFLAGS
1382195618Srpaulo#undef	PREQ_TADDR
1383195618Srpaulo#undef	PREQ_TSEQ
1384195618Srpaulo
1385195618Srpaulostatic int
1386195618Srpaulohwmp_ioctl_get80211(struct ieee80211vap *vap, struct ieee80211req *ireq)
1387195618Srpaulo{
1388195618Srpaulo	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
1389195618Srpaulo	int error;
1390195618Srpaulo
1391195618Srpaulo	if (vap->iv_opmode != IEEE80211_M_MBSS)
1392195618Srpaulo		return ENOSYS;
1393195618Srpaulo	error = 0;
1394195618Srpaulo	switch (ireq->i_type) {
1395195618Srpaulo	case IEEE80211_IOC_HWMP_ROOTMODE:
1396195618Srpaulo		ireq->i_val = hs->hs_rootmode;
1397195618Srpaulo		break;
1398195618Srpaulo	case IEEE80211_IOC_HWMP_MAXHOPS:
1399195618Srpaulo		ireq->i_val = hs->hs_maxhops;
1400195618Srpaulo		break;
1401195618Srpaulo	default:
1402195618Srpaulo		return ENOSYS;
1403195618Srpaulo	}
1404195618Srpaulo	return error;
1405195618Srpaulo}
1406195618SrpauloIEEE80211_IOCTL_GET(hwmp, hwmp_ioctl_get80211);
1407195618Srpaulo
1408195618Srpaulostatic int
1409195618Srpaulohwmp_ioctl_set80211(struct ieee80211vap *vap, struct ieee80211req *ireq)
1410195618Srpaulo{
1411195618Srpaulo	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
1412195618Srpaulo	int error;
1413195618Srpaulo
1414195618Srpaulo	if (vap->iv_opmode != IEEE80211_M_MBSS)
1415195618Srpaulo		return ENOSYS;
1416195618Srpaulo	error = 0;
1417195618Srpaulo	switch (ireq->i_type) {
1418195618Srpaulo	case IEEE80211_IOC_HWMP_ROOTMODE:
1419195618Srpaulo		if (ireq->i_val < 0 || ireq->i_val > 3)
1420195618Srpaulo			return EINVAL;
1421195618Srpaulo		hs->hs_rootmode = ireq->i_val;
1422195618Srpaulo		hwmp_rootmode_setup(vap);
1423195618Srpaulo		break;
1424195618Srpaulo	case IEEE80211_IOC_HWMP_MAXHOPS:
1425195618Srpaulo		if (ireq->i_val <= 0 || ireq->i_val > 255)
1426195618Srpaulo			return EINVAL;
1427195618Srpaulo		hs->hs_maxhops = ireq->i_val;
1428195618Srpaulo		break;
1429195618Srpaulo	default:
1430195618Srpaulo		return ENOSYS;
1431195618Srpaulo	}
1432195618Srpaulo	return error;
1433195618Srpaulo}
1434195618SrpauloIEEE80211_IOCTL_SET(hwmp, hwmp_ioctl_set80211);
1435