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$");
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
164248085Smariusstatic SYSCTL_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);
710209013Srpaulo	if (rtorig == NULL) {
711208696Srpaulo		/* XXX stat */
712208696Srpaulo		return;
713209013Srpaulo	}
714195908Srpaulo	hrorig = IEEE80211_MESH_ROUTE_PRIV(rtorig, struct ieee80211_hwmp_route);
715195618Srpaulo	/*
716195908Srpaulo	 * Sequence number validation.
717195908Srpaulo	 */
718195908Srpaulo	if (HWMP_SEQ_LEQ(preq->preq_id, hrorig->hr_preqid) &&
719195908Srpaulo	    HWMP_SEQ_LEQ(preq->preq_origseq, hrorig->hr_seq)) {
720195908Srpaulo		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
721195908Srpaulo		    "discard PREQ from %s, old seq no %u <= %u",
722195908Srpaulo		    ether_sprintf(preq->preq_origaddr),
723195908Srpaulo		    preq->preq_origseq, hrorig->hr_seq);
724195908Srpaulo		return;
725195908Srpaulo	}
726195908Srpaulo	hrorig->hr_preqid = preq->preq_id;
727195908Srpaulo	hrorig->hr_seq = preq->preq_origseq;
728195908Srpaulo
729195908Srpaulo	/*
730195618Srpaulo	 * Check if the PREQ is addressed to us.
731195618Srpaulo	 */
732195618Srpaulo	if (IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0))) {
733195618Srpaulo		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
734195784Srpaulo		    "reply to %s", ether_sprintf(preq->preq_origaddr));
735195618Srpaulo		/*
736195618Srpaulo		 * Build and send a PREP frame.
737195618Srpaulo		 */
738195618Srpaulo		prep.prep_flags = 0;
739195618Srpaulo		prep.prep_hopcount = 0;
740195618Srpaulo		prep.prep_ttl = ms->ms_ttl;
741198230Srpaulo		IEEE80211_ADDR_COPY(prep.prep_targetaddr, vap->iv_myaddr);
742198230Srpaulo		prep.prep_targetseq = ++hs->hs_seq;
743195618Srpaulo		prep.prep_lifetime = preq->preq_lifetime;
744195618Srpaulo		prep.prep_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
745198230Srpaulo		IEEE80211_ADDR_COPY(prep.prep_origaddr, preq->preq_origaddr);
746198230Srpaulo		prep.prep_origseq = preq->preq_origseq;
747195618Srpaulo		hwmp_send_prep(ni, vap->iv_myaddr, wh->i_addr2, &prep);
748195618Srpaulo		/*
749195618Srpaulo		 * Build the reverse path, if we don't have it already.
750195618Srpaulo		 */
751195618Srpaulo		rt = ieee80211_mesh_rt_find(vap, preq->preq_origaddr);
752195618Srpaulo		if (rt == NULL)
753195618Srpaulo			hwmp_discover(vap, preq->preq_origaddr, NULL);
754195784Srpaulo		else if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0)
755195618Srpaulo			hwmp_discover(vap, rt->rt_dest, NULL);
756195618Srpaulo		return;
757195618Srpaulo	}
758195618Srpaulo	/*
759195618Srpaulo	 * Proactive PREQ: reply with a proactive PREP to the
760195618Srpaulo	 * root STA if requested.
761195618Srpaulo	 */
762195618Srpaulo	if (IEEE80211_ADDR_EQ(PREQ_TADDR(0), broadcastaddr) &&
763195618Srpaulo	    (PREQ_TFLAGS(0) &
764195618Srpaulo	    ((IEEE80211_MESHPREQ_TFLAGS_TO|IEEE80211_MESHPREQ_TFLAGS_RF) ==
765195618Srpaulo	    (IEEE80211_MESHPREQ_TFLAGS_TO|IEEE80211_MESHPREQ_TFLAGS_RF)))) {
766195618Srpaulo		uint8_t rootmac[IEEE80211_ADDR_LEN];
767195618Srpaulo
768195618Srpaulo		IEEE80211_ADDR_COPY(rootmac, preq->preq_origaddr);
769195618Srpaulo		rt = ieee80211_mesh_rt_find(vap, rootmac);
770195784Srpaulo		if (rt == NULL) {
771195618Srpaulo			rt = ieee80211_mesh_rt_add(vap, rootmac);
772195784Srpaulo			if (rt == NULL) {
773195784Srpaulo				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
774195784Srpaulo				    "unable to add root mesh path to %s",
775195784Srpaulo				    ether_sprintf(rootmac));
776195784Srpaulo				vap->iv_stats.is_mesh_rtaddfailed++;
777195784Srpaulo				return;
778195784Srpaulo			}
779195784Srpaulo		}
780195784Srpaulo		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
781195784Srpaulo		    "root mesh station @ %s", ether_sprintf(rootmac));
782195784Srpaulo
783195618Srpaulo		/*
784195618Srpaulo		 * Reply with a PREP if we don't have a path to the root
785195618Srpaulo		 * or if the root sent us a proactive PREQ.
786195618Srpaulo		 */
787195784Srpaulo		if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0 ||
788195618Srpaulo		    (preq->preq_flags & IEEE80211_MESHPREQ_FLAGS_PP)) {
789195618Srpaulo			prep.prep_flags = 0;
790195618Srpaulo			prep.prep_hopcount = 0;
791195618Srpaulo			prep.prep_ttl = ms->ms_ttl;
792198230Srpaulo			IEEE80211_ADDR_COPY(prep.prep_origaddr, rootmac);
793195618Srpaulo			prep.prep_origseq = preq->preq_origseq;
794195618Srpaulo			prep.prep_lifetime = preq->preq_lifetime;
795195618Srpaulo			prep.prep_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
796198230Srpaulo			IEEE80211_ADDR_COPY(prep.prep_targetaddr,
797198230Srpaulo			    vap->iv_myaddr);
798198230Srpaulo			prep.prep_targetseq = ++hs->hs_seq;
799195618Srpaulo			hwmp_send_prep(vap->iv_bss, vap->iv_myaddr,
800195618Srpaulo			    broadcastaddr, &prep);
801195618Srpaulo		}
802195618Srpaulo		hwmp_discover(vap, rootmac, NULL);
803195618Srpaulo		return;
804195618Srpaulo	}
805195618Srpaulo	rt = ieee80211_mesh_rt_find(vap, PREQ_TADDR(0));
806195618Srpaulo
807195618Srpaulo	/*
808195618Srpaulo	 * Forwarding and Intermediate reply for PREQs with 1 target.
809195618Srpaulo	 */
810195618Srpaulo	if (preq->preq_tcount == 1) {
811195618Srpaulo		struct ieee80211_meshpreq_ie ppreq; /* propagated PREQ */
812195618Srpaulo
813195618Srpaulo		memcpy(&ppreq, preq, sizeof(ppreq));
814195618Srpaulo		/*
815195618Srpaulo		 * We have a valid route to this node.
816195618Srpaulo		 */
817195618Srpaulo		if (rt != NULL &&
818195784Srpaulo		    (rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID)) {
819195618Srpaulo			if (preq->preq_ttl > 1 &&
820195618Srpaulo			    preq->preq_hopcount < hs->hs_maxhops) {
821195618Srpaulo				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
822195784Srpaulo				    "forward PREQ from %s",
823195618Srpaulo				    ether_sprintf(preq->preq_origaddr));
824195618Srpaulo				/*
825195618Srpaulo				 * Propagate the original PREQ.
826195618Srpaulo				 */
827195618Srpaulo				ppreq.preq_hopcount += 1;
828195618Srpaulo				ppreq.preq_ttl -= 1;
829195618Srpaulo				ppreq.preq_metric +=
830195618Srpaulo				    ms->ms_pmetric->mpm_metric(ni);
831195618Srpaulo				/*
832195618Srpaulo				 * Set TO and unset RF bits because we are going
833195618Srpaulo				 * to send a PREP next.
834195618Srpaulo				 */
835195618Srpaulo				ppreq.preq_targets[0].target_flags |=
836195618Srpaulo				    IEEE80211_MESHPREQ_TFLAGS_TO;
837195618Srpaulo				ppreq.preq_targets[0].target_flags &=
838195618Srpaulo				    ~IEEE80211_MESHPREQ_TFLAGS_RF;
839195618Srpaulo				hwmp_send_preq(ni, vap->iv_myaddr,
840195618Srpaulo				    broadcastaddr, &ppreq);
841195618Srpaulo			}
842195618Srpaulo			/*
843195618Srpaulo			 * Check if we can send an intermediate Path Reply,
844195618Srpaulo			 * i.e., Target Only bit is not set.
845195618Srpaulo			 */
846195618Srpaulo	    		if (!(PREQ_TFLAGS(0) & IEEE80211_MESHPREQ_TFLAGS_TO)) {
847195618Srpaulo				struct ieee80211_meshprep_ie prep;
848195618Srpaulo
849195618Srpaulo				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
850195618Srpaulo				    "intermediate reply for PREQ from %s",
851195618Srpaulo				    ether_sprintf(preq->preq_origaddr));
852195618Srpaulo				prep.prep_flags = 0;
853195618Srpaulo				prep.prep_hopcount = rt->rt_nhops + 1;
854195618Srpaulo				prep.prep_ttl = ms->ms_ttl;
855195618Srpaulo				IEEE80211_ADDR_COPY(&prep.prep_targetaddr,
856198230Srpaulo				    PREQ_TADDR(0));
857195908Srpaulo				prep.prep_targetseq = hrorig->hr_seq;
858195618Srpaulo				prep.prep_lifetime = preq->preq_lifetime;
859195618Srpaulo				prep.prep_metric = rt->rt_metric +
860195618Srpaulo				    ms->ms_pmetric->mpm_metric(ni);
861195618Srpaulo				IEEE80211_ADDR_COPY(&prep.prep_origaddr,
862198230Srpaulo				    preq->preq_origaddr);
863195908Srpaulo				prep.prep_origseq = hrorig->hr_seq;
864195618Srpaulo				hwmp_send_prep(ni, vap->iv_myaddr,
865195618Srpaulo				    broadcastaddr, &prep);
866195618Srpaulo			}
867195618Srpaulo		/*
868195618Srpaulo		 * We have no information about this path,
869195618Srpaulo		 * propagate the PREQ.
870195618Srpaulo		 */
871195618Srpaulo		} else if (preq->preq_ttl > 1 &&
872195618Srpaulo		    preq->preq_hopcount < hs->hs_maxhops) {
873195784Srpaulo			if (rt == NULL) {
874195618Srpaulo				rt = ieee80211_mesh_rt_add(vap, PREQ_TADDR(0));
875195784Srpaulo				if (rt == NULL) {
876195784Srpaulo					IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP,
877195784Srpaulo					    ni, "unable to add PREQ path to %s",
878195784Srpaulo					    ether_sprintf(PREQ_TADDR(0)));
879195784Srpaulo					vap->iv_stats.is_mesh_rtaddfailed++;
880195784Srpaulo					return;
881195784Srpaulo				}
882195784Srpaulo			}
883195618Srpaulo			rt->rt_metric = preq->preq_metric;
884195618Srpaulo			rt->rt_lifetime = preq->preq_lifetime;
885195908Srpaulo			hrorig = IEEE80211_MESH_ROUTE_PRIV(rt,
886195908Srpaulo			    struct ieee80211_hwmp_route);
887195908Srpaulo			hrorig->hr_seq = preq->preq_origseq;
888195908Srpaulo			hrorig->hr_preqid = preq->preq_id;
889195618Srpaulo
890195618Srpaulo			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
891195784Srpaulo			    "forward PREQ from %s",
892195618Srpaulo			    ether_sprintf(preq->preq_origaddr));
893195618Srpaulo			ppreq.preq_hopcount += 1;
894195618Srpaulo			ppreq.preq_ttl -= 1;
895195618Srpaulo			ppreq.preq_metric += ms->ms_pmetric->mpm_metric(ni);
896195618Srpaulo			hwmp_send_preq(ni, vap->iv_myaddr, broadcastaddr,
897195618Srpaulo			    &ppreq);
898195618Srpaulo		}
899195618Srpaulo	}
900195618Srpaulo
901195618Srpaulo}
902195618Srpaulo#undef	PREQ_TFLAGS
903195618Srpaulo#undef	PREQ_TADDR
904195618Srpaulo#undef	PREQ_TSEQ
905195618Srpaulo
906195618Srpaulostatic int
907195618Srpaulohwmp_send_preq(struct ieee80211_node *ni,
908195618Srpaulo    const uint8_t sa[IEEE80211_ADDR_LEN],
909195618Srpaulo    const uint8_t da[IEEE80211_ADDR_LEN],
910195618Srpaulo    struct ieee80211_meshpreq_ie *preq)
911195618Srpaulo{
912195618Srpaulo	struct ieee80211_hwmp_state *hs = ni->ni_vap->iv_hwmp;
913195618Srpaulo
914195618Srpaulo	/*
915195618Srpaulo	 * Enforce PREQ interval.
916195618Srpaulo	 */
917195618Srpaulo	if (ratecheck(&hs->hs_lastpreq, &ieee80211_hwmp_preqminint) == 0)
918195618Srpaulo		return EALREADY;
919195618Srpaulo	getmicrouptime(&hs->hs_lastpreq);
920195618Srpaulo
921195618Srpaulo	/*
922195618Srpaulo	 * mesh preq action frame format
923195618Srpaulo	 *     [6] da
924195618Srpaulo	 *     [6] sa
925195618Srpaulo	 *     [6] addr3 = sa
926195618Srpaulo	 *     [1] action
927195618Srpaulo	 *     [1] category
928195618Srpaulo	 *     [tlv] mesh path request
929195618Srpaulo	 */
930195618Srpaulo	preq->preq_ie = IEEE80211_ELEMID_MESHPREQ;
931195618Srpaulo	return hwmp_send_action(ni, sa, da, (uint8_t *)preq,
932195618Srpaulo	    sizeof(struct ieee80211_meshpreq_ie));
933195618Srpaulo}
934195618Srpaulo
935195618Srpaulostatic void
936195618Srpaulohwmp_recv_prep(struct ieee80211vap *vap, struct ieee80211_node *ni,
937195618Srpaulo    const struct ieee80211_frame *wh, const struct ieee80211_meshprep_ie *prep)
938195618Srpaulo{
939195618Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
940195618Srpaulo	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
941195618Srpaulo	struct ieee80211_mesh_route *rt = NULL;
942195618Srpaulo	struct ieee80211_hwmp_route *hr;
943195618Srpaulo	struct ieee80211com *ic = vap->iv_ic;
944195618Srpaulo	struct ifnet *ifp = vap->iv_ifp;
945195618Srpaulo	struct mbuf *m, *next;
946195618Srpaulo
947195618Srpaulo	/*
948195618Srpaulo	 * Acceptance criteria: if the corresponding PREQ was not generated
949195618Srpaulo	 * by us and forwarding is disabled, discard this PREP.
950195618Srpaulo	 */
951195618Srpaulo	if (ni == vap->iv_bss ||
952195618Srpaulo	    ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED)
953195618Srpaulo		return;
954195618Srpaulo	if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, prep->prep_origaddr) &&
955195618Srpaulo	    !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD))
956195618Srpaulo		return;
957195618Srpaulo
958195618Srpaulo	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
959198230Srpaulo	    "received PREP from %s", ether_sprintf(prep->prep_targetaddr));
960195618Srpaulo
961198230Srpaulo	rt = ieee80211_mesh_rt_find(vap, prep->prep_targetaddr);
962195618Srpaulo	if (rt == NULL) {
963195618Srpaulo		/*
964195618Srpaulo		 * If we have no entry this could be a reply to a root PREQ.
965195618Srpaulo		 */
966195618Srpaulo		if (hs->hs_rootmode != IEEE80211_HWMP_ROOTMODE_DISABLED) {
967198230Srpaulo			rt = ieee80211_mesh_rt_add(vap, prep->prep_targetaddr);
968195784Srpaulo			if (rt == NULL) {
969195784Srpaulo				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP,
970195784Srpaulo				    ni, "unable to add PREP path to %s",
971198230Srpaulo				    ether_sprintf(prep->prep_targetaddr));
972195784Srpaulo				vap->iv_stats.is_mesh_rtaddfailed++;
973195784Srpaulo				return;
974195784Srpaulo			}
975195618Srpaulo			IEEE80211_ADDR_COPY(rt->rt_nexthop, wh->i_addr2);
976195618Srpaulo			rt->rt_nhops = prep->prep_hopcount;
977195618Srpaulo			rt->rt_lifetime = prep->prep_lifetime;
978195618Srpaulo			rt->rt_metric = prep->prep_metric;
979195784Srpaulo			rt->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID;
980195908Srpaulo			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
981195908Srpaulo			    "add root path to %s nhops %d metric %d (PREP)",
982198230Srpaulo			    ether_sprintf(prep->prep_targetaddr),
983195908Srpaulo			    rt->rt_nhops, rt->rt_metric);
984195618Srpaulo			return;
985195618Srpaulo		}
986195618Srpaulo		return;
987195618Srpaulo	}
988195908Srpaulo	/*
989195908Srpaulo	 * Sequence number validation.
990195908Srpaulo	 */
991195618Srpaulo	hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
992198230Srpaulo	if (HWMP_SEQ_LEQ(prep->prep_targetseq, hr->hr_seq)) {
993195908Srpaulo		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
994195908Srpaulo		    "discard PREP from %s, old seq no %u <= %u",
995198230Srpaulo		    ether_sprintf(prep->prep_targetaddr),
996198230Srpaulo		    prep->prep_targetseq, hr->hr_seq);
997195908Srpaulo		return;
998195908Srpaulo	}
999198230Srpaulo	hr->hr_seq = prep->prep_targetseq;
1000195908Srpaulo	/*
1001195908Srpaulo	 * If it's NOT for us, propagate the PREP.
1002195908Srpaulo	 */
1003198230Srpaulo	if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, prep->prep_origaddr) &&
1004195908Srpaulo	    prep->prep_ttl > 1 && prep->prep_hopcount < hs->hs_maxhops) {
1005195908Srpaulo		struct ieee80211_meshprep_ie pprep; /* propagated PREP */
1006195908Srpaulo
1007195908Srpaulo		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1008195908Srpaulo		    "propagate PREP from %s",
1009198230Srpaulo		    ether_sprintf(prep->prep_targetaddr));
1010195908Srpaulo
1011195908Srpaulo		memcpy(&pprep, prep, sizeof(pprep));
1012195908Srpaulo		pprep.prep_hopcount += 1;
1013195908Srpaulo		pprep.prep_ttl -= 1;
1014195908Srpaulo		pprep.prep_metric += ms->ms_pmetric->mpm_metric(ni);
1015198230Srpaulo		IEEE80211_ADDR_COPY(pprep.prep_targetaddr, vap->iv_myaddr);
1016195908Srpaulo		hwmp_send_prep(ni, vap->iv_myaddr, broadcastaddr, &pprep);
1017195908Srpaulo	}
1018195908Srpaulo	hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
1019195908Srpaulo	if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY) {
1020195908Srpaulo		/* NB: never clobber a proxy entry */;
1021195908Srpaulo		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1022195908Srpaulo		    "discard PREP for %s, route is marked PROXY",
1023198230Srpaulo		    ether_sprintf(prep->prep_targetaddr));
1024195908Srpaulo		vap->iv_stats.is_hwmp_proxy++;
1025198230Srpaulo	} else if (prep->prep_origseq == hr->hr_origseq) {
1026195618Srpaulo		/*
1027195618Srpaulo		 * Check if we already have a path to this node.
1028195618Srpaulo		 * If we do, check if this path reply contains a
1029195618Srpaulo		 * better route.
1030195618Srpaulo		 */
1031195908Srpaulo		if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0 ||
1032195908Srpaulo		    (prep->prep_hopcount < rt->rt_nhops ||
1033195908Srpaulo		     prep->prep_metric < rt->rt_metric)) {
1034195908Srpaulo			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1035195908Srpaulo			    "%s path to %s, hopcount %d:%d metric %d:%d",
1036195908Srpaulo			    rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID ?
1037195908Srpaulo				"prefer" : "update",
1038195908Srpaulo			    ether_sprintf(prep->prep_origaddr),
1039195908Srpaulo			    rt->rt_nhops, prep->prep_hopcount,
1040195908Srpaulo			    rt->rt_metric, prep->prep_metric);
1041195618Srpaulo			IEEE80211_ADDR_COPY(rt->rt_nexthop, wh->i_addr2);
1042195618Srpaulo			rt->rt_nhops = prep->prep_hopcount;
1043195618Srpaulo			rt->rt_lifetime = prep->prep_lifetime;
1044195618Srpaulo			rt->rt_metric = prep->prep_metric;
1045195784Srpaulo			rt->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID;
1046195908Srpaulo		} else {
1047195908Srpaulo			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1048195908Srpaulo			    "ignore PREP for %s, hopcount %d:%d metric %d:%d",
1049198230Srpaulo			    ether_sprintf(prep->prep_targetaddr),
1050195908Srpaulo			    rt->rt_nhops, prep->prep_hopcount,
1051195908Srpaulo			    rt->rt_metric, prep->prep_metric);
1052195618Srpaulo		}
1053195618Srpaulo	} else {
1054195618Srpaulo		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1055195908Srpaulo		    "discard PREP for %s, wrong seqno %u != %u",
1056198230Srpaulo		    ether_sprintf(prep->prep_targetaddr), prep->prep_origseq,
1057195618Srpaulo		    hr->hr_seq);
1058195618Srpaulo		vap->iv_stats.is_hwmp_wrongseq++;
1059195618Srpaulo	}
1060195618Srpaulo	/*
1061195618Srpaulo	 * Check for frames queued awaiting path discovery.
1062195618Srpaulo	 * XXX probably can tell exactly and avoid remove call
1063195618Srpaulo	 * NB: hash may have false matches, if so they will get
1064195618Srpaulo	 *     stuck back on the stageq because there won't be
1065195618Srpaulo	 *     a path.
1066195618Srpaulo	 */
1067195618Srpaulo	m = ieee80211_ageq_remove(&ic->ic_stageq,
1068195618Srpaulo	    (struct ieee80211_node *)(uintptr_t)
1069195618Srpaulo		ieee80211_mac_hash(ic, rt->rt_dest));
1070195618Srpaulo	for (; m != NULL; m = next) {
1071195618Srpaulo		next = m->m_nextpkt;
1072195618Srpaulo		m->m_nextpkt = NULL;
1073195784Srpaulo		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1074195784Srpaulo		    "flush queued frame %p len %d", m, m->m_pkthdr.len);
1075195618Srpaulo		ifp->if_transmit(ifp, m);
1076195618Srpaulo	}
1077195618Srpaulo}
1078195618Srpaulo
1079195618Srpaulostatic int
1080195618Srpaulohwmp_send_prep(struct ieee80211_node *ni,
1081195618Srpaulo    const uint8_t sa[IEEE80211_ADDR_LEN],
1082195618Srpaulo    const uint8_t da[IEEE80211_ADDR_LEN],
1083195618Srpaulo    struct ieee80211_meshprep_ie *prep)
1084195618Srpaulo{
1085195784Srpaulo	/* NB: there's no PREP minimum interval. */
1086195618Srpaulo
1087195618Srpaulo	/*
1088195618Srpaulo	 * mesh prep action frame format
1089195618Srpaulo	 *     [6] da
1090195618Srpaulo	 *     [6] sa
1091195618Srpaulo	 *     [6] addr3 = sa
1092195618Srpaulo	 *     [1] action
1093195618Srpaulo	 *     [1] category
1094195618Srpaulo	 *     [tlv] mesh path reply
1095195618Srpaulo	 */
1096195618Srpaulo	prep->prep_ie = IEEE80211_ELEMID_MESHPREP;
1097195618Srpaulo	return hwmp_send_action(ni, sa, da, (uint8_t *)prep,
1098195618Srpaulo	    sizeof(struct ieee80211_meshprep_ie));
1099195618Srpaulo}
1100195618Srpaulo
1101197413Srpaulo#define	PERR_DFLAGS(n)	perr.perr_dests[n].dest_flags
1102195618Srpaulo#define	PERR_DADDR(n)	perr.perr_dests[n].dest_addr
1103195618Srpaulo#define	PERR_DSEQ(n)	perr.perr_dests[n].dest_seq
1104197413Srpaulo#define	PERR_DRCODE(n)	perr.perr_dests[n].dest_rcode
1105195618Srpaulostatic void
1106195618Srpaulohwmp_peerdown(struct ieee80211_node *ni)
1107195618Srpaulo{
1108195618Srpaulo	struct ieee80211vap *vap = ni->ni_vap;
1109197413Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
1110195618Srpaulo	struct ieee80211_meshperr_ie perr;
1111195618Srpaulo	struct ieee80211_mesh_route *rt;
1112195618Srpaulo	struct ieee80211_hwmp_route *hr;
1113195618Srpaulo
1114195618Srpaulo	rt = ieee80211_mesh_rt_find(vap, ni->ni_macaddr);
1115195618Srpaulo	if (rt == NULL)
1116195618Srpaulo		return;
1117195618Srpaulo	hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
1118195618Srpaulo	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1119195784Srpaulo	    "%s", "delete route entry");
1120197413Srpaulo	perr.perr_ttl = ms->ms_ttl;
1121195618Srpaulo	perr.perr_ndests = 1;
1122198260Srpaulo	PERR_DFLAGS(0) = 0;
1123197413Srpaulo	if (hr->hr_seq == 0)
1124197413Srpaulo		PERR_DFLAGS(0) |= IEEE80211_MESHPERR_DFLAGS_USN;
1125197413Srpaulo	PERR_DFLAGS(0) |= IEEE80211_MESHPERR_DFLAGS_RC;
1126195618Srpaulo	IEEE80211_ADDR_COPY(PERR_DADDR(0), rt->rt_dest);
1127195618Srpaulo	PERR_DSEQ(0) = hr->hr_seq;
1128197413Srpaulo	PERR_DRCODE(0) = IEEE80211_REASON_MESH_PERR_DEST_UNREACH;
1129195908Srpaulo	/* NB: flush everything passing through peer */
1130195908Srpaulo	ieee80211_mesh_rt_flush_peer(vap, ni->ni_macaddr);
1131195618Srpaulo	hwmp_send_perr(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &perr);
1132195618Srpaulo}
1133197413Srpaulo#undef	PERR_DFLAGS
1134195618Srpaulo#undef	PERR_DADDR
1135195618Srpaulo#undef	PERR_DSEQ
1136197413Srpaulo#undef	PERR_DRCODE
1137195618Srpaulo
1138197413Srpaulo#define	PERR_DFLAGS(n)	perr->perr_dests[n].dest_flags
1139195618Srpaulo#define	PERR_DADDR(n)	perr->perr_dests[n].dest_addr
1140195618Srpaulo#define	PERR_DSEQ(n)	perr->perr_dests[n].dest_seq
1141197413Srpaulo#define	PERR_DRCODE(n)	perr->perr_dests[n].dest_rcode
1142195618Srpaulostatic void
1143195618Srpaulohwmp_recv_perr(struct ieee80211vap *vap, struct ieee80211_node *ni,
1144195618Srpaulo    const struct ieee80211_frame *wh, const struct ieee80211_meshperr_ie *perr)
1145195618Srpaulo{
1146195618Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
1147195618Srpaulo	struct ieee80211_mesh_route *rt = NULL;
1148195618Srpaulo	struct ieee80211_hwmp_route *hr;
1149195618Srpaulo 	struct ieee80211_meshperr_ie pperr;
1150195618Srpaulo	int i, forward = 0;
1151195618Srpaulo
1152195618Srpaulo	/*
1153195618Srpaulo	 * Acceptance criteria: check if we received a PERR from a
1154195618Srpaulo	 * neighbor and forwarding is enabled.
1155195618Srpaulo	 */
1156195618Srpaulo	if (ni == vap->iv_bss ||
1157195618Srpaulo	    ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED ||
1158195618Srpaulo	    !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD))
1159195618Srpaulo		return;
1160195618Srpaulo	/*
1161195618Srpaulo	 * Find all routing entries that match and delete them.
1162195618Srpaulo	 */
1163195618Srpaulo	for (i = 0; i < perr->perr_ndests; i++) {
1164195618Srpaulo		rt = ieee80211_mesh_rt_find(vap, PERR_DADDR(i));
1165195618Srpaulo		if (rt == NULL)
1166195618Srpaulo			continue;
1167197413Srpaulo		hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
1168197413Srpaulo		if (!(PERR_DFLAGS(0) & IEEE80211_MESHPERR_DFLAGS_USN) &&
1169197413Srpaulo		    HWMP_SEQ_GEQ(PERR_DSEQ(i), hr->hr_seq)) {
1170195618Srpaulo			ieee80211_mesh_rt_del(vap, rt->rt_dest);
1171195908Srpaulo			ieee80211_mesh_rt_flush_peer(vap, rt->rt_dest);
1172195618Srpaulo			rt = NULL;
1173195618Srpaulo			forward = 1;
1174195618Srpaulo		}
1175195618Srpaulo	}
1176195618Srpaulo	/*
1177195618Srpaulo	 * Propagate the PERR if we previously found it on our routing table.
1178195618Srpaulo	 * XXX handle ndest > 1
1179195618Srpaulo	 */
1180197413Srpaulo	if (forward && perr->perr_ttl > 1) {
1181195618Srpaulo		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1182195784Srpaulo		    "propagate PERR from %s", ether_sprintf(wh->i_addr2));
1183195618Srpaulo		memcpy(&pperr, perr, sizeof(*perr));
1184197413Srpaulo		pperr.perr_ttl--;
1185195908Srpaulo		hwmp_send_perr(vap->iv_bss, vap->iv_myaddr, broadcastaddr,
1186195908Srpaulo		    &pperr);
1187195618Srpaulo	}
1188195618Srpaulo}
1189195618Srpaulo#undef	PEER_DADDR
1190195618Srpaulo#undef	PERR_DSEQ
1191195618Srpaulo
1192195618Srpaulostatic int
1193195618Srpaulohwmp_send_perr(struct ieee80211_node *ni,
1194195618Srpaulo    const uint8_t sa[IEEE80211_ADDR_LEN],
1195195618Srpaulo    const uint8_t da[IEEE80211_ADDR_LEN],
1196195618Srpaulo    struct ieee80211_meshperr_ie *perr)
1197195618Srpaulo{
1198195618Srpaulo	struct ieee80211_hwmp_state *hs = ni->ni_vap->iv_hwmp;
1199195618Srpaulo
1200195618Srpaulo	/*
1201195618Srpaulo	 * Enforce PERR interval.
1202195618Srpaulo	 */
1203195618Srpaulo	if (ratecheck(&hs->hs_lastperr, &ieee80211_hwmp_perrminint) == 0)
1204195618Srpaulo		return EALREADY;
1205195618Srpaulo	getmicrouptime(&hs->hs_lastperr);
1206195618Srpaulo
1207195618Srpaulo	/*
1208195618Srpaulo	 * mesh perr action frame format
1209195618Srpaulo	 *     [6] da
1210195618Srpaulo	 *     [6] sa
1211195618Srpaulo	 *     [6] addr3 = sa
1212195618Srpaulo	 *     [1] action
1213195618Srpaulo	 *     [1] category
1214195618Srpaulo	 *     [tlv] mesh path error
1215195618Srpaulo	 */
1216195618Srpaulo	perr->perr_ie = IEEE80211_ELEMID_MESHPERR;
1217195618Srpaulo	return hwmp_send_action(ni, sa, da, (uint8_t *)perr,
1218195618Srpaulo	    sizeof(struct ieee80211_meshperr_ie));
1219195618Srpaulo}
1220195618Srpaulo
1221195618Srpaulostatic void
1222195618Srpaulohwmp_recv_rann(struct ieee80211vap *vap, struct ieee80211_node *ni,
1223195618Srpaulo    const struct ieee80211_frame *wh, const struct ieee80211_meshrann_ie *rann)
1224195618Srpaulo{
1225195618Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
1226195618Srpaulo	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
1227195618Srpaulo	struct ieee80211_mesh_route *rt = NULL;
1228195618Srpaulo	struct ieee80211_hwmp_route *hr;
1229195618Srpaulo	struct ieee80211_meshrann_ie prann;
1230195618Srpaulo
1231195618Srpaulo	if (ni == vap->iv_bss ||
1232198230Srpaulo	    ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED ||
1233198230Srpaulo	    IEEE80211_ADDR_EQ(rann->rann_addr, vap->iv_myaddr))
1234195618Srpaulo		return;
1235195618Srpaulo
1236195618Srpaulo	rt = ieee80211_mesh_rt_find(vap, rann->rann_addr);
1237195618Srpaulo	/*
1238195618Srpaulo	 * Discover the path to the root mesh STA.
1239195618Srpaulo	 * If we already know it, propagate the RANN element.
1240195618Srpaulo	 */
1241195618Srpaulo	if (rt == NULL) {
1242195618Srpaulo		hwmp_discover(vap, rann->rann_addr, NULL);
1243195618Srpaulo		return;
1244195618Srpaulo	}
1245195618Srpaulo	hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
1246198581Srpaulo	if (HWMP_SEQ_GT(rann->rann_seq, hr->hr_seq)) {
1247198581Srpaulo		hr->hr_seq = rann->rann_seq;
1248198581Srpaulo		if (rann->rann_ttl > 1 &&
1249198581Srpaulo		    rann->rann_hopcount < hs->hs_maxhops &&
1250198581Srpaulo		    (ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) {
1251198581Srpaulo			memcpy(&prann, rann, sizeof(prann));
1252198581Srpaulo			prann.rann_hopcount += 1;
1253198581Srpaulo			prann.rann_ttl -= 1;
1254198581Srpaulo			prann.rann_metric += ms->ms_pmetric->mpm_metric(ni);
1255198581Srpaulo			hwmp_send_rann(vap->iv_bss, vap->iv_myaddr,
1256198581Srpaulo			    broadcastaddr, &prann);
1257198581Srpaulo		}
1258195618Srpaulo	}
1259195618Srpaulo}
1260195618Srpaulo
1261195618Srpaulostatic int
1262195618Srpaulohwmp_send_rann(struct ieee80211_node *ni,
1263195618Srpaulo    const uint8_t sa[IEEE80211_ADDR_LEN],
1264195618Srpaulo    const uint8_t da[IEEE80211_ADDR_LEN],
1265195618Srpaulo    struct ieee80211_meshrann_ie *rann)
1266195618Srpaulo{
1267195618Srpaulo	/*
1268195618Srpaulo	 * mesh rann action frame format
1269195618Srpaulo	 *     [6] da
1270195618Srpaulo	 *     [6] sa
1271195618Srpaulo	 *     [6] addr3 = sa
1272195618Srpaulo	 *     [1] action
1273195618Srpaulo	 *     [1] category
1274195618Srpaulo	 *     [tlv] root annoucement
1275195618Srpaulo	 */
1276195618Srpaulo	rann->rann_ie = IEEE80211_ELEMID_MESHRANN;
1277195618Srpaulo	return hwmp_send_action(ni, sa, da, (uint8_t *)rann,
1278195618Srpaulo	    sizeof(struct ieee80211_meshrann_ie));
1279195618Srpaulo}
1280195618Srpaulo
1281195618Srpaulo#define	PREQ_TFLAGS(n)	preq.preq_targets[n].target_flags
1282195618Srpaulo#define	PREQ_TADDR(n)	preq.preq_targets[n].target_addr
1283195618Srpaulo#define	PREQ_TSEQ(n)	preq.preq_targets[n].target_seq
1284195618Srpaulostatic struct ieee80211_node *
1285195618Srpaulohwmp_discover(struct ieee80211vap *vap,
1286195618Srpaulo    const uint8_t dest[IEEE80211_ADDR_LEN], struct mbuf *m)
1287195618Srpaulo{
1288195618Srpaulo	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
1289195618Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
1290195618Srpaulo	struct ieee80211_mesh_route *rt = NULL;
1291195618Srpaulo	struct ieee80211_hwmp_route *hr;
1292195618Srpaulo	struct ieee80211_meshpreq_ie preq;
1293195618Srpaulo	struct ieee80211_node *ni;
1294195618Srpaulo	int sendpreq = 0;
1295195618Srpaulo
1296195618Srpaulo	KASSERT(vap->iv_opmode == IEEE80211_M_MBSS,
1297195618Srpaulo	    ("not a mesh vap, opmode %d", vap->iv_opmode));
1298195618Srpaulo
1299195618Srpaulo	KASSERT(!IEEE80211_ADDR_EQ(vap->iv_myaddr, dest),
1300195618Srpaulo	    ("%s: discovering self!", __func__));
1301195618Srpaulo
1302195618Srpaulo	ni = NULL;
1303195618Srpaulo	if (!IEEE80211_IS_MULTICAST(dest)) {
1304195618Srpaulo		rt = ieee80211_mesh_rt_find(vap, dest);
1305195618Srpaulo		if (rt == NULL) {
1306195618Srpaulo			rt = ieee80211_mesh_rt_add(vap, dest);
1307195618Srpaulo			if (rt == NULL) {
1308195784Srpaulo				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP,
1309195784Srpaulo				    ni, "unable to add discovery path to %s",
1310195784Srpaulo				    ether_sprintf(dest));
1311195784Srpaulo				vap->iv_stats.is_mesh_rtaddfailed++;
1312195618Srpaulo				goto done;
1313195618Srpaulo			}
1314195618Srpaulo		}
1315195618Srpaulo		hr = IEEE80211_MESH_ROUTE_PRIV(rt,
1316195618Srpaulo		    struct ieee80211_hwmp_route);
1317195784Srpaulo		if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0) {
1318198230Srpaulo			if (hr->hr_origseq == 0)
1319198230Srpaulo				hr->hr_origseq = ++hs->hs_seq;
1320195618Srpaulo			rt->rt_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
1321195618Srpaulo			rt->rt_lifetime =
1322195813Ssam			    ticks_to_msecs(ieee80211_hwmp_pathtimeout);
1323195618Srpaulo			/* XXX check preq retries */
1324195618Srpaulo			sendpreq = 1;
1325195618Srpaulo			IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP, dest,
1326195784Srpaulo			    "start path discovery (src %s)",
1327195784Srpaulo			    m == NULL ? "<none>" : ether_sprintf(
1328195784Srpaulo				mtod(m, struct ether_header *)->ether_shost));
1329195618Srpaulo			/*
1330195618Srpaulo			 * Try to discover the path for this node.
1331195618Srpaulo			 */
1332195618Srpaulo			preq.preq_flags = 0;
1333195618Srpaulo			preq.preq_hopcount = 0;
1334195618Srpaulo			preq.preq_ttl = ms->ms_ttl;
1335195908Srpaulo			preq.preq_id = ++hs->hs_preqid;
1336195618Srpaulo			IEEE80211_ADDR_COPY(preq.preq_origaddr, vap->iv_myaddr);
1337198230Srpaulo			preq.preq_origseq = hr->hr_origseq;
1338195618Srpaulo			preq.preq_lifetime = rt->rt_lifetime;
1339195618Srpaulo			preq.preq_metric = rt->rt_metric;
1340195618Srpaulo			preq.preq_tcount = 1;
1341195618Srpaulo			IEEE80211_ADDR_COPY(PREQ_TADDR(0), dest);
1342195618Srpaulo			PREQ_TFLAGS(0) = 0;
1343195618Srpaulo			if (ieee80211_hwmp_targetonly)
1344195618Srpaulo				PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_TO;
1345195618Srpaulo			if (ieee80211_hwmp_replyforward)
1346195618Srpaulo				PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_RF;
1347195618Srpaulo			PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_USN;
1348195618Srpaulo			PREQ_TSEQ(0) = 0;
1349195618Srpaulo			/* XXX check return value */
1350195618Srpaulo			hwmp_send_preq(vap->iv_bss, vap->iv_myaddr,
1351195618Srpaulo			    broadcastaddr, &preq);
1352195618Srpaulo		}
1353195784Srpaulo		if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID)
1354195618Srpaulo			ni = ieee80211_find_txnode(vap, rt->rt_nexthop);
1355195618Srpaulo	} else {
1356195618Srpaulo		ni = ieee80211_find_txnode(vap, dest);
1357195784Srpaulo		/* NB: if null then we leak mbuf */
1358195784Srpaulo		KASSERT(ni != NULL, ("leak mcast frame"));
1359195618Srpaulo		return ni;
1360195618Srpaulo	}
1361195618Srpaulodone:
1362195618Srpaulo	if (ni == NULL && m != NULL) {
1363195618Srpaulo		if (sendpreq) {
1364195618Srpaulo			struct ieee80211com *ic = vap->iv_ic;
1365195618Srpaulo			/*
1366195618Srpaulo			 * Queue packet for transmit when path discovery
1367195618Srpaulo			 * completes.  If discovery never completes the
1368195618Srpaulo			 * frame will be flushed by way of the aging timer.
1369195618Srpaulo			 */
1370195784Srpaulo			IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP, dest,
1371195784Srpaulo			    "%s", "queue frame until path found");
1372195618Srpaulo			m->m_pkthdr.rcvif = (void *)(uintptr_t)
1373195618Srpaulo			    ieee80211_mac_hash(ic, dest);
1374195618Srpaulo			/* XXX age chosen randomly */
1375195618Srpaulo			ieee80211_ageq_append(&ic->ic_stageq, m,
1376195618Srpaulo			    IEEE80211_INACT_WAIT);
1377195784Srpaulo		} else {
1378195784Srpaulo			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_HWMP,
1379195784Srpaulo			    dest, NULL, "%s", "no valid path to this node");
1380195618Srpaulo			m_freem(m);
1381195784Srpaulo		}
1382195618Srpaulo	}
1383195618Srpaulo	return ni;
1384195618Srpaulo}
1385195618Srpaulo#undef	PREQ_TFLAGS
1386195618Srpaulo#undef	PREQ_TADDR
1387195618Srpaulo#undef	PREQ_TSEQ
1388195618Srpaulo
1389195618Srpaulostatic int
1390195618Srpaulohwmp_ioctl_get80211(struct ieee80211vap *vap, struct ieee80211req *ireq)
1391195618Srpaulo{
1392195618Srpaulo	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
1393195618Srpaulo	int error;
1394195618Srpaulo
1395195618Srpaulo	if (vap->iv_opmode != IEEE80211_M_MBSS)
1396195618Srpaulo		return ENOSYS;
1397195618Srpaulo	error = 0;
1398195618Srpaulo	switch (ireq->i_type) {
1399195618Srpaulo	case IEEE80211_IOC_HWMP_ROOTMODE:
1400195618Srpaulo		ireq->i_val = hs->hs_rootmode;
1401195618Srpaulo		break;
1402195618Srpaulo	case IEEE80211_IOC_HWMP_MAXHOPS:
1403195618Srpaulo		ireq->i_val = hs->hs_maxhops;
1404195618Srpaulo		break;
1405195618Srpaulo	default:
1406195618Srpaulo		return ENOSYS;
1407195618Srpaulo	}
1408195618Srpaulo	return error;
1409195618Srpaulo}
1410195618SrpauloIEEE80211_IOCTL_GET(hwmp, hwmp_ioctl_get80211);
1411195618Srpaulo
1412195618Srpaulostatic int
1413195618Srpaulohwmp_ioctl_set80211(struct ieee80211vap *vap, struct ieee80211req *ireq)
1414195618Srpaulo{
1415195618Srpaulo	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
1416195618Srpaulo	int error;
1417195618Srpaulo
1418195618Srpaulo	if (vap->iv_opmode != IEEE80211_M_MBSS)
1419195618Srpaulo		return ENOSYS;
1420195618Srpaulo	error = 0;
1421195618Srpaulo	switch (ireq->i_type) {
1422195618Srpaulo	case IEEE80211_IOC_HWMP_ROOTMODE:
1423195618Srpaulo		if (ireq->i_val < 0 || ireq->i_val > 3)
1424195618Srpaulo			return EINVAL;
1425195618Srpaulo		hs->hs_rootmode = ireq->i_val;
1426195618Srpaulo		hwmp_rootmode_setup(vap);
1427195618Srpaulo		break;
1428195618Srpaulo	case IEEE80211_IOC_HWMP_MAXHOPS:
1429195618Srpaulo		if (ireq->i_val <= 0 || ireq->i_val > 255)
1430195618Srpaulo			return EINVAL;
1431195618Srpaulo		hs->hs_maxhops = ireq->i_val;
1432195618Srpaulo		break;
1433195618Srpaulo	default:
1434195618Srpaulo		return ENOSYS;
1435195618Srpaulo	}
1436195618Srpaulo	return error;
1437195618Srpaulo}
1438195618SrpauloIEEE80211_IOCTL_SET(hwmp, hwmp_ioctl_set80211);
1439