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.
36230408Sadrian *
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>
43230408Sadrian#include <sys/systm.h>
44230408Sadrian#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);
71246512Smonthadarstatic int	hwmp_send_action(struct ieee80211vap *,
72195618Srpaulo		    const uint8_t [IEEE80211_ADDR_LEN],
73195618Srpaulo		    uint8_t *, size_t);
74195618Srpaulostatic uint8_t * hwmp_add_meshpreq(uint8_t *,
75195618Srpaulo		    const struct ieee80211_meshpreq_ie *);
76195618Srpaulostatic uint8_t * hwmp_add_meshprep(uint8_t *,
77195618Srpaulo		    const struct ieee80211_meshprep_ie *);
78195618Srpaulostatic uint8_t * hwmp_add_meshperr(uint8_t *,
79195618Srpaulo		    const struct ieee80211_meshperr_ie *);
80195618Srpaulostatic uint8_t * hwmp_add_meshrann(uint8_t *,
81195618Srpaulo		    const struct ieee80211_meshrann_ie *);
82195618Srpaulostatic void	hwmp_rootmode_setup(struct ieee80211vap *);
83195618Srpaulostatic void	hwmp_rootmode_cb(void *);
84195618Srpaulostatic void	hwmp_rootmode_rann_cb(void *);
85195618Srpaulostatic void	hwmp_recv_preq(struct ieee80211vap *, struct ieee80211_node *,
86195618Srpaulo		    const struct ieee80211_frame *,
87195618Srpaulo		    const struct ieee80211_meshpreq_ie *);
88246512Smonthadarstatic int	hwmp_send_preq(struct ieee80211vap *,
89195618Srpaulo		    const uint8_t [IEEE80211_ADDR_LEN],
90234882Smonthadar		    struct ieee80211_meshpreq_ie *,
91234882Smonthadar		    struct timeval *, struct timeval *);
92195618Srpaulostatic void	hwmp_recv_prep(struct ieee80211vap *, struct ieee80211_node *,
93195618Srpaulo		    const struct ieee80211_frame *,
94195618Srpaulo		    const struct ieee80211_meshprep_ie *);
95246512Smonthadarstatic int	hwmp_send_prep(struct ieee80211vap *,
96195618Srpaulo		    const uint8_t [IEEE80211_ADDR_LEN],
97195618Srpaulo		    struct ieee80211_meshprep_ie *);
98195618Srpaulostatic void	hwmp_recv_perr(struct ieee80211vap *, struct ieee80211_node *,
99195618Srpaulo		    const struct ieee80211_frame *,
100195618Srpaulo		    const struct ieee80211_meshperr_ie *);
101246512Smonthadarstatic int	hwmp_send_perr(struct ieee80211vap *,
102195618Srpaulo		    const uint8_t [IEEE80211_ADDR_LEN],
103195618Srpaulo		    struct ieee80211_meshperr_ie *);
104234890Smonthadarstatic void	hwmp_senderror(struct ieee80211vap *,
105234890Smonthadar		    const uint8_t [IEEE80211_ADDR_LEN],
106234890Smonthadar		    struct ieee80211_mesh_route *, int);
107195618Srpaulostatic void	hwmp_recv_rann(struct ieee80211vap *, struct ieee80211_node *,
108195618Srpaulo		   const struct ieee80211_frame *,
109195618Srpaulo		   const struct ieee80211_meshrann_ie *);
110246512Smonthadarstatic int	hwmp_send_rann(struct ieee80211vap *,
111195618Srpaulo		    const uint8_t [IEEE80211_ADDR_LEN],
112195618Srpaulo		    struct ieee80211_meshrann_ie *);
113195618Srpaulostatic struct ieee80211_node *
114195618Srpaulo		hwmp_discover(struct ieee80211vap *,
115195618Srpaulo		    const uint8_t [IEEE80211_ADDR_LEN], struct mbuf *);
116195618Srpaulostatic void	hwmp_peerdown(struct ieee80211_node *);
117195618Srpaulo
118195813Ssamstatic struct timeval ieee80211_hwmp_preqminint = { 0, 100000 };
119195813Ssamstatic struct timeval ieee80211_hwmp_perrminint = { 0, 100000 };
120195618Srpaulo
121195618Srpaulo/* unalligned little endian access */
122195618Srpaulo#define LE_WRITE_2(p, v) do {				\
123195618Srpaulo	((uint8_t *)(p))[0] = (v) & 0xff;		\
124195618Srpaulo	((uint8_t *)(p))[1] = ((v) >> 8) & 0xff;	\
125195618Srpaulo} while (0)
126195618Srpaulo#define LE_WRITE_4(p, v) do {				\
127195618Srpaulo	((uint8_t *)(p))[0] = (v) & 0xff;		\
128195618Srpaulo	((uint8_t *)(p))[1] = ((v) >> 8) & 0xff;	\
129195618Srpaulo	((uint8_t *)(p))[2] = ((v) >> 16) & 0xff;	\
130195618Srpaulo	((uint8_t *)(p))[3] = ((v) >> 24) & 0xff;	\
131195618Srpaulo} while (0)
132195618Srpaulo
133195618Srpaulo
134195618Srpaulo/* NB: the Target Address set in a Proactive PREQ is the broadcast address. */
135195618Srpaulostatic const uint8_t	broadcastaddr[IEEE80211_ADDR_LEN] =
136195618Srpaulo	{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
137195618Srpaulo
138195618Srpaulotypedef uint32_t ieee80211_hwmp_seq;
139195908Srpaulo#define	HWMP_SEQ_LT(a, b)	((int32_t)((a)-(b)) < 0)
140195908Srpaulo#define	HWMP_SEQ_LEQ(a, b)	((int32_t)((a)-(b)) <= 0)
141230409Sadrian#define	HWMP_SEQ_EQ(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
145234878Smonthadar#define HWMP_SEQ_MAX(a, b)	(a > b ? a : b)
146234878Smonthadar
147195618Srpaulo/*
148195618Srpaulo * Private extension of ieee80211_mesh_route.
149195618Srpaulo */
150195618Srpaulostruct ieee80211_hwmp_route {
151195908Srpaulo	ieee80211_hwmp_seq	hr_seq;		/* last HWMP seq seen from dst*/
152195908Srpaulo	ieee80211_hwmp_seq	hr_preqid;	/* last PREQ ID seen from dst */
153198230Srpaulo	ieee80211_hwmp_seq	hr_origseq;	/* seq. no. on our latest PREQ*/
154234882Smonthadar	struct timeval		hr_lastpreq;	/* last time we sent a PREQ */
155234891Smonthadar	struct timeval		hr_lastrootconf; /* last sent PREQ root conf */
156234883Smonthadar	int			hr_preqretries;	/* number of discoveries */
157234883Smonthadar	int			hr_lastdiscovery; /* last discovery in ticks */
158195618Srpaulo};
159195618Srpaulostruct ieee80211_hwmp_state {
160195618Srpaulo	ieee80211_hwmp_seq	hs_seq;		/* next seq to be used */
161195618Srpaulo	ieee80211_hwmp_seq	hs_preqid;	/* next PREQ ID to be used */
162234882Smonthadar	int			hs_rootmode;	/* proactive HWMP */
163195618Srpaulo	struct timeval		hs_lastperr;	/* last time we sent a PERR */
164195618Srpaulo	struct callout		hs_roottimer;
165195618Srpaulo	uint8_t			hs_maxhops;	/* max hop count */
166195618Srpaulo};
167195618Srpaulo
168227309Sedstatic SYSCTL_NODE(_net_wlan, OID_AUTO, hwmp, CTLFLAG_RD, 0,
169195618Srpaulo    "IEEE 802.11s HWMP parameters");
170195813Ssamstatic int	ieee80211_hwmp_targetonly = 0;
171195618SrpauloSYSCTL_INT(_net_wlan_hwmp, OID_AUTO, targetonly, CTLTYPE_INT | CTLFLAG_RW,
172195618Srpaulo    &ieee80211_hwmp_targetonly, 0, "Set TO bit on generated PREQs");
173195813Ssamstatic int	ieee80211_hwmp_pathtimeout = -1;
174195813SsamSYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, pathlifetime, CTLTYPE_INT | CTLFLAG_RW,
175195813Ssam    &ieee80211_hwmp_pathtimeout, 0, ieee80211_sysctl_msecs_ticks, "I",
176195813Ssam    "path entry lifetime (ms)");
177234883Smonthadarstatic int	ieee80211_hwmp_maxpreq_retries = -1;
178234883SmonthadarSYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, maxpreq_retries, CTLTYPE_INT | CTLFLAG_RW,
179234883Smonthadar    &ieee80211_hwmp_maxpreq_retries, 0, ieee80211_sysctl_msecs_ticks, "I",
180234883Smonthadar    "maximum number of preq retries");
181234883Smonthadarstatic int	ieee80211_hwmp_net_diameter_traversaltime = -1;
182234883SmonthadarSYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, net_diameter_traversal_time,
183234883Smonthadar    CTLTYPE_INT | CTLFLAG_RW, &ieee80211_hwmp_net_diameter_traversaltime, 0,
184234883Smonthadar    ieee80211_sysctl_msecs_ticks, "I",
185234883Smonthadar    "estimate travelse time across the MBSS (ms)");
186195813Ssamstatic int	ieee80211_hwmp_roottimeout = -1;
187195813SsamSYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, roottimeout, CTLTYPE_INT | CTLFLAG_RW,
188195813Ssam    &ieee80211_hwmp_roottimeout, 0, ieee80211_sysctl_msecs_ticks, "I",
189195813Ssam    "root PREQ timeout (ms)");
190195813Ssamstatic int	ieee80211_hwmp_rootint = -1;
191195813SsamSYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, rootint, CTLTYPE_INT | CTLFLAG_RW,
192195813Ssam    &ieee80211_hwmp_rootint, 0, ieee80211_sysctl_msecs_ticks, "I",
193195813Ssam    "root interval (ms)");
194195813Ssamstatic int	ieee80211_hwmp_rannint = -1;
195195813SsamSYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, rannint, CTLTYPE_INT | CTLFLAG_RW,
196195813Ssam    &ieee80211_hwmp_rannint, 0, ieee80211_sysctl_msecs_ticks, "I",
197195813Ssam    "root announcement interval (ms)");
198234891Smonthadarstatic struct timeval ieee80211_hwmp_rootconfint = { 0, 0 };
199234891Smonthadarstatic int	ieee80211_hwmp_rootconfint_internal = -1;
200234891SmonthadarSYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, rootconfint, CTLTYPE_INT | CTLFLAG_RD,
201234891Smonthadar    &ieee80211_hwmp_rootconfint_internal, 0, ieee80211_sysctl_msecs_ticks, "I",
202234891Smonthadar    "root confirmation interval (ms) (read-only)");
203195618Srpaulo
204195618Srpaulo#define	IEEE80211_HWMP_DEFAULT_MAXHOPS	31
205195618Srpaulo
206197413Srpaulostatic	ieee80211_recv_action_func hwmp_recv_action_meshpath;
207195618Srpaulo
208195813Ssamstatic struct ieee80211_mesh_proto_path mesh_proto_hwmp = {
209195618Srpaulo	.mpp_descr	= "HWMP",
210197975Srpaulo	.mpp_ie		= IEEE80211_MESHCONF_PATH_HWMP,
211195618Srpaulo	.mpp_discover	= hwmp_discover,
212195618Srpaulo	.mpp_peerdown	= hwmp_peerdown,
213234890Smonthadar	.mpp_senderror	= hwmp_senderror,
214195618Srpaulo	.mpp_vattach	= hwmp_vattach,
215195618Srpaulo	.mpp_vdetach	= hwmp_vdetach,
216195618Srpaulo	.mpp_newstate	= hwmp_newstate,
217195618Srpaulo	.mpp_privlen	= sizeof(struct ieee80211_hwmp_route),
218195618Srpaulo};
219195813SsamSYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, inact, CTLTYPE_INT | CTLFLAG_RW,
220195813Ssam	&mesh_proto_hwmp.mpp_inact, 0, ieee80211_sysctl_msecs_ticks, "I",
221195813Ssam	"mesh route inactivity timeout (ms)");
222195618Srpaulo
223195618Srpaulo
224195618Srpaulostatic void
225195618Srpauloieee80211_hwmp_init(void)
226195618Srpaulo{
227234883Smonthadar	/* Default values as per amendment */
228195813Ssam	ieee80211_hwmp_pathtimeout = msecs_to_ticks(5*1000);
229195813Ssam	ieee80211_hwmp_roottimeout = msecs_to_ticks(5*1000);
230195813Ssam	ieee80211_hwmp_rootint = msecs_to_ticks(2*1000);
231195813Ssam	ieee80211_hwmp_rannint = msecs_to_ticks(1*1000);
232234891Smonthadar	ieee80211_hwmp_rootconfint_internal = msecs_to_ticks(2*1000);
233234883Smonthadar	ieee80211_hwmp_maxpreq_retries = 3;
234234883Smonthadar	/*
235234883Smonthadar	 * (TU): A measurement of time equal to 1024 ��s,
236234883Smonthadar	 * 500 TU is 512 ms.
237234883Smonthadar	 */
238234883Smonthadar	ieee80211_hwmp_net_diameter_traversaltime = msecs_to_ticks(512);
239195813Ssam
240195618Srpaulo	/*
241234891Smonthadar	 * NB: I dont know how to make SYSCTL_PROC that calls ms to ticks
242234891Smonthadar	 * and return a struct timeval...
243234891Smonthadar	 */
244234891Smonthadar	ieee80211_hwmp_rootconfint.tv_usec =
245234891Smonthadar	    ieee80211_hwmp_rootconfint_internal * 1000;
246234891Smonthadar
247234891Smonthadar	/*
248197413Srpaulo	 * Register action frame handler.
249195618Srpaulo	 */
250232479Sadrian	ieee80211_recv_action_register(IEEE80211_ACTION_CAT_MESH,
251232479Sadrian	    IEEE80211_ACTION_MESH_HWMP, hwmp_recv_action_meshpath);
252195618Srpaulo
253195813Ssam	/* NB: default is 5 secs per spec */
254195813Ssam	mesh_proto_hwmp.mpp_inact = msecs_to_ticks(5*1000);
255195813Ssam
256195618Srpaulo	/*
257195618Srpaulo	 * Register HWMP.
258195618Srpaulo	 */
259195618Srpaulo	ieee80211_mesh_register_proto_path(&mesh_proto_hwmp);
260195618Srpaulo}
261195618SrpauloSYSINIT(wlan_hwmp, SI_SUB_DRIVERS, SI_ORDER_SECOND, ieee80211_hwmp_init, NULL);
262195618Srpaulo
263195618Srpaulovoid
264195618Srpaulohwmp_vattach(struct ieee80211vap *vap)
265195618Srpaulo{
266195618Srpaulo	struct ieee80211_hwmp_state *hs;
267195618Srpaulo
268195618Srpaulo	KASSERT(vap->iv_opmode == IEEE80211_M_MBSS,
269195618Srpaulo	    ("not a mesh vap, opmode %d", vap->iv_opmode));
270195618Srpaulo
271195618Srpaulo	hs = malloc(sizeof(struct ieee80211_hwmp_state), M_80211_VAP,
272195618Srpaulo	    M_NOWAIT | M_ZERO);
273195618Srpaulo	if (hs == NULL) {
274195618Srpaulo		printf("%s: couldn't alloc HWMP state\n", __func__);
275195618Srpaulo		return;
276195618Srpaulo	}
277195618Srpaulo	hs->hs_maxhops = IEEE80211_HWMP_DEFAULT_MAXHOPS;
278195618Srpaulo	callout_init(&hs->hs_roottimer, CALLOUT_MPSAFE);
279195618Srpaulo	vap->iv_hwmp = hs;
280195618Srpaulo}
281195618Srpaulo
282195618Srpaulovoid
283195618Srpaulohwmp_vdetach(struct ieee80211vap *vap)
284195618Srpaulo{
285195618Srpaulo	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
286195618Srpaulo
287195784Srpaulo	callout_drain(&hs->hs_roottimer);
288195618Srpaulo	free(vap->iv_hwmp, M_80211_VAP);
289195618Srpaulo	vap->iv_hwmp = NULL;
290195618Srpaulo}
291195618Srpaulo
292195618Srpauloint
293195618Srpaulohwmp_newstate(struct ieee80211vap *vap, enum ieee80211_state ostate, int arg)
294195618Srpaulo{
295195618Srpaulo	enum ieee80211_state nstate = vap->iv_state;
296195618Srpaulo	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
297195618Srpaulo
298195618Srpaulo	IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n",
299195618Srpaulo	    __func__, ieee80211_state_name[ostate],
300195618Srpaulo	    ieee80211_state_name[nstate], arg);
301195618Srpaulo
302195618Srpaulo	if (nstate != IEEE80211_S_RUN && ostate == IEEE80211_S_RUN)
303195618Srpaulo		callout_drain(&hs->hs_roottimer);
304195784Srpaulo	if (nstate == IEEE80211_S_RUN)
305195784Srpaulo		hwmp_rootmode_setup(vap);
306195618Srpaulo	return 0;
307195618Srpaulo}
308195618Srpaulo
309232625Sadrian/*
310232625Sadrian * Verify the length of an HWMP PREQ and return the number
311232625Sadrian * of destinations >= 1, if verification fails -1 is returned.
312232625Sadrian */
313195618Srpaulostatic int
314232625Sadrianverify_mesh_preq_len(struct ieee80211vap *vap,
315232625Sadrian    const struct ieee80211_frame *wh, const uint8_t *iefrm)
316232625Sadrian{
317232625Sadrian	int alloc_sz = -1;
318232625Sadrian	int ndest = -1;
319232625Sadrian	if (iefrm[2] & IEEE80211_MESHPREQ_FLAGS_AE) {
320232625Sadrian		/* Originator External Address  present */
321232625Sadrian		alloc_sz =  IEEE80211_MESHPREQ_BASE_SZ_AE;
322232625Sadrian		ndest = iefrm[IEEE80211_MESHPREQ_TCNT_OFFSET_AE];
323232625Sadrian	} else {
324232625Sadrian		/* w/o Originator External Address */
325232625Sadrian		alloc_sz =  IEEE80211_MESHPREQ_BASE_SZ;
326232625Sadrian		ndest = iefrm[IEEE80211_MESHPREQ_TCNT_OFFSET];
327232625Sadrian	}
328232625Sadrian	alloc_sz += ndest * IEEE80211_MESHPREQ_TRGT_SZ;
329232625Sadrian
330232625Sadrian	if(iefrm[1] != (alloc_sz)) {
331232625Sadrian		IEEE80211_DISCARD(vap,
332232625Sadrian		    IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
333232625Sadrian		    wh, NULL, "PREQ (AE=%s) with wrong len",
334232625Sadrian		    iefrm[2] & IEEE80211_MESHPREQ_FLAGS_AE ? "1" : "0");
335232625Sadrian		return (-1);
336232625Sadrian	}
337232625Sadrian	return ndest;
338232625Sadrian}
339232625Sadrian
340232625Sadrian/*
341232625Sadrian * Verify the length of an HWMP PREP and returns 1 on success,
342232625Sadrian * otherwise -1.
343232625Sadrian */
344232625Sadrianstatic int
345232625Sadrianverify_mesh_prep_len(struct ieee80211vap *vap,
346232625Sadrian    const struct ieee80211_frame *wh, const uint8_t *iefrm)
347232625Sadrian{
348232625Sadrian	int alloc_sz = -1;
349232625Sadrian	if (iefrm[2] & IEEE80211_MESHPREP_FLAGS_AE) {
350232625Sadrian		if (iefrm[1] == IEEE80211_MESHPREP_BASE_SZ_AE)
351232625Sadrian			alloc_sz = IEEE80211_MESHPREP_BASE_SZ_AE;
352232625Sadrian	} else if (iefrm[1] == IEEE80211_MESHPREP_BASE_SZ)
353232625Sadrian		alloc_sz = IEEE80211_MESHPREP_BASE_SZ;
354232625Sadrian	if(alloc_sz < 0) {
355232625Sadrian		IEEE80211_DISCARD(vap,
356232625Sadrian		    IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
357232625Sadrian		    wh, NULL, "PREP (AE=%s) with wrong len",
358232625Sadrian		    iefrm[2] & IEEE80211_MESHPREP_FLAGS_AE ? "1" : "0");
359232625Sadrian		return (-1);
360232625Sadrian	}
361232625Sadrian	return (1);
362232625Sadrian}
363232625Sadrian
364232625Sadrian/*
365232625Sadrian * Verify the length of an HWMP PERR and return the number
366232625Sadrian * of destinations >= 1, if verification fails -1 is returned.
367232625Sadrian */
368232625Sadrianstatic int
369232625Sadrianverify_mesh_perr_len(struct ieee80211vap *vap,
370232625Sadrian    const struct ieee80211_frame *wh, const uint8_t *iefrm)
371232625Sadrian{
372232625Sadrian	int alloc_sz = -1;
373232625Sadrian	const uint8_t *iefrm_t = iefrm;
374232625Sadrian	uint8_t ndest = iefrm_t[IEEE80211_MESHPERR_NDEST_OFFSET];
375232625Sadrian	int i;
376232625Sadrian
377232625Sadrian	if(ndest > IEEE80211_MESHPERR_MAXDEST) {
378232625Sadrian		IEEE80211_DISCARD(vap,
379232625Sadrian		    IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
380232625Sadrian		    wh, NULL, "PERR with wrong number of destionat (>19), %u",
381232625Sadrian		    ndest);
382232625Sadrian		return (-1);
383232625Sadrian	}
384232625Sadrian
385232625Sadrian	iefrm_t += IEEE80211_MESHPERR_NDEST_OFFSET + 1; /* flag is next field */
386232625Sadrian	/* We need to check each destionation flag to know size */
387232625Sadrian	for(i = 0; i<ndest; i++) {
388232625Sadrian		if ((*iefrm_t) & IEEE80211_MESHPERR_FLAGS_AE)
389232625Sadrian			iefrm_t += IEEE80211_MESHPERR_DEST_SZ_AE;
390232625Sadrian		else
391232625Sadrian			iefrm_t += IEEE80211_MESHPERR_DEST_SZ;
392232625Sadrian	}
393232625Sadrian
394232625Sadrian	alloc_sz = (iefrm_t - iefrm) - 2; /* action + code */
395232625Sadrian	if(alloc_sz !=  iefrm[1]) {
396232625Sadrian		IEEE80211_DISCARD(vap,
397232625Sadrian		    IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
398232625Sadrian		    wh, NULL, "%s", "PERR with wrong len");
399232625Sadrian		return (-1);
400232625Sadrian	}
401232625Sadrian	return ndest;
402232625Sadrian}
403232625Sadrian
404232625Sadrianstatic int
405197413Srpaulohwmp_recv_action_meshpath(struct ieee80211_node *ni,
406195618Srpaulo	const struct ieee80211_frame *wh,
407195618Srpaulo	const uint8_t *frm, const uint8_t *efrm)
408195618Srpaulo{
409195618Srpaulo	struct ieee80211vap *vap = ni->ni_vap;
410232625Sadrian	struct ieee80211_meshpreq_ie *preq;
411232625Sadrian	struct ieee80211_meshprep_ie *prep;
412232625Sadrian	struct ieee80211_meshperr_ie *perr;
413197413Srpaulo	struct ieee80211_meshrann_ie rann;
414195618Srpaulo	const uint8_t *iefrm = frm + 2; /* action + code */
415232625Sadrian	const uint8_t *iefrm_t = iefrm; /* temporary pointer */
416232625Sadrian	int ndest = -1;
417197413Srpaulo	int found = 0;
418195618Srpaulo
419195618Srpaulo	while (efrm - iefrm > 1) {
420195618Srpaulo		IEEE80211_VERIFY_LENGTH(efrm - iefrm, iefrm[1] + 2, return 0);
421197413Srpaulo		switch (*iefrm) {
422197413Srpaulo		case IEEE80211_ELEMID_MESHPREQ:
423197413Srpaulo		{
424232625Sadrian			int i = 0;
425232625Sadrian
426232625Sadrian			iefrm_t = iefrm;
427232625Sadrian			ndest = verify_mesh_preq_len(vap, wh, iefrm_t);
428232625Sadrian			if (ndest < 0) {
429195618Srpaulo				vap->iv_stats.is_rx_mgtdiscard++;
430197413Srpaulo				break;
431195618Srpaulo			}
432232625Sadrian			preq = malloc(sizeof(*preq) +
433232625Sadrian			    (ndest - 1) * sizeof(*preq->preq_targets),
434232625Sadrian			    M_80211_MESH_PREQ, M_NOWAIT | M_ZERO);
435232625Sadrian			KASSERT(preq != NULL, ("preq == NULL"));
436232625Sadrian
437232625Sadrian			preq->preq_ie = *iefrm_t++;
438232625Sadrian			preq->preq_len = *iefrm_t++;
439232625Sadrian			preq->preq_flags = *iefrm_t++;
440232625Sadrian			preq->preq_hopcount = *iefrm_t++;
441232625Sadrian			preq->preq_ttl = *iefrm_t++;
442232625Sadrian			preq->preq_id = LE_READ_4(iefrm_t); iefrm_t += 4;
443232625Sadrian			IEEE80211_ADDR_COPY(preq->preq_origaddr, iefrm_t);
444232625Sadrian			iefrm_t += 6;
445232625Sadrian			preq->preq_origseq = LE_READ_4(iefrm_t); iefrm_t += 4;
446232625Sadrian			/* NB: may have Originator Proxied Address */
447232625Sadrian			if (preq->preq_flags & IEEE80211_MESHPREQ_FLAGS_AE)  {
448232625Sadrian				IEEE80211_ADDR_COPY(
449232625Sadrian				    preq->preq_orig_ext_addr, iefrm_t);
450232625Sadrian				iefrm_t += 6;
451232625Sadrian			}
452232625Sadrian			preq->preq_lifetime = LE_READ_4(iefrm_t); iefrm_t += 4;
453232625Sadrian			preq->preq_metric = LE_READ_4(iefrm_t); iefrm_t += 4;
454232625Sadrian			preq->preq_tcount = *iefrm_t++;
455232625Sadrian
456232625Sadrian			for (i = 0; i < preq->preq_tcount; i++) {
457232625Sadrian				preq->preq_targets[i].target_flags = *iefrm_t++;
458232625Sadrian				IEEE80211_ADDR_COPY(
459232625Sadrian				    preq->preq_targets[i].target_addr, iefrm_t);
460232625Sadrian				iefrm_t += 6;
461232625Sadrian				preq->preq_targets[i].target_seq =
462232625Sadrian				    LE_READ_4(iefrm_t);
463232625Sadrian				iefrm_t += 4;
464232625Sadrian			}
465232625Sadrian
466232625Sadrian			hwmp_recv_preq(vap, ni, wh, preq);
467232625Sadrian			free(preq, M_80211_MESH_PREQ);
468197413Srpaulo			found++;
469232625Sadrian			break;
470195618Srpaulo		}
471197413Srpaulo		case IEEE80211_ELEMID_MESHPREP:
472197413Srpaulo		{
473232625Sadrian			iefrm_t = iefrm;
474232625Sadrian			ndest = verify_mesh_prep_len(vap, wh, iefrm_t);
475232625Sadrian			if (ndest < 0) {
476195618Srpaulo				vap->iv_stats.is_rx_mgtdiscard++;
477197413Srpaulo				break;
478195618Srpaulo			}
479232625Sadrian			prep = malloc(sizeof(*prep),
480232625Sadrian			    M_80211_MESH_PREP, M_NOWAIT | M_ZERO);
481232625Sadrian			KASSERT(prep != NULL, ("prep == NULL"));
482232625Sadrian
483232625Sadrian			prep->prep_ie = *iefrm_t++;
484232625Sadrian			prep->prep_len = *iefrm_t++;
485232625Sadrian			prep->prep_flags = *iefrm_t++;
486232625Sadrian			prep->prep_hopcount = *iefrm_t++;
487232625Sadrian			prep->prep_ttl = *iefrm_t++;
488232625Sadrian			IEEE80211_ADDR_COPY(prep->prep_targetaddr, iefrm_t);
489232625Sadrian			iefrm_t += 6;
490232625Sadrian			prep->prep_targetseq = LE_READ_4(iefrm_t); iefrm_t += 4;
491232625Sadrian			/* NB: May have Target Proxied Address */
492232625Sadrian			if (prep->prep_flags & IEEE80211_MESHPREP_FLAGS_AE)  {
493232625Sadrian				IEEE80211_ADDR_COPY(
494232625Sadrian				    prep->prep_target_ext_addr, iefrm_t);
495232625Sadrian				iefrm_t += 6;
496232625Sadrian			}
497232625Sadrian			prep->prep_lifetime = LE_READ_4(iefrm_t); iefrm_t += 4;
498232625Sadrian			prep->prep_metric = LE_READ_4(iefrm_t); iefrm_t += 4;
499232625Sadrian			IEEE80211_ADDR_COPY(prep->prep_origaddr, iefrm_t);
500232625Sadrian			iefrm_t += 6;
501232625Sadrian			prep->prep_origseq = LE_READ_4(iefrm_t); iefrm_t += 4;
502232625Sadrian
503232625Sadrian			hwmp_recv_prep(vap, ni, wh, prep);
504232625Sadrian			free(prep, M_80211_MESH_PREP);
505197413Srpaulo			found++;
506197413Srpaulo			break;
507195618Srpaulo		}
508197413Srpaulo		case IEEE80211_ELEMID_MESHPERR:
509197413Srpaulo		{
510232625Sadrian			int i = 0;
511232625Sadrian
512232625Sadrian			iefrm_t = iefrm;
513232625Sadrian			ndest = verify_mesh_perr_len(vap, wh, iefrm_t);
514232625Sadrian			if (ndest < 0) {
515195618Srpaulo				vap->iv_stats.is_rx_mgtdiscard++;
516197413Srpaulo				break;
517195618Srpaulo			}
518232625Sadrian			perr = malloc(sizeof(*perr) +
519232625Sadrian			    (ndest - 1) * sizeof(*perr->perr_dests),
520232625Sadrian			    M_80211_MESH_PERR, M_NOWAIT | M_ZERO);
521232625Sadrian			KASSERT(perr != NULL, ("perr == NULL"));
522232625Sadrian
523232625Sadrian			perr->perr_ie = *iefrm_t++;
524232625Sadrian			perr->perr_len = *iefrm_t++;
525232625Sadrian			perr->perr_ttl = *iefrm_t++;
526232625Sadrian			perr->perr_ndests = *iefrm_t++;
527232625Sadrian
528232625Sadrian			for (i = 0; i<perr->perr_ndests; i++) {
529232625Sadrian				perr->perr_dests[i].dest_flags = *iefrm_t++;
530232625Sadrian				IEEE80211_ADDR_COPY(
531232625Sadrian				    perr->perr_dests[i].dest_addr, iefrm_t);
532232625Sadrian				iefrm_t += 6;
533232625Sadrian				perr->perr_dests[i].dest_seq = LE_READ_4(iefrm_t);
534232625Sadrian				iefrm_t += 4;
535232625Sadrian				/* NB: May have Target Proxied Address */
536232625Sadrian				if (perr->perr_dests[i].dest_flags &
537232625Sadrian				    IEEE80211_MESHPERR_FLAGS_AE) {
538232625Sadrian					IEEE80211_ADDR_COPY(
539232625Sadrian					    perr->perr_dests[i].dest_ext_addr,
540232625Sadrian					    iefrm_t);
541232625Sadrian					iefrm_t += 6;
542232625Sadrian				}
543232625Sadrian				perr->perr_dests[i].dest_rcode =
544232625Sadrian				    LE_READ_2(iefrm_t);
545232625Sadrian				iefrm_t += 2;
546232625Sadrian			}
547232625Sadrian
548232625Sadrian			hwmp_recv_perr(vap, ni, wh, perr);
549232625Sadrian			free(perr, M_80211_MESH_PERR);
550197413Srpaulo			found++;
551197413Srpaulo			break;
552195618Srpaulo		}
553197413Srpaulo		case IEEE80211_ELEMID_MESHRANN:
554197413Srpaulo		{
555195618Srpaulo			const struct ieee80211_meshrann_ie *mrann =
556195618Srpaulo			    (const struct ieee80211_meshrann_ie *) iefrm;
557195618Srpaulo			if (mrann->rann_len !=
558195618Srpaulo			    sizeof(struct ieee80211_meshrann_ie) - 2) {
559195618Srpaulo				IEEE80211_DISCARD(vap,
560195618Srpaulo				    IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
561195618Srpaulo				    wh, NULL, "%s", "RAN with wrong len");
562232625Sadrian				    vap->iv_stats.is_rx_mgtdiscard++;
563195618Srpaulo				return 1;
564195618Srpaulo			}
565195618Srpaulo			memcpy(&rann, mrann, sizeof(rann));
566195618Srpaulo			rann.rann_seq = LE_READ_4(&mrann->rann_seq);
567234891Smonthadar			rann.rann_interval = LE_READ_4(&mrann->rann_interval);
568195618Srpaulo			rann.rann_metric = LE_READ_4(&mrann->rann_metric);
569195618Srpaulo			hwmp_recv_rann(vap, ni, wh, &rann);
570197413Srpaulo			found++;
571197413Srpaulo			break;
572195618Srpaulo		}
573197413Srpaulo		}
574195618Srpaulo		iefrm += iefrm[1] + 2;
575195618Srpaulo	}
576197413Srpaulo	if (!found) {
577197413Srpaulo		IEEE80211_DISCARD(vap,
578197413Srpaulo		    IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
579197413Srpaulo		    wh, NULL, "%s", "PATH SEL action without IE");
580197413Srpaulo		vap->iv_stats.is_rx_mgtdiscard++;
581197413Srpaulo	}
582195618Srpaulo	return 0;
583195618Srpaulo}
584195618Srpaulo
585195618Srpaulostatic int
586246512Smonthadarhwmp_send_action(struct ieee80211vap *vap,
587195618Srpaulo    const uint8_t da[IEEE80211_ADDR_LEN],
588195618Srpaulo    uint8_t *ie, size_t len)
589195618Srpaulo{
590246512Smonthadar	struct ieee80211_node *ni;
591246512Smonthadar	struct ieee80211com *ic;
592195618Srpaulo	struct ieee80211_bpf_params params;
593195618Srpaulo	struct mbuf *m;
594195618Srpaulo	uint8_t *frm;
595248069Sadrian	int ret;
596195618Srpaulo
597246512Smonthadar	if (IEEE80211_IS_MULTICAST(da)) {
598246512Smonthadar		ni = ieee80211_ref_node(vap->iv_bss);
599246512Smonthadar#ifdef IEEE80211_DEBUG_REFCNT
600246512Smonthadar		IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
601246512Smonthadar		"ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n",
602246512Smonthadar		__func__, __LINE__,
603246512Smonthadar		ni, ether_sprintf(ni->ni_macaddr),
604246512Smonthadar		ieee80211_node_refcnt(ni)+1);
605246512Smonthadar#endif
606246512Smonthadar		ieee80211_ref_node(ni);
607246512Smonthadar	}
608246512Smonthadar	else
609246512Smonthadar		ni = ieee80211_mesh_find_txnode(vap, da);
610246512Smonthadar
611195618Srpaulo	if (vap->iv_state == IEEE80211_S_CAC) {
612195618Srpaulo		IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, ni,
613195618Srpaulo		    "block %s frame in CAC state", "HWMP action");
614195618Srpaulo		vap->iv_stats.is_tx_badstate++;
615195618Srpaulo		return EIO;	/* XXX */
616195618Srpaulo	}
617195618Srpaulo
618195618Srpaulo	KASSERT(ni != NULL, ("null node"));
619246512Smonthadar	ic = ni->ni_ic;
620195618Srpaulo
621195618Srpaulo	m = ieee80211_getmgtframe(&frm,
622195618Srpaulo	    ic->ic_headroom + sizeof(struct ieee80211_frame),
623195618Srpaulo	    sizeof(struct ieee80211_action) + len
624195618Srpaulo	);
625195618Srpaulo	if (m == NULL) {
626195618Srpaulo		ieee80211_free_node(ni);
627195618Srpaulo		vap->iv_stats.is_tx_nobuf++;
628195618Srpaulo		return ENOMEM;
629195618Srpaulo	}
630232479Sadrian	*frm++ = IEEE80211_ACTION_CAT_MESH;
631232479Sadrian	*frm++ = IEEE80211_ACTION_MESH_HWMP;
632195618Srpaulo	switch (*ie) {
633195618Srpaulo	case IEEE80211_ELEMID_MESHPREQ:
634195618Srpaulo		frm = hwmp_add_meshpreq(frm,
635195618Srpaulo		    (struct ieee80211_meshpreq_ie *)ie);
636195618Srpaulo		break;
637195618Srpaulo	case IEEE80211_ELEMID_MESHPREP:
638195618Srpaulo		frm = hwmp_add_meshprep(frm,
639195618Srpaulo		    (struct ieee80211_meshprep_ie *)ie);
640195618Srpaulo		break;
641195618Srpaulo	case IEEE80211_ELEMID_MESHPERR:
642195618Srpaulo		frm = hwmp_add_meshperr(frm,
643195618Srpaulo		    (struct ieee80211_meshperr_ie *)ie);
644195618Srpaulo		break;
645195618Srpaulo	case IEEE80211_ELEMID_MESHRANN:
646195618Srpaulo		frm = hwmp_add_meshrann(frm,
647195618Srpaulo		    (struct ieee80211_meshrann_ie *)ie);
648195618Srpaulo		break;
649195618Srpaulo	}
650195618Srpaulo
651195618Srpaulo	m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
652243882Sglebius	M_PREPEND(m, sizeof(struct ieee80211_frame), M_NOWAIT);
653195618Srpaulo	if (m == NULL) {
654195618Srpaulo		ieee80211_free_node(ni);
655195618Srpaulo		vap->iv_stats.is_tx_nobuf++;
656195618Srpaulo		return ENOMEM;
657195618Srpaulo	}
658248069Sadrian
659248069Sadrian	IEEE80211_TX_LOCK(ic);
660248069Sadrian
661195618Srpaulo	ieee80211_send_setup(ni, m,
662195618Srpaulo	    IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_ACTION,
663246512Smonthadar	    IEEE80211_NONQOS_TID, vap->iv_myaddr, da, vap->iv_myaddr);
664195618Srpaulo
665195618Srpaulo	m->m_flags |= M_ENCAP;		/* mark encapsulated */
666195618Srpaulo	IEEE80211_NODE_STAT(ni, tx_mgmt);
667195618Srpaulo
668195618Srpaulo	memset(&params, 0, sizeof(params));
669195618Srpaulo	params.ibp_pri = WME_AC_VO;
670195618Srpaulo	params.ibp_rate0 = ni->ni_txparms->mgmtrate;
671195618Srpaulo	if (IEEE80211_IS_MULTICAST(da))
672195618Srpaulo		params.ibp_try0 = 1;
673195618Srpaulo	else
674195618Srpaulo		params.ibp_try0 = ni->ni_txparms->maxretry;
675195618Srpaulo	params.ibp_power = ni->ni_txpower;
676248069Sadrian	ret = ieee80211_raw_output(vap, ni, m, &params);
677248069Sadrian	IEEE80211_TX_UNLOCK(ic);
678248069Sadrian	return (ret);
679195618Srpaulo}
680195618Srpaulo
681197413Srpaulo#define ADDSHORT(frm, v) do {		\
682197413Srpaulo	frm[0] = (v) & 0xff;		\
683197413Srpaulo	frm[1] = (v) >> 8;		\
684197413Srpaulo	frm += 2;			\
685197413Srpaulo} while (0)
686195618Srpaulo#define ADDWORD(frm, v) do {		\
687195618Srpaulo	LE_WRITE_4(frm, v);		\
688195618Srpaulo	frm += 4;			\
689195618Srpaulo} while (0)
690195618Srpaulo/*
691195618Srpaulo * Add a Mesh Path Request IE to a frame.
692195618Srpaulo */
693232625Sadrian#define	PREQ_TFLAGS(n)	preq->preq_targets[n].target_flags
694232625Sadrian#define	PREQ_TADDR(n)	preq->preq_targets[n].target_addr
695232625Sadrian#define	PREQ_TSEQ(n)	preq->preq_targets[n].target_seq
696195618Srpaulostatic uint8_t *
697195618Srpaulohwmp_add_meshpreq(uint8_t *frm, const struct ieee80211_meshpreq_ie *preq)
698195618Srpaulo{
699195618Srpaulo	int i;
700195618Srpaulo
701195618Srpaulo	*frm++ = IEEE80211_ELEMID_MESHPREQ;
702232625Sadrian	*frm++ = preq->preq_len;	/* len already calculated */
703195618Srpaulo	*frm++ = preq->preq_flags;
704195618Srpaulo	*frm++ = preq->preq_hopcount;
705195618Srpaulo	*frm++ = preq->preq_ttl;
706195618Srpaulo	ADDWORD(frm, preq->preq_id);
707195618Srpaulo	IEEE80211_ADDR_COPY(frm, preq->preq_origaddr); frm += 6;
708195618Srpaulo	ADDWORD(frm, preq->preq_origseq);
709232625Sadrian	if (preq->preq_flags & IEEE80211_MESHPREQ_FLAGS_AE) {
710232625Sadrian		IEEE80211_ADDR_COPY(frm, preq->preq_orig_ext_addr);
711232625Sadrian		frm += 6;
712232625Sadrian	}
713195618Srpaulo	ADDWORD(frm, preq->preq_lifetime);
714195618Srpaulo	ADDWORD(frm, preq->preq_metric);
715195618Srpaulo	*frm++ = preq->preq_tcount;
716195618Srpaulo	for (i = 0; i < preq->preq_tcount; i++) {
717232625Sadrian		*frm++ = PREQ_TFLAGS(i);
718232625Sadrian		IEEE80211_ADDR_COPY(frm, PREQ_TADDR(i));
719195618Srpaulo		frm += 6;
720232625Sadrian		ADDWORD(frm, PREQ_TSEQ(i));
721195618Srpaulo	}
722195618Srpaulo	return frm;
723195618Srpaulo}
724232625Sadrian#undef	PREQ_TFLAGS
725232625Sadrian#undef	PREQ_TADDR
726232625Sadrian#undef	PREQ_TSEQ
727195618Srpaulo
728195618Srpaulo/*
729195618Srpaulo * Add a Mesh Path Reply IE to a frame.
730195618Srpaulo */
731195618Srpaulostatic uint8_t *
732195618Srpaulohwmp_add_meshprep(uint8_t *frm, const struct ieee80211_meshprep_ie *prep)
733195618Srpaulo{
734195618Srpaulo	*frm++ = IEEE80211_ELEMID_MESHPREP;
735232625Sadrian	*frm++ = prep->prep_len;	/* len already calculated */
736195618Srpaulo	*frm++ = prep->prep_flags;
737195618Srpaulo	*frm++ = prep->prep_hopcount;
738195618Srpaulo	*frm++ = prep->prep_ttl;
739195618Srpaulo	IEEE80211_ADDR_COPY(frm, prep->prep_targetaddr); frm += 6;
740195618Srpaulo	ADDWORD(frm, prep->prep_targetseq);
741232625Sadrian	if (prep->prep_flags & IEEE80211_MESHPREP_FLAGS_AE) {
742232625Sadrian		IEEE80211_ADDR_COPY(frm, prep->prep_target_ext_addr);
743232625Sadrian		frm += 6;
744232625Sadrian	}
745195618Srpaulo	ADDWORD(frm, prep->prep_lifetime);
746195618Srpaulo	ADDWORD(frm, prep->prep_metric);
747195618Srpaulo	IEEE80211_ADDR_COPY(frm, prep->prep_origaddr); frm += 6;
748195618Srpaulo	ADDWORD(frm, prep->prep_origseq);
749195618Srpaulo	return frm;
750195618Srpaulo}
751195618Srpaulo
752195618Srpaulo/*
753195618Srpaulo * Add a Mesh Path Error IE to a frame.
754195618Srpaulo */
755232625Sadrian#define	PERR_DFLAGS(n)	perr->perr_dests[n].dest_flags
756232625Sadrian#define	PERR_DADDR(n)	perr->perr_dests[n].dest_addr
757232625Sadrian#define	PERR_DSEQ(n)	perr->perr_dests[n].dest_seq
758232625Sadrian#define	PERR_EXTADDR(n)	perr->perr_dests[n].dest_ext_addr
759232625Sadrian#define	PERR_DRCODE(n)	perr->perr_dests[n].dest_rcode
760195618Srpaulostatic uint8_t *
761195618Srpaulohwmp_add_meshperr(uint8_t *frm, const struct ieee80211_meshperr_ie *perr)
762195618Srpaulo{
763195618Srpaulo	int i;
764195618Srpaulo
765195618Srpaulo	*frm++ = IEEE80211_ELEMID_MESHPERR;
766232625Sadrian	*frm++ = perr->perr_len;	/* len already calculated */
767197413Srpaulo	*frm++ = perr->perr_ttl;
768195618Srpaulo	*frm++ = perr->perr_ndests;
769195618Srpaulo	for (i = 0; i < perr->perr_ndests; i++) {
770232625Sadrian		*frm++ = PERR_DFLAGS(i);
771232625Sadrian		IEEE80211_ADDR_COPY(frm, PERR_DADDR(i));
772195618Srpaulo		frm += 6;
773232625Sadrian		ADDWORD(frm, PERR_DSEQ(i));
774232625Sadrian		if (PERR_DFLAGS(i) & IEEE80211_MESHPERR_FLAGS_AE) {
775232625Sadrian			IEEE80211_ADDR_COPY(frm, PERR_EXTADDR(i));
776232625Sadrian			frm += 6;
777232625Sadrian		}
778232625Sadrian		ADDSHORT(frm, PERR_DRCODE(i));
779195618Srpaulo	}
780195618Srpaulo	return frm;
781195618Srpaulo}
782232625Sadrian#undef	PERR_DFLAGS
783232625Sadrian#undef	PERR_DADDR
784232625Sadrian#undef	PERR_DSEQ
785232625Sadrian#undef	PERR_EXTADDR
786232625Sadrian#undef	PERR_DRCODE
787195618Srpaulo
788195618Srpaulo/*
789195618Srpaulo * Add a Root Annoucement IE to a frame.
790195618Srpaulo */
791195618Srpaulostatic uint8_t *
792195618Srpaulohwmp_add_meshrann(uint8_t *frm, const struct ieee80211_meshrann_ie *rann)
793195618Srpaulo{
794195618Srpaulo	*frm++ = IEEE80211_ELEMID_MESHRANN;
795232625Sadrian	*frm++ = rann->rann_len;
796195618Srpaulo	*frm++ = rann->rann_flags;
797195618Srpaulo	*frm++ = rann->rann_hopcount;
798195618Srpaulo	*frm++ = rann->rann_ttl;
799195618Srpaulo	IEEE80211_ADDR_COPY(frm, rann->rann_addr); frm += 6;
800195618Srpaulo	ADDWORD(frm, rann->rann_seq);
801232625Sadrian	ADDWORD(frm, rann->rann_interval);
802195618Srpaulo	ADDWORD(frm, rann->rann_metric);
803195618Srpaulo	return frm;
804195618Srpaulo}
805195618Srpaulo
806195618Srpaulostatic void
807195618Srpaulohwmp_rootmode_setup(struct ieee80211vap *vap)
808195618Srpaulo{
809195618Srpaulo	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
810246506Smonthadar	struct ieee80211_mesh_state *ms = vap->iv_mesh;
811195618Srpaulo
812195618Srpaulo	switch (hs->hs_rootmode) {
813195618Srpaulo	case IEEE80211_HWMP_ROOTMODE_DISABLED:
814195618Srpaulo		callout_drain(&hs->hs_roottimer);
815246506Smonthadar		ms->ms_flags &= ~IEEE80211_MESHFLAGS_ROOT;
816195618Srpaulo		break;
817195618Srpaulo	case IEEE80211_HWMP_ROOTMODE_NORMAL:
818195618Srpaulo	case IEEE80211_HWMP_ROOTMODE_PROACTIVE:
819195813Ssam		callout_reset(&hs->hs_roottimer, ieee80211_hwmp_rootint,
820195618Srpaulo		    hwmp_rootmode_cb, vap);
821246506Smonthadar		ms->ms_flags |= IEEE80211_MESHFLAGS_ROOT;
822195618Srpaulo		break;
823195618Srpaulo	case IEEE80211_HWMP_ROOTMODE_RANN:
824195813Ssam		callout_reset(&hs->hs_roottimer, ieee80211_hwmp_rannint,
825195618Srpaulo		    hwmp_rootmode_rann_cb, vap);
826246506Smonthadar		ms->ms_flags |= IEEE80211_MESHFLAGS_ROOT;
827195618Srpaulo		break;
828195618Srpaulo	}
829195618Srpaulo}
830195618Srpaulo
831195618Srpaulo/*
832195618Srpaulo * Send a broadcast Path Request to find all nodes on the mesh. We are
833195618Srpaulo * called when the vap is configured as a HWMP root node.
834195618Srpaulo */
835195618Srpaulo#define	PREQ_TFLAGS(n)	preq.preq_targets[n].target_flags
836195618Srpaulo#define	PREQ_TADDR(n)	preq.preq_targets[n].target_addr
837195618Srpaulo#define	PREQ_TSEQ(n)	preq.preq_targets[n].target_seq
838195618Srpaulostatic void
839195618Srpaulohwmp_rootmode_cb(void *arg)
840195618Srpaulo{
841195618Srpaulo	struct ieee80211vap *vap = (struct ieee80211vap *)arg;
842195618Srpaulo	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
843195618Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
844195618Srpaulo	struct ieee80211_meshpreq_ie preq;
845195618Srpaulo
846195618Srpaulo	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, vap->iv_bss,
847195784Srpaulo	    "%s", "send broadcast PREQ");
848195618Srpaulo
849238939Smonthadar	preq.preq_flags = 0;
850234892Smonthadar	if (ms->ms_flags & IEEE80211_MESHFLAGS_GATE)
851234892Smonthadar		preq.preq_flags |= IEEE80211_MESHPREQ_FLAGS_GATE;
852195618Srpaulo	if (hs->hs_rootmode == IEEE80211_HWMP_ROOTMODE_PROACTIVE)
853195618Srpaulo		preq.preq_flags |= IEEE80211_MESHPREQ_FLAGS_PP;
854195618Srpaulo	preq.preq_hopcount = 0;
855195618Srpaulo	preq.preq_ttl = ms->ms_ttl;
856195618Srpaulo	preq.preq_id = ++hs->hs_preqid;
857195618Srpaulo	IEEE80211_ADDR_COPY(preq.preq_origaddr, vap->iv_myaddr);
858195618Srpaulo	preq.preq_origseq = ++hs->hs_seq;
859195813Ssam	preq.preq_lifetime = ticks_to_msecs(ieee80211_hwmp_roottimeout);
860195618Srpaulo	preq.preq_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
861195618Srpaulo	preq.preq_tcount = 1;
862195618Srpaulo	IEEE80211_ADDR_COPY(PREQ_TADDR(0), broadcastaddr);
863234886Smonthadar	PREQ_TFLAGS(0) = IEEE80211_MESHPREQ_TFLAGS_TO |
864234886Smonthadar	    IEEE80211_MESHPREQ_TFLAGS_USN;
865195618Srpaulo	PREQ_TSEQ(0) = 0;
866195618Srpaulo	vap->iv_stats.is_hwmp_rootreqs++;
867246512Smonthadar	/* NB: we enforce rate check ourself */
868246512Smonthadar	hwmp_send_preq(vap, broadcastaddr, &preq, NULL, NULL);
869195618Srpaulo	hwmp_rootmode_setup(vap);
870195618Srpaulo}
871195618Srpaulo#undef	PREQ_TFLAGS
872195618Srpaulo#undef	PREQ_TADDR
873195618Srpaulo#undef	PREQ_TSEQ
874195618Srpaulo
875195618Srpaulo/*
876195618Srpaulo * Send a Root Annoucement (RANN) to find all the nodes on the mesh. We are
877195618Srpaulo * called when the vap is configured as a HWMP RANN root node.
878195618Srpaulo */
879195618Srpaulostatic void
880195618Srpaulohwmp_rootmode_rann_cb(void *arg)
881195618Srpaulo{
882195618Srpaulo	struct ieee80211vap *vap = (struct ieee80211vap *)arg;
883195618Srpaulo	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
884195618Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
885195618Srpaulo	struct ieee80211_meshrann_ie rann;
886195618Srpaulo
887195618Srpaulo	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, vap->iv_bss,
888195784Srpaulo	    "%s", "send broadcast RANN");
889195618Srpaulo
890198369Srpaulo	rann.rann_flags = 0;
891234892Smonthadar	if (ms->ms_flags & IEEE80211_MESHFLAGS_GATE)
892234892Smonthadar		rann.rann_flags |= IEEE80211_MESHFLAGS_GATE;
893195618Srpaulo	rann.rann_hopcount = 0;
894195618Srpaulo	rann.rann_ttl = ms->ms_ttl;
895195618Srpaulo	IEEE80211_ADDR_COPY(rann.rann_addr, vap->iv_myaddr);
896195618Srpaulo	rann.rann_seq = ++hs->hs_seq;
897234891Smonthadar	rann.rann_interval = ieee80211_hwmp_rannint;
898195618Srpaulo	rann.rann_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
899195618Srpaulo
900195618Srpaulo	vap->iv_stats.is_hwmp_rootrann++;
901246512Smonthadar	hwmp_send_rann(vap, broadcastaddr, &rann);
902195618Srpaulo	hwmp_rootmode_setup(vap);
903195618Srpaulo}
904195618Srpaulo
905246519Smonthadar/*
906246519Smonthadar * Update forwarding information to TA if metric improves.
907246519Smonthadar */
908246519Smonthadarstatic void
909246519Smonthadarhwmp_update_transmitter(struct ieee80211vap *vap, struct ieee80211_node *ni,
910246519Smonthadar    const char *hwmp_frame)
911246519Smonthadar{
912246519Smonthadar	struct ieee80211_mesh_state *ms = vap->iv_mesh;
913246519Smonthadar	struct ieee80211_mesh_route *rttran = NULL;	/* Transmitter */
914246519Smonthadar	int metric = 0;
915246519Smonthadar
916246519Smonthadar	rttran = ieee80211_mesh_rt_find(vap, ni->ni_macaddr);
917246519Smonthadar	if (rttran == NULL) {
918246519Smonthadar		rttran = ieee80211_mesh_rt_add(vap, ni->ni_macaddr);
919246519Smonthadar		if (rttran == NULL) {
920246519Smonthadar			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
921246519Smonthadar			    "unable to add path to transmitter %6D of %s",
922246519Smonthadar			    ni->ni_macaddr, ":", hwmp_frame);
923246519Smonthadar			vap->iv_stats.is_mesh_rtaddfailed++;
924246519Smonthadar			return;
925246519Smonthadar		}
926246519Smonthadar	}
927246519Smonthadar	metric = ms->ms_pmetric->mpm_metric(ni);
928246519Smonthadar	if (!(rttran->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) ||
929246519Smonthadar	    rttran->rt_metric > metric)
930246519Smonthadar	{
931246519Smonthadar		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
932246519Smonthadar		    "%s path to transmiter %6D of %s, metric %d:%d",
933246519Smonthadar		    rttran->rt_flags & IEEE80211_MESHRT_FLAGS_VALID ?
934246519Smonthadar		    "prefer" : "update", ni->ni_macaddr, ":", hwmp_frame,
935246519Smonthadar		    rttran->rt_metric, metric);
936246519Smonthadar		IEEE80211_ADDR_COPY(rttran->rt_nexthop, ni->ni_macaddr);
937246519Smonthadar		rttran->rt_metric = metric;
938246519Smonthadar		rttran->rt_nhops  = 1;
939246519Smonthadar		ieee80211_mesh_rt_update(rttran, ms->ms_ppath->mpp_inact);
940246519Smonthadar		rttran->rt_flags = IEEE80211_MESHRT_FLAGS_VALID;
941246519Smonthadar	}
942246519Smonthadar}
943246519Smonthadar
944195618Srpaulo#define	PREQ_TFLAGS(n)	preq->preq_targets[n].target_flags
945195618Srpaulo#define	PREQ_TADDR(n)	preq->preq_targets[n].target_addr
946195618Srpaulo#define	PREQ_TSEQ(n)	preq->preq_targets[n].target_seq
947195618Srpaulostatic void
948195618Srpaulohwmp_recv_preq(struct ieee80211vap *vap, struct ieee80211_node *ni,
949195618Srpaulo    const struct ieee80211_frame *wh, const struct ieee80211_meshpreq_ie *preq)
950195618Srpaulo{
951195618Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
952195908Srpaulo	struct ieee80211_mesh_route *rtorig = NULL;
953234888Smonthadar	struct ieee80211_mesh_route *rtorig_ext = NULL;
954230409Sadrian	struct ieee80211_mesh_route *rttarg = NULL;
955230409Sadrian	struct ieee80211_hwmp_route *hrorig = NULL;
956230409Sadrian	struct ieee80211_hwmp_route *hrtarg = NULL;
957195618Srpaulo	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
958195618Srpaulo	struct ieee80211_meshprep_ie prep;
959234888Smonthadar	ieee80211_hwmp_seq preqid;	/* last seen preqid for orig */
960238940Smonthadar	uint32_t metric = 0;
961195618Srpaulo
962195618Srpaulo	/*
963195618Srpaulo	 * Ignore PREQs from us. Could happen because someone forward it
964195618Srpaulo	 * back to us.
965195618Srpaulo	 */
966195618Srpaulo	if (IEEE80211_ADDR_EQ(vap->iv_myaddr, preq->preq_origaddr))
967195618Srpaulo		return;
968195618Srpaulo
969195618Srpaulo	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
970234881Smonthadar	    "received PREQ, orig %6D, targ(0) %6D", preq->preq_origaddr, ":",
971234881Smonthadar	    PREQ_TADDR(0), ":");
972195618Srpaulo
973195618Srpaulo	/*
974234887Smonthadar	 * Acceptance criteria: (if the PREQ is not for us or not broadcast,
975234887Smonthadar	 * or an external mac address not proxied by us),
976230409Sadrian	 * AND forwarding is disabled, discard this PREQ.
977195618Srpaulo	 */
978234887Smonthadar	rttarg = ieee80211_mesh_rt_find(vap, PREQ_TADDR(0));
979234887Smonthadar	if (!(ms->ms_flags & IEEE80211_MESHFLAGS_FWD) &&
980234887Smonthadar	    (!IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0)) ||
981234887Smonthadar	    !IEEE80211_IS_MULTICAST(PREQ_TADDR(0)) ||
982234887Smonthadar	    (rttarg != NULL &&
983234887Smonthadar	    rttarg->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY &&
984234887Smonthadar	    IEEE80211_ADDR_EQ(vap->iv_myaddr, rttarg->rt_mesh_gate)))) {
985195618Srpaulo		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_HWMP,
986195618Srpaulo		    preq->preq_origaddr, NULL, "%s", "not accepting PREQ");
987195618Srpaulo		return;
988195618Srpaulo	}
989230409Sadrian	/*
990230409Sadrian	 * Acceptance criteria: if unicast addressed
991230409Sadrian	 * AND no valid forwarding for Target of PREQ, discard this PREQ.
992230409Sadrian	 */
993230409Sadrian	if(rttarg != NULL)
994230409Sadrian		hrtarg = IEEE80211_MESH_ROUTE_PRIV(rttarg,
995230409Sadrian		    struct ieee80211_hwmp_route);
996230409Sadrian	/* Address mode: ucast */
997238939Smonthadar	if(preq->preq_flags & IEEE80211_MESHPREQ_FLAGS_AM &&
998230409Sadrian	    rttarg == NULL &&
999230409Sadrian	    !IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0))) {
1000230409Sadrian		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_HWMP,
1001230409Sadrian		    preq->preq_origaddr, NULL,
1002230409Sadrian		    "unicast addressed PREQ of unknown target %6D",
1003230409Sadrian		    PREQ_TADDR(0), ":");
1004230409Sadrian		return;
1005230409Sadrian	}
1006230409Sadrian
1007230409Sadrian	/* PREQ ACCEPTED */
1008230409Sadrian
1009195908Srpaulo	rtorig = ieee80211_mesh_rt_find(vap, preq->preq_origaddr);
1010230409Sadrian	if (rtorig == NULL) {
1011195908Srpaulo		rtorig = ieee80211_mesh_rt_add(vap, preq->preq_origaddr);
1012234888Smonthadar		if (rtorig == NULL) {
1013234888Smonthadar			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1014234888Smonthadar			    "unable to add orig path to %6D",
1015234888Smonthadar			    preq->preq_origaddr, ":");
1016234888Smonthadar			vap->iv_stats.is_mesh_rtaddfailed++;
1017234888Smonthadar			return;
1018234888Smonthadar		}
1019230409Sadrian		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1020230409Sadrian		    "adding originator %6D", preq->preq_origaddr, ":");
1021230409Sadrian	}
1022195908Srpaulo	hrorig = IEEE80211_MESH_ROUTE_PRIV(rtorig, struct ieee80211_hwmp_route);
1023230409Sadrian
1024234888Smonthadar	/* record last seen preqid */
1025234888Smonthadar	preqid = hrorig->hr_preqid;
1026234888Smonthadar	hrorig->hr_preqid = HWMP_SEQ_MAX(hrorig->hr_preqid, preq->preq_id);
1027234888Smonthadar
1028230409Sadrian	/* Data creation and update of forwarding information
1029230409Sadrian	 * according to Table 11C-8 for originator mesh STA.
1030195908Srpaulo	 */
1031238940Smonthadar	metric = preq->preq_metric + ms->ms_pmetric->mpm_metric(ni);
1032234877Smonthadar	if (HWMP_SEQ_GT(preq->preq_origseq, hrorig->hr_seq) ||
1033230409Sadrian	    (HWMP_SEQ_EQ(preq->preq_origseq, hrorig->hr_seq) &&
1034238940Smonthadar	    metric < rtorig->rt_metric)) {
1035230409Sadrian		hrorig->hr_seq = preq->preq_origseq;
1036230409Sadrian		IEEE80211_ADDR_COPY(rtorig->rt_nexthop, wh->i_addr2);
1037238940Smonthadar		rtorig->rt_metric = metric;
1038230409Sadrian		rtorig->rt_nhops  = preq->preq_hopcount + 1;
1039234877Smonthadar		ieee80211_mesh_rt_update(rtorig, preq->preq_lifetime);
1040246498Smonthadar		/* Path to orig is valid now.
1041246498Smonthadar		 * NB: we know it can't be Proxy, and if it is GATE
1042246498Smonthadar		 * it will be marked below.
1043246498Smonthadar		 */
1044246498Smonthadar		rtorig->rt_flags = IEEE80211_MESHRT_FLAGS_VALID;
1045246515Smonthadar	} else if ((hrtarg != NULL &&
1046246515Smonthadar	    !HWMP_SEQ_EQ(hrtarg->hr_seq, PREQ_TSEQ(0))) ||
1047246515Smonthadar	    (rtorig->rt_flags & IEEE80211_MESHRT_FLAGS_VALID &&
1048246515Smonthadar	    preqid >= preq->preq_id)) {
1049195908Srpaulo		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1050234888Smonthadar		    "discard PREQ from %6D, old seqno %u <= %u,"
1051234888Smonthadar		    " or old preqid %u < %u",
1052230408Sadrian		    preq->preq_origaddr, ":",
1053234888Smonthadar		    preq->preq_origseq, hrorig->hr_seq,
1054234888Smonthadar		    preq->preq_id, preqid);
1055195908Srpaulo		return;
1056195908Srpaulo	}
1057195908Srpaulo
1058246519Smonthadar	/* Update forwarding information to TA if metric improves. */
1059246519Smonthadar	hwmp_update_transmitter(vap, ni, "PREQ");
1060230409Sadrian
1061230409Sadrian	/*
1062195618Srpaulo	 * Check if the PREQ is addressed to us.
1063246513Smonthadar	 * or a Proxy currently gated by us.
1064195618Srpaulo	 */
1065234878Smonthadar	if (IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0)) ||
1066246513Smonthadar	    (ms->ms_flags & IEEE80211_MESHFLAGS_GATE &&
1067246513Smonthadar	    rttarg != NULL &&
1068246513Smonthadar	    IEEE80211_ADDR_EQ(vap->iv_myaddr, rttarg->rt_mesh_gate) &&
1069234878Smonthadar	    rttarg->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY &&
1070234878Smonthadar	    rttarg->rt_flags & IEEE80211_MESHRT_FLAGS_VALID)) {
1071195618Srpaulo		/*
1072234878Smonthadar		 * When we are the target we shall update our own HWMP seq
1073234878Smonthadar		 * number with max of (current and preq->seq) + 1
1074234878Smonthadar		 */
1075234878Smonthadar		hs->hs_seq = HWMP_SEQ_MAX(hs->hs_seq, PREQ_TSEQ(0)) + 1;
1076234878Smonthadar
1077234878Smonthadar		prep.prep_flags = 0;
1078238939Smonthadar		prep.prep_hopcount = 0;
1079246513Smonthadar		prep.prep_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
1080238939Smonthadar		IEEE80211_ADDR_COPY(prep.prep_targetaddr, vap->iv_myaddr);
1081234878Smonthadar		if (rttarg != NULL && /* if NULL it means we are the target */
1082234878Smonthadar		    rttarg->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY) {
1083234878Smonthadar			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1084234878Smonthadar			    "reply for proxy %6D", rttarg->rt_dest, ":");
1085234878Smonthadar			prep.prep_flags |= IEEE80211_MESHPREP_FLAGS_AE;
1086234878Smonthadar			IEEE80211_ADDR_COPY(prep.prep_target_ext_addr,
1087234878Smonthadar			    rttarg->rt_dest);
1088234878Smonthadar			/* update proxy seqno to HWMP seqno */
1089234878Smonthadar			rttarg->rt_ext_seq = hs->hs_seq;
1090238939Smonthadar			prep.prep_hopcount = rttarg->rt_nhops;
1091246513Smonthadar			prep.prep_metric = rttarg->rt_metric;
1092238939Smonthadar			IEEE80211_ADDR_COPY(prep.prep_targetaddr, rttarg->rt_mesh_gate);
1093234878Smonthadar		}
1094234878Smonthadar		/*
1095195618Srpaulo		 * Build and send a PREP frame.
1096195618Srpaulo		 */
1097195618Srpaulo		prep.prep_ttl = ms->ms_ttl;
1098234878Smonthadar		prep.prep_targetseq = hs->hs_seq;
1099195618Srpaulo		prep.prep_lifetime = preq->preq_lifetime;
1100198230Srpaulo		IEEE80211_ADDR_COPY(prep.prep_origaddr, preq->preq_origaddr);
1101198230Srpaulo		prep.prep_origseq = preq->preq_origseq;
1102234878Smonthadar
1103234878Smonthadar		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1104234878Smonthadar		    "reply to %6D", preq->preq_origaddr, ":");
1105246512Smonthadar		hwmp_send_prep(vap, wh->i_addr2, &prep);
1106195618Srpaulo		return;
1107195618Srpaulo	}
1108234888Smonthadar	/* we may update our proxy information for the orig external */
1109234888Smonthadar	else if (preq->preq_flags & IEEE80211_MESHPREQ_FLAGS_AE) {
1110234888Smonthadar		rtorig_ext =
1111234888Smonthadar		    ieee80211_mesh_rt_find(vap, preq->preq_orig_ext_addr);
1112234888Smonthadar		if (rtorig_ext == NULL) {
1113234888Smonthadar			rtorig_ext = ieee80211_mesh_rt_add(vap,
1114234888Smonthadar			    preq->preq_orig_ext_addr);
1115234888Smonthadar			if (rtorig_ext == NULL) {
1116234888Smonthadar				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1117234888Smonthadar				    "unable to add orig ext proxy to %6D",
1118234888Smonthadar				    preq->preq_orig_ext_addr, ":");
1119234888Smonthadar				vap->iv_stats.is_mesh_rtaddfailed++;
1120234888Smonthadar				return;
1121234888Smonthadar			}
1122234888Smonthadar			IEEE80211_ADDR_COPY(rtorig_ext->rt_mesh_gate,
1123234888Smonthadar			    preq->preq_origaddr);
1124234888Smonthadar		}
1125234888Smonthadar		rtorig_ext->rt_ext_seq = preq->preq_origseq;
1126234888Smonthadar		ieee80211_mesh_rt_update(rtorig_ext, preq->preq_lifetime);
1127234888Smonthadar	}
1128195618Srpaulo	/*
1129195618Srpaulo	 * Proactive PREQ: reply with a proactive PREP to the
1130195618Srpaulo	 * root STA if requested.
1131195618Srpaulo	 */
1132195618Srpaulo	if (IEEE80211_ADDR_EQ(PREQ_TADDR(0), broadcastaddr) &&
1133234881Smonthadar	    (PREQ_TFLAGS(0) & IEEE80211_MESHPREQ_TFLAGS_TO)) {
1134195784Srpaulo		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1135234896Smonthadar		    "root mesh station @ %6D", preq->preq_origaddr, ":");
1136195784Srpaulo
1137246509Smonthadar		/* Check if root is a mesh gate, mark it */
1138246509Smonthadar		if (preq->preq_flags & IEEE80211_MESHPREQ_FLAGS_GATE) {
1139246509Smonthadar			struct ieee80211_mesh_gate_route *gr;
1140246509Smonthadar
1141246509Smonthadar			rtorig->rt_flags |= IEEE80211_MESHRT_FLAGS_GATE;
1142246509Smonthadar			gr = ieee80211_mesh_mark_gate(vap, preq->preq_origaddr,
1143246509Smonthadar			    rtorig);
1144246509Smonthadar			gr->gr_lastseq = 0; /* NOT GANN */
1145246509Smonthadar		}
1146246509Smonthadar
1147195618Srpaulo		/*
1148195618Srpaulo		 * Reply with a PREP if we don't have a path to the root
1149195618Srpaulo		 * or if the root sent us a proactive PREQ.
1150195618Srpaulo		 */
1151234896Smonthadar		if ((rtorig->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0 ||
1152195618Srpaulo		    (preq->preq_flags & IEEE80211_MESHPREQ_FLAGS_PP)) {
1153195618Srpaulo			prep.prep_flags = 0;
1154195618Srpaulo			prep.prep_hopcount = 0;
1155195618Srpaulo			prep.prep_ttl = ms->ms_ttl;
1156234896Smonthadar			IEEE80211_ADDR_COPY(prep.prep_origaddr,
1157234896Smonthadar			    preq->preq_origaddr);
1158195618Srpaulo			prep.prep_origseq = preq->preq_origseq;
1159195618Srpaulo			prep.prep_lifetime = preq->preq_lifetime;
1160195618Srpaulo			prep.prep_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
1161198230Srpaulo			IEEE80211_ADDR_COPY(prep.prep_targetaddr,
1162198230Srpaulo			    vap->iv_myaddr);
1163198230Srpaulo			prep.prep_targetseq = ++hs->hs_seq;
1164246512Smonthadar			hwmp_send_prep(vap, rtorig->rt_nexthop, &prep);
1165195618Srpaulo		}
1166195618Srpaulo	}
1167195618Srpaulo
1168195618Srpaulo	/*
1169195618Srpaulo	 * Forwarding and Intermediate reply for PREQs with 1 target.
1170195618Srpaulo	 */
1171234888Smonthadar	if ((preq->preq_tcount == 1) && (preq->preq_ttl > 1) &&
1172234888Smonthadar	    (ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) {
1173195618Srpaulo		struct ieee80211_meshpreq_ie ppreq; /* propagated PREQ */
1174195618Srpaulo
1175195618Srpaulo		memcpy(&ppreq, preq, sizeof(ppreq));
1176234888Smonthadar
1177195618Srpaulo		/*
1178195618Srpaulo		 * We have a valid route to this node.
1179246514Smonthadar		 * NB: if target is proxy dont reply.
1180195618Srpaulo		 */
1181234882Smonthadar		if (rttarg != NULL &&
1182246514Smonthadar		    rttarg->rt_flags & IEEE80211_MESHRT_FLAGS_VALID &&
1183246514Smonthadar		    !(rttarg->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY)) {
1184195618Srpaulo			/*
1185195618Srpaulo			 * Check if we can send an intermediate Path Reply,
1186234888Smonthadar			 * i.e., Target Only bit is not set and target is not
1187234888Smonthadar			 * the MAC broadcast address.
1188195618Srpaulo			 */
1189234881Smonthadar			if (!(PREQ_TFLAGS(0) & IEEE80211_MESHPREQ_TFLAGS_TO) &&
1190234888Smonthadar			    !IEEE80211_ADDR_EQ(PREQ_TADDR(0), broadcastaddr)) {
1191195618Srpaulo				struct ieee80211_meshprep_ie prep;
1192195618Srpaulo
1193195618Srpaulo				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1194230408Sadrian				    "intermediate reply for PREQ from %6D",
1195230408Sadrian				    preq->preq_origaddr, ":");
1196195618Srpaulo				prep.prep_flags = 0;
1197234881Smonthadar				prep.prep_hopcount = rttarg->rt_nhops;
1198195618Srpaulo				prep.prep_ttl = ms->ms_ttl;
1199195618Srpaulo				IEEE80211_ADDR_COPY(&prep.prep_targetaddr,
1200198230Srpaulo				    PREQ_TADDR(0));
1201234881Smonthadar				prep.prep_targetseq = hrtarg->hr_seq;
1202195618Srpaulo				prep.prep_lifetime = preq->preq_lifetime;
1203234881Smonthadar				prep.prep_metric =rttarg->rt_metric;
1204195618Srpaulo				IEEE80211_ADDR_COPY(&prep.prep_origaddr,
1205198230Srpaulo				    preq->preq_origaddr);
1206195908Srpaulo				prep.prep_origseq = hrorig->hr_seq;
1207246512Smonthadar				hwmp_send_prep(vap, rtorig->rt_nexthop, &prep);
1208234888Smonthadar
1209234888Smonthadar				/*
1210234888Smonthadar				 * Set TO and unset RF bits because we have
1211234888Smonthadar				 * sent a PREP.
1212234888Smonthadar				 */
1213234888Smonthadar				ppreq.preq_targets[0].target_flags |=
1214234888Smonthadar				    IEEE80211_MESHPREQ_TFLAGS_TO;
1215195618Srpaulo			}
1216234888Smonthadar		}
1217195618Srpaulo
1218234888Smonthadar		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1219234888Smonthadar		    "forward PREQ from %6D",
1220234888Smonthadar		    preq->preq_origaddr, ":");
1221234888Smonthadar		ppreq.preq_hopcount += 1;
1222234888Smonthadar		ppreq.preq_ttl -= 1;
1223234888Smonthadar		ppreq.preq_metric += ms->ms_pmetric->mpm_metric(ni);
1224234888Smonthadar
1225234888Smonthadar		/* don't do PREQ ratecheck when we propagate */
1226246512Smonthadar		hwmp_send_preq(vap, broadcastaddr, &ppreq, NULL, NULL);
1227195618Srpaulo	}
1228195618Srpaulo}
1229195618Srpaulo#undef	PREQ_TFLAGS
1230195618Srpaulo#undef	PREQ_TADDR
1231195618Srpaulo#undef	PREQ_TSEQ
1232195618Srpaulo
1233195618Srpaulostatic int
1234246512Smonthadarhwmp_send_preq(struct ieee80211vap *vap,
1235195618Srpaulo    const uint8_t da[IEEE80211_ADDR_LEN],
1236234882Smonthadar    struct ieee80211_meshpreq_ie *preq,
1237234882Smonthadar    struct timeval *last, struct timeval *minint)
1238195618Srpaulo{
1239195618Srpaulo
1240195618Srpaulo	/*
1241195618Srpaulo	 * Enforce PREQ interval.
1242234882Smonthadar	 * NB: Proactive ROOT PREQs rate is handled by cb task.
1243195618Srpaulo	 */
1244234882Smonthadar	if (last != NULL && minint != NULL) {
1245234882Smonthadar		if (ratecheck(last, minint) == 0)
1246234882Smonthadar			return EALREADY; /* XXX: we should postpone */
1247234882Smonthadar		getmicrouptime(last);
1248234882Smonthadar	}
1249195618Srpaulo
1250195618Srpaulo	/*
1251195618Srpaulo	 * mesh preq action frame format
1252195618Srpaulo	 *     [6] da
1253230409Sadrian	 *     [6] sa
1254195618Srpaulo	 *     [6] addr3 = sa
1255195618Srpaulo	 *     [1] action
1256195618Srpaulo	 *     [1] category
1257195618Srpaulo	 *     [tlv] mesh path request
1258195618Srpaulo	 */
1259195618Srpaulo	preq->preq_ie = IEEE80211_ELEMID_MESHPREQ;
1260232625Sadrian	preq->preq_len = (preq->preq_flags & IEEE80211_MESHPREQ_FLAGS_AE ?
1261232625Sadrian	    IEEE80211_MESHPREQ_BASE_SZ_AE : IEEE80211_MESHPREQ_BASE_SZ) +
1262232625Sadrian	    preq->preq_tcount * IEEE80211_MESHPREQ_TRGT_SZ;
1263246512Smonthadar	return hwmp_send_action(vap, da, (uint8_t *)preq, preq->preq_len+2);
1264195618Srpaulo}
1265195618Srpaulo
1266195618Srpaulostatic void
1267195618Srpaulohwmp_recv_prep(struct ieee80211vap *vap, struct ieee80211_node *ni,
1268195618Srpaulo    const struct ieee80211_frame *wh, const struct ieee80211_meshprep_ie *prep)
1269195618Srpaulo{
1270234881Smonthadar#define	IS_PROXY(rt)	(rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY)
1271234881Smonthadar#define	PROXIED_BY_US(rt)		\
1272234881Smonthadar    (IEEE80211_ADDR_EQ(vap->iv_myaddr, rt->rt_mesh_gate))
1273195618Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
1274195618Srpaulo	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
1275195618Srpaulo	struct ieee80211_mesh_route *rt = NULL;
1276234878Smonthadar	struct ieee80211_mesh_route *rtorig = NULL;
1277234878Smonthadar	struct ieee80211_mesh_route *rtext = NULL;
1278195618Srpaulo	struct ieee80211_hwmp_route *hr;
1279195618Srpaulo	struct ieee80211com *ic = vap->iv_ic;
1280195618Srpaulo	struct mbuf *m, *next;
1281230409Sadrian	uint32_t metric = 0;
1282234878Smonthadar	const uint8_t *addr;
1283195618Srpaulo
1284234881Smonthadar	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1285234881Smonthadar	    "received PREP, orig %6D, targ %6D", prep->prep_origaddr, ":",
1286234881Smonthadar	    prep->prep_targetaddr, ":");
1287234881Smonthadar
1288234881Smonthadar	/*
1289234881Smonthadar	 * Acceptance criteria: (If the corresponding PREP was not generated
1290234881Smonthadar	 * by us OR not generated by an external mac that is not proxied by us)
1291234881Smonthadar	 * AND forwarding is disabled, discard this PREP.
1292234881Smonthadar	 */
1293234878Smonthadar	rtorig = ieee80211_mesh_rt_find(vap, prep->prep_origaddr);
1294234881Smonthadar	if ((!IEEE80211_ADDR_EQ(vap->iv_myaddr, prep->prep_origaddr) ||
1295234881Smonthadar	    (rtorig != NULL && IS_PROXY(rtorig) && !PROXIED_BY_US(rtorig))) &&
1296234881Smonthadar	    !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD)){
1297234878Smonthadar		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1298234881Smonthadar		    "discard PREP, orig(%6D) not proxied or generated by us",
1299234881Smonthadar		    prep->prep_origaddr, ":");
1300234878Smonthadar		return;
1301234878Smonthadar	}
1302195618Srpaulo
1303234878Smonthadar	/* PREP ACCEPTED */
1304234878Smonthadar
1305195908Srpaulo	/*
1306234878Smonthadar	 * If accepted shall create or update the active forwarding information
1307234878Smonthadar	 * it maintains for the target mesh STA of the PREP (according to the
1308234878Smonthadar	 * rules defined in 13.10.8.4). If the conditions for creating or
1309234878Smonthadar	 * updating the forwarding information have not been met in those
1310234878Smonthadar	 * rules, no further steps are applied to the PREP.
1311195908Srpaulo	 */
1312234878Smonthadar	rt = ieee80211_mesh_rt_find(vap, prep->prep_targetaddr);
1313234878Smonthadar	if (rt == NULL) {
1314234878Smonthadar		rt = ieee80211_mesh_rt_add(vap, prep->prep_targetaddr);
1315234878Smonthadar		if (rt == NULL) {
1316234878Smonthadar			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1317234878Smonthadar			    "unable to add PREP path to %6D",
1318234878Smonthadar			    prep->prep_targetaddr, ":");
1319234878Smonthadar			vap->iv_stats.is_mesh_rtaddfailed++;
1320234878Smonthadar			return;
1321234878Smonthadar		}
1322234881Smonthadar		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1323234881Smonthadar		    "adding target %6D", prep->prep_targetaddr, ":");
1324234878Smonthadar	}
1325195618Srpaulo	hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
1326234878Smonthadar	/* update path metric */
1327234878Smonthadar	metric = prep->prep_metric + ms->ms_pmetric->mpm_metric(ni);
1328230752Sadrian	if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID)) {
1329230752Sadrian		if (HWMP_SEQ_LT(prep->prep_targetseq, hr->hr_seq)) {
1330230752Sadrian			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1331230752Sadrian			    "discard PREP from %6D, old seq no %u < %u",
1332230752Sadrian			    prep->prep_targetaddr, ":",
1333230752Sadrian			    prep->prep_targetseq, hr->hr_seq);
1334230752Sadrian			return;
1335230752Sadrian		} else if (HWMP_SEQ_LEQ(prep->prep_targetseq, hr->hr_seq) &&
1336234878Smonthadar		    metric > rt->rt_metric) {
1337230752Sadrian			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1338230752Sadrian			    "discard PREP from %6D, new metric %u > %u",
1339230752Sadrian			    prep->prep_targetaddr, ":",
1340238942Smonthadar			    metric, rt->rt_metric);
1341230752Sadrian			return;
1342230752Sadrian		}
1343195908Srpaulo	}
1344230409Sadrian
1345234878Smonthadar	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1346234878Smonthadar	    "%s path to %6D, hopcount %d:%d metric %d:%d",
1347234878Smonthadar	    rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID ?
1348234878Smonthadar	    "prefer" : "update",
1349234878Smonthadar	    prep->prep_targetaddr, ":",
1350238942Smonthadar	    rt->rt_nhops, prep->prep_hopcount + 1,
1351234878Smonthadar	    rt->rt_metric, metric);
1352234878Smonthadar
1353198230Srpaulo	hr->hr_seq = prep->prep_targetseq;
1354234883Smonthadar	hr->hr_preqretries = 0;
1355234878Smonthadar	IEEE80211_ADDR_COPY(rt->rt_nexthop, ni->ni_macaddr);
1356234878Smonthadar	rt->rt_metric = metric;
1357234878Smonthadar	rt->rt_nhops = prep->prep_hopcount + 1;
1358234878Smonthadar	ieee80211_mesh_rt_update(rt, prep->prep_lifetime);
1359234896Smonthadar	if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_DISCOVER) {
1360234896Smonthadar		/* discovery complete */
1361234896Smonthadar		rt->rt_flags &= ~IEEE80211_MESHRT_FLAGS_DISCOVER;
1362234896Smonthadar	}
1363234896Smonthadar	rt->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID; /* mark valid */
1364234878Smonthadar
1365246519Smonthadar	/* Update forwarding information to TA if metric improves */
1366246519Smonthadar	hwmp_update_transmitter(vap, ni, "PREP");
1367246519Smonthadar
1368195908Srpaulo	/*
1369234881Smonthadar	 * If it's NOT for us, propagate the PREP
1370195908Srpaulo	 */
1371198230Srpaulo	if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, prep->prep_origaddr) &&
1372234881Smonthadar	    prep->prep_ttl > 1 &&
1373234881Smonthadar	    prep->prep_hopcount < hs->hs_maxhops) {
1374195908Srpaulo		struct ieee80211_meshprep_ie pprep; /* propagated PREP */
1375234881Smonthadar		/*
1376234881Smonthadar		 * NB: We should already have setup the path to orig
1377234881Smonthadar		 * mesh STA when we propagated PREQ to target mesh STA,
1378234881Smonthadar		 * no PREP is generated without a corresponding PREQ.
1379234881Smonthadar		 * XXX: for now just ignore.
1380234881Smonthadar		 */
1381234881Smonthadar		if (rtorig == NULL) {
1382234881Smonthadar			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1383234881Smonthadar			    "received PREP for an unknown orig(%6D)",
1384234881Smonthadar			    prep->prep_origaddr, ":");
1385234881Smonthadar			return;
1386234881Smonthadar		}
1387234881Smonthadar
1388195908Srpaulo		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1389230408Sadrian		    "propagate PREP from %6D",
1390230408Sadrian		    prep->prep_targetaddr, ":");
1391195908Srpaulo
1392195908Srpaulo		memcpy(&pprep, prep, sizeof(pprep));
1393195908Srpaulo		pprep.prep_hopcount += 1;
1394195908Srpaulo		pprep.prep_ttl -= 1;
1395195908Srpaulo		pprep.prep_metric += ms->ms_pmetric->mpm_metric(ni);
1396246512Smonthadar		hwmp_send_prep(vap, rtorig->rt_nexthop, &pprep);
1397234878Smonthadar
1398234878Smonthadar		/* precursor list for the Target Mesh STA Address is updated */
1399195908Srpaulo	}
1400234881Smonthadar
1401234881Smonthadar	/*
1402234881Smonthadar	 * Check if we received a PREP w/ AE and store target external address.
1403234881Smonthadar	 * We may store target external address if recevied PREP w/ AE
1404234881Smonthadar	 * and we are not final destination
1405234881Smonthadar	 */
1406234881Smonthadar	if (prep->prep_flags & IEEE80211_MESHPREP_FLAGS_AE) {
1407234878Smonthadar		rtext = ieee80211_mesh_rt_find(vap,
1408234878Smonthadar			prep->prep_target_ext_addr);
1409234878Smonthadar		if (rtext == NULL) {
1410234878Smonthadar			rtext = ieee80211_mesh_rt_add(vap,
1411234878Smonthadar				prep->prep_target_ext_addr);
1412234878Smonthadar			if (rtext == NULL) {
1413234878Smonthadar				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1414234878Smonthadar				    "unable to add PREP path to proxy %6D",
1415234878Smonthadar				    prep->prep_targetaddr, ":");
1416234878Smonthadar				vap->iv_stats.is_mesh_rtaddfailed++;
1417234878Smonthadar				return;
1418234878Smonthadar			}
1419195618Srpaulo		}
1420195618Srpaulo		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1421234878Smonthadar		    "%s path to %6D, hopcount %d:%d metric %d:%d",
1422234878Smonthadar		    rtext->rt_flags & IEEE80211_MESHRT_FLAGS_VALID ?
1423234878Smonthadar		    "prefer" : "update",
1424234878Smonthadar		    prep->prep_target_ext_addr, ":",
1425238942Smonthadar		    rtext->rt_nhops, prep->prep_hopcount + 1,
1426234878Smonthadar		    rtext->rt_metric, metric);
1427234878Smonthadar
1428234878Smonthadar		rtext->rt_flags = IEEE80211_MESHRT_FLAGS_PROXY |
1429234878Smonthadar			IEEE80211_MESHRT_FLAGS_VALID;
1430234878Smonthadar		IEEE80211_ADDR_COPY(rtext->rt_dest,
1431234878Smonthadar		    prep->prep_target_ext_addr);
1432234878Smonthadar		IEEE80211_ADDR_COPY(rtext->rt_mesh_gate,
1433234878Smonthadar		    prep->prep_targetaddr);
1434234878Smonthadar		IEEE80211_ADDR_COPY(rtext->rt_nexthop, wh->i_addr2);
1435234878Smonthadar		rtext->rt_metric = metric;
1436234878Smonthadar		rtext->rt_lifetime = prep->prep_lifetime;
1437234878Smonthadar		rtext->rt_nhops = prep->prep_hopcount + 1;
1438234881Smonthadar		rtext->rt_ext_seq = prep->prep_origseq; /* new proxy seq */
1439234881Smonthadar		/*
1440234881Smonthadar		 * XXX: proxy entries have no HWMP priv data,
1441234881Smonthadar		 * nullify them to be sure?
1442234881Smonthadar		 */
1443230409Sadrian	}
1444195618Srpaulo	/*
1445195618Srpaulo	 * Check for frames queued awaiting path discovery.
1446195618Srpaulo	 * XXX probably can tell exactly and avoid remove call
1447195618Srpaulo	 * NB: hash may have false matches, if so they will get
1448195618Srpaulo	 *     stuck back on the stageq because there won't be
1449195618Srpaulo	 *     a path.
1450195618Srpaulo	 */
1451234878Smonthadar	addr = prep->prep_flags & IEEE80211_MESHPREP_FLAGS_AE ?
1452234878Smonthadar	    prep->prep_target_ext_addr : prep->prep_targetaddr;
1453230409Sadrian	m = ieee80211_ageq_remove(&ic->ic_stageq,
1454195618Srpaulo	    (struct ieee80211_node *)(uintptr_t)
1455234878Smonthadar	    ieee80211_mac_hash(ic, addr)); /* either dest or ext_dest */
1456248069Sadrian
1457248069Sadrian	/*
1458248069Sadrian	 * All frames in the stageq here should be non-M_ENCAP; or things
1459248069Sadrian	 * will get very unhappy.
1460248069Sadrian	 */
1461195618Srpaulo	for (; m != NULL; m = next) {
1462195618Srpaulo		next = m->m_nextpkt;
1463195618Srpaulo		m->m_nextpkt = NULL;
1464195784Srpaulo		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1465195784Srpaulo		    "flush queued frame %p len %d", m, m->m_pkthdr.len);
1466245097Sadrian		/*
1467245097Sadrian		 * If the mbuf has M_ENCAP set, ensure we free it.
1468245097Sadrian		 * Note that after if_transmit() is called, m is invalid.
1469245097Sadrian		 */
1470254082Sadrian		(void) ieee80211_vap_xmitpkt(vap, m);
1471195618Srpaulo	}
1472234881Smonthadar#undef	IS_PROXY
1473234881Smonthadar#undef	PROXIED_BY_US
1474195618Srpaulo}
1475195618Srpaulo
1476195618Srpaulostatic int
1477246512Smonthadarhwmp_send_prep(struct ieee80211vap *vap,
1478195618Srpaulo    const uint8_t da[IEEE80211_ADDR_LEN],
1479195618Srpaulo    struct ieee80211_meshprep_ie *prep)
1480195618Srpaulo{
1481195784Srpaulo	/* NB: there's no PREP minimum interval. */
1482195618Srpaulo
1483195618Srpaulo	/*
1484195618Srpaulo	 * mesh prep action frame format
1485195618Srpaulo	 *     [6] da
1486230409Sadrian	 *     [6] sa
1487195618Srpaulo	 *     [6] addr3 = sa
1488195618Srpaulo	 *     [1] action
1489195618Srpaulo	 *     [1] category
1490195618Srpaulo	 *     [tlv] mesh path reply
1491195618Srpaulo	 */
1492195618Srpaulo	prep->prep_ie = IEEE80211_ELEMID_MESHPREP;
1493232625Sadrian	prep->prep_len = prep->prep_flags & IEEE80211_MESHPREP_FLAGS_AE ?
1494232625Sadrian	    IEEE80211_MESHPREP_BASE_SZ_AE : IEEE80211_MESHPREP_BASE_SZ;
1495246512Smonthadar	return hwmp_send_action(vap, da, (uint8_t *)prep, prep->prep_len + 2);
1496195618Srpaulo}
1497195618Srpaulo
1498197413Srpaulo#define	PERR_DFLAGS(n)	perr.perr_dests[n].dest_flags
1499195618Srpaulo#define	PERR_DADDR(n)	perr.perr_dests[n].dest_addr
1500195618Srpaulo#define	PERR_DSEQ(n)	perr.perr_dests[n].dest_seq
1501197413Srpaulo#define	PERR_DRCODE(n)	perr.perr_dests[n].dest_rcode
1502195618Srpaulostatic void
1503195618Srpaulohwmp_peerdown(struct ieee80211_node *ni)
1504195618Srpaulo{
1505195618Srpaulo	struct ieee80211vap *vap = ni->ni_vap;
1506197413Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
1507195618Srpaulo	struct ieee80211_meshperr_ie perr;
1508195618Srpaulo	struct ieee80211_mesh_route *rt;
1509195618Srpaulo	struct ieee80211_hwmp_route *hr;
1510195618Srpaulo
1511195618Srpaulo	rt = ieee80211_mesh_rt_find(vap, ni->ni_macaddr);
1512195618Srpaulo	if (rt == NULL)
1513195618Srpaulo		return;
1514195618Srpaulo	hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
1515195618Srpaulo	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1516195784Srpaulo	    "%s", "delete route entry");
1517197413Srpaulo	perr.perr_ttl = ms->ms_ttl;
1518195618Srpaulo	perr.perr_ndests = 1;
1519198260Srpaulo	PERR_DFLAGS(0) = 0;
1520197413Srpaulo	if (hr->hr_seq == 0)
1521197413Srpaulo		PERR_DFLAGS(0) |= IEEE80211_MESHPERR_DFLAGS_USN;
1522197413Srpaulo	PERR_DFLAGS(0) |= IEEE80211_MESHPERR_DFLAGS_RC;
1523195618Srpaulo	IEEE80211_ADDR_COPY(PERR_DADDR(0), rt->rt_dest);
1524234889Smonthadar	PERR_DSEQ(0) = ++hr->hr_seq;
1525197413Srpaulo	PERR_DRCODE(0) = IEEE80211_REASON_MESH_PERR_DEST_UNREACH;
1526195908Srpaulo	/* NB: flush everything passing through peer */
1527195908Srpaulo	ieee80211_mesh_rt_flush_peer(vap, ni->ni_macaddr);
1528246512Smonthadar	hwmp_send_perr(vap, broadcastaddr, &perr);
1529195618Srpaulo}
1530197413Srpaulo#undef	PERR_DFLAGS
1531195618Srpaulo#undef	PERR_DADDR
1532195618Srpaulo#undef	PERR_DSEQ
1533197413Srpaulo#undef	PERR_DRCODE
1534195618Srpaulo
1535234889Smonthadar#define	PERR_DFLAGS(n)		perr->perr_dests[n].dest_flags
1536234889Smonthadar#define	PERR_DADDR(n)		perr->perr_dests[n].dest_addr
1537234889Smonthadar#define	PERR_DSEQ(n)		perr->perr_dests[n].dest_seq
1538234889Smonthadar#define	PERR_DEXTADDR(n)	perr->perr_dests[n].dest_ext_addr
1539234889Smonthadar#define	PERR_DRCODE(n)		perr->perr_dests[n].dest_rcode
1540195618Srpaulostatic void
1541195618Srpaulohwmp_recv_perr(struct ieee80211vap *vap, struct ieee80211_node *ni,
1542195618Srpaulo    const struct ieee80211_frame *wh, const struct ieee80211_meshperr_ie *perr)
1543195618Srpaulo{
1544195618Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
1545195618Srpaulo	struct ieee80211_mesh_route *rt = NULL;
1546234889Smonthadar	struct ieee80211_mesh_route *rt_ext = NULL;
1547195618Srpaulo	struct ieee80211_hwmp_route *hr;
1548234889Smonthadar	struct ieee80211_meshperr_ie *pperr = NULL;
1549234889Smonthadar	int i, j = 0, forward = 0;
1550195618Srpaulo
1551234889Smonthadar	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1552234889Smonthadar	    "received PERR from %6D", wh->i_addr2, ":");
1553234889Smonthadar
1554195618Srpaulo	/*
1555234889Smonthadar	 * if forwarding is true, prepare pperr
1556195618Srpaulo	 */
1557234889Smonthadar	if (ms->ms_flags & IEEE80211_MESHFLAGS_FWD) {
1558234889Smonthadar		forward = 1;
1559234889Smonthadar		pperr = malloc(sizeof(*perr) + 31*sizeof(*perr->perr_dests),
1560234889Smonthadar		    M_80211_MESH_PERR, M_NOWAIT); /* XXX: magic number, 32 err dests */
1561234889Smonthadar	}
1562234889Smonthadar
1563234889Smonthadar	/*
1564234889Smonthadar	 * Acceptance criteria: check if we have forwarding information
1565234889Smonthadar	 * stored about destination, and that nexthop == TA of this PERR.
1566234889Smonthadar	 * NB: we also build a new PERR to propagate in case we should forward.
1567234889Smonthadar	 */
1568195618Srpaulo	for (i = 0; i < perr->perr_ndests; i++) {
1569195618Srpaulo		rt = ieee80211_mesh_rt_find(vap, PERR_DADDR(i));
1570246500Smonthadar		if (rt == NULL)
1571195618Srpaulo			continue;
1572234889Smonthadar		if (!IEEE80211_ADDR_EQ(rt->rt_nexthop, wh->i_addr2))
1573234889Smonthadar			continue;
1574234889Smonthadar
1575234889Smonthadar		/* found and accepted a PERR ndest element, process it... */
1576234889Smonthadar		if (forward)
1577234889Smonthadar			memcpy(&pperr->perr_dests[j], &perr->perr_dests[i],
1578234889Smonthadar			    sizeof(*perr->perr_dests));
1579197413Srpaulo		hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
1580234889Smonthadar		switch(PERR_DFLAGS(i)) {
1581234889Smonthadar		case (IEEE80211_REASON_MESH_PERR_NO_FI):
1582234889Smonthadar			if (PERR_DSEQ(i) == 0) {
1583234889Smonthadar				hr->hr_seq++;
1584234889Smonthadar				if (forward) {
1585234889Smonthadar					pperr->perr_dests[j].dest_seq =
1586234889Smonthadar					    hr->hr_seq;
1587234889Smonthadar				}
1588234889Smonthadar			} else {
1589234889Smonthadar				hr->hr_seq = PERR_DSEQ(i);
1590234889Smonthadar			}
1591234889Smonthadar			rt->rt_flags &= ~IEEE80211_MESHRT_FLAGS_VALID;
1592234889Smonthadar			j++;
1593234889Smonthadar			break;
1594234889Smonthadar		case (IEEE80211_REASON_MESH_PERR_DEST_UNREACH):
1595234889Smonthadar			if(HWMP_SEQ_GT(PERR_DSEQ(i), hr->hr_seq)) {
1596234889Smonthadar				hr->hr_seq = PERR_DSEQ(i);
1597234889Smonthadar				rt->rt_flags &= ~IEEE80211_MESHRT_FLAGS_VALID;
1598234889Smonthadar				j++;
1599234889Smonthadar			}
1600234889Smonthadar			break;
1601234889Smonthadar		case (IEEE80211_REASON_MESH_PERR_NO_PROXY):
1602234889Smonthadar			rt_ext = ieee80211_mesh_rt_find(vap, PERR_DEXTADDR(i));
1603234889Smonthadar			if (rt_ext != NULL) {
1604234889Smonthadar				rt_ext->rt_flags &=
1605234889Smonthadar				    ~IEEE80211_MESHRT_FLAGS_VALID;
1606234889Smonthadar				j++;
1607234889Smonthadar			}
1608234889Smonthadar			break;
1609234889Smonthadar		default:
1610234889Smonthadar			IEEE80211_DISCARD(vap, IEEE80211_MSG_HWMP, wh, NULL,
1611234889Smonthadar			    "PERR, unknown reason code %u\n", PERR_DFLAGS(i));
1612234889Smonthadar			goto done; /* XXX: stats?? */
1613195618Srpaulo		}
1614246518Smonthadar		ieee80211_mesh_rt_flush_peer(vap, PERR_DADDR(i));
1615234889Smonthadar		KASSERT(j < 32, ("PERR, error ndest >= 32 (%u)", j));
1616195618Srpaulo	}
1617234889Smonthadar	if (j == 0) {
1618234889Smonthadar		IEEE80211_DISCARD(vap, IEEE80211_MSG_HWMP, wh, NULL, "%s",
1619234889Smonthadar		    "PERR not accepted");
1620234889Smonthadar		goto done; /* XXX: stats?? */
1621234889Smonthadar	}
1622234889Smonthadar
1623195618Srpaulo	/*
1624195618Srpaulo	 * Propagate the PERR if we previously found it on our routing table.
1625195618Srpaulo	 */
1626197413Srpaulo	if (forward && perr->perr_ttl > 1) {
1627195618Srpaulo		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1628230408Sadrian		    "propagate PERR from %6D", wh->i_addr2, ":");
1629234889Smonthadar		pperr->perr_ndests = j;
1630234889Smonthadar		pperr->perr_ttl--;
1631246512Smonthadar		hwmp_send_perr(vap, broadcastaddr, pperr);
1632195618Srpaulo	}
1633234889Smonthadardone:
1634234889Smonthadar	if (pperr != NULL)
1635234889Smonthadar		free(pperr, M_80211_MESH_PERR);
1636195618Srpaulo}
1637232625Sadrian#undef	PERR_DFLAGS
1638234890Smonthadar#undef	PERR_DADDR
1639195618Srpaulo#undef	PERR_DSEQ
1640234889Smonthadar#undef	PERR_DEXTADDR
1641232625Sadrian#undef	PERR_DRCODE
1642195618Srpaulo
1643195618Srpaulostatic int
1644246512Smonthadarhwmp_send_perr(struct ieee80211vap *vap,
1645195618Srpaulo    const uint8_t da[IEEE80211_ADDR_LEN],
1646195618Srpaulo    struct ieee80211_meshperr_ie *perr)
1647195618Srpaulo{
1648246512Smonthadar	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
1649232625Sadrian	int i;
1650232625Sadrian	uint8_t length = 0;
1651195618Srpaulo
1652195618Srpaulo	/*
1653195618Srpaulo	 * Enforce PERR interval.
1654195618Srpaulo	 */
1655195618Srpaulo	if (ratecheck(&hs->hs_lastperr, &ieee80211_hwmp_perrminint) == 0)
1656195618Srpaulo		return EALREADY;
1657195618Srpaulo	getmicrouptime(&hs->hs_lastperr);
1658195618Srpaulo
1659195618Srpaulo	/*
1660195618Srpaulo	 * mesh perr action frame format
1661195618Srpaulo	 *     [6] da
1662195618Srpaulo	 *     [6] sa
1663195618Srpaulo	 *     [6] addr3 = sa
1664195618Srpaulo	 *     [1] action
1665195618Srpaulo	 *     [1] category
1666195618Srpaulo	 *     [tlv] mesh path error
1667195618Srpaulo	 */
1668195618Srpaulo	perr->perr_ie = IEEE80211_ELEMID_MESHPERR;
1669232625Sadrian	length = IEEE80211_MESHPERR_BASE_SZ;
1670232625Sadrian	for (i = 0; i<perr->perr_ndests; i++) {
1671232625Sadrian		if (perr->perr_dests[i].dest_flags &
1672232625Sadrian		    IEEE80211_MESHPERR_FLAGS_AE) {
1673232625Sadrian			length += IEEE80211_MESHPERR_DEST_SZ_AE;
1674232625Sadrian			continue ;
1675232625Sadrian		}
1676232625Sadrian		length += IEEE80211_MESHPERR_DEST_SZ;
1677232625Sadrian	}
1678232625Sadrian	perr->perr_len =length;
1679246512Smonthadar	return hwmp_send_action(vap, da, (uint8_t *)perr, perr->perr_len+2);
1680195618Srpaulo}
1681195618Srpaulo
1682234890Smonthadar/*
1683234890Smonthadar * Called from the rest of the net80211 code (mesh code for example).
1684234890Smonthadar * NB: IEEE80211_REASON_MESH_PERR_DEST_UNREACH can be trigger by the fact that
1685234890Smonthadar * a mesh STA is unable to forward an MSDU/MMPDU to a next-hop mesh STA.
1686234890Smonthadar */
1687234890Smonthadar#define	PERR_DFLAGS(n)		perr.perr_dests[n].dest_flags
1688234890Smonthadar#define	PERR_DADDR(n)		perr.perr_dests[n].dest_addr
1689234890Smonthadar#define	PERR_DSEQ(n)		perr.perr_dests[n].dest_seq
1690234890Smonthadar#define	PERR_DEXTADDR(n)	perr.perr_dests[n].dest_ext_addr
1691234890Smonthadar#define	PERR_DRCODE(n)		perr.perr_dests[n].dest_rcode
1692195618Srpaulostatic void
1693234890Smonthadarhwmp_senderror(struct ieee80211vap *vap,
1694234890Smonthadar    const uint8_t addr[IEEE80211_ADDR_LEN],
1695234890Smonthadar    struct ieee80211_mesh_route *rt, int rcode)
1696234890Smonthadar{
1697234890Smonthadar	struct ieee80211_mesh_state *ms = vap->iv_mesh;
1698234890Smonthadar	struct ieee80211_hwmp_route *hr = NULL;
1699234890Smonthadar	struct ieee80211_meshperr_ie perr;
1700234890Smonthadar
1701234890Smonthadar	if (rt != NULL)
1702234890Smonthadar		hr = IEEE80211_MESH_ROUTE_PRIV(rt,
1703234890Smonthadar		    struct ieee80211_hwmp_route);
1704234890Smonthadar
1705234890Smonthadar	perr.perr_ndests = 1;
1706234890Smonthadar	perr.perr_ttl = ms->ms_ttl;
1707234890Smonthadar	PERR_DFLAGS(0) = 0;
1708234890Smonthadar	PERR_DRCODE(0) = rcode;
1709234890Smonthadar
1710234890Smonthadar	switch (rcode) {
1711234890Smonthadar	case IEEE80211_REASON_MESH_PERR_NO_FI:
1712234890Smonthadar		IEEE80211_ADDR_COPY(PERR_DADDR(0), addr);
1713234890Smonthadar		PERR_DSEQ(0) = 0; /* reserved */
1714234890Smonthadar		break;
1715234890Smonthadar	case IEEE80211_REASON_MESH_PERR_NO_PROXY:
1716234890Smonthadar		KASSERT(rt != NULL, ("no proxy info for sending PERR"));
1717234890Smonthadar		KASSERT(rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY,
1718234890Smonthadar		    ("route is not marked proxy"));
1719234890Smonthadar		PERR_DFLAGS(0) |= IEEE80211_MESHPERR_FLAGS_AE;
1720234890Smonthadar		IEEE80211_ADDR_COPY(PERR_DADDR(0), vap->iv_myaddr);
1721234890Smonthadar		PERR_DSEQ(0) = rt->rt_ext_seq;
1722234890Smonthadar		IEEE80211_ADDR_COPY(PERR_DEXTADDR(0), addr);
1723234890Smonthadar		break;
1724234890Smonthadar	case IEEE80211_REASON_MESH_PERR_DEST_UNREACH:
1725234890Smonthadar		KASSERT(rt != NULL, ("no route info for sending PERR"));
1726234890Smonthadar		IEEE80211_ADDR_COPY(PERR_DADDR(0), addr);
1727234890Smonthadar		PERR_DSEQ(0) = hr->hr_seq;
1728234890Smonthadar		break;
1729234890Smonthadar	default:
1730234890Smonthadar		KASSERT(0, ("unknown reason code for HWMP PERR (%u)", rcode));
1731234890Smonthadar	}
1732246512Smonthadar	hwmp_send_perr(vap, broadcastaddr, &perr);
1733234890Smonthadar}
1734234890Smonthadar#undef	PERR_DFLAGS
1735234890Smonthadar#undef	PEER_DADDR
1736234890Smonthadar#undef	PERR_DSEQ
1737234890Smonthadar#undef	PERR_DEXTADDR
1738234890Smonthadar#undef	PERR_DRCODE
1739234890Smonthadar
1740234890Smonthadarstatic void
1741195618Srpaulohwmp_recv_rann(struct ieee80211vap *vap, struct ieee80211_node *ni,
1742195618Srpaulo    const struct ieee80211_frame *wh, const struct ieee80211_meshrann_ie *rann)
1743195618Srpaulo{
1744195618Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
1745195618Srpaulo	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
1746195618Srpaulo	struct ieee80211_mesh_route *rt = NULL;
1747195618Srpaulo	struct ieee80211_hwmp_route *hr;
1748234891Smonthadar	struct ieee80211_meshpreq_ie preq;
1749195618Srpaulo	struct ieee80211_meshrann_ie prann;
1750234891Smonthadar	uint32_t metric = 0;
1751195618Srpaulo
1752246503Smonthadar	if (IEEE80211_ADDR_EQ(rann->rann_addr, vap->iv_myaddr))
1753195618Srpaulo		return;
1754195618Srpaulo
1755195618Srpaulo	rt = ieee80211_mesh_rt_find(vap, rann->rann_addr);
1756234891Smonthadar	if (rt != NULL && rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) {
1757234891Smonthadar		hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
1758234891Smonthadar
1759234891Smonthadar		/* Acceptance criteria: if RANN.seq < stored seq, discard RANN */
1760234891Smonthadar		if (HWMP_SEQ_LT(rann->rann_seq, hr->hr_seq)) {
1761234891Smonthadar			IEEE80211_DISCARD(vap, IEEE80211_MSG_HWMP, wh, NULL,
1762234891Smonthadar			"RANN seq %u < %u", rann->rann_seq, hr->hr_seq);
1763234891Smonthadar			return;
1764234891Smonthadar		}
1765234891Smonthadar
1766234891Smonthadar		/* Acceptance criteria: if RANN.seq == stored seq AND
1767234891Smonthadar		* RANN.metric > stored metric, discard RANN */
1768234891Smonthadar		if (HWMP_SEQ_EQ(rann->rann_seq, hr->hr_seq) &&
1769234891Smonthadar		rann->rann_metric > rt->rt_metric) {
1770234891Smonthadar			IEEE80211_DISCARD(vap, IEEE80211_MSG_HWMP, wh, NULL,
1771234891Smonthadar			"RANN metric %u > %u", rann->rann_metric, rt->rt_metric);
1772234891Smonthadar			return;
1773234891Smonthadar		}
1774234891Smonthadar	}
1775234891Smonthadar
1776234891Smonthadar	/* RANN ACCEPTED */
1777234891Smonthadar
1778234891Smonthadar	ieee80211_hwmp_rannint = rann->rann_interval; /* XXX: mtx lock? */
1779234891Smonthadar	metric = rann->rann_metric + ms->ms_pmetric->mpm_metric(ni);
1780234891Smonthadar
1781195618Srpaulo	if (rt == NULL) {
1782234891Smonthadar		rt = ieee80211_mesh_rt_add(vap, rann->rann_addr);
1783234891Smonthadar		if (rt == NULL) {
1784234891Smonthadar			IEEE80211_DISCARD(vap, IEEE80211_MSG_HWMP, wh, NULL,
1785234891Smonthadar			    "unable to add mac for RANN root %6D",
1786234891Smonthadar			    rann->rann_addr, ":");
1787234891Smonthadar			    vap->iv_stats.is_mesh_rtaddfailed++;
1788234891Smonthadar			return;
1789234891Smonthadar		}
1790195618Srpaulo	}
1791195618Srpaulo	hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
1792246509Smonthadar	/* Check if root is a mesh gate, mark it */
1793246509Smonthadar	if (rann->rann_flags & IEEE80211_MESHRANN_FLAGS_GATE) {
1794246509Smonthadar		struct ieee80211_mesh_gate_route *gr;
1795246509Smonthadar
1796246509Smonthadar		rt->rt_flags |= IEEE80211_MESHRT_FLAGS_GATE;
1797246509Smonthadar		gr = ieee80211_mesh_mark_gate(vap, rann->rann_addr,
1798246509Smonthadar			rt);
1799246509Smonthadar		gr->gr_lastseq = 0; /* NOT GANN */
1800246509Smonthadar	}
1801234891Smonthadar	/* discovery timeout */
1802234891Smonthadar	ieee80211_mesh_rt_update(rt,
1803234891Smonthadar	    ticks_to_msecs(ieee80211_hwmp_roottimeout));
1804234891Smonthadar
1805234891Smonthadar	preq.preq_flags = IEEE80211_MESHPREQ_FLAGS_AM;
1806234891Smonthadar	preq.preq_hopcount = 0;
1807234891Smonthadar	preq.preq_ttl = ms->ms_ttl;
1808234891Smonthadar	preq.preq_id = 0; /* reserved */
1809234891Smonthadar	IEEE80211_ADDR_COPY(preq.preq_origaddr, vap->iv_myaddr);
1810234891Smonthadar	preq.preq_origseq = ++hs->hs_seq;
1811234891Smonthadar	preq.preq_lifetime = ieee80211_hwmp_roottimeout;
1812234891Smonthadar	preq.preq_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
1813234891Smonthadar	preq.preq_tcount = 1;
1814234891Smonthadar	preq.preq_targets[0].target_flags = IEEE80211_MESHPREQ_TFLAGS_TO;
1815234891Smonthadar	/* NB: IEEE80211_MESHPREQ_TFLAGS_USN = 0 implicitly implied */
1816234891Smonthadar	IEEE80211_ADDR_COPY(preq.preq_targets[0].target_addr, rann->rann_addr);
1817234891Smonthadar	preq.preq_targets[0].target_seq = rann->rann_seq;
1818234891Smonthadar	/* XXX: if rootconfint have not passed, we built this preq in vain */
1819246512Smonthadar	hwmp_send_preq(vap, wh->i_addr2, &preq, &hr->hr_lastrootconf,
1820246512Smonthadar	    &ieee80211_hwmp_rootconfint);
1821234891Smonthadar
1822234891Smonthadar	/* propagate a RANN */
1823234891Smonthadar	if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID &&
1824234891Smonthadar	    rann->rann_ttl > 1 &&
1825234891Smonthadar	    ms->ms_flags & IEEE80211_MESHFLAGS_FWD) {
1826198581Srpaulo		hr->hr_seq = rann->rann_seq;
1827234891Smonthadar		memcpy(&prann, rann, sizeof(prann));
1828234891Smonthadar		prann.rann_hopcount += 1;
1829234891Smonthadar		prann.rann_ttl -= 1;
1830234891Smonthadar		prann.rann_metric += ms->ms_pmetric->mpm_metric(ni);
1831246512Smonthadar		hwmp_send_rann(vap, broadcastaddr, &prann);
1832195618Srpaulo	}
1833195618Srpaulo}
1834195618Srpaulo
1835195618Srpaulostatic int
1836246512Smonthadarhwmp_send_rann(struct ieee80211vap *vap,
1837195618Srpaulo    const uint8_t da[IEEE80211_ADDR_LEN],
1838195618Srpaulo    struct ieee80211_meshrann_ie *rann)
1839195618Srpaulo{
1840195618Srpaulo	/*
1841195618Srpaulo	 * mesh rann action frame format
1842195618Srpaulo	 *     [6] da
1843230409Sadrian	 *     [6] sa
1844195618Srpaulo	 *     [6] addr3 = sa
1845195618Srpaulo	 *     [1] action
1846195618Srpaulo	 *     [1] category
1847195618Srpaulo	 *     [tlv] root annoucement
1848195618Srpaulo	 */
1849195618Srpaulo	rann->rann_ie = IEEE80211_ELEMID_MESHRANN;
1850232625Sadrian	rann->rann_len = IEEE80211_MESHRANN_BASE_SZ;
1851246512Smonthadar	return hwmp_send_action(vap, da, (uint8_t *)rann, rann->rann_len + 2);
1852195618Srpaulo}
1853195618Srpaulo
1854195618Srpaulo#define	PREQ_TFLAGS(n)	preq.preq_targets[n].target_flags
1855195618Srpaulo#define	PREQ_TADDR(n)	preq.preq_targets[n].target_addr
1856195618Srpaulo#define	PREQ_TSEQ(n)	preq.preq_targets[n].target_seq
1857234894Smonthadarstatic void
1858234894Smonthadarhwmp_rediscover_cb(void *arg)
1859234894Smonthadar{
1860234894Smonthadar	struct ieee80211_mesh_route *rt = arg;
1861234894Smonthadar	struct ieee80211vap *vap = rt->rt_vap;
1862234894Smonthadar	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
1863234894Smonthadar	struct ieee80211_mesh_state *ms = vap->iv_mesh;
1864234894Smonthadar	struct ieee80211_hwmp_route *hr;
1865234894Smonthadar	struct ieee80211_meshpreq_ie preq; /* Optimize: storing first preq? */
1866234894Smonthadar
1867234894Smonthadar	if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID))
1868234894Smonthadar		return ; /* nothing to do */
1869234894Smonthadar
1870234894Smonthadar	hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
1871234894Smonthadar	if (hr->hr_preqretries >=
1872234894Smonthadar		ieee80211_hwmp_maxpreq_retries) {
1873246510Smonthadar		IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ANY,
1874246510Smonthadar			rt->rt_dest, "%s",
1875246510Smonthadar			"max number of discovery, send queued frames to GATE");
1876246510Smonthadar		ieee80211_mesh_forward_to_gates(vap, rt);
1877234894Smonthadar		vap->iv_stats.is_mesh_fwd_nopath++;
1878234894Smonthadar		return ; /* XXX: flush queue? */
1879234894Smonthadar	}
1880234894Smonthadar
1881234894Smonthadar	hr->hr_preqretries++;
1882234894Smonthadar
1883234894Smonthadar
1884234894Smonthadar	IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP, rt->rt_dest,
1885234894Smonthadar	    "start path rediscovery , target seq %u", hr->hr_seq);
1886234894Smonthadar	/*
1887234894Smonthadar	 * Try to discover the path for this node.
1888234894Smonthadar	 * Group addressed PREQ Case A
1889234894Smonthadar	 */
1890234894Smonthadar	preq.preq_flags = 0;
1891234894Smonthadar	preq.preq_hopcount = 0;
1892234894Smonthadar	preq.preq_ttl = ms->ms_ttl;
1893234894Smonthadar	preq.preq_id = ++hs->hs_preqid;
1894234894Smonthadar	IEEE80211_ADDR_COPY(preq.preq_origaddr, vap->iv_myaddr);
1895234894Smonthadar	preq.preq_origseq = hr->hr_origseq;
1896234894Smonthadar	preq.preq_lifetime = ticks_to_msecs(ieee80211_hwmp_pathtimeout);
1897234894Smonthadar	preq.preq_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
1898234894Smonthadar	preq.preq_tcount = 1;
1899234894Smonthadar	IEEE80211_ADDR_COPY(PREQ_TADDR(0), rt->rt_dest);
1900234894Smonthadar	PREQ_TFLAGS(0) = 0;
1901234894Smonthadar	if (ieee80211_hwmp_targetonly)
1902234894Smonthadar		PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_TO;
1903234894Smonthadar	PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_USN;
1904234894Smonthadar	PREQ_TSEQ(0) = 0; /* RESERVED when USN flag is set */
1905234894Smonthadar	/* XXX check return value */
1906246512Smonthadar	hwmp_send_preq(vap, broadcastaddr, &preq, &hr->hr_lastpreq,
1907246512Smonthadar	    &ieee80211_hwmp_preqminint);
1908234894Smonthadar	callout_reset(&rt->rt_discovery,
1909234894Smonthadar		ieee80211_hwmp_net_diameter_traversaltime * 2,
1910234894Smonthadar		hwmp_rediscover_cb, rt);
1911234894Smonthadar}
1912234894Smonthadar
1913195618Srpaulostatic struct ieee80211_node *
1914195618Srpaulohwmp_discover(struct ieee80211vap *vap,
1915195618Srpaulo    const uint8_t dest[IEEE80211_ADDR_LEN], struct mbuf *m)
1916195618Srpaulo{
1917195618Srpaulo	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
1918195618Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
1919195618Srpaulo	struct ieee80211_mesh_route *rt = NULL;
1920195618Srpaulo	struct ieee80211_hwmp_route *hr;
1921195618Srpaulo	struct ieee80211_meshpreq_ie preq;
1922195618Srpaulo	struct ieee80211_node *ni;
1923195618Srpaulo	int sendpreq = 0;
1924195618Srpaulo
1925195618Srpaulo	KASSERT(vap->iv_opmode == IEEE80211_M_MBSS,
1926195618Srpaulo	    ("not a mesh vap, opmode %d", vap->iv_opmode));
1927195618Srpaulo
1928195618Srpaulo	KASSERT(!IEEE80211_ADDR_EQ(vap->iv_myaddr, dest),
1929195618Srpaulo	    ("%s: discovering self!", __func__));
1930195618Srpaulo
1931195618Srpaulo	ni = NULL;
1932195618Srpaulo	if (!IEEE80211_IS_MULTICAST(dest)) {
1933195618Srpaulo		rt = ieee80211_mesh_rt_find(vap, dest);
1934195618Srpaulo		if (rt == NULL) {
1935195618Srpaulo			rt = ieee80211_mesh_rt_add(vap, dest);
1936195618Srpaulo			if (rt == NULL) {
1937195784Srpaulo				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP,
1938230408Sadrian				    ni, "unable to add discovery path to %6D",
1939230408Sadrian				    dest, ":");
1940195784Srpaulo				vap->iv_stats.is_mesh_rtaddfailed++;
1941195618Srpaulo				goto done;
1942195618Srpaulo			}
1943195618Srpaulo		}
1944195618Srpaulo		hr = IEEE80211_MESH_ROUTE_PRIV(rt,
1945195618Srpaulo		    struct ieee80211_hwmp_route);
1946246510Smonthadar		if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_DISCOVER) {
1947246510Smonthadar			IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP, dest,
1948246510Smonthadar			    "%s", "already discovering queue frame until path found");
1949246510Smonthadar			sendpreq = 1;
1950246510Smonthadar			goto done;
1951246510Smonthadar		}
1952195784Srpaulo		if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0) {
1953234883Smonthadar			if (hr->hr_lastdiscovery != 0 &&
1954234883Smonthadar			    (ticks - hr->hr_lastdiscovery <
1955234883Smonthadar			    (ieee80211_hwmp_net_diameter_traversaltime * 2))) {
1956234883Smonthadar				IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
1957234883Smonthadar			            dest, NULL, "%s",
1958234883Smonthadar				    "too frequent discovery requeust");
1959246510Smonthadar				sendpreq = 1;
1960234883Smonthadar				goto done;
1961234883Smonthadar			}
1962234883Smonthadar			hr->hr_lastdiscovery = ticks;
1963234883Smonthadar			if (hr->hr_preqretries >=
1964234883Smonthadar			    ieee80211_hwmp_maxpreq_retries) {
1965234883Smonthadar				IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
1966234883Smonthadar			            dest, NULL, "%s",
1967234883Smonthadar				    "no valid path , max number of discovery");
1968234883Smonthadar				vap->iv_stats.is_mesh_fwd_nopath++;
1969234883Smonthadar				goto done;
1970234883Smonthadar			}
1971234894Smonthadar			rt->rt_flags = IEEE80211_MESHRT_FLAGS_DISCOVER;
1972234883Smonthadar			hr->hr_preqretries++;
1973198230Srpaulo			if (hr->hr_origseq == 0)
1974198230Srpaulo				hr->hr_origseq = ++hs->hs_seq;
1975195618Srpaulo			rt->rt_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
1976195618Srpaulo			sendpreq = 1;
1977195618Srpaulo			IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP, dest,
1978230409Sadrian			    "start path discovery (src %s), target seq %u",
1979195784Srpaulo			    m == NULL ? "<none>" : ether_sprintf(
1980230409Sadrian			    mtod(m, struct ether_header *)->ether_shost),
1981230409Sadrian			    hr->hr_seq);
1982195618Srpaulo			/*
1983195618Srpaulo			 * Try to discover the path for this node.
1984230409Sadrian			 * Group addressed PREQ Case A
1985195618Srpaulo			 */
1986234884Smonthadar			preq.preq_flags = 0;
1987195618Srpaulo			preq.preq_hopcount = 0;
1988195618Srpaulo			preq.preq_ttl = ms->ms_ttl;
1989195908Srpaulo			preq.preq_id = ++hs->hs_preqid;
1990195618Srpaulo			IEEE80211_ADDR_COPY(preq.preq_origaddr, vap->iv_myaddr);
1991198230Srpaulo			preq.preq_origseq = hr->hr_origseq;
1992234877Smonthadar			preq.preq_lifetime =
1993234877Smonthadar			    ticks_to_msecs(ieee80211_hwmp_pathtimeout);
1994234885Smonthadar			preq.preq_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
1995195618Srpaulo			preq.preq_tcount = 1;
1996195618Srpaulo			IEEE80211_ADDR_COPY(PREQ_TADDR(0), dest);
1997195618Srpaulo			PREQ_TFLAGS(0) = 0;
1998195618Srpaulo			if (ieee80211_hwmp_targetonly)
1999195618Srpaulo				PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_TO;
2000195618Srpaulo			PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_USN;
2001234885Smonthadar			PREQ_TSEQ(0) = 0; /* RESERVED when USN flag is set */
2002195618Srpaulo			/* XXX check return value */
2003246512Smonthadar			hwmp_send_preq(vap, broadcastaddr, &preq,
2004246512Smonthadar			    &hr->hr_lastpreq, &ieee80211_hwmp_preqminint);
2005234894Smonthadar			callout_reset(&rt->rt_discovery,
2006234894Smonthadar			    ieee80211_hwmp_net_diameter_traversaltime * 2,
2007234894Smonthadar			    hwmp_rediscover_cb, rt);
2008195618Srpaulo		}
2009195784Srpaulo		if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID)
2010195618Srpaulo			ni = ieee80211_find_txnode(vap, rt->rt_nexthop);
2011195618Srpaulo	} else {
2012195618Srpaulo		ni = ieee80211_find_txnode(vap, dest);
2013195784Srpaulo		/* NB: if null then we leak mbuf */
2014195784Srpaulo		KASSERT(ni != NULL, ("leak mcast frame"));
2015195618Srpaulo		return ni;
2016195618Srpaulo	}
2017195618Srpaulodone:
2018195618Srpaulo	if (ni == NULL && m != NULL) {
2019195618Srpaulo		if (sendpreq) {
2020195618Srpaulo			struct ieee80211com *ic = vap->iv_ic;
2021195618Srpaulo			/*
2022195618Srpaulo			 * Queue packet for transmit when path discovery
2023195618Srpaulo			 * completes.  If discovery never completes the
2024195618Srpaulo			 * frame will be flushed by way of the aging timer.
2025195618Srpaulo			 */
2026195784Srpaulo			IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP, dest,
2027195784Srpaulo			    "%s", "queue frame until path found");
2028195618Srpaulo			m->m_pkthdr.rcvif = (void *)(uintptr_t)
2029195618Srpaulo			    ieee80211_mac_hash(ic, dest);
2030195618Srpaulo			/* XXX age chosen randomly */
2031195618Srpaulo			ieee80211_ageq_append(&ic->ic_stageq, m,
2032195618Srpaulo			    IEEE80211_INACT_WAIT);
2033195784Srpaulo		} else {
2034195784Srpaulo			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_HWMP,
2035195784Srpaulo			    dest, NULL, "%s", "no valid path to this node");
2036195618Srpaulo			m_freem(m);
2037195784Srpaulo		}
2038195618Srpaulo	}
2039195618Srpaulo	return ni;
2040195618Srpaulo}
2041195618Srpaulo#undef	PREQ_TFLAGS
2042195618Srpaulo#undef	PREQ_TADDR
2043195618Srpaulo#undef	PREQ_TSEQ
2044195618Srpaulo
2045195618Srpaulostatic int
2046195618Srpaulohwmp_ioctl_get80211(struct ieee80211vap *vap, struct ieee80211req *ireq)
2047195618Srpaulo{
2048195618Srpaulo	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
2049195618Srpaulo	int error;
2050230409Sadrian
2051195618Srpaulo	if (vap->iv_opmode != IEEE80211_M_MBSS)
2052195618Srpaulo		return ENOSYS;
2053195618Srpaulo	error = 0;
2054195618Srpaulo	switch (ireq->i_type) {
2055195618Srpaulo	case IEEE80211_IOC_HWMP_ROOTMODE:
2056195618Srpaulo		ireq->i_val = hs->hs_rootmode;
2057195618Srpaulo		break;
2058195618Srpaulo	case IEEE80211_IOC_HWMP_MAXHOPS:
2059195618Srpaulo		ireq->i_val = hs->hs_maxhops;
2060195618Srpaulo		break;
2061195618Srpaulo	default:
2062195618Srpaulo		return ENOSYS;
2063195618Srpaulo	}
2064195618Srpaulo	return error;
2065195618Srpaulo}
2066195618SrpauloIEEE80211_IOCTL_GET(hwmp, hwmp_ioctl_get80211);
2067195618Srpaulo
2068195618Srpaulostatic int
2069195618Srpaulohwmp_ioctl_set80211(struct ieee80211vap *vap, struct ieee80211req *ireq)
2070195618Srpaulo{
2071195618Srpaulo	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
2072195618Srpaulo	int error;
2073195618Srpaulo
2074195618Srpaulo	if (vap->iv_opmode != IEEE80211_M_MBSS)
2075195618Srpaulo		return ENOSYS;
2076195618Srpaulo	error = 0;
2077195618Srpaulo	switch (ireq->i_type) {
2078195618Srpaulo	case IEEE80211_IOC_HWMP_ROOTMODE:
2079195618Srpaulo		if (ireq->i_val < 0 || ireq->i_val > 3)
2080195618Srpaulo			return EINVAL;
2081195618Srpaulo		hs->hs_rootmode = ireq->i_val;
2082195618Srpaulo		hwmp_rootmode_setup(vap);
2083195618Srpaulo		break;
2084195618Srpaulo	case IEEE80211_IOC_HWMP_MAXHOPS:
2085195618Srpaulo		if (ireq->i_val <= 0 || ireq->i_val > 255)
2086195618Srpaulo			return EINVAL;
2087195618Srpaulo		hs->hs_maxhops = ireq->i_val;
2088195618Srpaulo		break;
2089195618Srpaulo	default:
2090195618Srpaulo		return ENOSYS;
2091195618Srpaulo	}
2092195618Srpaulo	return error;
2093195618Srpaulo}
2094195618SrpauloIEEE80211_IOCTL_SET(hwmp, hwmp_ioctl_set80211);
2095