ieee80211_hwmp.c revision 195784
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 195784 2009-07-20 19:12:08Z 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
119195618Srpaulostatic int	ieee80211_hwmp_targetonly = 0;
120195618Srpaulostatic int	ieee80211_hwmp_replyforward = 1;
121195618Srpaulostatic const int ieee80211_hwmp_maxprepretries = 3;
122195618Srpaulostatic const struct timeval ieee80211_hwmp_maxhopstime = { 0, 500000 };
123195618Srpaulostatic const struct timeval ieee80211_hwmp_preqminint = { 0, 100000 };
124195618Srpaulostatic const struct timeval ieee80211_hwmp_perrminint = { 0, 100000 };
125195618Srpaulostatic const struct timeval ieee80211_hwmp_roottimeout = { 5, 0 };
126195618Srpaulostatic const struct timeval ieee80211_hwmp_pathtimeout = { 5, 0 };
127195618Srpaulostatic const struct timeval ieee80211_hwmp_pathtoroottimeout = { 5, 0 };
128195618Srpaulostatic const struct timeval ieee80211_hwmp_rootint = { 2, 0 };
129195618Srpaulostatic const struct timeval ieee80211_hwmp_rannint = { 1, 0 };
130195618Srpaulostatic const struct timeval ieee80211_hwmp_pathmaintenanceint = { 2, 0 };
131195618Srpaulostatic const struct timeval ieee80211_hwmp_confirmint = { 2, 0 };
132195618Srpaulo
133195618Srpaulo#define	timeval2msecs(tv)	(tv.tv_sec * 1000 + tv.tv_usec / 1000)
134195618Srpaulo
135195618Srpaulo#define	HWMP_ROOTMODEINT msecs_to_ticks(timeval2msecs(ieee80211_hwmp_rootint))
136195618Srpaulo#define	HWMP_RANNMODEINT msecs_to_ticks(timeval2msecs(ieee80211_hwmp_rannint))
137195618Srpaulo
138195618Srpaulo/* unalligned little endian access */
139195618Srpaulo#define LE_WRITE_2(p, v) do {				\
140195618Srpaulo	((uint8_t *)(p))[0] = (v) & 0xff;		\
141195618Srpaulo	((uint8_t *)(p))[1] = ((v) >> 8) & 0xff;	\
142195618Srpaulo} while (0)
143195618Srpaulo#define LE_WRITE_4(p, v) do {				\
144195618Srpaulo	((uint8_t *)(p))[0] = (v) & 0xff;		\
145195618Srpaulo	((uint8_t *)(p))[1] = ((v) >> 8) & 0xff;	\
146195618Srpaulo	((uint8_t *)(p))[2] = ((v) >> 16) & 0xff;	\
147195618Srpaulo	((uint8_t *)(p))[3] = ((v) >> 24) & 0xff;	\
148195618Srpaulo} while (0)
149195618Srpaulo
150195618Srpaulo
151195618Srpaulo/* NB: the Target Address set in a Proactive PREQ is the broadcast address. */
152195618Srpaulostatic const uint8_t	broadcastaddr[IEEE80211_ADDR_LEN] =
153195618Srpaulo	{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
154195618Srpaulo
155195618Srpaulotypedef uint32_t ieee80211_hwmp_seq;
156195618Srpaulo#define	IEEE80211_HWMP_SEQ_LEQ(a, b)	((int32_t)((a)-(b)) <= 0)
157195618Srpaulo#define	IEEE80211_HWMP_SEQ_GEQ(a, b)	((int32_t)((a)-(b)) >= 0)
158195618Srpaulo
159195618Srpaulo/*
160195618Srpaulo * Private extension of ieee80211_mesh_route.
161195618Srpaulo */
162195618Srpaulostruct ieee80211_hwmp_route {
163195618Srpaulo	ieee80211_hwmp_seq	hr_seq;		/* HWMP sequence number */
164195618Srpaulo	ieee80211_hwmp_seq	hr_preqid;	/* Last PREQ ID seen */
165195618Srpaulo	int			hr_preqretries;
166195618Srpaulo};
167195618Srpaulostruct ieee80211_hwmp_state {
168195618Srpaulo	ieee80211_hwmp_seq	hs_seq;		/* next seq to be used */
169195618Srpaulo	ieee80211_hwmp_seq	hs_preqid;	/* next PREQ ID to be used */
170195618Srpaulo	struct timeval		hs_lastpreq;	/* last time we sent a PREQ */
171195618Srpaulo	struct timeval		hs_lastperr;	/* last time we sent a PERR */
172195618Srpaulo	int			hs_rootmode;	/* proactive HWMP */
173195618Srpaulo	struct callout		hs_roottimer;
174195618Srpaulo	uint8_t			hs_maxhops;	/* max hop count */
175195618Srpaulo};
176195618Srpaulo
177195618SrpauloSYSCTL_NODE(_net_wlan, OID_AUTO, hwmp, CTLFLAG_RD, 0,
178195618Srpaulo    "IEEE 802.11s HWMP parameters");
179195618SrpauloSYSCTL_INT(_net_wlan_hwmp, OID_AUTO, targetonly, CTLTYPE_INT | CTLFLAG_RW,
180195618Srpaulo    &ieee80211_hwmp_targetonly, 0, "Set TO bit on generated PREQs");
181195618SrpauloSYSCTL_INT(_net_wlan_hwmp, OID_AUTO, replyforward, CTLTYPE_INT | CTLFLAG_RW,
182195618Srpaulo    &ieee80211_hwmp_replyforward, 0, "Set RF bit on generated PREQs");
183195618Srpaulo
184195618Srpaulo#define	IEEE80211_HWMP_DEFAULT_MAXHOPS	31
185195618Srpaulo
186195618Srpaulostatic	ieee80211_recv_action_func hwmp_recv_action_meshpath_preq;
187195618Srpaulostatic	ieee80211_recv_action_func hwmp_recv_action_meshpath_prep;
188195618Srpaulostatic	ieee80211_recv_action_func hwmp_recv_action_meshpath_perr;
189195618Srpaulostatic	ieee80211_recv_action_func hwmp_recv_action_meshpath_rann;
190195618Srpaulo
191195618Srpaulostatic const struct ieee80211_mesh_proto_path mesh_proto_hwmp = {
192195618Srpaulo	.mpp_descr	= "HWMP",
193195618Srpaulo	.mpp_ie		= IEEE80211_MESHCONF_HWMP,
194195618Srpaulo	.mpp_discover	= hwmp_discover,
195195618Srpaulo	.mpp_peerdown	= hwmp_peerdown,
196195618Srpaulo	.mpp_vattach	= hwmp_vattach,
197195618Srpaulo	.mpp_vdetach	= hwmp_vdetach,
198195618Srpaulo	.mpp_newstate	= hwmp_newstate,
199195618Srpaulo	.mpp_privlen	= sizeof(struct ieee80211_hwmp_route),
200195784Srpaulo	/* ieee80211_hwmp_pathtimeout */
201195784Srpaulo	.mpp_inact	= { 5, 0 },
202195618Srpaulo};
203195618Srpaulo
204195618Srpaulo
205195618Srpaulostatic void
206195618Srpauloieee80211_hwmp_init(void)
207195618Srpaulo{
208195618Srpaulo	/*
209195618Srpaulo	 * Register action frame handlers.
210195618Srpaulo	 */
211195618Srpaulo	ieee80211_recv_action_register(IEEE80211_ACTION_CAT_MESHPATH,
212195618Srpaulo	    IEEE80211_ACTION_MESHPATH_REQ, hwmp_recv_action_meshpath_preq);
213195618Srpaulo	ieee80211_recv_action_register(IEEE80211_ACTION_CAT_MESHPATH,
214195618Srpaulo	    IEEE80211_ACTION_MESHPATH_REP, hwmp_recv_action_meshpath_prep);
215195618Srpaulo	ieee80211_recv_action_register(IEEE80211_ACTION_CAT_MESHPATH,
216195618Srpaulo	    IEEE80211_ACTION_MESHPATH_ERR, hwmp_recv_action_meshpath_perr);
217195618Srpaulo	ieee80211_recv_action_register(IEEE80211_ACTION_CAT_MESHPATH,
218195618Srpaulo	    IEEE80211_ACTION_MESHPATH_RANN, hwmp_recv_action_meshpath_rann);
219195618Srpaulo
220195618Srpaulo	/*
221195618Srpaulo	 * Register HWMP.
222195618Srpaulo	 */
223195618Srpaulo	ieee80211_mesh_register_proto_path(&mesh_proto_hwmp);
224195618Srpaulo}
225195618SrpauloSYSINIT(wlan_hwmp, SI_SUB_DRIVERS, SI_ORDER_SECOND, ieee80211_hwmp_init, NULL);
226195618Srpaulo
227195618Srpaulovoid
228195618Srpaulohwmp_vattach(struct ieee80211vap *vap)
229195618Srpaulo{
230195618Srpaulo	struct ieee80211_hwmp_state *hs;
231195618Srpaulo
232195618Srpaulo	KASSERT(vap->iv_opmode == IEEE80211_M_MBSS,
233195618Srpaulo	    ("not a mesh vap, opmode %d", vap->iv_opmode));
234195618Srpaulo
235195618Srpaulo	hs = malloc(sizeof(struct ieee80211_hwmp_state), M_80211_VAP,
236195618Srpaulo	    M_NOWAIT | M_ZERO);
237195618Srpaulo	if (hs == NULL) {
238195618Srpaulo		printf("%s: couldn't alloc HWMP state\n", __func__);
239195618Srpaulo		return;
240195618Srpaulo	}
241195618Srpaulo	hs->hs_maxhops = IEEE80211_HWMP_DEFAULT_MAXHOPS;
242195618Srpaulo	callout_init(&hs->hs_roottimer, CALLOUT_MPSAFE);
243195618Srpaulo	vap->iv_hwmp = hs;
244195618Srpaulo}
245195618Srpaulo
246195618Srpaulovoid
247195618Srpaulohwmp_vdetach(struct ieee80211vap *vap)
248195618Srpaulo{
249195618Srpaulo	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
250195618Srpaulo
251195784Srpaulo	callout_drain(&hs->hs_roottimer);
252195618Srpaulo	free(vap->iv_hwmp, M_80211_VAP);
253195618Srpaulo	vap->iv_hwmp = NULL;
254195618Srpaulo}
255195618Srpaulo
256195618Srpauloint
257195618Srpaulohwmp_newstate(struct ieee80211vap *vap, enum ieee80211_state ostate, int arg)
258195618Srpaulo{
259195618Srpaulo	enum ieee80211_state nstate = vap->iv_state;
260195618Srpaulo	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
261195618Srpaulo
262195618Srpaulo	IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n",
263195618Srpaulo	    __func__, ieee80211_state_name[ostate],
264195618Srpaulo	    ieee80211_state_name[nstate], arg);
265195618Srpaulo
266195618Srpaulo	/* Flush the table on RUN -> !RUN, e.g. interface down & up */
267195618Srpaulo	if (nstate != IEEE80211_S_RUN && ostate == IEEE80211_S_RUN)
268195618Srpaulo		callout_drain(&hs->hs_roottimer);
269195784Srpaulo	if (nstate == IEEE80211_S_RUN)
270195784Srpaulo		hwmp_rootmode_setup(vap);
271195618Srpaulo	return 0;
272195618Srpaulo}
273195618Srpaulo
274195618Srpaulostatic int
275195618Srpaulohwmp_recv_action_meshpath_preq(struct ieee80211_node *ni,
276195618Srpaulo	const struct ieee80211_frame *wh,
277195618Srpaulo	const uint8_t *frm, const uint8_t *efrm)
278195618Srpaulo{
279195618Srpaulo	struct ieee80211vap *vap = ni->ni_vap;
280195618Srpaulo	struct ieee80211_meshpreq_ie preq;
281195618Srpaulo	const uint8_t *iefrm = frm + 2; /* action + code */
282195618Srpaulo
283195618Srpaulo	while (efrm - iefrm > 1) {
284195618Srpaulo		IEEE80211_VERIFY_LENGTH(efrm - iefrm, iefrm[1] + 2, return 0);
285195618Srpaulo		if (*iefrm == IEEE80211_ELEMID_MESHPREQ) {
286195618Srpaulo			const struct ieee80211_meshpreq_ie *mpreq =
287195618Srpaulo			    (const struct ieee80211_meshpreq_ie *) iefrm;
288195618Srpaulo			/* XXX > 1 target */
289195618Srpaulo			if (mpreq->preq_len !=
290195618Srpaulo			    sizeof(struct ieee80211_meshpreq_ie) - 2) {
291195618Srpaulo				IEEE80211_DISCARD(vap,
292195618Srpaulo				    IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
293195618Srpaulo				    wh, NULL, "%s", "PREQ with wrong len");
294195618Srpaulo				vap->iv_stats.is_rx_mgtdiscard++;
295195618Srpaulo				return 1;
296195618Srpaulo			}
297195618Srpaulo			memcpy(&preq, mpreq, sizeof(preq));
298195618Srpaulo			preq.preq_id = LE_READ_4(&mpreq->preq_id);
299195618Srpaulo			preq.preq_origseq = LE_READ_4(&mpreq->preq_origseq);
300195618Srpaulo			preq.preq_lifetime = LE_READ_4(&mpreq->preq_lifetime);
301195618Srpaulo			preq.preq_metric = LE_READ_4(&mpreq->preq_metric);
302195618Srpaulo			preq.preq_targets[0].target_seq =
303195618Srpaulo			    LE_READ_4(&mpreq->preq_targets[0].target_seq);
304195618Srpaulo			hwmp_recv_preq(vap, ni, wh, &preq);
305195618Srpaulo			return 0;
306195618Srpaulo		}
307195618Srpaulo		iefrm += iefrm[1] + 2;
308195618Srpaulo	}
309195618Srpaulo	IEEE80211_DISCARD(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
310195618Srpaulo	    wh, NULL, "%s", "PREQ without IE");
311195618Srpaulo	vap->iv_stats.is_rx_mgtdiscard++;
312195618Srpaulo	return 0;
313195618Srpaulo}
314195618Srpaulo
315195618Srpaulostatic int
316195618Srpaulohwmp_recv_action_meshpath_prep(struct ieee80211_node *ni,
317195618Srpaulo	const struct ieee80211_frame *wh,
318195618Srpaulo	const uint8_t *frm, const uint8_t *efrm)
319195618Srpaulo{
320195618Srpaulo	struct ieee80211vap *vap = ni->ni_vap;
321195618Srpaulo	struct ieee80211_meshprep_ie prep;
322195618Srpaulo	const uint8_t *iefrm = frm + 2; /* action + code */
323195618Srpaulo
324195618Srpaulo	while (efrm - iefrm > 1) {
325195618Srpaulo		IEEE80211_VERIFY_LENGTH(efrm - iefrm, iefrm[1] + 2, return 0);
326195618Srpaulo		if (*iefrm == IEEE80211_ELEMID_MESHPREP) {
327195618Srpaulo			const struct ieee80211_meshprep_ie *mprep =
328195618Srpaulo			    (const struct ieee80211_meshprep_ie *) iefrm;
329195618Srpaulo			if (mprep->prep_len !=
330195618Srpaulo			    sizeof(struct ieee80211_meshprep_ie) - 2) {
331195618Srpaulo				IEEE80211_DISCARD(vap,
332195618Srpaulo				    IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
333195618Srpaulo				    wh, NULL, "%s", "PREP with wrong len");
334195618Srpaulo				vap->iv_stats.is_rx_mgtdiscard++;
335195618Srpaulo				return 1;
336195618Srpaulo			}
337195618Srpaulo			memcpy(&prep, mprep, sizeof(prep));
338195618Srpaulo			prep.prep_targetseq = LE_READ_4(&mprep->prep_targetseq);
339195618Srpaulo			prep.prep_lifetime = LE_READ_4(&mprep->prep_lifetime);
340195618Srpaulo			prep.prep_metric = LE_READ_4(&mprep->prep_metric);
341195618Srpaulo			prep.prep_origseq = LE_READ_4(&mprep->prep_origseq);
342195618Srpaulo			hwmp_recv_prep(vap, ni, wh, &prep);
343195618Srpaulo			return 0;
344195618Srpaulo		}
345195618Srpaulo		iefrm += iefrm[1] + 2;
346195618Srpaulo	}
347195618Srpaulo	IEEE80211_DISCARD(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
348195618Srpaulo	    wh, NULL, "%s", "PREP without IE");
349195618Srpaulo	vap->iv_stats.is_rx_mgtdiscard++;
350195618Srpaulo	return 0;
351195618Srpaulo}
352195618Srpaulo
353195618Srpaulostatic int
354195618Srpaulohwmp_recv_action_meshpath_perr(struct ieee80211_node *ni,
355195618Srpaulo	const struct ieee80211_frame *wh,
356195618Srpaulo	const uint8_t *frm, const uint8_t *efrm)
357195618Srpaulo{
358195618Srpaulo	struct ieee80211_meshperr_ie perr;
359195618Srpaulo	struct ieee80211vap *vap = ni->ni_vap;
360195618Srpaulo	const uint8_t *iefrm = frm + 2; /* action + code */
361195618Srpaulo
362195618Srpaulo	while (efrm - iefrm > 1) {
363195618Srpaulo		IEEE80211_VERIFY_LENGTH(efrm - iefrm, iefrm[1] + 2, return 0);
364195618Srpaulo		if (*iefrm == IEEE80211_ELEMID_MESHPERR) {
365195618Srpaulo			const struct ieee80211_meshperr_ie *mperr =
366195618Srpaulo			    (const struct ieee80211_meshperr_ie *) iefrm;
367195618Srpaulo			/* XXX > 1 target */
368195618Srpaulo			if (mperr->perr_len !=
369195618Srpaulo			    sizeof(struct ieee80211_meshperr_ie) - 2) {
370195618Srpaulo				IEEE80211_DISCARD(vap,
371195618Srpaulo				    IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
372195618Srpaulo				    wh, NULL, "%s", "PERR with wrong len");
373195618Srpaulo				vap->iv_stats.is_rx_mgtdiscard++;
374195618Srpaulo				return 1;
375195618Srpaulo			}
376195618Srpaulo			memcpy(&perr, mperr, sizeof(perr));
377195618Srpaulo			perr.perr_dests[0].dest_seq =
378195618Srpaulo			    LE_READ_4(&mperr->perr_dests[0].dest_seq);
379195618Srpaulo			hwmp_recv_perr(vap, ni, wh, &perr);
380195618Srpaulo			return 0;
381195618Srpaulo		}
382195618Srpaulo		iefrm += iefrm[1] + 2;
383195618Srpaulo	}
384195618Srpaulo	IEEE80211_DISCARD(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
385195618Srpaulo	    wh, NULL, "%s", "PERR without IE");
386195618Srpaulo	vap->iv_stats.is_rx_mgtdiscard++;
387195618Srpaulo	return 0;
388195618Srpaulo}
389195618Srpaulo
390195618Srpaulostatic int
391195618Srpaulohwmp_recv_action_meshpath_rann(struct ieee80211_node *ni,
392195618Srpaulo	const struct ieee80211_frame *wh,
393195618Srpaulo	const uint8_t *frm, const uint8_t *efrm)
394195618Srpaulo{
395195618Srpaulo	struct ieee80211vap *vap = ni->ni_vap;
396195618Srpaulo	struct ieee80211_meshrann_ie rann;
397195618Srpaulo	const uint8_t *iefrm = frm + 2; /* action + code */
398195618Srpaulo
399195618Srpaulo	while (efrm - iefrm > 1) {
400195618Srpaulo		IEEE80211_VERIFY_LENGTH(efrm - iefrm, iefrm[1] + 2, return 0);
401195618Srpaulo		if (*iefrm == IEEE80211_ELEMID_MESHRANN) {
402195618Srpaulo			const struct ieee80211_meshrann_ie *mrann =
403195618Srpaulo			    (const struct ieee80211_meshrann_ie *) iefrm;
404195618Srpaulo			if (mrann->rann_len !=
405195618Srpaulo			    sizeof(struct ieee80211_meshrann_ie) - 2) {
406195618Srpaulo				IEEE80211_DISCARD(vap,
407195618Srpaulo				    IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
408195618Srpaulo				    wh, NULL, "%s", "RAN with wrong len");
409195618Srpaulo				vap->iv_stats.is_rx_mgtdiscard++;
410195618Srpaulo				return 1;
411195618Srpaulo			}
412195618Srpaulo			memcpy(&rann, mrann, sizeof(rann));
413195618Srpaulo			rann.rann_seq = LE_READ_4(&mrann->rann_seq);
414195618Srpaulo			rann.rann_metric = LE_READ_4(&mrann->rann_metric);
415195618Srpaulo			hwmp_recv_rann(vap, ni, wh, &rann);
416195618Srpaulo			return 0;
417195618Srpaulo		}
418195618Srpaulo		iefrm += iefrm[1] + 2;
419195618Srpaulo	}
420195618Srpaulo	IEEE80211_DISCARD(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
421195618Srpaulo	    wh, NULL, "%s", "RANN without IE");
422195618Srpaulo	vap->iv_stats.is_rx_mgtdiscard++;
423195618Srpaulo	return 0;
424195618Srpaulo}
425195618Srpaulo
426195618Srpaulostatic int
427195618Srpaulohwmp_send_action(struct ieee80211_node *ni,
428195618Srpaulo    const uint8_t sa[IEEE80211_ADDR_LEN],
429195618Srpaulo    const uint8_t da[IEEE80211_ADDR_LEN],
430195618Srpaulo    uint8_t *ie, size_t len)
431195618Srpaulo{
432195618Srpaulo	struct ieee80211vap *vap = ni->ni_vap;
433195618Srpaulo	struct ieee80211com *ic = ni->ni_ic;
434195618Srpaulo	struct ieee80211_bpf_params params;
435195618Srpaulo	struct mbuf *m;
436195618Srpaulo	uint8_t *frm;
437195618Srpaulo
438195618Srpaulo	if (vap->iv_state == IEEE80211_S_CAC) {
439195618Srpaulo		IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, ni,
440195618Srpaulo		    "block %s frame in CAC state", "HWMP action");
441195618Srpaulo		vap->iv_stats.is_tx_badstate++;
442195618Srpaulo		return EIO;	/* XXX */
443195618Srpaulo	}
444195618Srpaulo
445195618Srpaulo	KASSERT(ni != NULL, ("null node"));
446195618Srpaulo	/*
447195618Srpaulo	 * Hold a reference on the node so it doesn't go away until after
448195618Srpaulo	 * the xmit is complete all the way in the driver.  On error we
449195618Srpaulo	 * will remove our reference.
450195618Srpaulo	 */
451195618Srpaulo#ifdef IEEE80211_DEBUG_REFCNT
452195618Srpaulo	IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
453195618Srpaulo	    "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n",
454195618Srpaulo	    __func__, __LINE__,
455195618Srpaulo	    ni, ether_sprintf(ni->ni_macaddr),
456195618Srpaulo	    ieee80211_node_refcnt(ni)+1);
457195618Srpaulo#endif
458195618Srpaulo	ieee80211_ref_node(ni);
459195618Srpaulo
460195618Srpaulo	m = ieee80211_getmgtframe(&frm,
461195618Srpaulo	    ic->ic_headroom + sizeof(struct ieee80211_frame),
462195618Srpaulo	    sizeof(struct ieee80211_action) + len
463195618Srpaulo	);
464195618Srpaulo	if (m == NULL) {
465195618Srpaulo		ieee80211_free_node(ni);
466195618Srpaulo		vap->iv_stats.is_tx_nobuf++;
467195618Srpaulo		return ENOMEM;
468195618Srpaulo	}
469195618Srpaulo	*frm++ = IEEE80211_ACTION_CAT_MESHPATH;
470195618Srpaulo	switch (*ie) {
471195618Srpaulo	case IEEE80211_ELEMID_MESHPREQ:
472195618Srpaulo		*frm++ = IEEE80211_ACTION_MESHPATH_REQ;
473195618Srpaulo		frm = hwmp_add_meshpreq(frm,
474195618Srpaulo		    (struct ieee80211_meshpreq_ie *)ie);
475195618Srpaulo		break;
476195618Srpaulo	case IEEE80211_ELEMID_MESHPREP:
477195618Srpaulo		*frm++ = IEEE80211_ACTION_MESHPATH_REP;
478195618Srpaulo		frm = hwmp_add_meshprep(frm,
479195618Srpaulo		    (struct ieee80211_meshprep_ie *)ie);
480195618Srpaulo		break;
481195618Srpaulo	case IEEE80211_ELEMID_MESHPERR:
482195618Srpaulo		*frm++ = IEEE80211_ACTION_MESHPATH_ERR;
483195618Srpaulo		frm = hwmp_add_meshperr(frm,
484195618Srpaulo		    (struct ieee80211_meshperr_ie *)ie);
485195618Srpaulo		break;
486195618Srpaulo	case IEEE80211_ELEMID_MESHRANN:
487195618Srpaulo		*frm++ = IEEE80211_ACTION_MESHPATH_RANN;
488195618Srpaulo		frm = hwmp_add_meshrann(frm,
489195618Srpaulo		    (struct ieee80211_meshrann_ie *)ie);
490195618Srpaulo		break;
491195618Srpaulo	}
492195618Srpaulo
493195618Srpaulo	m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
494195618Srpaulo	M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT);
495195618Srpaulo	if (m == NULL) {
496195618Srpaulo		ieee80211_free_node(ni);
497195618Srpaulo		vap->iv_stats.is_tx_nobuf++;
498195618Srpaulo		return ENOMEM;
499195618Srpaulo	}
500195618Srpaulo	ieee80211_send_setup(ni, m,
501195618Srpaulo	    IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_ACTION,
502195618Srpaulo	    IEEE80211_NONQOS_TID, sa, da, sa);
503195618Srpaulo
504195618Srpaulo	m->m_flags |= M_ENCAP;		/* mark encapsulated */
505195618Srpaulo	IEEE80211_NODE_STAT(ni, tx_mgmt);
506195618Srpaulo
507195618Srpaulo	memset(&params, 0, sizeof(params));
508195618Srpaulo	params.ibp_pri = WME_AC_VO;
509195618Srpaulo	params.ibp_rate0 = ni->ni_txparms->mgmtrate;
510195618Srpaulo	if (IEEE80211_IS_MULTICAST(da))
511195618Srpaulo		params.ibp_try0 = 1;
512195618Srpaulo	else
513195618Srpaulo		params.ibp_try0 = ni->ni_txparms->maxretry;
514195618Srpaulo	params.ibp_power = ni->ni_txpower;
515195618Srpaulo	return ic->ic_raw_xmit(ni, m, &params);
516195618Srpaulo}
517195618Srpaulo
518195618Srpaulo#define ADDWORD(frm, v) do {		\
519195618Srpaulo	LE_WRITE_4(frm, v);		\
520195618Srpaulo	frm += 4;			\
521195618Srpaulo} while (0)
522195618Srpaulo/*
523195618Srpaulo * Add a Mesh Path Request IE to a frame.
524195618Srpaulo */
525195618Srpaulostatic uint8_t *
526195618Srpaulohwmp_add_meshpreq(uint8_t *frm, const struct ieee80211_meshpreq_ie *preq)
527195618Srpaulo{
528195618Srpaulo	int i;
529195618Srpaulo
530195618Srpaulo	*frm++ = IEEE80211_ELEMID_MESHPREQ;
531195618Srpaulo	*frm++ = sizeof(struct ieee80211_meshpreq_ie) - 2 +
532195618Srpaulo	    (preq->preq_tcount - 1) * sizeof(*preq->preq_targets);
533195618Srpaulo	*frm++ = preq->preq_flags;
534195618Srpaulo	*frm++ = preq->preq_hopcount;
535195618Srpaulo	*frm++ = preq->preq_ttl;
536195618Srpaulo	ADDWORD(frm, preq->preq_id);
537195618Srpaulo	IEEE80211_ADDR_COPY(frm, preq->preq_origaddr); frm += 6;
538195618Srpaulo	ADDWORD(frm, preq->preq_origseq);
539195618Srpaulo	ADDWORD(frm, preq->preq_lifetime);
540195618Srpaulo	ADDWORD(frm, preq->preq_metric);
541195618Srpaulo	*frm++ = preq->preq_tcount;
542195618Srpaulo	for (i = 0; i < preq->preq_tcount; i++) {
543195618Srpaulo		*frm++ = preq->preq_targets[i].target_flags;
544195618Srpaulo		IEEE80211_ADDR_COPY(frm, preq->preq_targets[i].target_addr);
545195618Srpaulo		frm += 6;
546195618Srpaulo		ADDWORD(frm, preq->preq_targets[i].target_seq);
547195618Srpaulo	}
548195618Srpaulo	return frm;
549195618Srpaulo}
550195618Srpaulo
551195618Srpaulo/*
552195618Srpaulo * Add a Mesh Path Reply IE to a frame.
553195618Srpaulo */
554195618Srpaulostatic uint8_t *
555195618Srpaulohwmp_add_meshprep(uint8_t *frm, const struct ieee80211_meshprep_ie *prep)
556195618Srpaulo{
557195618Srpaulo	*frm++ = IEEE80211_ELEMID_MESHPREP;
558195618Srpaulo	*frm++ = sizeof(struct ieee80211_meshprep_ie) - 2;
559195618Srpaulo	*frm++ = prep->prep_flags;
560195618Srpaulo	*frm++ = prep->prep_hopcount;
561195618Srpaulo	*frm++ = prep->prep_ttl;
562195618Srpaulo	IEEE80211_ADDR_COPY(frm, prep->prep_targetaddr); frm += 6;
563195618Srpaulo	ADDWORD(frm, prep->prep_targetseq);
564195618Srpaulo	ADDWORD(frm, prep->prep_lifetime);
565195618Srpaulo	ADDWORD(frm, prep->prep_metric);
566195618Srpaulo	IEEE80211_ADDR_COPY(frm, prep->prep_origaddr); frm += 6;
567195618Srpaulo	ADDWORD(frm, prep->prep_origseq);
568195618Srpaulo	return frm;
569195618Srpaulo}
570195618Srpaulo
571195618Srpaulo/*
572195618Srpaulo * Add a Mesh Path Error IE to a frame.
573195618Srpaulo */
574195618Srpaulostatic uint8_t *
575195618Srpaulohwmp_add_meshperr(uint8_t *frm, const struct ieee80211_meshperr_ie *perr)
576195618Srpaulo{
577195618Srpaulo	int i;
578195618Srpaulo
579195618Srpaulo	*frm++ = IEEE80211_ELEMID_MESHPERR;
580195618Srpaulo	*frm++ = sizeof(struct ieee80211_meshperr_ie) - 2 +
581195618Srpaulo	    (perr->perr_ndests - 1) * sizeof(*perr->perr_dests);
582195618Srpaulo	*frm++ = perr->perr_mode;
583195618Srpaulo	*frm++ = perr->perr_ndests;
584195618Srpaulo	for (i = 0; i < perr->perr_ndests; i++) {
585195618Srpaulo		IEEE80211_ADDR_COPY(frm, perr->perr_dests[i].dest_addr);
586195618Srpaulo		frm += 6;
587195618Srpaulo		ADDWORD(frm, perr->perr_dests[i].dest_seq);
588195618Srpaulo	}
589195618Srpaulo	return frm;
590195618Srpaulo}
591195618Srpaulo
592195618Srpaulo/*
593195618Srpaulo * Add a Root Annoucement IE to a frame.
594195618Srpaulo */
595195618Srpaulostatic uint8_t *
596195618Srpaulohwmp_add_meshrann(uint8_t *frm, const struct ieee80211_meshrann_ie *rann)
597195618Srpaulo{
598195618Srpaulo	*frm++ = IEEE80211_ELEMID_MESHRANN;
599195618Srpaulo	*frm++ = sizeof(struct ieee80211_meshrann_ie) - 2;
600195618Srpaulo	*frm++ = rann->rann_flags;
601195618Srpaulo	*frm++ = rann->rann_hopcount;
602195618Srpaulo	*frm++ = rann->rann_ttl;
603195618Srpaulo	IEEE80211_ADDR_COPY(frm, rann->rann_addr); frm += 6;
604195618Srpaulo	ADDWORD(frm, rann->rann_seq);
605195618Srpaulo	ADDWORD(frm, rann->rann_metric);
606195618Srpaulo	return frm;
607195618Srpaulo}
608195618Srpaulo
609195618Srpaulostatic void
610195618Srpaulohwmp_rootmode_setup(struct ieee80211vap *vap)
611195618Srpaulo{
612195618Srpaulo	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
613195618Srpaulo
614195618Srpaulo	switch (hs->hs_rootmode) {
615195618Srpaulo	case IEEE80211_HWMP_ROOTMODE_DISABLED:
616195618Srpaulo		callout_drain(&hs->hs_roottimer);
617195618Srpaulo		break;
618195618Srpaulo	case IEEE80211_HWMP_ROOTMODE_NORMAL:
619195618Srpaulo	case IEEE80211_HWMP_ROOTMODE_PROACTIVE:
620195618Srpaulo		callout_reset(&hs->hs_roottimer, HWMP_ROOTMODEINT,
621195618Srpaulo		    hwmp_rootmode_cb, vap);
622195618Srpaulo		break;
623195618Srpaulo	case IEEE80211_HWMP_ROOTMODE_RANN:
624195618Srpaulo		callout_reset(&hs->hs_roottimer, HWMP_RANNMODEINT,
625195618Srpaulo		    hwmp_rootmode_rann_cb, vap);
626195618Srpaulo		break;
627195618Srpaulo	}
628195618Srpaulo}
629195618Srpaulo
630195618Srpaulo/*
631195618Srpaulo * Send a broadcast Path Request to find all nodes on the mesh. We are
632195618Srpaulo * called when the vap is configured as a HWMP root node.
633195618Srpaulo */
634195618Srpaulo#define	PREQ_TFLAGS(n)	preq.preq_targets[n].target_flags
635195618Srpaulo#define	PREQ_TADDR(n)	preq.preq_targets[n].target_addr
636195618Srpaulo#define	PREQ_TSEQ(n)	preq.preq_targets[n].target_seq
637195618Srpaulostatic void
638195618Srpaulohwmp_rootmode_cb(void *arg)
639195618Srpaulo{
640195618Srpaulo	struct ieee80211vap *vap = (struct ieee80211vap *)arg;
641195618Srpaulo	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
642195618Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
643195618Srpaulo	struct ieee80211_meshpreq_ie preq;
644195618Srpaulo
645195618Srpaulo	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, vap->iv_bss,
646195784Srpaulo	    "%s", "send broadcast PREQ");
647195618Srpaulo
648195618Srpaulo	/* XXX check portal role */
649195618Srpaulo	preq.preq_flags = IEEE80211_MESHPREQ_FLAGS_AM;
650195618Srpaulo	if (hs->hs_rootmode == IEEE80211_HWMP_ROOTMODE_PROACTIVE)
651195618Srpaulo		preq.preq_flags |= IEEE80211_MESHPREQ_FLAGS_PP;
652195618Srpaulo	preq.preq_hopcount = 0;
653195618Srpaulo	preq.preq_ttl = ms->ms_ttl;
654195618Srpaulo	preq.preq_id = ++hs->hs_preqid;
655195618Srpaulo	IEEE80211_ADDR_COPY(preq.preq_origaddr, vap->iv_myaddr);
656195618Srpaulo	preq.preq_origseq = ++hs->hs_seq;
657195618Srpaulo	preq.preq_lifetime = timeval2msecs(ieee80211_hwmp_roottimeout);
658195618Srpaulo	preq.preq_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
659195618Srpaulo	preq.preq_tcount = 1;
660195618Srpaulo	IEEE80211_ADDR_COPY(PREQ_TADDR(0), broadcastaddr);
661195618Srpaulo	PREQ_TFLAGS(0) = IEEE80211_MESHPREQ_TFLAGS_TO |
662195618Srpaulo	    IEEE80211_MESHPREQ_TFLAGS_RF;
663195618Srpaulo	PREQ_TSEQ(0) = 0;
664195618Srpaulo	vap->iv_stats.is_hwmp_rootreqs++;
665195618Srpaulo	hwmp_send_preq(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &preq);
666195618Srpaulo	hwmp_rootmode_setup(vap);
667195618Srpaulo}
668195618Srpaulo#undef	PREQ_TFLAGS
669195618Srpaulo#undef	PREQ_TADDR
670195618Srpaulo#undef	PREQ_TSEQ
671195618Srpaulo
672195618Srpaulo/*
673195618Srpaulo * Send a Root Annoucement (RANN) to find all the nodes on the mesh. We are
674195618Srpaulo * called when the vap is configured as a HWMP RANN root node.
675195618Srpaulo */
676195618Srpaulostatic void
677195618Srpaulohwmp_rootmode_rann_cb(void *arg)
678195618Srpaulo{
679195618Srpaulo	struct ieee80211vap *vap = (struct ieee80211vap *)arg;
680195618Srpaulo	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
681195618Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
682195618Srpaulo	struct ieee80211_meshrann_ie rann;
683195618Srpaulo
684195618Srpaulo	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, vap->iv_bss,
685195784Srpaulo	    "%s", "send broadcast RANN");
686195618Srpaulo
687195618Srpaulo	/* XXX check portal role */
688195618Srpaulo	rann.rann_flags = 0;
689195618Srpaulo	rann.rann_hopcount = 0;
690195618Srpaulo	rann.rann_ttl = ms->ms_ttl;
691195618Srpaulo	IEEE80211_ADDR_COPY(rann.rann_addr, vap->iv_myaddr);
692195618Srpaulo	rann.rann_seq = ++hs->hs_seq;
693195618Srpaulo	rann.rann_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
694195618Srpaulo
695195618Srpaulo	vap->iv_stats.is_hwmp_rootrann++;
696195618Srpaulo	hwmp_send_rann(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &rann);
697195618Srpaulo	hwmp_rootmode_setup(vap);
698195618Srpaulo}
699195618Srpaulo
700195618Srpaulo#define	PREQ_TFLAGS(n)	preq->preq_targets[n].target_flags
701195618Srpaulo#define	PREQ_TADDR(n)	preq->preq_targets[n].target_addr
702195618Srpaulo#define	PREQ_TSEQ(n)	preq->preq_targets[n].target_seq
703195618Srpaulostatic void
704195618Srpaulohwmp_recv_preq(struct ieee80211vap *vap, struct ieee80211_node *ni,
705195618Srpaulo    const struct ieee80211_frame *wh, const struct ieee80211_meshpreq_ie *preq)
706195618Srpaulo{
707195618Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
708195618Srpaulo	struct ieee80211_mesh_route *rt = NULL;
709195618Srpaulo	struct ieee80211_hwmp_route *hr;
710195618Srpaulo	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
711195618Srpaulo	struct ieee80211_meshprep_ie prep;
712195618Srpaulo
713195618Srpaulo	if (ni == vap->iv_bss ||
714195618Srpaulo	    ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED)
715195618Srpaulo		return;
716195618Srpaulo	/*
717195618Srpaulo	 * Ignore PREQs from us. Could happen because someone forward it
718195618Srpaulo	 * back to us.
719195618Srpaulo	 */
720195618Srpaulo	if (IEEE80211_ADDR_EQ(vap->iv_myaddr, preq->preq_origaddr))
721195618Srpaulo		return;
722195618Srpaulo
723195618Srpaulo	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
724195618Srpaulo	    "received PREQ, source %s", ether_sprintf(preq->preq_origaddr));
725195618Srpaulo
726195618Srpaulo	/*
727195618Srpaulo	 * Acceptance criteria: if the PREQ is not for us and
728195618Srpaulo	 * forwarding is disabled, discard this PREQ.
729195618Srpaulo	 */
730195618Srpaulo	if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0)) &&
731195618Srpaulo	    !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) {
732195618Srpaulo		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_HWMP,
733195618Srpaulo		    preq->preq_origaddr, NULL, "%s", "not accepting PREQ");
734195618Srpaulo		return;
735195618Srpaulo	}
736195618Srpaulo	/*
737195618Srpaulo	 * Check if the PREQ is addressed to us.
738195618Srpaulo	 * XXX: check if this is part of a proxy address.
739195618Srpaulo	 */
740195618Srpaulo	if (IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0))) {
741195618Srpaulo		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
742195784Srpaulo		    "reply to %s", ether_sprintf(preq->preq_origaddr));
743195618Srpaulo		/*
744195618Srpaulo		 * Build and send a PREP frame.
745195618Srpaulo		 */
746195618Srpaulo		prep.prep_flags = 0;
747195618Srpaulo		prep.prep_hopcount = 0;
748195618Srpaulo		prep.prep_ttl = ms->ms_ttl;
749195618Srpaulo		IEEE80211_ADDR_COPY(prep.prep_targetaddr, preq->preq_origaddr);
750195618Srpaulo		prep.prep_targetseq = preq->preq_origseq;
751195618Srpaulo		prep.prep_lifetime = preq->preq_lifetime;
752195618Srpaulo		prep.prep_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
753195618Srpaulo		IEEE80211_ADDR_COPY(prep.prep_origaddr, vap->iv_myaddr);
754195618Srpaulo		prep.prep_origseq = ++hs->hs_seq;
755195618Srpaulo		hwmp_send_prep(ni, vap->iv_myaddr, wh->i_addr2, &prep);
756195618Srpaulo		/*
757195618Srpaulo		 * Build the reverse path, if we don't have it already.
758195618Srpaulo		 */
759195618Srpaulo		rt = ieee80211_mesh_rt_find(vap, preq->preq_origaddr);
760195618Srpaulo		if (rt == NULL)
761195618Srpaulo			hwmp_discover(vap, preq->preq_origaddr, NULL);
762195784Srpaulo		else if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0)
763195618Srpaulo			hwmp_discover(vap, rt->rt_dest, NULL);
764195618Srpaulo		return;
765195618Srpaulo	}
766195618Srpaulo	/*
767195618Srpaulo	 * Proactive PREQ: reply with a proactive PREP to the
768195618Srpaulo	 * root STA if requested.
769195618Srpaulo	 */
770195618Srpaulo	if (IEEE80211_ADDR_EQ(PREQ_TADDR(0), broadcastaddr) &&
771195618Srpaulo	    (PREQ_TFLAGS(0) &
772195618Srpaulo	    ((IEEE80211_MESHPREQ_TFLAGS_TO|IEEE80211_MESHPREQ_TFLAGS_RF) ==
773195618Srpaulo	    (IEEE80211_MESHPREQ_TFLAGS_TO|IEEE80211_MESHPREQ_TFLAGS_RF)))) {
774195618Srpaulo		uint8_t rootmac[IEEE80211_ADDR_LEN];
775195618Srpaulo
776195618Srpaulo		IEEE80211_ADDR_COPY(rootmac, preq->preq_origaddr);
777195618Srpaulo		rt = ieee80211_mesh_rt_find(vap, rootmac);
778195784Srpaulo		if (rt == NULL) {
779195618Srpaulo			rt = ieee80211_mesh_rt_add(vap, rootmac);
780195784Srpaulo			if (rt == NULL) {
781195784Srpaulo				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
782195784Srpaulo				    "unable to add root mesh path to %s",
783195784Srpaulo				    ether_sprintf(rootmac));
784195784Srpaulo				vap->iv_stats.is_mesh_rtaddfailed++;
785195784Srpaulo				return;
786195784Srpaulo			}
787195784Srpaulo		}
788195784Srpaulo		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
789195784Srpaulo		    "root mesh station @ %s", ether_sprintf(rootmac));
790195784Srpaulo
791195618Srpaulo		/*
792195618Srpaulo		 * Reply with a PREP if we don't have a path to the root
793195618Srpaulo		 * or if the root sent us a proactive PREQ.
794195618Srpaulo		 */
795195784Srpaulo		if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0 ||
796195618Srpaulo		    (preq->preq_flags & IEEE80211_MESHPREQ_FLAGS_PP)) {
797195618Srpaulo			prep.prep_flags = 0;
798195618Srpaulo			prep.prep_hopcount = 0;
799195618Srpaulo			prep.prep_ttl = ms->ms_ttl;
800195784Srpaulo			IEEE80211_ADDR_COPY(prep.prep_origaddr, vap->iv_myaddr);
801195618Srpaulo			prep.prep_origseq = preq->preq_origseq;
802195618Srpaulo			prep.prep_targetseq = ++hs->hs_seq;
803195618Srpaulo			prep.prep_lifetime = preq->preq_lifetime;
804195618Srpaulo			prep.prep_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
805195784Srpaulo			IEEE80211_ADDR_COPY(prep.prep_targetaddr, rootmac);
806195618Srpaulo			prep.prep_targetseq = PREQ_TSEQ(0);
807195618Srpaulo			hwmp_send_prep(vap->iv_bss, vap->iv_myaddr,
808195618Srpaulo			    broadcastaddr, &prep);
809195618Srpaulo		}
810195618Srpaulo		hwmp_discover(vap, rootmac, NULL);
811195618Srpaulo		return;
812195618Srpaulo	}
813195618Srpaulo	rt = ieee80211_mesh_rt_find(vap, PREQ_TADDR(0));
814195618Srpaulo
815195618Srpaulo	/* XXX missing. Check for AE bit and update proxy information */
816195618Srpaulo
817195618Srpaulo	/*
818195618Srpaulo	 * Forwarding and Intermediate reply for PREQs with 1 target.
819195618Srpaulo	 */
820195618Srpaulo	if (preq->preq_tcount == 1) {
821195618Srpaulo		struct ieee80211_meshpreq_ie ppreq; /* propagated PREQ */
822195618Srpaulo
823195618Srpaulo		memcpy(&ppreq, preq, sizeof(ppreq));
824195618Srpaulo		/*
825195618Srpaulo		 * We have a valid route to this node.
826195618Srpaulo		 */
827195618Srpaulo		if (rt != NULL &&
828195784Srpaulo		    (rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID)) {
829195618Srpaulo
830195618Srpaulo			hr = IEEE80211_MESH_ROUTE_PRIV(rt,
831195618Srpaulo			    struct ieee80211_hwmp_route);
832195618Srpaulo			hr->hr_preqid = preq->preq_id;
833195618Srpaulo			hr->hr_seq = preq->preq_origseq;
834195618Srpaulo			if (preq->preq_ttl > 1 &&
835195618Srpaulo			    preq->preq_hopcount < hs->hs_maxhops) {
836195618Srpaulo				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
837195784Srpaulo				    "forward PREQ from %s",
838195618Srpaulo				    ether_sprintf(preq->preq_origaddr));
839195618Srpaulo				/*
840195618Srpaulo				 * Propagate the original PREQ.
841195618Srpaulo				 */
842195618Srpaulo				ppreq.preq_hopcount += 1;
843195618Srpaulo				ppreq.preq_ttl -= 1;
844195618Srpaulo				ppreq.preq_metric +=
845195618Srpaulo				    ms->ms_pmetric->mpm_metric(ni);
846195618Srpaulo				/*
847195618Srpaulo				 * Set TO and unset RF bits because we are going
848195618Srpaulo				 * to send a PREP next.
849195618Srpaulo				 */
850195618Srpaulo				ppreq.preq_targets[0].target_flags |=
851195618Srpaulo				    IEEE80211_MESHPREQ_TFLAGS_TO;
852195618Srpaulo				ppreq.preq_targets[0].target_flags &=
853195618Srpaulo				    ~IEEE80211_MESHPREQ_TFLAGS_RF;
854195618Srpaulo				hwmp_send_preq(ni, vap->iv_myaddr,
855195618Srpaulo				    broadcastaddr, &ppreq);
856195618Srpaulo			}
857195618Srpaulo			/*
858195618Srpaulo			 * Check if we can send an intermediate Path Reply,
859195618Srpaulo			 * i.e., Target Only bit is not set.
860195618Srpaulo			 */
861195618Srpaulo	    		if (!(PREQ_TFLAGS(0) & IEEE80211_MESHPREQ_TFLAGS_TO)) {
862195618Srpaulo				struct ieee80211_meshprep_ie prep;
863195618Srpaulo
864195618Srpaulo				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
865195618Srpaulo				    "intermediate reply for PREQ from %s",
866195618Srpaulo				    ether_sprintf(preq->preq_origaddr));
867195618Srpaulo				prep.prep_flags = 0;
868195618Srpaulo				prep.prep_hopcount = rt->rt_nhops + 1;
869195618Srpaulo				prep.prep_ttl = ms->ms_ttl;
870195618Srpaulo				IEEE80211_ADDR_COPY(&prep.prep_targetaddr,
871195618Srpaulo				    preq->preq_origaddr);
872195618Srpaulo				prep.prep_targetseq = hr->hr_seq;
873195618Srpaulo				prep.prep_lifetime = preq->preq_lifetime;
874195618Srpaulo				prep.prep_metric = rt->rt_metric +
875195618Srpaulo				    ms->ms_pmetric->mpm_metric(ni);
876195618Srpaulo				IEEE80211_ADDR_COPY(&prep.prep_origaddr,
877195618Srpaulo				    PREQ_TADDR(0));
878195618Srpaulo				prep.prep_origseq = hs->hs_seq++;
879195618Srpaulo				hwmp_send_prep(ni, vap->iv_myaddr,
880195618Srpaulo				    broadcastaddr, &prep);
881195618Srpaulo			}
882195618Srpaulo		/*
883195618Srpaulo		 * We have no information about this path,
884195618Srpaulo		 * propagate the PREQ.
885195618Srpaulo		 */
886195618Srpaulo		} else if (preq->preq_ttl > 1 &&
887195618Srpaulo		    preq->preq_hopcount < hs->hs_maxhops) {
888195784Srpaulo			if (rt == NULL) {
889195618Srpaulo				rt = ieee80211_mesh_rt_add(vap, PREQ_TADDR(0));
890195784Srpaulo				if (rt == NULL) {
891195784Srpaulo					IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP,
892195784Srpaulo					    ni, "unable to add PREQ path to %s",
893195784Srpaulo					    ether_sprintf(PREQ_TADDR(0)));
894195784Srpaulo					vap->iv_stats.is_mesh_rtaddfailed++;
895195784Srpaulo					return;
896195784Srpaulo				}
897195784Srpaulo			}
898195618Srpaulo			hr = IEEE80211_MESH_ROUTE_PRIV(rt,
899195618Srpaulo			    struct ieee80211_hwmp_route);
900195618Srpaulo			rt->rt_metric = preq->preq_metric;
901195618Srpaulo			rt->rt_lifetime = preq->preq_lifetime;
902195618Srpaulo			hr->hr_seq = preq->preq_origseq;
903195618Srpaulo			hr->hr_preqid = preq->preq_id;
904195618Srpaulo
905195618Srpaulo			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
906195784Srpaulo			    "forward PREQ from %s",
907195618Srpaulo			    ether_sprintf(preq->preq_origaddr));
908195618Srpaulo			ppreq.preq_hopcount += 1;
909195618Srpaulo			ppreq.preq_ttl -= 1;
910195618Srpaulo			ppreq.preq_metric += ms->ms_pmetric->mpm_metric(ni);
911195618Srpaulo			hwmp_send_preq(ni, vap->iv_myaddr, broadcastaddr,
912195618Srpaulo			    &ppreq);
913195618Srpaulo		}
914195618Srpaulo	}
915195618Srpaulo
916195618Srpaulo}
917195618Srpaulo#undef	PREQ_TFLAGS
918195618Srpaulo#undef	PREQ_TADDR
919195618Srpaulo#undef	PREQ_TSEQ
920195618Srpaulo
921195618Srpaulostatic int
922195618Srpaulohwmp_send_preq(struct ieee80211_node *ni,
923195618Srpaulo    const uint8_t sa[IEEE80211_ADDR_LEN],
924195618Srpaulo    const uint8_t da[IEEE80211_ADDR_LEN],
925195618Srpaulo    struct ieee80211_meshpreq_ie *preq)
926195618Srpaulo{
927195618Srpaulo	struct ieee80211_hwmp_state *hs = ni->ni_vap->iv_hwmp;
928195618Srpaulo
929195618Srpaulo	/*
930195618Srpaulo	 * Enforce PREQ interval.
931195618Srpaulo	 */
932195618Srpaulo	if (ratecheck(&hs->hs_lastpreq, &ieee80211_hwmp_preqminint) == 0)
933195618Srpaulo		return EALREADY;
934195618Srpaulo	getmicrouptime(&hs->hs_lastpreq);
935195618Srpaulo
936195618Srpaulo	/*
937195618Srpaulo	 * mesh preq action frame format
938195618Srpaulo	 *     [6] da
939195618Srpaulo	 *     [6] sa
940195618Srpaulo	 *     [6] addr3 = sa
941195618Srpaulo	 *     [1] action
942195618Srpaulo	 *     [1] category
943195618Srpaulo	 *     [tlv] mesh path request
944195618Srpaulo	 */
945195618Srpaulo	preq->preq_ie = IEEE80211_ELEMID_MESHPREQ;
946195618Srpaulo	return hwmp_send_action(ni, sa, da, (uint8_t *)preq,
947195618Srpaulo	    sizeof(struct ieee80211_meshpreq_ie));
948195618Srpaulo}
949195618Srpaulo
950195618Srpaulostatic void
951195618Srpaulohwmp_recv_prep(struct ieee80211vap *vap, struct ieee80211_node *ni,
952195618Srpaulo    const struct ieee80211_frame *wh, const struct ieee80211_meshprep_ie *prep)
953195618Srpaulo{
954195618Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
955195618Srpaulo	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
956195618Srpaulo	struct ieee80211_mesh_route *rt = NULL;
957195618Srpaulo	struct ieee80211_hwmp_route *hr;
958195618Srpaulo	struct ieee80211com *ic = vap->iv_ic;
959195618Srpaulo	struct ifnet *ifp = vap->iv_ifp;
960195618Srpaulo	struct mbuf *m, *next;
961195618Srpaulo
962195618Srpaulo	/*
963195618Srpaulo	 * Acceptance criteria: if the corresponding PREQ was not generated
964195618Srpaulo	 * by us and forwarding is disabled, discard this PREP.
965195618Srpaulo	 */
966195618Srpaulo	if (ni == vap->iv_bss ||
967195618Srpaulo	    ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED)
968195618Srpaulo		return;
969195618Srpaulo	if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, prep->prep_origaddr) &&
970195618Srpaulo	    !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD))
971195618Srpaulo		return;
972195618Srpaulo
973195618Srpaulo	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
974195618Srpaulo	    "received PREP from %s", ether_sprintf(prep->prep_origaddr));
975195618Srpaulo
976195618Srpaulo	/*
977195618Srpaulo	 * If it's NOT for us, propagate the PREP.
978195618Srpaulo	 */
979195618Srpaulo	if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, prep->prep_targetaddr) &&
980195618Srpaulo	    prep->prep_ttl > 1 && prep->prep_hopcount < hs->hs_maxhops) {
981195618Srpaulo		struct ieee80211_meshprep_ie pprep; /* propagated PREP */
982195618Srpaulo
983195618Srpaulo		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
984195784Srpaulo		    "propagate PREP from %s",
985195618Srpaulo		    ether_sprintf(prep->prep_origaddr));
986195618Srpaulo
987195618Srpaulo		memcpy(&pprep, prep, sizeof(pprep));
988195618Srpaulo		pprep.prep_hopcount += 1;
989195618Srpaulo		pprep.prep_ttl -= 1;
990195618Srpaulo		pprep.prep_metric += ms->ms_pmetric->mpm_metric(ni);
991195618Srpaulo		IEEE80211_ADDR_COPY(pprep.prep_origaddr, vap->iv_myaddr);
992195618Srpaulo		hwmp_send_prep(ni, vap->iv_myaddr, broadcastaddr, &pprep);
993195618Srpaulo	}
994195618Srpaulo
995195618Srpaulo	rt = ieee80211_mesh_rt_find(vap, prep->prep_origaddr);
996195618Srpaulo	if (rt == NULL) {
997195618Srpaulo		/*
998195618Srpaulo		 * If we have no entry this could be a reply to a root PREQ.
999195618Srpaulo		 */
1000195618Srpaulo		if (hs->hs_rootmode != IEEE80211_HWMP_ROOTMODE_DISABLED) {
1001195618Srpaulo			rt = ieee80211_mesh_rt_add(vap, prep->prep_origaddr);
1002195784Srpaulo			if (rt == NULL) {
1003195784Srpaulo				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP,
1004195784Srpaulo				    ni, "unable to add PREP path to %s",
1005195784Srpaulo				    ether_sprintf(prep->prep_origaddr));
1006195784Srpaulo				vap->iv_stats.is_mesh_rtaddfailed++;
1007195784Srpaulo				return;
1008195784Srpaulo			}
1009195618Srpaulo			IEEE80211_ADDR_COPY(rt->rt_nexthop, wh->i_addr2);
1010195618Srpaulo			rt->rt_nhops = prep->prep_hopcount;
1011195618Srpaulo			rt->rt_lifetime = prep->prep_lifetime;
1012195618Srpaulo			rt->rt_metric = prep->prep_metric;
1013195784Srpaulo			rt->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID;
1014195618Srpaulo			return;
1015195618Srpaulo		}
1016195618Srpaulo		return;
1017195618Srpaulo	}
1018195618Srpaulo	hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
1019195618Srpaulo	if (prep->prep_targetseq == hr->hr_seq) {
1020195618Srpaulo		int useprep = 0;
1021195618Srpaulo		/*
1022195618Srpaulo		 * Check if we already have a path to this node.
1023195618Srpaulo		 * If we do, check if this path reply contains a
1024195618Srpaulo		 * better route.
1025195618Srpaulo		 */
1026195784Srpaulo		if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0)
1027195618Srpaulo			useprep = 1;
1028195618Srpaulo		else if (prep->prep_hopcount < rt->rt_nhops ||
1029195618Srpaulo		    prep->prep_metric < rt->rt_metric)
1030195618Srpaulo			useprep = 1;
1031195618Srpaulo		if (useprep) {
1032195618Srpaulo			IEEE80211_ADDR_COPY(rt->rt_nexthop, wh->i_addr2);
1033195618Srpaulo			rt->rt_nhops = prep->prep_hopcount;
1034195618Srpaulo			rt->rt_lifetime = prep->prep_lifetime;
1035195618Srpaulo			rt->rt_metric = prep->prep_metric;
1036195784Srpaulo			rt->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID;
1037195618Srpaulo		}
1038195618Srpaulo	} else {
1039195618Srpaulo		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1040195618Srpaulo		    "discard PREP from %s, wrong seqno %u != %u",
1041195618Srpaulo		    ether_sprintf(prep->prep_origaddr), prep->prep_targetseq,
1042195618Srpaulo		    hr->hr_seq);
1043195618Srpaulo		vap->iv_stats.is_hwmp_wrongseq++;
1044195618Srpaulo	}
1045195618Srpaulo
1046195618Srpaulo	/*
1047195618Srpaulo	 * XXX: If it's for us and the AE bit is set, update the
1048195618Srpaulo	 * proxy information table.
1049195618Srpaulo	 */
1050195618Srpaulo
1051195618Srpaulo	/*
1052195618Srpaulo	 * XXX: If it's NOT for us and the AE bit is set,
1053195618Srpaulo	 * update the proxy information table.
1054195618Srpaulo	 */
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
1097195618Srpaulo#define	PERR_DADDR(n)	perr.perr_dests[n].dest_addr
1098195618Srpaulo#define	PERR_DSEQ(n)	perr.perr_dests[n].dest_seq
1099195618Srpaulostatic void
1100195618Srpaulohwmp_peerdown(struct ieee80211_node *ni)
1101195618Srpaulo{
1102195618Srpaulo	struct ieee80211vap *vap = ni->ni_vap;
1103195618Srpaulo	struct ieee80211_meshperr_ie perr;
1104195618Srpaulo	struct ieee80211_mesh_route *rt;
1105195618Srpaulo	struct ieee80211_hwmp_route *hr;
1106195618Srpaulo
1107195618Srpaulo	rt = ieee80211_mesh_rt_find(vap, ni->ni_macaddr);
1108195618Srpaulo	if (rt == NULL)
1109195618Srpaulo		return;
1110195618Srpaulo	hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
1111195618Srpaulo	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1112195784Srpaulo	    "%s", "delete route entry");
1113195618Srpaulo	perr.perr_mode = 0;
1114195618Srpaulo	perr.perr_ndests = 1;
1115195618Srpaulo	IEEE80211_ADDR_COPY(PERR_DADDR(0), rt->rt_dest);
1116195618Srpaulo	PERR_DSEQ(0) = hr->hr_seq;
1117195618Srpaulo	ieee80211_mesh_rt_del(vap, ni->ni_macaddr);
1118195618Srpaulo	hwmp_send_perr(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &perr);
1119195618Srpaulo}
1120195618Srpaulo#undef	PERR_DADDR
1121195618Srpaulo#undef	PERR_DSEQ
1122195618Srpaulo
1123195618Srpaulo#define	PERR_DADDR(n)	perr->perr_dests[n].dest_addr
1124195618Srpaulo#define	PERR_DSEQ(n)	perr->perr_dests[n].dest_seq
1125195618Srpaulostatic void
1126195618Srpaulohwmp_recv_perr(struct ieee80211vap *vap, struct ieee80211_node *ni,
1127195618Srpaulo    const struct ieee80211_frame *wh, const struct ieee80211_meshperr_ie *perr)
1128195618Srpaulo{
1129195618Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
1130195618Srpaulo	struct ieee80211_mesh_route *rt = NULL;
1131195618Srpaulo	struct ieee80211_hwmp_route *hr;
1132195618Srpaulo 	struct ieee80211_meshperr_ie pperr;
1133195618Srpaulo	int i, forward = 0;
1134195618Srpaulo
1135195618Srpaulo	/*
1136195618Srpaulo	 * Acceptance criteria: check if we received a PERR from a
1137195618Srpaulo	 * neighbor and forwarding is enabled.
1138195618Srpaulo	 */
1139195618Srpaulo	if (ni == vap->iv_bss ||
1140195618Srpaulo	    ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED ||
1141195618Srpaulo	    !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD))
1142195618Srpaulo		return;
1143195618Srpaulo	/*
1144195618Srpaulo	 * Find all routing entries that match and delete them.
1145195618Srpaulo	 */
1146195618Srpaulo	for (i = 0; i < perr->perr_ndests; i++) {
1147195618Srpaulo		rt = ieee80211_mesh_rt_find(vap, PERR_DADDR(i));
1148195618Srpaulo		if (rt == NULL)
1149195618Srpaulo			continue;
1150195618Srpaulo		hr = IEEE80211_MESH_ROUTE_PRIV(rt,
1151195618Srpaulo		    struct ieee80211_hwmp_route);
1152195618Srpaulo		if (PERR_DSEQ(i) >= hr->hr_seq) {
1153195618Srpaulo			ieee80211_mesh_rt_del(vap, rt->rt_dest);
1154195618Srpaulo			rt = NULL;
1155195618Srpaulo			forward = 1;
1156195618Srpaulo		}
1157195618Srpaulo	}
1158195618Srpaulo	/*
1159195618Srpaulo	 * Propagate the PERR if we previously found it on our routing table.
1160195618Srpaulo	 * XXX handle ndest > 1
1161195618Srpaulo	 */
1162195618Srpaulo	if (forward) {
1163195618Srpaulo		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1164195784Srpaulo		    "propagate PERR from %s", ether_sprintf(wh->i_addr2));
1165195618Srpaulo		memcpy(&pperr, perr, sizeof(*perr));
1166195618Srpaulo		hwmp_send_perr(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &pperr);
1167195618Srpaulo	}
1168195618Srpaulo}
1169195618Srpaulo#undef	PEER_DADDR
1170195618Srpaulo#undef	PERR_DSEQ
1171195618Srpaulo
1172195618Srpaulostatic int
1173195618Srpaulohwmp_send_perr(struct ieee80211_node *ni,
1174195618Srpaulo    const uint8_t sa[IEEE80211_ADDR_LEN],
1175195618Srpaulo    const uint8_t da[IEEE80211_ADDR_LEN],
1176195618Srpaulo    struct ieee80211_meshperr_ie *perr)
1177195618Srpaulo{
1178195618Srpaulo	struct ieee80211_hwmp_state *hs = ni->ni_vap->iv_hwmp;
1179195618Srpaulo
1180195618Srpaulo	/*
1181195618Srpaulo	 * Enforce PERR interval.
1182195618Srpaulo	 */
1183195618Srpaulo	if (ratecheck(&hs->hs_lastperr, &ieee80211_hwmp_perrminint) == 0)
1184195618Srpaulo		return EALREADY;
1185195618Srpaulo	getmicrouptime(&hs->hs_lastperr);
1186195618Srpaulo
1187195618Srpaulo	/*
1188195618Srpaulo	 * mesh perr action frame format
1189195618Srpaulo	 *     [6] da
1190195618Srpaulo	 *     [6] sa
1191195618Srpaulo	 *     [6] addr3 = sa
1192195618Srpaulo	 *     [1] action
1193195618Srpaulo	 *     [1] category
1194195618Srpaulo	 *     [tlv] mesh path error
1195195618Srpaulo	 */
1196195618Srpaulo	perr->perr_ie = IEEE80211_ELEMID_MESHPERR;
1197195618Srpaulo	return hwmp_send_action(ni, sa, da, (uint8_t *)perr,
1198195618Srpaulo	    sizeof(struct ieee80211_meshperr_ie));
1199195618Srpaulo}
1200195618Srpaulo
1201195618Srpaulostatic void
1202195618Srpaulohwmp_recv_rann(struct ieee80211vap *vap, struct ieee80211_node *ni,
1203195618Srpaulo    const struct ieee80211_frame *wh, const struct ieee80211_meshrann_ie *rann)
1204195618Srpaulo{
1205195618Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
1206195618Srpaulo	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
1207195618Srpaulo	struct ieee80211_mesh_route *rt = NULL;
1208195618Srpaulo	struct ieee80211_hwmp_route *hr;
1209195618Srpaulo	struct ieee80211_meshrann_ie prann;
1210195618Srpaulo
1211195618Srpaulo	if (ni == vap->iv_bss ||
1212195618Srpaulo	    ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED)
1213195618Srpaulo		return;
1214195618Srpaulo
1215195618Srpaulo	rt = ieee80211_mesh_rt_find(vap, rann->rann_addr);
1216195618Srpaulo	/*
1217195618Srpaulo	 * Discover the path to the root mesh STA.
1218195618Srpaulo	 * If we already know it, propagate the RANN element.
1219195618Srpaulo	 */
1220195618Srpaulo	if (rt == NULL) {
1221195618Srpaulo		hwmp_discover(vap, rann->rann_addr, NULL);
1222195618Srpaulo		return;
1223195618Srpaulo	}
1224195618Srpaulo	hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
1225195618Srpaulo	if (rann->rann_seq > hr->hr_seq && rann->rann_ttl > 1 &&
1226195618Srpaulo	    rann->rann_hopcount < hs->hs_maxhops &&
1227195618Srpaulo	    (ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) {
1228195618Srpaulo		memcpy(&prann, rann, sizeof(prann));
1229195618Srpaulo		prann.rann_hopcount += 1;
1230195618Srpaulo		prann.rann_ttl -= 1;
1231195618Srpaulo		prann.rann_metric += ms->ms_pmetric->mpm_metric(ni);
1232195618Srpaulo		hwmp_send_rann(vap->iv_bss, vap->iv_myaddr, broadcastaddr,
1233195618Srpaulo		    &prann);
1234195618Srpaulo	}
1235195618Srpaulo}
1236195618Srpaulo
1237195618Srpaulostatic int
1238195618Srpaulohwmp_send_rann(struct ieee80211_node *ni,
1239195618Srpaulo    const uint8_t sa[IEEE80211_ADDR_LEN],
1240195618Srpaulo    const uint8_t da[IEEE80211_ADDR_LEN],
1241195618Srpaulo    struct ieee80211_meshrann_ie *rann)
1242195618Srpaulo{
1243195618Srpaulo	/*
1244195618Srpaulo	 * mesh rann action frame format
1245195618Srpaulo	 *     [6] da
1246195618Srpaulo	 *     [6] sa
1247195618Srpaulo	 *     [6] addr3 = sa
1248195618Srpaulo	 *     [1] action
1249195618Srpaulo	 *     [1] category
1250195618Srpaulo	 *     [tlv] root annoucement
1251195618Srpaulo	 */
1252195618Srpaulo	rann->rann_ie = IEEE80211_ELEMID_MESHRANN;
1253195618Srpaulo	return hwmp_send_action(ni, sa, da, (uint8_t *)rann,
1254195618Srpaulo	    sizeof(struct ieee80211_meshrann_ie));
1255195618Srpaulo}
1256195618Srpaulo
1257195618Srpaulo#define	PREQ_TFLAGS(n)	preq.preq_targets[n].target_flags
1258195618Srpaulo#define	PREQ_TADDR(n)	preq.preq_targets[n].target_addr
1259195618Srpaulo#define	PREQ_TSEQ(n)	preq.preq_targets[n].target_seq
1260195618Srpaulostatic struct ieee80211_node *
1261195618Srpaulohwmp_discover(struct ieee80211vap *vap,
1262195618Srpaulo    const uint8_t dest[IEEE80211_ADDR_LEN], struct mbuf *m)
1263195618Srpaulo{
1264195618Srpaulo	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
1265195618Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
1266195618Srpaulo	struct ieee80211_mesh_route *rt = NULL;
1267195618Srpaulo	struct ieee80211_hwmp_route *hr;
1268195618Srpaulo	struct ieee80211_meshpreq_ie preq;
1269195618Srpaulo	struct ieee80211_node *ni;
1270195618Srpaulo	int sendpreq = 0;
1271195618Srpaulo
1272195618Srpaulo	KASSERT(vap->iv_opmode == IEEE80211_M_MBSS,
1273195618Srpaulo	    ("not a mesh vap, opmode %d", vap->iv_opmode));
1274195618Srpaulo
1275195618Srpaulo	KASSERT(!IEEE80211_ADDR_EQ(vap->iv_myaddr, dest),
1276195618Srpaulo	    ("%s: discovering self!", __func__));
1277195618Srpaulo
1278195618Srpaulo	ni = NULL;
1279195618Srpaulo	if (!IEEE80211_IS_MULTICAST(dest)) {
1280195618Srpaulo		rt = ieee80211_mesh_rt_find(vap, dest);
1281195618Srpaulo		if (rt == NULL) {
1282195618Srpaulo			rt = ieee80211_mesh_rt_add(vap, dest);
1283195618Srpaulo			if (rt == NULL) {
1284195784Srpaulo				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP,
1285195784Srpaulo				    ni, "unable to add discovery path to %s",
1286195784Srpaulo				    ether_sprintf(dest));
1287195784Srpaulo				vap->iv_stats.is_mesh_rtaddfailed++;
1288195618Srpaulo				goto done;
1289195618Srpaulo			}
1290195618Srpaulo		}
1291195618Srpaulo		hr = IEEE80211_MESH_ROUTE_PRIV(rt,
1292195618Srpaulo		    struct ieee80211_hwmp_route);
1293195784Srpaulo		if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0) {
1294195618Srpaulo			if (hr->hr_preqid == 0) {
1295195618Srpaulo				hr->hr_seq = ++hs->hs_seq;
1296195618Srpaulo				hr->hr_preqid = ++hs->hs_preqid;
1297195618Srpaulo			}
1298195618Srpaulo			rt->rt_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
1299195618Srpaulo			rt->rt_lifetime =
1300195618Srpaulo			    timeval2msecs(ieee80211_hwmp_pathtimeout);
1301195618Srpaulo			/* XXX check preq retries */
1302195618Srpaulo			sendpreq = 1;
1303195618Srpaulo			IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP, dest,
1304195784Srpaulo			    "start path discovery (src %s)",
1305195784Srpaulo			    m == NULL ? "<none>" : ether_sprintf(
1306195784Srpaulo				mtod(m, struct ether_header *)->ether_shost));
1307195618Srpaulo			/*
1308195618Srpaulo			 * Try to discover the path for this node.
1309195618Srpaulo			 */
1310195618Srpaulo			preq.preq_flags = 0;
1311195618Srpaulo			preq.preq_hopcount = 0;
1312195618Srpaulo			preq.preq_ttl = ms->ms_ttl;
1313195618Srpaulo			preq.preq_id = hr->hr_preqid;
1314195618Srpaulo			IEEE80211_ADDR_COPY(preq.preq_origaddr, vap->iv_myaddr);
1315195618Srpaulo			preq.preq_origseq = hr->hr_seq;
1316195618Srpaulo			preq.preq_lifetime = rt->rt_lifetime;
1317195618Srpaulo			preq.preq_metric = rt->rt_metric;
1318195618Srpaulo			preq.preq_tcount = 1;
1319195618Srpaulo			IEEE80211_ADDR_COPY(PREQ_TADDR(0), dest);
1320195618Srpaulo			PREQ_TFLAGS(0) = 0;
1321195618Srpaulo			if (ieee80211_hwmp_targetonly)
1322195618Srpaulo				PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_TO;
1323195618Srpaulo			if (ieee80211_hwmp_replyforward)
1324195618Srpaulo				PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_RF;
1325195618Srpaulo			PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_USN;
1326195618Srpaulo			PREQ_TSEQ(0) = 0;
1327195618Srpaulo			/* XXX check return value */
1328195618Srpaulo			hwmp_send_preq(vap->iv_bss, vap->iv_myaddr,
1329195618Srpaulo			    broadcastaddr, &preq);
1330195618Srpaulo		}
1331195784Srpaulo		if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID)
1332195618Srpaulo			ni = ieee80211_find_txnode(vap, rt->rt_nexthop);
1333195618Srpaulo	} else {
1334195618Srpaulo		ni = ieee80211_find_txnode(vap, dest);
1335195784Srpaulo		/* NB: if null then we leak mbuf */
1336195784Srpaulo		KASSERT(ni != NULL, ("leak mcast frame"));
1337195618Srpaulo		return ni;
1338195618Srpaulo	}
1339195618Srpaulodone:
1340195618Srpaulo	if (ni == NULL && m != NULL) {
1341195618Srpaulo		if (sendpreq) {
1342195618Srpaulo			struct ieee80211com *ic = vap->iv_ic;
1343195618Srpaulo			/*
1344195618Srpaulo			 * Queue packet for transmit when path discovery
1345195618Srpaulo			 * completes.  If discovery never completes the
1346195618Srpaulo			 * frame will be flushed by way of the aging timer.
1347195618Srpaulo			 */
1348195784Srpaulo			IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP, dest,
1349195784Srpaulo			    "%s", "queue frame until path found");
1350195618Srpaulo			m->m_pkthdr.rcvif = (void *)(uintptr_t)
1351195618Srpaulo			    ieee80211_mac_hash(ic, dest);
1352195618Srpaulo			/* XXX age chosen randomly */
1353195618Srpaulo			ieee80211_ageq_append(&ic->ic_stageq, m,
1354195618Srpaulo			    IEEE80211_INACT_WAIT);
1355195784Srpaulo		} else {
1356195784Srpaulo			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_HWMP,
1357195784Srpaulo			    dest, NULL, "%s", "no valid path to this node");
1358195618Srpaulo			m_freem(m);
1359195784Srpaulo		}
1360195618Srpaulo	}
1361195618Srpaulo	return ni;
1362195618Srpaulo}
1363195618Srpaulo#undef	PREQ_TFLAGS
1364195618Srpaulo#undef	PREQ_TADDR
1365195618Srpaulo#undef	PREQ_TSEQ
1366195618Srpaulo
1367195618Srpaulostatic int
1368195618Srpaulohwmp_ioctl_get80211(struct ieee80211vap *vap, struct ieee80211req *ireq)
1369195618Srpaulo{
1370195618Srpaulo	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
1371195618Srpaulo	int error;
1372195618Srpaulo
1373195618Srpaulo	if (vap->iv_opmode != IEEE80211_M_MBSS)
1374195618Srpaulo		return ENOSYS;
1375195618Srpaulo	error = 0;
1376195618Srpaulo	switch (ireq->i_type) {
1377195618Srpaulo	case IEEE80211_IOC_HWMP_ROOTMODE:
1378195618Srpaulo		ireq->i_val = hs->hs_rootmode;
1379195618Srpaulo		break;
1380195618Srpaulo	case IEEE80211_IOC_HWMP_MAXHOPS:
1381195618Srpaulo		ireq->i_val = hs->hs_maxhops;
1382195618Srpaulo		break;
1383195618Srpaulo	default:
1384195618Srpaulo		return ENOSYS;
1385195618Srpaulo	}
1386195618Srpaulo	return error;
1387195618Srpaulo}
1388195618SrpauloIEEE80211_IOCTL_GET(hwmp, hwmp_ioctl_get80211);
1389195618Srpaulo
1390195618Srpaulostatic int
1391195618Srpaulohwmp_ioctl_set80211(struct ieee80211vap *vap, struct ieee80211req *ireq)
1392195618Srpaulo{
1393195618Srpaulo	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
1394195618Srpaulo	int error;
1395195618Srpaulo
1396195618Srpaulo	if (vap->iv_opmode != IEEE80211_M_MBSS)
1397195618Srpaulo		return ENOSYS;
1398195618Srpaulo	error = 0;
1399195618Srpaulo	switch (ireq->i_type) {
1400195618Srpaulo	case IEEE80211_IOC_HWMP_ROOTMODE:
1401195618Srpaulo		if (ireq->i_val < 0 || ireq->i_val > 3)
1402195618Srpaulo			return EINVAL;
1403195618Srpaulo		hs->hs_rootmode = ireq->i_val;
1404195618Srpaulo		hwmp_rootmode_setup(vap);
1405195618Srpaulo		break;
1406195618Srpaulo	case IEEE80211_IOC_HWMP_MAXHOPS:
1407195618Srpaulo		if (ireq->i_val <= 0 || ireq->i_val > 255)
1408195618Srpaulo			return EINVAL;
1409195618Srpaulo		hs->hs_maxhops = ireq->i_val;
1410195618Srpaulo		break;
1411195618Srpaulo	default:
1412195618Srpaulo		return ENOSYS;
1413195618Srpaulo	}
1414195618Srpaulo	return error;
1415195618Srpaulo}
1416195618SrpauloIEEE80211_IOCTL_SET(hwmp, hwmp_ioctl_set80211);
1417