132176cfdSRui Paulo /*-
232176cfdSRui Paulo  * Copyright (c) 2009 The FreeBSD Foundation
332176cfdSRui Paulo  * All rights reserved.
432176cfdSRui Paulo  *
532176cfdSRui Paulo  * This software was developed by Rui Paulo under sponsorship from the
632176cfdSRui Paulo  * FreeBSD Foundation.
732176cfdSRui Paulo  *
832176cfdSRui Paulo  * Redistribution and use in source and binary forms, with or without
932176cfdSRui Paulo  * modification, are permitted provided that the following conditions
1032176cfdSRui Paulo  * are met:
1132176cfdSRui Paulo  * 1. Redistributions of source code must retain the above copyright
1232176cfdSRui Paulo  *    notice, this list of conditions and the following disclaimer.
1332176cfdSRui Paulo  * 2. Redistributions in binary form must reproduce the above copyright
1432176cfdSRui Paulo  *    notice, this list of conditions and the following disclaimer in the
1532176cfdSRui Paulo  *    documentation and/or other materials provided with the distribution.
1632176cfdSRui Paulo  *
1732176cfdSRui Paulo  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1832176cfdSRui Paulo  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1932176cfdSRui Paulo  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2032176cfdSRui Paulo  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2132176cfdSRui Paulo  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2232176cfdSRui Paulo  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2332176cfdSRui Paulo  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2432176cfdSRui Paulo  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2532176cfdSRui Paulo  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2632176cfdSRui Paulo  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2732176cfdSRui Paulo  * SUCH DAMAGE.
2832176cfdSRui Paulo  */
29085ff963SMatthew Dillon #include <sys/cdefs.h>
30085ff963SMatthew Dillon #ifdef __FreeBSD__
31085ff963SMatthew Dillon __FBSDID("$FreeBSD$");
32085ff963SMatthew Dillon #endif
3332176cfdSRui Paulo 
3432176cfdSRui Paulo /*
3532176cfdSRui Paulo  * IEEE 802.11s Hybrid Wireless Mesh Protocol, HWMP.
3632176cfdSRui Paulo  *
3732176cfdSRui Paulo  * Based on March 2009, D3.0 802.11s draft spec.
3832176cfdSRui Paulo  */
3932176cfdSRui Paulo #include "opt_inet.h"
4032176cfdSRui Paulo #include "opt_wlan.h"
4132176cfdSRui Paulo 
4232176cfdSRui Paulo #include <sys/param.h>
4332176cfdSRui Paulo #include <sys/systm.h>
4432176cfdSRui Paulo #include <sys/mbuf.h>
4532176cfdSRui Paulo #include <sys/malloc.h>
4632176cfdSRui Paulo #include <sys/kernel.h>
4732176cfdSRui Paulo 
4832176cfdSRui Paulo #include <sys/socket.h>
4932176cfdSRui Paulo #include <sys/sockio.h>
5032176cfdSRui Paulo #include <sys/endian.h>
5132176cfdSRui Paulo #include <sys/errno.h>
5232176cfdSRui Paulo #include <sys/proc.h>
5332176cfdSRui Paulo #include <sys/sysctl.h>
5432176cfdSRui Paulo 
5532176cfdSRui Paulo #include <net/if.h>
56*bff82488SAaron LI #include <net/if_var.h>
5732176cfdSRui Paulo #include <net/if_media.h>
5832176cfdSRui Paulo #include <net/if_llc.h>
5932176cfdSRui Paulo #include <net/ethernet.h>
6032176cfdSRui Paulo 
6132176cfdSRui Paulo #include <net/bpf.h>
6232176cfdSRui Paulo 
6332176cfdSRui Paulo #include <netproto/802_11/ieee80211_var.h>
6432176cfdSRui Paulo #include <netproto/802_11/ieee80211_action.h>
6532176cfdSRui Paulo #include <netproto/802_11/ieee80211_input.h>
6632176cfdSRui Paulo #include <netproto/802_11/ieee80211_mesh.h>
6732176cfdSRui Paulo 
6832176cfdSRui Paulo static void	hwmp_vattach(struct ieee80211vap *);
6932176cfdSRui Paulo static void	hwmp_vdetach(struct ieee80211vap *);
7032176cfdSRui Paulo static int	hwmp_newstate(struct ieee80211vap *,
7132176cfdSRui Paulo 		    enum ieee80211_state, int);
72085ff963SMatthew Dillon static int	hwmp_send_action(struct ieee80211vap *,
7332176cfdSRui Paulo 		    const uint8_t [IEEE80211_ADDR_LEN],
7432176cfdSRui Paulo 		    uint8_t *, size_t);
7532176cfdSRui Paulo static uint8_t * hwmp_add_meshpreq(uint8_t *,
7632176cfdSRui Paulo 		    const struct ieee80211_meshpreq_ie *);
7732176cfdSRui Paulo static uint8_t * hwmp_add_meshprep(uint8_t *,
7832176cfdSRui Paulo 		    const struct ieee80211_meshprep_ie *);
7932176cfdSRui Paulo static uint8_t * hwmp_add_meshperr(uint8_t *,
8032176cfdSRui Paulo 		    const struct ieee80211_meshperr_ie *);
8132176cfdSRui Paulo static uint8_t * hwmp_add_meshrann(uint8_t *,
8232176cfdSRui Paulo 		    const struct ieee80211_meshrann_ie *);
8332176cfdSRui Paulo static void	hwmp_rootmode_setup(struct ieee80211vap *);
84085ff963SMatthew Dillon static void	hwmp_rootmode_cb(void *);
85085ff963SMatthew Dillon static void	hwmp_rootmode_rann_cb(void *);
8632176cfdSRui Paulo static void	hwmp_recv_preq(struct ieee80211vap *, struct ieee80211_node *,
8732176cfdSRui Paulo 		    const struct ieee80211_frame *,
8832176cfdSRui Paulo 		    const struct ieee80211_meshpreq_ie *);
89085ff963SMatthew Dillon static int	hwmp_send_preq(struct ieee80211vap *,
9032176cfdSRui Paulo 		    const uint8_t [IEEE80211_ADDR_LEN],
91085ff963SMatthew Dillon 		    struct ieee80211_meshpreq_ie *,
92085ff963SMatthew Dillon 		    struct timeval *, struct timeval *);
9332176cfdSRui Paulo static void	hwmp_recv_prep(struct ieee80211vap *, struct ieee80211_node *,
9432176cfdSRui Paulo 		    const struct ieee80211_frame *,
9532176cfdSRui Paulo 		    const struct ieee80211_meshprep_ie *);
96085ff963SMatthew Dillon static int	hwmp_send_prep(struct ieee80211vap *,
9732176cfdSRui Paulo 		    const uint8_t [IEEE80211_ADDR_LEN],
9832176cfdSRui Paulo 		    struct ieee80211_meshprep_ie *);
9932176cfdSRui Paulo static void	hwmp_recv_perr(struct ieee80211vap *, struct ieee80211_node *,
10032176cfdSRui Paulo 		    const struct ieee80211_frame *,
10132176cfdSRui Paulo 		    const struct ieee80211_meshperr_ie *);
102085ff963SMatthew Dillon static int	hwmp_send_perr(struct ieee80211vap *,
10332176cfdSRui Paulo 		    const uint8_t [IEEE80211_ADDR_LEN],
10432176cfdSRui Paulo 		    struct ieee80211_meshperr_ie *);
105085ff963SMatthew Dillon static void	hwmp_senderror(struct ieee80211vap *,
106085ff963SMatthew Dillon 		    const uint8_t [IEEE80211_ADDR_LEN],
107085ff963SMatthew Dillon 		    struct ieee80211_mesh_route *, int);
10832176cfdSRui Paulo static void	hwmp_recv_rann(struct ieee80211vap *, struct ieee80211_node *,
10932176cfdSRui Paulo 		   const struct ieee80211_frame *,
11032176cfdSRui Paulo 		   const struct ieee80211_meshrann_ie *);
111085ff963SMatthew Dillon static int	hwmp_send_rann(struct ieee80211vap *,
11232176cfdSRui Paulo 		    const uint8_t [IEEE80211_ADDR_LEN],
11332176cfdSRui Paulo 		    struct ieee80211_meshrann_ie *);
11432176cfdSRui Paulo static struct ieee80211_node *
11532176cfdSRui Paulo 		hwmp_discover(struct ieee80211vap *,
11632176cfdSRui Paulo 		    const uint8_t [IEEE80211_ADDR_LEN], struct mbuf *);
11732176cfdSRui Paulo static void	hwmp_peerdown(struct ieee80211_node *);
11832176cfdSRui Paulo 
11932176cfdSRui Paulo static struct timeval ieee80211_hwmp_preqminint = { 0, 100000 };
12032176cfdSRui Paulo static struct timeval ieee80211_hwmp_perrminint = { 0, 100000 };
12132176cfdSRui Paulo 
12232176cfdSRui Paulo 
12332176cfdSRui Paulo /* NB: the Target Address set in a Proactive PREQ is the broadcast address. */
12432176cfdSRui Paulo static const uint8_t	broadcastaddr[IEEE80211_ADDR_LEN] =
12532176cfdSRui Paulo 	{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
12632176cfdSRui Paulo 
12732176cfdSRui Paulo typedef uint32_t ieee80211_hwmp_seq;
12832176cfdSRui Paulo #define	HWMP_SEQ_LT(a, b)	((int32_t)((a)-(b)) < 0)
12932176cfdSRui Paulo #define	HWMP_SEQ_LEQ(a, b)	((int32_t)((a)-(b)) <= 0)
130085ff963SMatthew Dillon #define	HWMP_SEQ_EQ(a, b)	((int32_t)((a)-(b)) == 0)
13132176cfdSRui Paulo #define	HWMP_SEQ_GT(a, b)	((int32_t)((a)-(b)) > 0)
13232176cfdSRui Paulo #define	HWMP_SEQ_GEQ(a, b)	((int32_t)((a)-(b)) >= 0)
13332176cfdSRui Paulo 
134085ff963SMatthew Dillon #define HWMP_SEQ_MAX(a, b)	(a > b ? a : b)
135085ff963SMatthew Dillon 
13632176cfdSRui Paulo /*
13732176cfdSRui Paulo  * Private extension of ieee80211_mesh_route.
13832176cfdSRui Paulo  */
13932176cfdSRui Paulo struct ieee80211_hwmp_route {
14032176cfdSRui Paulo 	ieee80211_hwmp_seq	hr_seq;		/* last HWMP seq seen from dst*/
14132176cfdSRui Paulo 	ieee80211_hwmp_seq	hr_preqid;	/* last PREQ ID seen from dst */
14232176cfdSRui Paulo 	ieee80211_hwmp_seq	hr_origseq;	/* seq. no. on our latest PREQ*/
143085ff963SMatthew Dillon 	struct timeval		hr_lastpreq;	/* last time we sent a PREQ */
144085ff963SMatthew Dillon 	struct timeval		hr_lastrootconf; /* last sent PREQ root conf */
145085ff963SMatthew Dillon 	int			hr_preqretries;	/* number of discoveries */
146085ff963SMatthew Dillon 	int			hr_lastdiscovery; /* last discovery in ticks */
14732176cfdSRui Paulo };
14832176cfdSRui Paulo struct ieee80211_hwmp_state {
14932176cfdSRui Paulo 	ieee80211_hwmp_seq	hs_seq;		/* next seq to be used */
15032176cfdSRui Paulo 	ieee80211_hwmp_seq	hs_preqid;	/* next PREQ ID to be used */
15132176cfdSRui Paulo 	int			hs_rootmode;	/* proactive HWMP */
152085ff963SMatthew Dillon 	struct timeval		hs_lastperr;	/* last time we sent a PERR */
15332176cfdSRui Paulo 	struct callout		hs_roottimer;
15432176cfdSRui Paulo 	uint8_t			hs_maxhops;	/* max hop count */
15532176cfdSRui Paulo };
15632176cfdSRui Paulo 
157085ff963SMatthew Dillon static SYSCTL_NODE(_net_wlan, OID_AUTO, hwmp, CTLFLAG_RD, 0,
15832176cfdSRui Paulo     "IEEE 802.11s HWMP parameters");
15932176cfdSRui Paulo static int	ieee80211_hwmp_targetonly = 0;
160085ff963SMatthew Dillon SYSCTL_INT(_net_wlan_hwmp, OID_AUTO, targetonly, CTLFLAG_RW,
16132176cfdSRui Paulo     &ieee80211_hwmp_targetonly, 0, "Set TO bit on generated PREQs");
16232176cfdSRui Paulo static int	ieee80211_hwmp_pathtimeout = -1;
16332176cfdSRui Paulo SYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, pathlifetime, CTLTYPE_INT | CTLFLAG_RW,
16432176cfdSRui Paulo     &ieee80211_hwmp_pathtimeout, 0, ieee80211_sysctl_msecs_ticks, "I",
16532176cfdSRui Paulo     "path entry lifetime (ms)");
166085ff963SMatthew Dillon static int	ieee80211_hwmp_maxpreq_retries = -1;
167085ff963SMatthew Dillon SYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, maxpreq_retries, CTLTYPE_INT | CTLFLAG_RW,
168085ff963SMatthew Dillon     &ieee80211_hwmp_maxpreq_retries, 0, ieee80211_sysctl_msecs_ticks, "I",
169085ff963SMatthew Dillon     "maximum number of preq retries");
170085ff963SMatthew Dillon static int	ieee80211_hwmp_net_diameter_traversaltime = -1;
171085ff963SMatthew Dillon SYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, net_diameter_traversal_time,
172085ff963SMatthew Dillon     CTLTYPE_INT | CTLFLAG_RW, &ieee80211_hwmp_net_diameter_traversaltime, 0,
173085ff963SMatthew Dillon     ieee80211_sysctl_msecs_ticks, "I",
174085ff963SMatthew Dillon     "estimate travelse time across the MBSS (ms)");
17532176cfdSRui Paulo static int	ieee80211_hwmp_roottimeout = -1;
17632176cfdSRui Paulo SYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, roottimeout, CTLTYPE_INT | CTLFLAG_RW,
17732176cfdSRui Paulo     &ieee80211_hwmp_roottimeout, 0, ieee80211_sysctl_msecs_ticks, "I",
17832176cfdSRui Paulo     "root PREQ timeout (ms)");
17932176cfdSRui Paulo static int	ieee80211_hwmp_rootint = -1;
18032176cfdSRui Paulo SYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, rootint, CTLTYPE_INT | CTLFLAG_RW,
18132176cfdSRui Paulo     &ieee80211_hwmp_rootint, 0, ieee80211_sysctl_msecs_ticks, "I",
18232176cfdSRui Paulo     "root interval (ms)");
18332176cfdSRui Paulo static int	ieee80211_hwmp_rannint = -1;
18432176cfdSRui Paulo SYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, rannint, CTLTYPE_INT | CTLFLAG_RW,
18532176cfdSRui Paulo     &ieee80211_hwmp_rannint, 0, ieee80211_sysctl_msecs_ticks, "I",
18632176cfdSRui Paulo     "root announcement interval (ms)");
187085ff963SMatthew Dillon static struct timeval ieee80211_hwmp_rootconfint = { 0, 0 };
188085ff963SMatthew Dillon static int	ieee80211_hwmp_rootconfint_internal = -1;
189085ff963SMatthew Dillon SYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, rootconfint, CTLTYPE_INT | CTLFLAG_RD,
190085ff963SMatthew Dillon     &ieee80211_hwmp_rootconfint_internal, 0, ieee80211_sysctl_msecs_ticks, "I",
191085ff963SMatthew Dillon     "root confirmation interval (ms) (read-only)");
19232176cfdSRui Paulo 
19332176cfdSRui Paulo #define	IEEE80211_HWMP_DEFAULT_MAXHOPS	31
19432176cfdSRui Paulo 
19532176cfdSRui Paulo static	ieee80211_recv_action_func hwmp_recv_action_meshpath;
19632176cfdSRui Paulo 
19732176cfdSRui Paulo static struct ieee80211_mesh_proto_path mesh_proto_hwmp = {
19832176cfdSRui Paulo 	.mpp_descr	= "HWMP",
19932176cfdSRui Paulo 	.mpp_ie		= IEEE80211_MESHCONF_PATH_HWMP,
20032176cfdSRui Paulo 	.mpp_discover	= hwmp_discover,
20132176cfdSRui Paulo 	.mpp_peerdown	= hwmp_peerdown,
202085ff963SMatthew Dillon 	.mpp_senderror	= hwmp_senderror,
20332176cfdSRui Paulo 	.mpp_vattach	= hwmp_vattach,
20432176cfdSRui Paulo 	.mpp_vdetach	= hwmp_vdetach,
20532176cfdSRui Paulo 	.mpp_newstate	= hwmp_newstate,
20632176cfdSRui Paulo 	.mpp_privlen	= sizeof(struct ieee80211_hwmp_route),
20732176cfdSRui Paulo };
20832176cfdSRui Paulo SYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, inact, CTLTYPE_INT | CTLFLAG_RW,
20932176cfdSRui Paulo 	&mesh_proto_hwmp.mpp_inact, 0, ieee80211_sysctl_msecs_ticks, "I",
21032176cfdSRui Paulo 	"mesh route inactivity timeout (ms)");
21132176cfdSRui Paulo 
21232176cfdSRui Paulo 
21332176cfdSRui Paulo static void
ieee80211_hwmp_init(void)21432176cfdSRui Paulo ieee80211_hwmp_init(void)
21532176cfdSRui Paulo {
216085ff963SMatthew Dillon 	/* Default values as per amendment */
21732176cfdSRui Paulo 	ieee80211_hwmp_pathtimeout = msecs_to_ticks(5*1000);
21832176cfdSRui Paulo 	ieee80211_hwmp_roottimeout = msecs_to_ticks(5*1000);
21932176cfdSRui Paulo 	ieee80211_hwmp_rootint = msecs_to_ticks(2*1000);
22032176cfdSRui Paulo 	ieee80211_hwmp_rannint = msecs_to_ticks(1*1000);
221085ff963SMatthew Dillon 	ieee80211_hwmp_rootconfint_internal = msecs_to_ticks(2*1000);
222085ff963SMatthew Dillon 	ieee80211_hwmp_maxpreq_retries = 3;
223085ff963SMatthew Dillon 	/*
224085ff963SMatthew Dillon 	 * (TU): A measurement of time equal to 1024 μs,
225085ff963SMatthew Dillon 	 * 500 TU is 512 ms.
226085ff963SMatthew Dillon 	 */
227085ff963SMatthew Dillon 	ieee80211_hwmp_net_diameter_traversaltime = msecs_to_ticks(512);
228085ff963SMatthew Dillon 
229085ff963SMatthew Dillon 	/*
230085ff963SMatthew Dillon 	 * NB: I dont know how to make SYSCTL_PROC that calls ms to ticks
231085ff963SMatthew Dillon 	 * and return a struct timeval...
232085ff963SMatthew Dillon 	 */
233085ff963SMatthew Dillon 	ieee80211_hwmp_rootconfint.tv_usec =
234085ff963SMatthew Dillon 	    ieee80211_hwmp_rootconfint_internal * 1000;
23532176cfdSRui Paulo 
23632176cfdSRui Paulo 	/*
23732176cfdSRui Paulo 	 * Register action frame handler.
23832176cfdSRui Paulo 	 */
239085ff963SMatthew Dillon 	ieee80211_recv_action_register(IEEE80211_ACTION_CAT_MESH,
240085ff963SMatthew Dillon 	    IEEE80211_ACTION_MESH_HWMP, hwmp_recv_action_meshpath);
24132176cfdSRui Paulo 
24232176cfdSRui Paulo 	/* NB: default is 5 secs per spec */
24332176cfdSRui Paulo 	mesh_proto_hwmp.mpp_inact = msecs_to_ticks(5*1000);
24432176cfdSRui Paulo 
24532176cfdSRui Paulo 	/*
24632176cfdSRui Paulo 	 * Register HWMP.
24732176cfdSRui Paulo 	 */
24832176cfdSRui Paulo 	ieee80211_mesh_register_proto_path(&mesh_proto_hwmp);
24932176cfdSRui Paulo }
25032176cfdSRui Paulo SYSINIT(wlan_hwmp, SI_SUB_DRIVERS, SI_ORDER_SECOND, ieee80211_hwmp_init, NULL);
25132176cfdSRui Paulo 
252822aeeabSSascha Wildner static void
hwmp_vattach(struct ieee80211vap * vap)25332176cfdSRui Paulo hwmp_vattach(struct ieee80211vap *vap)
25432176cfdSRui Paulo {
25532176cfdSRui Paulo 	struct ieee80211_hwmp_state *hs;
25632176cfdSRui Paulo 
25732176cfdSRui Paulo 	KASSERT(vap->iv_opmode == IEEE80211_M_MBSS,
25832176cfdSRui Paulo 	    ("not a mesh vap, opmode %d", vap->iv_opmode));
25932176cfdSRui Paulo 
2604f655ef5SMatthew Dillon #if defined(__DragonFly__)
26132176cfdSRui Paulo 	hs = kmalloc(sizeof(struct ieee80211_hwmp_state), M_80211_VAP,
262fcaa651dSRui Paulo 		M_INTWAIT | M_ZERO);
2634f655ef5SMatthew Dillon #else
2644f655ef5SMatthew Dillon 	hs = IEEE80211_MALLOC(sizeof(struct ieee80211_hwmp_state), M_80211_VAP,
2654f655ef5SMatthew Dillon 		IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
2664f655ef5SMatthew Dillon #endif
267085ff963SMatthew Dillon 	if (hs == NULL) {
268085ff963SMatthew Dillon 		kprintf("%s: couldn't alloc HWMP state\n", __func__);
269085ff963SMatthew Dillon 		return;
270085ff963SMatthew Dillon 	}
27132176cfdSRui Paulo 	hs->hs_maxhops = IEEE80211_HWMP_DEFAULT_MAXHOPS;
2724f655ef5SMatthew Dillon #if defined(__DragonFly__)
27334a60cf6SRui Paulo 	callout_init_mp(&hs->hs_roottimer);
2744f655ef5SMatthew Dillon #else
2754f655ef5SMatthew Dillon 	callout_init(&hs->hs_roottimer, 1);
2764f655ef5SMatthew Dillon #endif
27732176cfdSRui Paulo 	vap->iv_hwmp = hs;
27832176cfdSRui Paulo }
27932176cfdSRui Paulo 
280822aeeabSSascha Wildner static void
hwmp_vdetach(struct ieee80211vap * vap)28132176cfdSRui Paulo hwmp_vdetach(struct ieee80211vap *vap)
28232176cfdSRui Paulo {
28332176cfdSRui Paulo 	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
28432176cfdSRui Paulo 
285085ff963SMatthew Dillon 	callout_drain(&hs->hs_roottimer);
2864f655ef5SMatthew Dillon 	IEEE80211_FREE(vap->iv_hwmp, M_80211_VAP);
28732176cfdSRui Paulo 	vap->iv_hwmp = NULL;
28832176cfdSRui Paulo }
28932176cfdSRui Paulo 
290822aeeabSSascha Wildner static int
hwmp_newstate(struct ieee80211vap * vap,enum ieee80211_state ostate,int arg)29132176cfdSRui Paulo hwmp_newstate(struct ieee80211vap *vap, enum ieee80211_state ostate, int arg)
29232176cfdSRui Paulo {
29332176cfdSRui Paulo 	enum ieee80211_state nstate = vap->iv_state;
29432176cfdSRui Paulo 	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
29532176cfdSRui Paulo 
29632176cfdSRui Paulo 	IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n",
29732176cfdSRui Paulo 	    __func__, ieee80211_state_name[ostate],
29832176cfdSRui Paulo 	    ieee80211_state_name[nstate], arg);
29932176cfdSRui Paulo 
30032176cfdSRui Paulo 	if (nstate != IEEE80211_S_RUN && ostate == IEEE80211_S_RUN)
301085ff963SMatthew Dillon 		callout_drain(&hs->hs_roottimer);
30232176cfdSRui Paulo 	if (nstate == IEEE80211_S_RUN)
30332176cfdSRui Paulo 		hwmp_rootmode_setup(vap);
30432176cfdSRui Paulo 	return 0;
30532176cfdSRui Paulo }
30632176cfdSRui Paulo 
307085ff963SMatthew Dillon /*
308085ff963SMatthew Dillon  * Verify the length of an HWMP PREQ and return the number
309085ff963SMatthew Dillon  * of destinations >= 1, if verification fails -1 is returned.
310085ff963SMatthew Dillon  */
311085ff963SMatthew Dillon static int
verify_mesh_preq_len(struct ieee80211vap * vap,const struct ieee80211_frame * wh,const uint8_t * iefrm)312085ff963SMatthew Dillon verify_mesh_preq_len(struct ieee80211vap *vap,
313085ff963SMatthew Dillon     const struct ieee80211_frame *wh, const uint8_t *iefrm)
314085ff963SMatthew Dillon {
315085ff963SMatthew Dillon 	int alloc_sz = -1;
316085ff963SMatthew Dillon 	int ndest = -1;
317085ff963SMatthew Dillon 	if (iefrm[2] & IEEE80211_MESHPREQ_FLAGS_AE) {
318085ff963SMatthew Dillon 		/* Originator External Address  present */
319085ff963SMatthew Dillon 		alloc_sz =  IEEE80211_MESHPREQ_BASE_SZ_AE;
320085ff963SMatthew Dillon 		ndest = iefrm[IEEE80211_MESHPREQ_TCNT_OFFSET_AE];
321085ff963SMatthew Dillon 	} else {
322085ff963SMatthew Dillon 		/* w/o Originator External Address */
323085ff963SMatthew Dillon 		alloc_sz =  IEEE80211_MESHPREQ_BASE_SZ;
324085ff963SMatthew Dillon 		ndest = iefrm[IEEE80211_MESHPREQ_TCNT_OFFSET];
325085ff963SMatthew Dillon 	}
326085ff963SMatthew Dillon 	alloc_sz += ndest * IEEE80211_MESHPREQ_TRGT_SZ;
327085ff963SMatthew Dillon 
328085ff963SMatthew Dillon 	if(iefrm[1] != (alloc_sz)) {
329085ff963SMatthew Dillon 		IEEE80211_DISCARD(vap,
330085ff963SMatthew Dillon 		    IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
331085ff963SMatthew Dillon 		    wh, NULL, "PREQ (AE=%s) with wrong len",
332085ff963SMatthew Dillon 		    iefrm[2] & IEEE80211_MESHPREQ_FLAGS_AE ? "1" : "0");
333085ff963SMatthew Dillon 		return (-1);
334085ff963SMatthew Dillon 	}
335085ff963SMatthew Dillon 	return ndest;
336085ff963SMatthew Dillon }
337085ff963SMatthew Dillon 
338085ff963SMatthew Dillon /*
339085ff963SMatthew Dillon  * Verify the length of an HWMP PREP and returns 1 on success,
340085ff963SMatthew Dillon  * otherwise -1.
341085ff963SMatthew Dillon  */
342085ff963SMatthew Dillon static int
verify_mesh_prep_len(struct ieee80211vap * vap,const struct ieee80211_frame * wh,const uint8_t * iefrm)343085ff963SMatthew Dillon verify_mesh_prep_len(struct ieee80211vap *vap,
344085ff963SMatthew Dillon     const struct ieee80211_frame *wh, const uint8_t *iefrm)
345085ff963SMatthew Dillon {
346085ff963SMatthew Dillon 	int alloc_sz = -1;
347085ff963SMatthew Dillon 	if (iefrm[2] & IEEE80211_MESHPREP_FLAGS_AE) {
348085ff963SMatthew Dillon 		if (iefrm[1] == IEEE80211_MESHPREP_BASE_SZ_AE)
349085ff963SMatthew Dillon 			alloc_sz = IEEE80211_MESHPREP_BASE_SZ_AE;
350085ff963SMatthew Dillon 	} else if (iefrm[1] == IEEE80211_MESHPREP_BASE_SZ)
351085ff963SMatthew Dillon 		alloc_sz = IEEE80211_MESHPREP_BASE_SZ;
352085ff963SMatthew Dillon 	if(alloc_sz < 0) {
353085ff963SMatthew Dillon 		IEEE80211_DISCARD(vap,
354085ff963SMatthew Dillon 		    IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
355085ff963SMatthew Dillon 		    wh, NULL, "PREP (AE=%s) with wrong len",
356085ff963SMatthew Dillon 		    iefrm[2] & IEEE80211_MESHPREP_FLAGS_AE ? "1" : "0");
357085ff963SMatthew Dillon 		return (-1);
358085ff963SMatthew Dillon 	}
359085ff963SMatthew Dillon 	return (1);
360085ff963SMatthew Dillon }
361085ff963SMatthew Dillon 
362085ff963SMatthew Dillon /*
363085ff963SMatthew Dillon  * Verify the length of an HWMP PERR and return the number
364085ff963SMatthew Dillon  * of destinations >= 1, if verification fails -1 is returned.
365085ff963SMatthew Dillon  */
366085ff963SMatthew Dillon static int
verify_mesh_perr_len(struct ieee80211vap * vap,const struct ieee80211_frame * wh,const uint8_t * iefrm)367085ff963SMatthew Dillon verify_mesh_perr_len(struct ieee80211vap *vap,
368085ff963SMatthew Dillon     const struct ieee80211_frame *wh, const uint8_t *iefrm)
369085ff963SMatthew Dillon {
370085ff963SMatthew Dillon 	int alloc_sz = -1;
371085ff963SMatthew Dillon 	const uint8_t *iefrm_t = iefrm;
372085ff963SMatthew Dillon 	uint8_t ndest = iefrm_t[IEEE80211_MESHPERR_NDEST_OFFSET];
373085ff963SMatthew Dillon 	int i;
374085ff963SMatthew Dillon 
375085ff963SMatthew Dillon 	if(ndest > IEEE80211_MESHPERR_MAXDEST) {
376085ff963SMatthew Dillon 		IEEE80211_DISCARD(vap,
377085ff963SMatthew Dillon 		    IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
378085ff963SMatthew Dillon 		    wh, NULL, "PERR with wrong number of destionat (>19), %u",
379085ff963SMatthew Dillon 		    ndest);
380085ff963SMatthew Dillon 		return (-1);
381085ff963SMatthew Dillon 	}
382085ff963SMatthew Dillon 
383085ff963SMatthew Dillon 	iefrm_t += IEEE80211_MESHPERR_NDEST_OFFSET + 1; /* flag is next field */
384085ff963SMatthew Dillon 	/* We need to check each destionation flag to know size */
385085ff963SMatthew Dillon 	for(i = 0; i<ndest; i++) {
386085ff963SMatthew Dillon 		if ((*iefrm_t) & IEEE80211_MESHPERR_FLAGS_AE)
387085ff963SMatthew Dillon 			iefrm_t += IEEE80211_MESHPERR_DEST_SZ_AE;
388085ff963SMatthew Dillon 		else
389085ff963SMatthew Dillon 			iefrm_t += IEEE80211_MESHPERR_DEST_SZ;
390085ff963SMatthew Dillon 	}
391085ff963SMatthew Dillon 
392085ff963SMatthew Dillon 	alloc_sz = (iefrm_t - iefrm) - 2; /* action + code */
393085ff963SMatthew Dillon 	if(alloc_sz !=  iefrm[1]) {
394085ff963SMatthew Dillon 		IEEE80211_DISCARD(vap,
395085ff963SMatthew Dillon 		    IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
396085ff963SMatthew Dillon 		    wh, NULL, "%s", "PERR with wrong len");
397085ff963SMatthew Dillon 		return (-1);
398085ff963SMatthew Dillon 	}
399085ff963SMatthew Dillon 	return ndest;
400085ff963SMatthew Dillon }
401085ff963SMatthew Dillon 
40232176cfdSRui Paulo static int
hwmp_recv_action_meshpath(struct ieee80211_node * ni,const struct ieee80211_frame * wh,const uint8_t * frm,const uint8_t * efrm)40332176cfdSRui Paulo hwmp_recv_action_meshpath(struct ieee80211_node *ni,
40432176cfdSRui Paulo 	const struct ieee80211_frame *wh,
40532176cfdSRui Paulo 	const uint8_t *frm, const uint8_t *efrm)
40632176cfdSRui Paulo {
40732176cfdSRui Paulo 	struct ieee80211vap *vap = ni->ni_vap;
408085ff963SMatthew Dillon 	struct ieee80211_meshpreq_ie *preq;
409085ff963SMatthew Dillon 	struct ieee80211_meshprep_ie *prep;
410085ff963SMatthew Dillon 	struct ieee80211_meshperr_ie *perr;
41132176cfdSRui Paulo 	struct ieee80211_meshrann_ie rann;
41232176cfdSRui Paulo 	const uint8_t *iefrm = frm + 2; /* action + code */
413085ff963SMatthew Dillon 	const uint8_t *iefrm_t = iefrm; /* temporary pointer */
414085ff963SMatthew Dillon 	int ndest = -1;
41532176cfdSRui Paulo 	int found = 0;
41632176cfdSRui Paulo 
41732176cfdSRui Paulo 	while (efrm - iefrm > 1) {
41832176cfdSRui Paulo 		IEEE80211_VERIFY_LENGTH(efrm - iefrm, iefrm[1] + 2, return 0);
41932176cfdSRui Paulo 		switch (*iefrm) {
42032176cfdSRui Paulo 		case IEEE80211_ELEMID_MESHPREQ:
42132176cfdSRui Paulo 		{
422085ff963SMatthew Dillon 			int i = 0;
423085ff963SMatthew Dillon 
424085ff963SMatthew Dillon 			iefrm_t = iefrm;
425085ff963SMatthew Dillon 			ndest = verify_mesh_preq_len(vap, wh, iefrm_t);
426085ff963SMatthew Dillon 			if (ndest < 0) {
42732176cfdSRui Paulo 				vap->iv_stats.is_rx_mgtdiscard++;
42832176cfdSRui Paulo 				break;
42932176cfdSRui Paulo 			}
4304f655ef5SMatthew Dillon #if defined(__DragonFly__)
431085ff963SMatthew Dillon 			preq = kmalloc(sizeof(*preq) +
432085ff963SMatthew Dillon 			    (ndest - 1) * sizeof(*preq->preq_targets),
433085ff963SMatthew Dillon 			    M_80211_MESH_PREQ, M_INTWAIT | M_ZERO);
4344f655ef5SMatthew Dillon #else
4354f655ef5SMatthew Dillon 			preq = IEEE80211_MALLOC(sizeof(*preq) +
4364f655ef5SMatthew Dillon 			    (ndest - 1) * sizeof(*preq->preq_targets),
4374f655ef5SMatthew Dillon 			    M_80211_MESH_PREQ,
4384f655ef5SMatthew Dillon 			    IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
4394f655ef5SMatthew Dillon #endif
440085ff963SMatthew Dillon 			KASSERT(preq != NULL, ("preq == NULL"));
441085ff963SMatthew Dillon 
442085ff963SMatthew Dillon 			preq->preq_ie = *iefrm_t++;
443085ff963SMatthew Dillon 			preq->preq_len = *iefrm_t++;
444085ff963SMatthew Dillon 			preq->preq_flags = *iefrm_t++;
445085ff963SMatthew Dillon 			preq->preq_hopcount = *iefrm_t++;
446085ff963SMatthew Dillon 			preq->preq_ttl = *iefrm_t++;
4474f655ef5SMatthew Dillon 			preq->preq_id = le32dec(iefrm_t); iefrm_t += 4;
448085ff963SMatthew Dillon 			IEEE80211_ADDR_COPY(preq->preq_origaddr, iefrm_t);
449085ff963SMatthew Dillon 			iefrm_t += 6;
4504f655ef5SMatthew Dillon 			preq->preq_origseq = le32dec(iefrm_t); iefrm_t += 4;
451085ff963SMatthew Dillon 			/* NB: may have Originator Proxied Address */
452085ff963SMatthew Dillon 			if (preq->preq_flags & IEEE80211_MESHPREQ_FLAGS_AE)  {
453085ff963SMatthew Dillon 				IEEE80211_ADDR_COPY(
454085ff963SMatthew Dillon 				    preq->preq_orig_ext_addr, iefrm_t);
455085ff963SMatthew Dillon 				iefrm_t += 6;
456085ff963SMatthew Dillon 			}
4574f655ef5SMatthew Dillon 			preq->preq_lifetime = le32dec(iefrm_t); iefrm_t += 4;
4584f655ef5SMatthew Dillon 			preq->preq_metric = le32dec(iefrm_t); iefrm_t += 4;
459085ff963SMatthew Dillon 			preq->preq_tcount = *iefrm_t++;
460085ff963SMatthew Dillon 
461085ff963SMatthew Dillon 			for (i = 0; i < preq->preq_tcount; i++) {
462085ff963SMatthew Dillon 				preq->preq_targets[i].target_flags = *iefrm_t++;
463085ff963SMatthew Dillon 				IEEE80211_ADDR_COPY(
464085ff963SMatthew Dillon 				    preq->preq_targets[i].target_addr, iefrm_t);
465085ff963SMatthew Dillon 				iefrm_t += 6;
466085ff963SMatthew Dillon 				preq->preq_targets[i].target_seq =
4674f655ef5SMatthew Dillon 				    le32dec(iefrm_t);
468085ff963SMatthew Dillon 				iefrm_t += 4;
469085ff963SMatthew Dillon 			}
470085ff963SMatthew Dillon 
471085ff963SMatthew Dillon 			hwmp_recv_preq(vap, ni, wh, preq);
4724f655ef5SMatthew Dillon 			IEEE80211_FREE(preq, M_80211_MESH_PREQ);
47332176cfdSRui Paulo 			found++;
47432176cfdSRui Paulo 			break;
47532176cfdSRui Paulo 		}
47632176cfdSRui Paulo 		case IEEE80211_ELEMID_MESHPREP:
47732176cfdSRui Paulo 		{
478085ff963SMatthew Dillon 			iefrm_t = iefrm;
479085ff963SMatthew Dillon 			ndest = verify_mesh_prep_len(vap, wh, iefrm_t);
480085ff963SMatthew Dillon 			if (ndest < 0) {
48132176cfdSRui Paulo 				vap->iv_stats.is_rx_mgtdiscard++;
48232176cfdSRui Paulo 				break;
48332176cfdSRui Paulo 			}
4844f655ef5SMatthew Dillon #if defined(__DragonFly__)
485085ff963SMatthew Dillon 			prep = kmalloc(sizeof(*prep),
486085ff963SMatthew Dillon 			    M_80211_MESH_PREP, M_INTWAIT | M_ZERO);
4874f655ef5SMatthew Dillon #else
4884f655ef5SMatthew Dillon 			prep = IEEE80211_MALLOC(sizeof(*prep),
4894f655ef5SMatthew Dillon 			    M_80211_MESH_PREP,
4904f655ef5SMatthew Dillon 			    IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
4914f655ef5SMatthew Dillon #endif
492085ff963SMatthew Dillon 			KASSERT(prep != NULL, ("prep == NULL"));
493085ff963SMatthew Dillon 
494085ff963SMatthew Dillon 			prep->prep_ie = *iefrm_t++;
495085ff963SMatthew Dillon 			prep->prep_len = *iefrm_t++;
496085ff963SMatthew Dillon 			prep->prep_flags = *iefrm_t++;
497085ff963SMatthew Dillon 			prep->prep_hopcount = *iefrm_t++;
498085ff963SMatthew Dillon 			prep->prep_ttl = *iefrm_t++;
499085ff963SMatthew Dillon 			IEEE80211_ADDR_COPY(prep->prep_targetaddr, iefrm_t);
500085ff963SMatthew Dillon 			iefrm_t += 6;
5014f655ef5SMatthew Dillon 			prep->prep_targetseq = le32dec(iefrm_t); iefrm_t += 4;
502085ff963SMatthew Dillon 			/* NB: May have Target Proxied Address */
503085ff963SMatthew Dillon 			if (prep->prep_flags & IEEE80211_MESHPREP_FLAGS_AE)  {
504085ff963SMatthew Dillon 				IEEE80211_ADDR_COPY(
505085ff963SMatthew Dillon 				    prep->prep_target_ext_addr, iefrm_t);
506085ff963SMatthew Dillon 				iefrm_t += 6;
507085ff963SMatthew Dillon 			}
5084f655ef5SMatthew Dillon 			prep->prep_lifetime = le32dec(iefrm_t); iefrm_t += 4;
5094f655ef5SMatthew Dillon 			prep->prep_metric = le32dec(iefrm_t); iefrm_t += 4;
510085ff963SMatthew Dillon 			IEEE80211_ADDR_COPY(prep->prep_origaddr, iefrm_t);
511085ff963SMatthew Dillon 			iefrm_t += 6;
5124f655ef5SMatthew Dillon 			prep->prep_origseq = le32dec(iefrm_t); iefrm_t += 4;
513085ff963SMatthew Dillon 
514085ff963SMatthew Dillon 			hwmp_recv_prep(vap, ni, wh, prep);
5154f655ef5SMatthew Dillon 			IEEE80211_FREE(prep, M_80211_MESH_PREP);
51632176cfdSRui Paulo 			found++;
51732176cfdSRui Paulo 			break;
51832176cfdSRui Paulo 		}
51932176cfdSRui Paulo 		case IEEE80211_ELEMID_MESHPERR:
52032176cfdSRui Paulo 		{
521085ff963SMatthew Dillon 			int i = 0;
522085ff963SMatthew Dillon 
523085ff963SMatthew Dillon 			iefrm_t = iefrm;
524085ff963SMatthew Dillon 			ndest = verify_mesh_perr_len(vap, wh, iefrm_t);
525085ff963SMatthew Dillon 			if (ndest < 0) {
52632176cfdSRui Paulo 				vap->iv_stats.is_rx_mgtdiscard++;
52732176cfdSRui Paulo 				break;
52832176cfdSRui Paulo 			}
5294f655ef5SMatthew Dillon #if defined(__DragonFly__)
530085ff963SMatthew Dillon 			perr = kmalloc(sizeof(*perr) +
531085ff963SMatthew Dillon 			    (ndest - 1) * sizeof(*perr->perr_dests),
532085ff963SMatthew Dillon 			    M_80211_MESH_PERR, M_INTWAIT | M_ZERO);
5334f655ef5SMatthew Dillon #else
5344f655ef5SMatthew Dillon 			perr = IEEE80211_MALLOC(sizeof(*perr) +
5354f655ef5SMatthew Dillon 			    (ndest - 1) * sizeof(*perr->perr_dests),
5364f655ef5SMatthew Dillon 			    M_80211_MESH_PERR,
5374f655ef5SMatthew Dillon 			    IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
5384f655ef5SMatthew Dillon #endif
539085ff963SMatthew Dillon 			KASSERT(perr != NULL, ("perr == NULL"));
540085ff963SMatthew Dillon 
541085ff963SMatthew Dillon 			perr->perr_ie = *iefrm_t++;
542085ff963SMatthew Dillon 			perr->perr_len = *iefrm_t++;
543085ff963SMatthew Dillon 			perr->perr_ttl = *iefrm_t++;
544085ff963SMatthew Dillon 			perr->perr_ndests = *iefrm_t++;
545085ff963SMatthew Dillon 
546085ff963SMatthew Dillon 			for (i = 0; i<perr->perr_ndests; i++) {
547085ff963SMatthew Dillon 				perr->perr_dests[i].dest_flags = *iefrm_t++;
548085ff963SMatthew Dillon 				IEEE80211_ADDR_COPY(
549085ff963SMatthew Dillon 				    perr->perr_dests[i].dest_addr, iefrm_t);
550085ff963SMatthew Dillon 				iefrm_t += 6;
5514f655ef5SMatthew Dillon 				perr->perr_dests[i].dest_seq = le32dec(iefrm_t);
552085ff963SMatthew Dillon 				iefrm_t += 4;
553085ff963SMatthew Dillon 				/* NB: May have Target Proxied Address */
554085ff963SMatthew Dillon 				if (perr->perr_dests[i].dest_flags &
555085ff963SMatthew Dillon 				    IEEE80211_MESHPERR_FLAGS_AE) {
556085ff963SMatthew Dillon 					IEEE80211_ADDR_COPY(
557085ff963SMatthew Dillon 					    perr->perr_dests[i].dest_ext_addr,
558085ff963SMatthew Dillon 					    iefrm_t);
559085ff963SMatthew Dillon 					iefrm_t += 6;
560085ff963SMatthew Dillon 				}
561085ff963SMatthew Dillon 				perr->perr_dests[i].dest_rcode =
5624f655ef5SMatthew Dillon 				    le16dec(iefrm_t);
563085ff963SMatthew Dillon 				iefrm_t += 2;
564085ff963SMatthew Dillon 			}
565085ff963SMatthew Dillon 
566085ff963SMatthew Dillon 			hwmp_recv_perr(vap, ni, wh, perr);
5674f655ef5SMatthew Dillon 			IEEE80211_FREE(perr, M_80211_MESH_PERR);
56832176cfdSRui Paulo 			found++;
56932176cfdSRui Paulo 			break;
57032176cfdSRui Paulo 		}
57132176cfdSRui Paulo 		case IEEE80211_ELEMID_MESHRANN:
57232176cfdSRui Paulo 		{
57332176cfdSRui Paulo 			const struct ieee80211_meshrann_ie *mrann =
57432176cfdSRui Paulo 			    (const struct ieee80211_meshrann_ie *) iefrm;
57532176cfdSRui Paulo 			if (mrann->rann_len !=
57632176cfdSRui Paulo 			    sizeof(struct ieee80211_meshrann_ie) - 2) {
57732176cfdSRui Paulo 				IEEE80211_DISCARD(vap,
57832176cfdSRui Paulo 				    IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
57932176cfdSRui Paulo 				    wh, NULL, "%s", "RAN with wrong len");
58032176cfdSRui Paulo 				    vap->iv_stats.is_rx_mgtdiscard++;
58132176cfdSRui Paulo 				return 1;
58232176cfdSRui Paulo 			}
58332176cfdSRui Paulo 			memcpy(&rann, mrann, sizeof(rann));
5844f655ef5SMatthew Dillon 			rann.rann_seq = le32dec(&mrann->rann_seq);
5854f655ef5SMatthew Dillon 			rann.rann_interval = le32dec(&mrann->rann_interval);
5864f655ef5SMatthew Dillon 			rann.rann_metric = le32dec(&mrann->rann_metric);
58732176cfdSRui Paulo 			hwmp_recv_rann(vap, ni, wh, &rann);
58832176cfdSRui Paulo 			found++;
58932176cfdSRui Paulo 			break;
59032176cfdSRui Paulo 		}
59132176cfdSRui Paulo 		}
59232176cfdSRui Paulo 		iefrm += iefrm[1] + 2;
59332176cfdSRui Paulo 	}
59432176cfdSRui Paulo 	if (!found) {
59532176cfdSRui Paulo 		IEEE80211_DISCARD(vap,
59632176cfdSRui Paulo 		    IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
59732176cfdSRui Paulo 		    wh, NULL, "%s", "PATH SEL action without IE");
59832176cfdSRui Paulo 		vap->iv_stats.is_rx_mgtdiscard++;
59932176cfdSRui Paulo 	}
60032176cfdSRui Paulo 	return 0;
60132176cfdSRui Paulo }
60232176cfdSRui Paulo 
60332176cfdSRui Paulo static int
hwmp_send_action(struct ieee80211vap * vap,const uint8_t da[IEEE80211_ADDR_LEN],uint8_t * ie,size_t len)604085ff963SMatthew Dillon hwmp_send_action(struct ieee80211vap *vap,
60532176cfdSRui Paulo     const uint8_t da[IEEE80211_ADDR_LEN],
60632176cfdSRui Paulo     uint8_t *ie, size_t len)
60732176cfdSRui Paulo {
608085ff963SMatthew Dillon 	struct ieee80211_node *ni;
609085ff963SMatthew Dillon 	struct ieee80211com *ic;
61032176cfdSRui Paulo 	struct ieee80211_bpf_params params;
61132176cfdSRui Paulo 	struct mbuf *m;
61232176cfdSRui Paulo 	uint8_t *frm;
613085ff963SMatthew Dillon 	int ret;
614085ff963SMatthew Dillon 
615085ff963SMatthew Dillon 	if (IEEE80211_IS_MULTICAST(da)) {
616085ff963SMatthew Dillon 		ni = ieee80211_ref_node(vap->iv_bss);
6171e290df3SAntonio Huete Jimenez #ifdef IEEE80211_DEBUG_REFCNT
618085ff963SMatthew Dillon 		IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
619085ff963SMatthew Dillon 		"ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n",
620085ff963SMatthew Dillon 		__func__, __LINE__,
621085ff963SMatthew Dillon 		ni, ether_sprintf(ni->ni_macaddr),
622085ff963SMatthew Dillon 		ieee80211_node_refcnt(ni)+1);
6231e290df3SAntonio Huete Jimenez #endif
624085ff963SMatthew Dillon 		ieee80211_ref_node(ni);
625085ff963SMatthew Dillon 	}
626085ff963SMatthew Dillon 	else
627085ff963SMatthew Dillon 		ni = ieee80211_mesh_find_txnode(vap, da);
628085ff963SMatthew Dillon 
62932176cfdSRui Paulo 	if (vap->iv_state == IEEE80211_S_CAC) {
63032176cfdSRui Paulo 		IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, ni,
63132176cfdSRui Paulo 		    "block %s frame in CAC state", "HWMP action");
63232176cfdSRui Paulo 		vap->iv_stats.is_tx_badstate++;
63332176cfdSRui Paulo 		return EIO;	/* XXX */
63432176cfdSRui Paulo 	}
63532176cfdSRui Paulo 
63632176cfdSRui Paulo 	KASSERT(ni != NULL, ("null node"));
637085ff963SMatthew Dillon 	ic = ni->ni_ic;
63832176cfdSRui Paulo 
63932176cfdSRui Paulo 	m = ieee80211_getmgtframe(&frm,
64032176cfdSRui Paulo 	    ic->ic_headroom + sizeof(struct ieee80211_frame),
64132176cfdSRui Paulo 	    sizeof(struct ieee80211_action) + len
64232176cfdSRui Paulo 	);
64332176cfdSRui Paulo 	if (m == NULL) {
64432176cfdSRui Paulo 		ieee80211_free_node(ni);
64532176cfdSRui Paulo 		vap->iv_stats.is_tx_nobuf++;
64632176cfdSRui Paulo 		return ENOMEM;
64732176cfdSRui Paulo 	}
648085ff963SMatthew Dillon 	*frm++ = IEEE80211_ACTION_CAT_MESH;
649085ff963SMatthew Dillon 	*frm++ = IEEE80211_ACTION_MESH_HWMP;
65032176cfdSRui Paulo 	switch (*ie) {
65132176cfdSRui Paulo 	case IEEE80211_ELEMID_MESHPREQ:
65232176cfdSRui Paulo 		frm = hwmp_add_meshpreq(frm,
65332176cfdSRui Paulo 		    (struct ieee80211_meshpreq_ie *)ie);
65432176cfdSRui Paulo 		break;
65532176cfdSRui Paulo 	case IEEE80211_ELEMID_MESHPREP:
65632176cfdSRui Paulo 		frm = hwmp_add_meshprep(frm,
65732176cfdSRui Paulo 		    (struct ieee80211_meshprep_ie *)ie);
65832176cfdSRui Paulo 		break;
65932176cfdSRui Paulo 	case IEEE80211_ELEMID_MESHPERR:
66032176cfdSRui Paulo 		frm = hwmp_add_meshperr(frm,
66132176cfdSRui Paulo 		    (struct ieee80211_meshperr_ie *)ie);
66232176cfdSRui Paulo 		break;
66332176cfdSRui Paulo 	case IEEE80211_ELEMID_MESHRANN:
66432176cfdSRui Paulo 		frm = hwmp_add_meshrann(frm,
66532176cfdSRui Paulo 		    (struct ieee80211_meshrann_ie *)ie);
66632176cfdSRui Paulo 		break;
66732176cfdSRui Paulo 	}
66832176cfdSRui Paulo 
66932176cfdSRui Paulo 	m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
670b5523eacSSascha Wildner 	M_PREPEND(m, sizeof(struct ieee80211_frame), M_NOWAIT);
67132176cfdSRui Paulo 	if (m == NULL) {
67232176cfdSRui Paulo 		ieee80211_free_node(ni);
67332176cfdSRui Paulo 		vap->iv_stats.is_tx_nobuf++;
67432176cfdSRui Paulo 		return ENOMEM;
67532176cfdSRui Paulo 	}
676085ff963SMatthew Dillon 
677085ff963SMatthew Dillon 	IEEE80211_TX_LOCK(ic);
678085ff963SMatthew Dillon 
67932176cfdSRui Paulo 	ieee80211_send_setup(ni, m,
68032176cfdSRui Paulo 	    IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_ACTION,
681085ff963SMatthew Dillon 	    IEEE80211_NONQOS_TID, vap->iv_myaddr, da, vap->iv_myaddr);
68232176cfdSRui Paulo 
68332176cfdSRui Paulo 	m->m_flags |= M_ENCAP;		/* mark encapsulated */
68432176cfdSRui Paulo 	IEEE80211_NODE_STAT(ni, tx_mgmt);
68532176cfdSRui Paulo 
68632176cfdSRui Paulo 	memset(&params, 0, sizeof(params));
68732176cfdSRui Paulo 	params.ibp_pri = WME_AC_VO;
68832176cfdSRui Paulo 	params.ibp_rate0 = ni->ni_txparms->mgmtrate;
68932176cfdSRui Paulo 	if (IEEE80211_IS_MULTICAST(da))
69032176cfdSRui Paulo 		params.ibp_try0 = 1;
69132176cfdSRui Paulo 	else
69232176cfdSRui Paulo 		params.ibp_try0 = ni->ni_txparms->maxretry;
69332176cfdSRui Paulo 	params.ibp_power = ni->ni_txpower;
694085ff963SMatthew Dillon 	ret = ieee80211_raw_output(vap, ni, m, &params);
695085ff963SMatthew Dillon 	IEEE80211_TX_UNLOCK(ic);
696085ff963SMatthew Dillon 	return (ret);
69732176cfdSRui Paulo }
69832176cfdSRui Paulo 
69932176cfdSRui Paulo #define ADDSHORT(frm, v) do {		\
7004f655ef5SMatthew Dillon 	le16enc(frm, v);		\
70132176cfdSRui Paulo 	frm += 2;			\
70232176cfdSRui Paulo } while (0)
70332176cfdSRui Paulo #define ADDWORD(frm, v) do {		\
7044f655ef5SMatthew Dillon 	le32enc(frm, v);		\
70532176cfdSRui Paulo 	frm += 4;			\
70632176cfdSRui Paulo } while (0)
70732176cfdSRui Paulo /*
70832176cfdSRui Paulo  * Add a Mesh Path Request IE to a frame.
70932176cfdSRui Paulo  */
710085ff963SMatthew Dillon #define	PREQ_TFLAGS(n)	preq->preq_targets[n].target_flags
711085ff963SMatthew Dillon #define	PREQ_TADDR(n)	preq->preq_targets[n].target_addr
712085ff963SMatthew Dillon #define	PREQ_TSEQ(n)	preq->preq_targets[n].target_seq
71332176cfdSRui Paulo static uint8_t *
hwmp_add_meshpreq(uint8_t * frm,const struct ieee80211_meshpreq_ie * preq)71432176cfdSRui Paulo hwmp_add_meshpreq(uint8_t *frm, const struct ieee80211_meshpreq_ie *preq)
71532176cfdSRui Paulo {
71632176cfdSRui Paulo 	int i;
71732176cfdSRui Paulo 
71832176cfdSRui Paulo 	*frm++ = IEEE80211_ELEMID_MESHPREQ;
719085ff963SMatthew Dillon 	*frm++ = preq->preq_len;	/* len already calculated */
72032176cfdSRui Paulo 	*frm++ = preq->preq_flags;
72132176cfdSRui Paulo 	*frm++ = preq->preq_hopcount;
72232176cfdSRui Paulo 	*frm++ = preq->preq_ttl;
72332176cfdSRui Paulo 	ADDWORD(frm, preq->preq_id);
72432176cfdSRui Paulo 	IEEE80211_ADDR_COPY(frm, preq->preq_origaddr); frm += 6;
72532176cfdSRui Paulo 	ADDWORD(frm, preq->preq_origseq);
726085ff963SMatthew Dillon 	if (preq->preq_flags & IEEE80211_MESHPREQ_FLAGS_AE) {
727085ff963SMatthew Dillon 		IEEE80211_ADDR_COPY(frm, preq->preq_orig_ext_addr);
728085ff963SMatthew Dillon 		frm += 6;
729085ff963SMatthew Dillon 	}
73032176cfdSRui Paulo 	ADDWORD(frm, preq->preq_lifetime);
73132176cfdSRui Paulo 	ADDWORD(frm, preq->preq_metric);
73232176cfdSRui Paulo 	*frm++ = preq->preq_tcount;
73332176cfdSRui Paulo 	for (i = 0; i < preq->preq_tcount; i++) {
734085ff963SMatthew Dillon 		*frm++ = PREQ_TFLAGS(i);
735085ff963SMatthew Dillon 		IEEE80211_ADDR_COPY(frm, PREQ_TADDR(i));
73632176cfdSRui Paulo 		frm += 6;
737085ff963SMatthew Dillon 		ADDWORD(frm, PREQ_TSEQ(i));
73832176cfdSRui Paulo 	}
73932176cfdSRui Paulo 	return frm;
74032176cfdSRui Paulo }
741085ff963SMatthew Dillon #undef	PREQ_TFLAGS
742085ff963SMatthew Dillon #undef	PREQ_TADDR
743085ff963SMatthew Dillon #undef	PREQ_TSEQ
74432176cfdSRui Paulo 
74532176cfdSRui Paulo /*
74632176cfdSRui Paulo  * Add a Mesh Path Reply IE to a frame.
74732176cfdSRui Paulo  */
74832176cfdSRui Paulo static uint8_t *
hwmp_add_meshprep(uint8_t * frm,const struct ieee80211_meshprep_ie * prep)74932176cfdSRui Paulo hwmp_add_meshprep(uint8_t *frm, const struct ieee80211_meshprep_ie *prep)
75032176cfdSRui Paulo {
75132176cfdSRui Paulo 	*frm++ = IEEE80211_ELEMID_MESHPREP;
752085ff963SMatthew Dillon 	*frm++ = prep->prep_len;	/* len already calculated */
75332176cfdSRui Paulo 	*frm++ = prep->prep_flags;
75432176cfdSRui Paulo 	*frm++ = prep->prep_hopcount;
75532176cfdSRui Paulo 	*frm++ = prep->prep_ttl;
75632176cfdSRui Paulo 	IEEE80211_ADDR_COPY(frm, prep->prep_targetaddr); frm += 6;
75732176cfdSRui Paulo 	ADDWORD(frm, prep->prep_targetseq);
758085ff963SMatthew Dillon 	if (prep->prep_flags & IEEE80211_MESHPREP_FLAGS_AE) {
759085ff963SMatthew Dillon 		IEEE80211_ADDR_COPY(frm, prep->prep_target_ext_addr);
760085ff963SMatthew Dillon 		frm += 6;
761085ff963SMatthew Dillon 	}
76232176cfdSRui Paulo 	ADDWORD(frm, prep->prep_lifetime);
76332176cfdSRui Paulo 	ADDWORD(frm, prep->prep_metric);
76432176cfdSRui Paulo 	IEEE80211_ADDR_COPY(frm, prep->prep_origaddr); frm += 6;
76532176cfdSRui Paulo 	ADDWORD(frm, prep->prep_origseq);
76632176cfdSRui Paulo 	return frm;
76732176cfdSRui Paulo }
76832176cfdSRui Paulo 
76932176cfdSRui Paulo /*
77032176cfdSRui Paulo  * Add a Mesh Path Error IE to a frame.
77132176cfdSRui Paulo  */
772085ff963SMatthew Dillon #define	PERR_DFLAGS(n)	perr->perr_dests[n].dest_flags
773085ff963SMatthew Dillon #define	PERR_DADDR(n)	perr->perr_dests[n].dest_addr
774085ff963SMatthew Dillon #define	PERR_DSEQ(n)	perr->perr_dests[n].dest_seq
775085ff963SMatthew Dillon #define	PERR_EXTADDR(n)	perr->perr_dests[n].dest_ext_addr
776085ff963SMatthew Dillon #define	PERR_DRCODE(n)	perr->perr_dests[n].dest_rcode
77732176cfdSRui Paulo static uint8_t *
hwmp_add_meshperr(uint8_t * frm,const struct ieee80211_meshperr_ie * perr)77832176cfdSRui Paulo hwmp_add_meshperr(uint8_t *frm, const struct ieee80211_meshperr_ie *perr)
77932176cfdSRui Paulo {
78032176cfdSRui Paulo 	int i;
78132176cfdSRui Paulo 
78232176cfdSRui Paulo 	*frm++ = IEEE80211_ELEMID_MESHPERR;
783085ff963SMatthew Dillon 	*frm++ = perr->perr_len;	/* len already calculated */
78432176cfdSRui Paulo 	*frm++ = perr->perr_ttl;
78532176cfdSRui Paulo 	*frm++ = perr->perr_ndests;
78632176cfdSRui Paulo 	for (i = 0; i < perr->perr_ndests; i++) {
787085ff963SMatthew Dillon 		*frm++ = PERR_DFLAGS(i);
788085ff963SMatthew Dillon 		IEEE80211_ADDR_COPY(frm, PERR_DADDR(i));
78932176cfdSRui Paulo 		frm += 6;
790085ff963SMatthew Dillon 		ADDWORD(frm, PERR_DSEQ(i));
791085ff963SMatthew Dillon 		if (PERR_DFLAGS(i) & IEEE80211_MESHPERR_FLAGS_AE) {
792085ff963SMatthew Dillon 			IEEE80211_ADDR_COPY(frm, PERR_EXTADDR(i));
793085ff963SMatthew Dillon 			frm += 6;
794085ff963SMatthew Dillon 		}
795085ff963SMatthew Dillon 		ADDSHORT(frm, PERR_DRCODE(i));
79632176cfdSRui Paulo 	}
79732176cfdSRui Paulo 	return frm;
79832176cfdSRui Paulo }
799085ff963SMatthew Dillon #undef	PERR_DFLAGS
800085ff963SMatthew Dillon #undef	PERR_DADDR
801085ff963SMatthew Dillon #undef	PERR_DSEQ
802085ff963SMatthew Dillon #undef	PERR_EXTADDR
803085ff963SMatthew Dillon #undef	PERR_DRCODE
80432176cfdSRui Paulo 
80532176cfdSRui Paulo /*
80632176cfdSRui Paulo  * Add a Root Annoucement IE to a frame.
80732176cfdSRui Paulo  */
80832176cfdSRui Paulo static uint8_t *
hwmp_add_meshrann(uint8_t * frm,const struct ieee80211_meshrann_ie * rann)80932176cfdSRui Paulo hwmp_add_meshrann(uint8_t *frm, const struct ieee80211_meshrann_ie *rann)
81032176cfdSRui Paulo {
81132176cfdSRui Paulo 	*frm++ = IEEE80211_ELEMID_MESHRANN;
812085ff963SMatthew Dillon 	*frm++ = rann->rann_len;
81332176cfdSRui Paulo 	*frm++ = rann->rann_flags;
81432176cfdSRui Paulo 	*frm++ = rann->rann_hopcount;
81532176cfdSRui Paulo 	*frm++ = rann->rann_ttl;
81632176cfdSRui Paulo 	IEEE80211_ADDR_COPY(frm, rann->rann_addr); frm += 6;
81732176cfdSRui Paulo 	ADDWORD(frm, rann->rann_seq);
818085ff963SMatthew Dillon 	ADDWORD(frm, rann->rann_interval);
81932176cfdSRui Paulo 	ADDWORD(frm, rann->rann_metric);
82032176cfdSRui Paulo 	return frm;
82132176cfdSRui Paulo }
82232176cfdSRui Paulo 
82332176cfdSRui Paulo static void
hwmp_rootmode_setup(struct ieee80211vap * vap)82432176cfdSRui Paulo hwmp_rootmode_setup(struct ieee80211vap *vap)
82532176cfdSRui Paulo {
82632176cfdSRui Paulo 	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
827085ff963SMatthew Dillon 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
82832176cfdSRui Paulo 
82932176cfdSRui Paulo 	switch (hs->hs_rootmode) {
83032176cfdSRui Paulo 	case IEEE80211_HWMP_ROOTMODE_DISABLED:
831085ff963SMatthew Dillon 		callout_drain(&hs->hs_roottimer);
832085ff963SMatthew Dillon 		ms->ms_flags &= ~IEEE80211_MESHFLAGS_ROOT;
83332176cfdSRui Paulo 		break;
83432176cfdSRui Paulo 	case IEEE80211_HWMP_ROOTMODE_NORMAL:
83532176cfdSRui Paulo 	case IEEE80211_HWMP_ROOTMODE_PROACTIVE:
83632176cfdSRui Paulo 		callout_reset(&hs->hs_roottimer, ieee80211_hwmp_rootint,
837085ff963SMatthew Dillon 		    hwmp_rootmode_cb, vap);
838085ff963SMatthew Dillon 		ms->ms_flags |= IEEE80211_MESHFLAGS_ROOT;
83932176cfdSRui Paulo 		break;
84032176cfdSRui Paulo 	case IEEE80211_HWMP_ROOTMODE_RANN:
84132176cfdSRui Paulo 		callout_reset(&hs->hs_roottimer, ieee80211_hwmp_rannint,
842085ff963SMatthew Dillon 		    hwmp_rootmode_rann_cb, vap);
843085ff963SMatthew Dillon 		ms->ms_flags |= IEEE80211_MESHFLAGS_ROOT;
84432176cfdSRui Paulo 		break;
84532176cfdSRui Paulo 	}
84632176cfdSRui Paulo }
84732176cfdSRui Paulo 
84832176cfdSRui Paulo /*
84932176cfdSRui Paulo  * Send a broadcast Path Request to find all nodes on the mesh. We are
85032176cfdSRui Paulo  * called when the vap is configured as a HWMP root node.
85132176cfdSRui Paulo  */
85232176cfdSRui Paulo #define	PREQ_TFLAGS(n)	preq.preq_targets[n].target_flags
85332176cfdSRui Paulo #define	PREQ_TADDR(n)	preq.preq_targets[n].target_addr
85432176cfdSRui Paulo #define	PREQ_TSEQ(n)	preq.preq_targets[n].target_seq
85532176cfdSRui Paulo static void
hwmp_rootmode_cb(void * arg)856085ff963SMatthew Dillon hwmp_rootmode_cb(void *arg)
85732176cfdSRui Paulo {
85832176cfdSRui Paulo 	struct ieee80211vap *vap = (struct ieee80211vap *)arg;
859085ff963SMatthew Dillon 	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
860085ff963SMatthew Dillon 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
86132176cfdSRui Paulo 	struct ieee80211_meshpreq_ie preq;
86232176cfdSRui Paulo 
86332176cfdSRui Paulo 	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, vap->iv_bss,
86432176cfdSRui Paulo 	    "%s", "send broadcast PREQ");
86532176cfdSRui Paulo 
866085ff963SMatthew Dillon 	preq.preq_flags = 0;
867085ff963SMatthew Dillon 	if (ms->ms_flags & IEEE80211_MESHFLAGS_GATE)
868085ff963SMatthew Dillon 		preq.preq_flags |= IEEE80211_MESHPREQ_FLAGS_GATE;
86932176cfdSRui Paulo 	if (hs->hs_rootmode == IEEE80211_HWMP_ROOTMODE_PROACTIVE)
87032176cfdSRui Paulo 		preq.preq_flags |= IEEE80211_MESHPREQ_FLAGS_PP;
87132176cfdSRui Paulo 	preq.preq_hopcount = 0;
87232176cfdSRui Paulo 	preq.preq_ttl = ms->ms_ttl;
87332176cfdSRui Paulo 	preq.preq_id = ++hs->hs_preqid;
87432176cfdSRui Paulo 	IEEE80211_ADDR_COPY(preq.preq_origaddr, vap->iv_myaddr);
87532176cfdSRui Paulo 	preq.preq_origseq = ++hs->hs_seq;
87632176cfdSRui Paulo 	preq.preq_lifetime = ticks_to_msecs(ieee80211_hwmp_roottimeout);
87732176cfdSRui Paulo 	preq.preq_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
87832176cfdSRui Paulo 	preq.preq_tcount = 1;
87932176cfdSRui Paulo 	IEEE80211_ADDR_COPY(PREQ_TADDR(0), broadcastaddr);
88032176cfdSRui Paulo 	PREQ_TFLAGS(0) = IEEE80211_MESHPREQ_TFLAGS_TO |
881085ff963SMatthew Dillon 	    IEEE80211_MESHPREQ_TFLAGS_USN;
88232176cfdSRui Paulo 	PREQ_TSEQ(0) = 0;
88332176cfdSRui Paulo 	vap->iv_stats.is_hwmp_rootreqs++;
884085ff963SMatthew Dillon 	/* NB: we enforce rate check ourself */
885085ff963SMatthew Dillon 	hwmp_send_preq(vap, broadcastaddr, &preq, NULL, NULL);
88632176cfdSRui Paulo 	hwmp_rootmode_setup(vap);
88732176cfdSRui Paulo }
88832176cfdSRui Paulo #undef	PREQ_TFLAGS
88932176cfdSRui Paulo #undef	PREQ_TADDR
89032176cfdSRui Paulo #undef	PREQ_TSEQ
89132176cfdSRui Paulo 
89232176cfdSRui Paulo /*
89332176cfdSRui Paulo  * Send a Root Annoucement (RANN) to find all the nodes on the mesh. We are
89432176cfdSRui Paulo  * called when the vap is configured as a HWMP RANN root node.
89532176cfdSRui Paulo  */
89632176cfdSRui Paulo static void
hwmp_rootmode_rann_cb(void * arg)897085ff963SMatthew Dillon hwmp_rootmode_rann_cb(void *arg)
89832176cfdSRui Paulo {
89932176cfdSRui Paulo 	struct ieee80211vap *vap = (struct ieee80211vap *)arg;
900085ff963SMatthew Dillon 	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
901085ff963SMatthew Dillon 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
90232176cfdSRui Paulo 	struct ieee80211_meshrann_ie rann;
90332176cfdSRui Paulo 
90432176cfdSRui Paulo 	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, vap->iv_bss,
90532176cfdSRui Paulo 	    "%s", "send broadcast RANN");
90632176cfdSRui Paulo 
90732176cfdSRui Paulo 	rann.rann_flags = 0;
908085ff963SMatthew Dillon 	if (ms->ms_flags & IEEE80211_MESHFLAGS_GATE)
909085ff963SMatthew Dillon 		rann.rann_flags |= IEEE80211_MESHFLAGS_GATE;
91032176cfdSRui Paulo 	rann.rann_hopcount = 0;
91132176cfdSRui Paulo 	rann.rann_ttl = ms->ms_ttl;
91232176cfdSRui Paulo 	IEEE80211_ADDR_COPY(rann.rann_addr, vap->iv_myaddr);
91332176cfdSRui Paulo 	rann.rann_seq = ++hs->hs_seq;
914085ff963SMatthew Dillon 	rann.rann_interval = ieee80211_hwmp_rannint;
91532176cfdSRui Paulo 	rann.rann_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
91632176cfdSRui Paulo 
91732176cfdSRui Paulo 	vap->iv_stats.is_hwmp_rootrann++;
918085ff963SMatthew Dillon 	hwmp_send_rann(vap, broadcastaddr, &rann);
91932176cfdSRui Paulo 	hwmp_rootmode_setup(vap);
920085ff963SMatthew Dillon }
921085ff963SMatthew Dillon 
922085ff963SMatthew Dillon /*
923085ff963SMatthew Dillon  * Update forwarding information to TA if metric improves.
924085ff963SMatthew Dillon  */
925085ff963SMatthew Dillon static void
hwmp_update_transmitter(struct ieee80211vap * vap,struct ieee80211_node * ni,const char * hwmp_frame)926085ff963SMatthew Dillon hwmp_update_transmitter(struct ieee80211vap *vap, struct ieee80211_node *ni,
927085ff963SMatthew Dillon     const char *hwmp_frame)
928085ff963SMatthew Dillon {
929085ff963SMatthew Dillon 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
930085ff963SMatthew Dillon 	struct ieee80211_mesh_route *rttran = NULL;	/* Transmitter */
931085ff963SMatthew Dillon 	int metric = 0;
932085ff963SMatthew Dillon 
933085ff963SMatthew Dillon 	rttran = ieee80211_mesh_rt_find(vap, ni->ni_macaddr);
934085ff963SMatthew Dillon 	if (rttran == NULL) {
935085ff963SMatthew Dillon 		rttran = ieee80211_mesh_rt_add(vap, ni->ni_macaddr);
936085ff963SMatthew Dillon 		if (rttran == NULL) {
9374f655ef5SMatthew Dillon #if defined(__DragonFly__)
938085ff963SMatthew Dillon 			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
939f92fae3fSSascha Wildner 			    "unable to add path to transmitter %s of %s",
940f92fae3fSSascha Wildner 			    ether_sprintf(ni->ni_macaddr), hwmp_frame);
9414f655ef5SMatthew Dillon #else
9424f655ef5SMatthew Dillon 			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
9434f655ef5SMatthew Dillon 			    "unable to add path to transmitter %6D of %s",
9444f655ef5SMatthew Dillon 			    ni->ni_macaddr, ":", hwmp_frame);
9454f655ef5SMatthew Dillon #endif
946085ff963SMatthew Dillon 			vap->iv_stats.is_mesh_rtaddfailed++;
947085ff963SMatthew Dillon 			return;
948085ff963SMatthew Dillon 		}
949085ff963SMatthew Dillon 	}
950085ff963SMatthew Dillon 	metric = ms->ms_pmetric->mpm_metric(ni);
951085ff963SMatthew Dillon 	if (!(rttran->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) ||
952085ff963SMatthew Dillon 	    rttran->rt_metric > metric)
953085ff963SMatthew Dillon 	{
9544f655ef5SMatthew Dillon #if defined(__DragonFly__)
955085ff963SMatthew Dillon 		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
956f92fae3fSSascha Wildner 		    "%s path to transmitter %s of %s, metric %d:%d",
957085ff963SMatthew Dillon 		    rttran->rt_flags & IEEE80211_MESHRT_FLAGS_VALID ?
958f92fae3fSSascha Wildner 		    "prefer" : "update", ether_sprintf(ni->ni_macaddr),
959f92fae3fSSascha Wildner 		    hwmp_frame,
960085ff963SMatthew Dillon 		    rttran->rt_metric, metric);
9614f655ef5SMatthew Dillon #else
9624f655ef5SMatthew Dillon 		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
9634f655ef5SMatthew Dillon 		    "%s path to transmiter %6D of %s, metric %d:%d",
9644f655ef5SMatthew Dillon 		    rttran->rt_flags & IEEE80211_MESHRT_FLAGS_VALID ?
9654f655ef5SMatthew Dillon 		    "prefer" : "update", ni->ni_macaddr, ":", hwmp_frame,
9664f655ef5SMatthew Dillon 		    rttran->rt_metric, metric);
9674f655ef5SMatthew Dillon #endif
968085ff963SMatthew Dillon 		IEEE80211_ADDR_COPY(rttran->rt_nexthop, ni->ni_macaddr);
969085ff963SMatthew Dillon 		rttran->rt_metric = metric;
970085ff963SMatthew Dillon 		rttran->rt_nhops  = 1;
971085ff963SMatthew Dillon 		ieee80211_mesh_rt_update(rttran, ms->ms_ppath->mpp_inact);
972085ff963SMatthew Dillon 		rttran->rt_flags = IEEE80211_MESHRT_FLAGS_VALID;
973085ff963SMatthew Dillon 	}
97432176cfdSRui Paulo }
97532176cfdSRui Paulo 
97632176cfdSRui Paulo #define	PREQ_TFLAGS(n)	preq->preq_targets[n].target_flags
97732176cfdSRui Paulo #define	PREQ_TADDR(n)	preq->preq_targets[n].target_addr
97832176cfdSRui Paulo #define	PREQ_TSEQ(n)	preq->preq_targets[n].target_seq
97932176cfdSRui Paulo static void
hwmp_recv_preq(struct ieee80211vap * vap,struct ieee80211_node * ni,const struct ieee80211_frame * wh,const struct ieee80211_meshpreq_ie * preq)98032176cfdSRui Paulo hwmp_recv_preq(struct ieee80211vap *vap, struct ieee80211_node *ni,
98132176cfdSRui Paulo     const struct ieee80211_frame *wh, const struct ieee80211_meshpreq_ie *preq)
98232176cfdSRui Paulo {
98332176cfdSRui Paulo 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
98432176cfdSRui Paulo 	struct ieee80211_mesh_route *rtorig = NULL;
985085ff963SMatthew Dillon 	struct ieee80211_mesh_route *rtorig_ext = NULL;
986085ff963SMatthew Dillon 	struct ieee80211_mesh_route *rttarg = NULL;
987085ff963SMatthew Dillon 	struct ieee80211_hwmp_route *hrorig = NULL;
988085ff963SMatthew Dillon 	struct ieee80211_hwmp_route *hrtarg = NULL;
98932176cfdSRui Paulo 	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
99032176cfdSRui Paulo 	struct ieee80211_meshprep_ie prep;
991085ff963SMatthew Dillon 	ieee80211_hwmp_seq preqid;	/* last seen preqid for orig */
992085ff963SMatthew Dillon 	uint32_t metric = 0;
99332176cfdSRui Paulo 
99432176cfdSRui Paulo 	/*
99532176cfdSRui Paulo 	 * Ignore PREQs from us. Could happen because someone forward it
99632176cfdSRui Paulo 	 * back to us.
99732176cfdSRui Paulo 	 */
99832176cfdSRui Paulo 	if (IEEE80211_ADDR_EQ(vap->iv_myaddr, preq->preq_origaddr))
99932176cfdSRui Paulo 		return;
100032176cfdSRui Paulo 
10014f655ef5SMatthew Dillon #if defined(__DragonFly__)
100232176cfdSRui Paulo 	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1003f92fae3fSSascha Wildner 	    "received PREQ, orig %s, targ(0) %s",
1004f92fae3fSSascha Wildner 	    ether_sprintf(preq->preq_origaddr),
1005f92fae3fSSascha Wildner 	    ether_sprintf(PREQ_TADDR(0)));
10064f655ef5SMatthew Dillon #else
10074f655ef5SMatthew Dillon 	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
10084f655ef5SMatthew Dillon 	"received PREQ, orig %6D, targ(0) %6D", preq->preq_origaddr, ":",
10094f655ef5SMatthew Dillon 	PREQ_TADDR(0), ":");
10104f655ef5SMatthew Dillon #endif
101132176cfdSRui Paulo 
101232176cfdSRui Paulo 	/*
1013085ff963SMatthew Dillon 	 * Acceptance criteria: (if the PREQ is not for us or not broadcast,
1014085ff963SMatthew Dillon 	 * or an external mac address not proxied by us),
1015085ff963SMatthew Dillon 	 * AND forwarding is disabled, discard this PREQ.
101632176cfdSRui Paulo 	 */
1017085ff963SMatthew Dillon 	rttarg = ieee80211_mesh_rt_find(vap, PREQ_TADDR(0));
1018085ff963SMatthew Dillon 	if (!(ms->ms_flags & IEEE80211_MESHFLAGS_FWD) &&
1019085ff963SMatthew Dillon 	    (!IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0)) ||
1020085ff963SMatthew Dillon 	    !IEEE80211_IS_MULTICAST(PREQ_TADDR(0)) ||
1021085ff963SMatthew Dillon 	    (rttarg != NULL &&
1022085ff963SMatthew Dillon 	    rttarg->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY &&
1023085ff963SMatthew Dillon 	    IEEE80211_ADDR_EQ(vap->iv_myaddr, rttarg->rt_mesh_gate)))) {
102432176cfdSRui Paulo 		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_HWMP,
102532176cfdSRui Paulo 		    preq->preq_origaddr, NULL, "%s", "not accepting PREQ");
102632176cfdSRui Paulo 		return;
102732176cfdSRui Paulo 	}
1028085ff963SMatthew Dillon 	/*
1029085ff963SMatthew Dillon 	 * Acceptance criteria: if unicast addressed
1030085ff963SMatthew Dillon 	 * AND no valid forwarding for Target of PREQ, discard this PREQ.
1031085ff963SMatthew Dillon 	 */
1032085ff963SMatthew Dillon 	if(rttarg != NULL)
1033085ff963SMatthew Dillon 		hrtarg = IEEE80211_MESH_ROUTE_PRIV(rttarg,
1034085ff963SMatthew Dillon 		    struct ieee80211_hwmp_route);
1035085ff963SMatthew Dillon 	/* Address mode: ucast */
1036085ff963SMatthew Dillon 	if(preq->preq_flags & IEEE80211_MESHPREQ_FLAGS_AM &&
1037085ff963SMatthew Dillon 	    rttarg == NULL &&
1038085ff963SMatthew Dillon 	    !IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0))) {
10394f655ef5SMatthew Dillon #if defined(__DragonFly__)
1040085ff963SMatthew Dillon 		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_HWMP,
1041085ff963SMatthew Dillon 		    preq->preq_origaddr, NULL,
1042f92fae3fSSascha Wildner 		    "unicast addressed PREQ of unknown target %s",
1043f92fae3fSSascha Wildner 		    ether_sprintf(PREQ_TADDR(0)));
10444f655ef5SMatthew Dillon #else
10454f655ef5SMatthew Dillon 		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_HWMP,
10464f655ef5SMatthew Dillon 		    preq->preq_origaddr, NULL,
10474f655ef5SMatthew Dillon 		    "unicast addressed PREQ of unknown target %6D",
10484f655ef5SMatthew Dillon 		    PREQ_TADDR(0), ":");
10494f655ef5SMatthew Dillon #endif
1050085ff963SMatthew Dillon 		return;
1051085ff963SMatthew Dillon 	}
1052085ff963SMatthew Dillon 
1053085ff963SMatthew Dillon 	/* PREQ ACCEPTED */
1054085ff963SMatthew Dillon 
105532176cfdSRui Paulo 	rtorig = ieee80211_mesh_rt_find(vap, preq->preq_origaddr);
1056085ff963SMatthew Dillon 	if (rtorig == NULL) {
105732176cfdSRui Paulo 		rtorig = ieee80211_mesh_rt_add(vap, preq->preq_origaddr);
105836e4ebd1SJoe Talbott 		if (rtorig == NULL) {
10594f655ef5SMatthew Dillon #if defined(__DragonFly__)
1060085ff963SMatthew Dillon 			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1061f92fae3fSSascha Wildner 			    "unable to add orig path to %s",
1062f92fae3fSSascha Wildner 			    ether_sprintf(preq->preq_origaddr));
10634f655ef5SMatthew Dillon #else
10644f655ef5SMatthew Dillon 			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
10654f655ef5SMatthew Dillon 			"unable to add orig path to %6D",
10664f655ef5SMatthew Dillon 			preq->preq_origaddr, ":");
10674f655ef5SMatthew Dillon #endif
1068085ff963SMatthew Dillon 			vap->iv_stats.is_mesh_rtaddfailed++;
106936e4ebd1SJoe Talbott 			return;
107036e4ebd1SJoe Talbott 		}
10714f655ef5SMatthew Dillon #if defined(__DragonFly__)
1072085ff963SMatthew Dillon 		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1073f92fae3fSSascha Wildner 		    "adding originator %s",
1074f92fae3fSSascha Wildner 		    ether_sprintf(preq->preq_origaddr));
10754f655ef5SMatthew Dillon #else
10764f655ef5SMatthew Dillon 		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
10774f655ef5SMatthew Dillon 		    "adding originator %6D", preq->preq_origaddr, ":");
10784f655ef5SMatthew Dillon #endif
1079085ff963SMatthew Dillon 	}
108032176cfdSRui Paulo 	hrorig = IEEE80211_MESH_ROUTE_PRIV(rtorig, struct ieee80211_hwmp_route);
1081085ff963SMatthew Dillon 
1082085ff963SMatthew Dillon 	/* record last seen preqid */
1083085ff963SMatthew Dillon 	preqid = hrorig->hr_preqid;
1084085ff963SMatthew Dillon 	hrorig->hr_preqid = HWMP_SEQ_MAX(hrorig->hr_preqid, preq->preq_id);
1085085ff963SMatthew Dillon 
1086085ff963SMatthew Dillon 	/* Data creation and update of forwarding information
1087085ff963SMatthew Dillon 	 * according to Table 11C-8 for originator mesh STA.
108832176cfdSRui Paulo 	 */
1089085ff963SMatthew Dillon 	metric = preq->preq_metric + ms->ms_pmetric->mpm_metric(ni);
1090085ff963SMatthew Dillon 	if (HWMP_SEQ_GT(preq->preq_origseq, hrorig->hr_seq) ||
1091085ff963SMatthew Dillon 	    (HWMP_SEQ_EQ(preq->preq_origseq, hrorig->hr_seq) &&
1092085ff963SMatthew Dillon 	    metric < rtorig->rt_metric)) {
1093085ff963SMatthew Dillon 		hrorig->hr_seq = preq->preq_origseq;
1094085ff963SMatthew Dillon 		IEEE80211_ADDR_COPY(rtorig->rt_nexthop, wh->i_addr2);
1095085ff963SMatthew Dillon 		rtorig->rt_metric = metric;
1096085ff963SMatthew Dillon 		rtorig->rt_nhops  = preq->preq_hopcount + 1;
1097085ff963SMatthew Dillon 		ieee80211_mesh_rt_update(rtorig, preq->preq_lifetime);
1098085ff963SMatthew Dillon 		/* Path to orig is valid now.
1099085ff963SMatthew Dillon 		 * NB: we know it can't be Proxy, and if it is GATE
1100085ff963SMatthew Dillon 		 * it will be marked below.
1101085ff963SMatthew Dillon 		 */
1102085ff963SMatthew Dillon 		rtorig->rt_flags = IEEE80211_MESHRT_FLAGS_VALID;
1103085ff963SMatthew Dillon 	} else if ((hrtarg != NULL &&
1104085ff963SMatthew Dillon 	    !HWMP_SEQ_EQ(hrtarg->hr_seq, PREQ_TSEQ(0))) ||
1105085ff963SMatthew Dillon 	    (rtorig->rt_flags & IEEE80211_MESHRT_FLAGS_VALID &&
1106085ff963SMatthew Dillon 	    preqid >= preq->preq_id)) {
11074f655ef5SMatthew Dillon #if defined(__DragonFly__)
110832176cfdSRui Paulo 		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1109f92fae3fSSascha Wildner 		    "discard PREQ from %s, old seqno %u <= %u,"
1110085ff963SMatthew Dillon 		    " or old preqid %u < %u",
1111f92fae3fSSascha Wildner 		    ether_sprintf(preq->preq_origaddr),
1112085ff963SMatthew Dillon 		    preq->preq_origseq, hrorig->hr_seq,
1113085ff963SMatthew Dillon 		    preq->preq_id, preqid);
11144f655ef5SMatthew Dillon #else
11154f655ef5SMatthew Dillon 		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
11164f655ef5SMatthew Dillon 		    "discard PREQ from %6D, old seqno %u <= %u,"
11174f655ef5SMatthew Dillon 		    " or old preqid %u < %u",
11184f655ef5SMatthew Dillon 		    preq->preq_origaddr, ":",
11194f655ef5SMatthew Dillon 		    preq->preq_origseq, hrorig->hr_seq,
11204f655ef5SMatthew Dillon 		    preq->preq_id, preqid);
11214f655ef5SMatthew Dillon #endif
112232176cfdSRui Paulo 		return;
112332176cfdSRui Paulo 	}
1124085ff963SMatthew Dillon 
1125085ff963SMatthew Dillon 	/* Update forwarding information to TA if metric improves. */
1126085ff963SMatthew Dillon 	hwmp_update_transmitter(vap, ni, "PREQ");
112732176cfdSRui Paulo 
112832176cfdSRui Paulo 	/*
112932176cfdSRui Paulo 	 * Check if the PREQ is addressed to us.
1130085ff963SMatthew Dillon 	 * or a Proxy currently gated by us.
113132176cfdSRui Paulo 	 */
1132085ff963SMatthew Dillon 	if (IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0)) ||
1133085ff963SMatthew Dillon 	    (ms->ms_flags & IEEE80211_MESHFLAGS_GATE &&
1134085ff963SMatthew Dillon 	    rttarg != NULL &&
1135085ff963SMatthew Dillon 	    IEEE80211_ADDR_EQ(vap->iv_myaddr, rttarg->rt_mesh_gate) &&
1136085ff963SMatthew Dillon 	    rttarg->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY &&
1137085ff963SMatthew Dillon 	    rttarg->rt_flags & IEEE80211_MESHRT_FLAGS_VALID)) {
1138085ff963SMatthew Dillon 		/*
1139085ff963SMatthew Dillon 		 * When we are the target we shall update our own HWMP seq
1140085ff963SMatthew Dillon 		 * number with max of (current and preq->seq) + 1
1141085ff963SMatthew Dillon 		 */
1142085ff963SMatthew Dillon 		hs->hs_seq = HWMP_SEQ_MAX(hs->hs_seq, PREQ_TSEQ(0)) + 1;
1143085ff963SMatthew Dillon 
1144085ff963SMatthew Dillon 		prep.prep_flags = 0;
1145085ff963SMatthew Dillon 		prep.prep_hopcount = 0;
1146085ff963SMatthew Dillon 		prep.prep_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
1147085ff963SMatthew Dillon 		IEEE80211_ADDR_COPY(prep.prep_targetaddr, vap->iv_myaddr);
1148085ff963SMatthew Dillon 		if (rttarg != NULL && /* if NULL it means we are the target */
1149085ff963SMatthew Dillon 		    rttarg->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY) {
11504f655ef5SMatthew Dillon #if defined(__DragonFly__)
115132176cfdSRui Paulo 			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1152f92fae3fSSascha Wildner 			    "reply for proxy %s",
1153f92fae3fSSascha Wildner 			    ether_sprintf(rttarg->rt_dest));
11544f655ef5SMatthew Dillon #else
11554f655ef5SMatthew Dillon 			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
11564f655ef5SMatthew Dillon 			    "reply for proxy %6D", rttarg->rt_dest, ":");
11574f655ef5SMatthew Dillon #endif
1158085ff963SMatthew Dillon 			prep.prep_flags |= IEEE80211_MESHPREP_FLAGS_AE;
1159085ff963SMatthew Dillon 			IEEE80211_ADDR_COPY(prep.prep_target_ext_addr,
1160085ff963SMatthew Dillon 			    rttarg->rt_dest);
1161085ff963SMatthew Dillon 			/* update proxy seqno to HWMP seqno */
1162085ff963SMatthew Dillon 			rttarg->rt_ext_seq = hs->hs_seq;
1163085ff963SMatthew Dillon 			prep.prep_hopcount = rttarg->rt_nhops;
1164085ff963SMatthew Dillon 			prep.prep_metric = rttarg->rt_metric;
1165085ff963SMatthew Dillon 			IEEE80211_ADDR_COPY(prep.prep_targetaddr, rttarg->rt_mesh_gate);
1166085ff963SMatthew Dillon 		}
116732176cfdSRui Paulo 		/*
116832176cfdSRui Paulo 		 * Build and send a PREP frame.
116932176cfdSRui Paulo 		 */
117032176cfdSRui Paulo 		prep.prep_ttl = ms->ms_ttl;
1171085ff963SMatthew Dillon 		prep.prep_targetseq = hs->hs_seq;
117232176cfdSRui Paulo 		prep.prep_lifetime = preq->preq_lifetime;
117332176cfdSRui Paulo 		IEEE80211_ADDR_COPY(prep.prep_origaddr, preq->preq_origaddr);
117432176cfdSRui Paulo 		prep.prep_origseq = preq->preq_origseq;
1175085ff963SMatthew Dillon 
11764f655ef5SMatthew Dillon #if defined(__DragonFly__)
1177085ff963SMatthew Dillon 		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1178f92fae3fSSascha Wildner 		    "reply to %s", ether_sprintf(preq->preq_origaddr));
11794f655ef5SMatthew Dillon #else
11804f655ef5SMatthew Dillon 		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
11814f655ef5SMatthew Dillon 		"reply to %6D", preq->preq_origaddr, ":");
11824f655ef5SMatthew Dillon #endif
1183085ff963SMatthew Dillon 		hwmp_send_prep(vap, wh->i_addr2, &prep);
118432176cfdSRui Paulo 		return;
118532176cfdSRui Paulo 	}
1186085ff963SMatthew Dillon 	/* we may update our proxy information for the orig external */
1187085ff963SMatthew Dillon 	else if (preq->preq_flags & IEEE80211_MESHPREQ_FLAGS_AE) {
1188085ff963SMatthew Dillon 		rtorig_ext =
1189085ff963SMatthew Dillon 		    ieee80211_mesh_rt_find(vap, preq->preq_orig_ext_addr);
1190085ff963SMatthew Dillon 		if (rtorig_ext == NULL) {
1191085ff963SMatthew Dillon 			rtorig_ext = ieee80211_mesh_rt_add(vap,
1192085ff963SMatthew Dillon 			    preq->preq_orig_ext_addr);
1193085ff963SMatthew Dillon 			if (rtorig_ext == NULL) {
11944f655ef5SMatthew Dillon #if defined(__DragonFly__)
1195085ff963SMatthew Dillon 				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1196f92fae3fSSascha Wildner 				    "unable to add orig ext proxy to %s",
1197f92fae3fSSascha Wildner 				    ether_sprintf(preq->preq_orig_ext_addr));
11984f655ef5SMatthew Dillon #else
11994f655ef5SMatthew Dillon 				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
12004f655ef5SMatthew Dillon 				    "unable to add orig ext proxy to %6D",
12014f655ef5SMatthew Dillon 				    preq->preq_orig_ext_addr, ":");
12024f655ef5SMatthew Dillon #endif
1203085ff963SMatthew Dillon 				vap->iv_stats.is_mesh_rtaddfailed++;
1204085ff963SMatthew Dillon 				return;
1205085ff963SMatthew Dillon 			}
1206085ff963SMatthew Dillon 			IEEE80211_ADDR_COPY(rtorig_ext->rt_mesh_gate,
1207085ff963SMatthew Dillon 			    preq->preq_origaddr);
1208085ff963SMatthew Dillon 		}
1209085ff963SMatthew Dillon 		rtorig_ext->rt_ext_seq = preq->preq_origseq;
1210085ff963SMatthew Dillon 		ieee80211_mesh_rt_update(rtorig_ext, preq->preq_lifetime);
1211085ff963SMatthew Dillon 	}
121232176cfdSRui Paulo 	/*
121332176cfdSRui Paulo 	 * Proactive PREQ: reply with a proactive PREP to the
121432176cfdSRui Paulo 	 * root STA if requested.
121532176cfdSRui Paulo 	 */
121632176cfdSRui Paulo 	if (IEEE80211_ADDR_EQ(PREQ_TADDR(0), broadcastaddr) &&
1217085ff963SMatthew Dillon 	    (PREQ_TFLAGS(0) & IEEE80211_MESHPREQ_TFLAGS_TO)) {
12184f655ef5SMatthew Dillon #if defined(__DragonFly__)
1219085ff963SMatthew Dillon 		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1220f92fae3fSSascha Wildner 		    "root mesh station @ %s",
1221f92fae3fSSascha Wildner 		    ether_sprintf(preq->preq_origaddr));
12224f655ef5SMatthew Dillon #else
12234f655ef5SMatthew Dillon 		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
12244f655ef5SMatthew Dillon 		    "root mesh station @ %6D", preq->preq_origaddr, ":");
12254f655ef5SMatthew Dillon #endif
122632176cfdSRui Paulo 
1227085ff963SMatthew Dillon 		/* Check if root is a mesh gate, mark it */
1228085ff963SMatthew Dillon 		if (preq->preq_flags & IEEE80211_MESHPREQ_FLAGS_GATE) {
1229085ff963SMatthew Dillon 			struct ieee80211_mesh_gate_route *gr;
1230085ff963SMatthew Dillon 
1231085ff963SMatthew Dillon 			rtorig->rt_flags |= IEEE80211_MESHRT_FLAGS_GATE;
1232085ff963SMatthew Dillon 			gr = ieee80211_mesh_mark_gate(vap, preq->preq_origaddr,
1233085ff963SMatthew Dillon 			    rtorig);
1234085ff963SMatthew Dillon 			gr->gr_lastseq = 0; /* NOT GANN */
123532176cfdSRui Paulo 		}
123632176cfdSRui Paulo 
123732176cfdSRui Paulo 		/*
123832176cfdSRui Paulo 		 * Reply with a PREP if we don't have a path to the root
123932176cfdSRui Paulo 		 * or if the root sent us a proactive PREQ.
124032176cfdSRui Paulo 		 */
1241085ff963SMatthew Dillon 		if ((rtorig->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0 ||
124232176cfdSRui Paulo 		    (preq->preq_flags & IEEE80211_MESHPREQ_FLAGS_PP)) {
124332176cfdSRui Paulo 			prep.prep_flags = 0;
124432176cfdSRui Paulo 			prep.prep_hopcount = 0;
124532176cfdSRui Paulo 			prep.prep_ttl = ms->ms_ttl;
1246085ff963SMatthew Dillon 			IEEE80211_ADDR_COPY(prep.prep_origaddr,
1247085ff963SMatthew Dillon 			    preq->preq_origaddr);
124832176cfdSRui Paulo 			prep.prep_origseq = preq->preq_origseq;
124932176cfdSRui Paulo 			prep.prep_lifetime = preq->preq_lifetime;
125032176cfdSRui Paulo 			prep.prep_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
125132176cfdSRui Paulo 			IEEE80211_ADDR_COPY(prep.prep_targetaddr,
125232176cfdSRui Paulo 			    vap->iv_myaddr);
125332176cfdSRui Paulo 			prep.prep_targetseq = ++hs->hs_seq;
1254085ff963SMatthew Dillon 			hwmp_send_prep(vap, rtorig->rt_nexthop, &prep);
125532176cfdSRui Paulo 		}
125632176cfdSRui Paulo 	}
125732176cfdSRui Paulo 
125832176cfdSRui Paulo 	/*
125932176cfdSRui Paulo 	 * Forwarding and Intermediate reply for PREQs with 1 target.
126032176cfdSRui Paulo 	 */
1261085ff963SMatthew Dillon 	if ((preq->preq_tcount == 1) && (preq->preq_ttl > 1) &&
1262085ff963SMatthew Dillon 	    (ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) {
126332176cfdSRui Paulo 		struct ieee80211_meshpreq_ie ppreq; /* propagated PREQ */
126432176cfdSRui Paulo 
126532176cfdSRui Paulo 		memcpy(&ppreq, preq, sizeof(ppreq));
1266085ff963SMatthew Dillon 
126732176cfdSRui Paulo 		/*
126832176cfdSRui Paulo 		 * We have a valid route to this node.
1269085ff963SMatthew Dillon 		 * NB: if target is proxy dont reply.
127032176cfdSRui Paulo 		 */
1271085ff963SMatthew Dillon 		if (rttarg != NULL &&
1272085ff963SMatthew Dillon 		    rttarg->rt_flags & IEEE80211_MESHRT_FLAGS_VALID &&
1273085ff963SMatthew Dillon 		    !(rttarg->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY)) {
127432176cfdSRui Paulo 			/*
127532176cfdSRui Paulo 			 * Check if we can send an intermediate Path Reply,
1276085ff963SMatthew Dillon 			 * i.e., Target Only bit is not set and target is not
1277085ff963SMatthew Dillon 			 * the MAC broadcast address.
127832176cfdSRui Paulo 			 */
1279085ff963SMatthew Dillon 			if (!(PREQ_TFLAGS(0) & IEEE80211_MESHPREQ_TFLAGS_TO) &&
1280085ff963SMatthew Dillon 			    !IEEE80211_ADDR_EQ(PREQ_TADDR(0), broadcastaddr)) {
128132176cfdSRui Paulo 				struct ieee80211_meshprep_ie prep;
128232176cfdSRui Paulo 
12834f655ef5SMatthew Dillon #if defined(__DragonFly__)
128432176cfdSRui Paulo 				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1285f92fae3fSSascha Wildner 				    "intermediate reply for PREQ from %s",
1286f92fae3fSSascha Wildner 				    ether_sprintf(preq->preq_origaddr));
12874f655ef5SMatthew Dillon #else
12884f655ef5SMatthew Dillon 				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
12894f655ef5SMatthew Dillon 				    "intermediate reply for PREQ from %6D",
12904f655ef5SMatthew Dillon 				    preq->preq_origaddr, ":");
12914f655ef5SMatthew Dillon #endif
129232176cfdSRui Paulo 				prep.prep_flags = 0;
1293085ff963SMatthew Dillon 				prep.prep_hopcount = rttarg->rt_nhops;
129432176cfdSRui Paulo 				prep.prep_ttl = ms->ms_ttl;
129532176cfdSRui Paulo 				IEEE80211_ADDR_COPY(&prep.prep_targetaddr,
129632176cfdSRui Paulo 				    PREQ_TADDR(0));
1297085ff963SMatthew Dillon 				prep.prep_targetseq = hrtarg->hr_seq;
129832176cfdSRui Paulo 				prep.prep_lifetime = preq->preq_lifetime;
1299085ff963SMatthew Dillon 				prep.prep_metric =rttarg->rt_metric;
130032176cfdSRui Paulo 				IEEE80211_ADDR_COPY(&prep.prep_origaddr,
130132176cfdSRui Paulo 				    preq->preq_origaddr);
130232176cfdSRui Paulo 				prep.prep_origseq = hrorig->hr_seq;
1303085ff963SMatthew Dillon 				hwmp_send_prep(vap, rtorig->rt_nexthop, &prep);
1304085ff963SMatthew Dillon 
130532176cfdSRui Paulo 				/*
1306085ff963SMatthew Dillon 				 * Set TO and unset RF bits because we have
1307085ff963SMatthew Dillon 				 * sent a PREP.
130832176cfdSRui Paulo 				 */
1309085ff963SMatthew Dillon 				ppreq.preq_targets[0].target_flags |=
1310085ff963SMatthew Dillon 				    IEEE80211_MESHPREQ_TFLAGS_TO;
131132176cfdSRui Paulo 			}
131232176cfdSRui Paulo 		}
131332176cfdSRui Paulo 
13144f655ef5SMatthew Dillon #if defined(__DragonFly__)
131532176cfdSRui Paulo 		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1316f92fae3fSSascha Wildner 		    "forward PREQ from %s",
1317f92fae3fSSascha Wildner 		    ether_sprintf(preq->preq_origaddr));
13184f655ef5SMatthew Dillon #else
13194f655ef5SMatthew Dillon 		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
13204f655ef5SMatthew Dillon 		    "forward PREQ from %6D",
13214f655ef5SMatthew Dillon 		    preq->preq_origaddr, ":");
13224f655ef5SMatthew Dillon #endif
132332176cfdSRui Paulo 		ppreq.preq_hopcount += 1;
132432176cfdSRui Paulo 		ppreq.preq_ttl -= 1;
132532176cfdSRui Paulo 		ppreq.preq_metric += ms->ms_pmetric->mpm_metric(ni);
132632176cfdSRui Paulo 
1327085ff963SMatthew Dillon 		/* don't do PREQ ratecheck when we propagate */
1328085ff963SMatthew Dillon 		hwmp_send_preq(vap, broadcastaddr, &ppreq, NULL, NULL);
1329085ff963SMatthew Dillon 	}
133032176cfdSRui Paulo }
133132176cfdSRui Paulo #undef	PREQ_TFLAGS
133232176cfdSRui Paulo #undef	PREQ_TADDR
133332176cfdSRui Paulo #undef	PREQ_TSEQ
133432176cfdSRui Paulo 
133532176cfdSRui Paulo static int
hwmp_send_preq(struct ieee80211vap * vap,const uint8_t da[IEEE80211_ADDR_LEN],struct ieee80211_meshpreq_ie * preq,struct timeval * last,struct timeval * minint)1336085ff963SMatthew Dillon hwmp_send_preq(struct ieee80211vap *vap,
133732176cfdSRui Paulo     const uint8_t da[IEEE80211_ADDR_LEN],
1338085ff963SMatthew Dillon     struct ieee80211_meshpreq_ie *preq,
1339085ff963SMatthew Dillon     struct timeval *last, struct timeval *minint)
134032176cfdSRui Paulo {
134132176cfdSRui Paulo 
134232176cfdSRui Paulo 	/*
134332176cfdSRui Paulo 	 * Enforce PREQ interval.
1344085ff963SMatthew Dillon 	 * NB: Proactive ROOT PREQs rate is handled by cb task.
134532176cfdSRui Paulo 	 */
1346085ff963SMatthew Dillon 	if (last != NULL && minint != NULL) {
1347085ff963SMatthew Dillon 		if (ratecheck(last, minint) == 0)
1348085ff963SMatthew Dillon 			return EALREADY; /* XXX: we should postpone */
1349085ff963SMatthew Dillon 		getmicrouptime(last);
1350085ff963SMatthew Dillon 	}
135132176cfdSRui Paulo 
135232176cfdSRui Paulo 	/*
135332176cfdSRui Paulo 	 * mesh preq action frame format
135432176cfdSRui Paulo 	 *     [6] da
135532176cfdSRui Paulo 	 *     [6] sa
135632176cfdSRui Paulo 	 *     [6] addr3 = sa
135732176cfdSRui Paulo 	 *     [1] action
135832176cfdSRui Paulo 	 *     [1] category
135932176cfdSRui Paulo 	 *     [tlv] mesh path request
136032176cfdSRui Paulo 	 */
136132176cfdSRui Paulo 	preq->preq_ie = IEEE80211_ELEMID_MESHPREQ;
1362085ff963SMatthew Dillon 	preq->preq_len = (preq->preq_flags & IEEE80211_MESHPREQ_FLAGS_AE ?
1363085ff963SMatthew Dillon 	    IEEE80211_MESHPREQ_BASE_SZ_AE : IEEE80211_MESHPREQ_BASE_SZ) +
1364085ff963SMatthew Dillon 	    preq->preq_tcount * IEEE80211_MESHPREQ_TRGT_SZ;
1365085ff963SMatthew Dillon 	return hwmp_send_action(vap, da, (uint8_t *)preq, preq->preq_len+2);
136632176cfdSRui Paulo }
136732176cfdSRui Paulo 
136832176cfdSRui Paulo static void
hwmp_recv_prep(struct ieee80211vap * vap,struct ieee80211_node * ni,const struct ieee80211_frame * wh,const struct ieee80211_meshprep_ie * prep)136932176cfdSRui Paulo hwmp_recv_prep(struct ieee80211vap *vap, struct ieee80211_node *ni,
137032176cfdSRui Paulo     const struct ieee80211_frame *wh, const struct ieee80211_meshprep_ie *prep)
137132176cfdSRui Paulo {
1372085ff963SMatthew Dillon #define	IS_PROXY(rt)	(rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY)
1373085ff963SMatthew Dillon #define	PROXIED_BY_US(rt)		\
1374085ff963SMatthew Dillon     (IEEE80211_ADDR_EQ(vap->iv_myaddr, rt->rt_mesh_gate))
137532176cfdSRui Paulo 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
137632176cfdSRui Paulo 	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
137732176cfdSRui Paulo 	struct ieee80211_mesh_route *rt = NULL;
1378085ff963SMatthew Dillon 	struct ieee80211_mesh_route *rtorig = NULL;
1379085ff963SMatthew Dillon 	struct ieee80211_mesh_route *rtext = NULL;
138032176cfdSRui Paulo 	struct ieee80211_hwmp_route *hr;
138132176cfdSRui Paulo 	struct ieee80211com *ic = vap->iv_ic;
138232176cfdSRui Paulo 	struct mbuf *m, *next;
1383085ff963SMatthew Dillon 	uint32_t metric = 0;
1384085ff963SMatthew Dillon 	const uint8_t *addr;
138532176cfdSRui Paulo 
13864f655ef5SMatthew Dillon #if defined(__DragonFly__)
138732176cfdSRui Paulo 	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1388f92fae3fSSascha Wildner 	    "received PREP, orig %s, targ %s",
1389f92fae3fSSascha Wildner 	    ether_sprintf(prep->prep_origaddr),
1390f92fae3fSSascha Wildner 	    ether_sprintf(prep->prep_targetaddr));
13914f655ef5SMatthew Dillon #else
13924f655ef5SMatthew Dillon 	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
13934f655ef5SMatthew Dillon 	    "received PREP, orig %6D, targ %6D", prep->prep_origaddr, ":",
13944f655ef5SMatthew Dillon 	    prep->prep_targetaddr, ":");
13954f655ef5SMatthew Dillon #endif
139632176cfdSRui Paulo 
1397085ff963SMatthew Dillon 	/*
1398085ff963SMatthew Dillon 	 * Acceptance criteria: (If the corresponding PREP was not generated
1399085ff963SMatthew Dillon 	 * by us OR not generated by an external mac that is not proxied by us)
1400085ff963SMatthew Dillon 	 * AND forwarding is disabled, discard this PREP.
1401085ff963SMatthew Dillon 	 */
1402085ff963SMatthew Dillon 	rtorig = ieee80211_mesh_rt_find(vap, prep->prep_origaddr);
1403085ff963SMatthew Dillon 	if ((!IEEE80211_ADDR_EQ(vap->iv_myaddr, prep->prep_origaddr) ||
1404085ff963SMatthew Dillon 	    (rtorig != NULL && IS_PROXY(rtorig) && !PROXIED_BY_US(rtorig))) &&
1405085ff963SMatthew Dillon 	    !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD)){
14064f655ef5SMatthew Dillon #if defined(__DragonFly__)
1407085ff963SMatthew Dillon 		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1408f92fae3fSSascha Wildner 		    "discard PREP, orig(%s) not proxied or generated by us",
1409f92fae3fSSascha Wildner 		    ether_sprintf(prep->prep_origaddr));
14104f655ef5SMatthew Dillon #else
14114f655ef5SMatthew Dillon 		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
14124f655ef5SMatthew Dillon 		    "discard PREP, orig(%6D) not proxied or generated by us",
14134f655ef5SMatthew Dillon 		    prep->prep_origaddr, ":");
14144f655ef5SMatthew Dillon #endif
1415085ff963SMatthew Dillon 		return;
1416085ff963SMatthew Dillon 	}
1417085ff963SMatthew Dillon 
1418085ff963SMatthew Dillon 	/* PREP ACCEPTED */
1419085ff963SMatthew Dillon 
1420085ff963SMatthew Dillon 	/*
1421085ff963SMatthew Dillon 	 * If accepted shall create or update the active forwarding information
1422085ff963SMatthew Dillon 	 * it maintains for the target mesh STA of the PREP (according to the
1423085ff963SMatthew Dillon 	 * rules defined in 13.10.8.4). If the conditions for creating or
1424085ff963SMatthew Dillon 	 * updating the forwarding information have not been met in those
1425085ff963SMatthew Dillon 	 * rules, no further steps are applied to the PREP.
1426085ff963SMatthew Dillon 	 */
142732176cfdSRui Paulo 	rt = ieee80211_mesh_rt_find(vap, prep->prep_targetaddr);
142832176cfdSRui Paulo 	if (rt == NULL) {
142932176cfdSRui Paulo 		rt = ieee80211_mesh_rt_add(vap, prep->prep_targetaddr);
143032176cfdSRui Paulo 		if (rt == NULL) {
14314f655ef5SMatthew Dillon #if defined(__DragonFly__)
1432085ff963SMatthew Dillon 			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1433f92fae3fSSascha Wildner 			    "unable to add PREP path to %s",
1434f92fae3fSSascha Wildner 			    ether_sprintf(prep->prep_targetaddr));
14354f655ef5SMatthew Dillon #else
14364f655ef5SMatthew Dillon 			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
14374f655ef5SMatthew Dillon 			    "unable to add PREP path to %6D",
14384f655ef5SMatthew Dillon 			    prep->prep_targetaddr, ":");
14394f655ef5SMatthew Dillon #endif
144032176cfdSRui Paulo 			vap->iv_stats.is_mesh_rtaddfailed++;
144132176cfdSRui Paulo 			return;
144232176cfdSRui Paulo 		}
14434f655ef5SMatthew Dillon #if defined(__DragonFly__)
144432176cfdSRui Paulo 		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1445f92fae3fSSascha Wildner 		    "adding target %s", ether_sprintf(prep->prep_targetaddr));
14464f655ef5SMatthew Dillon #else
14474f655ef5SMatthew Dillon 		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
14484f655ef5SMatthew Dillon 		    "adding target %6D", prep->prep_targetaddr, ":");
14494f655ef5SMatthew Dillon #endif
145032176cfdSRui Paulo 	}
145132176cfdSRui Paulo 	hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
1452085ff963SMatthew Dillon 	/* update path metric */
1453085ff963SMatthew Dillon 	metric = prep->prep_metric + ms->ms_pmetric->mpm_metric(ni);
1454085ff963SMatthew Dillon 	if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID)) {
1455085ff963SMatthew Dillon 		if (HWMP_SEQ_LT(prep->prep_targetseq, hr->hr_seq)) {
14564f655ef5SMatthew Dillon #if defined(__DragonFly__)
145732176cfdSRui Paulo 			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1458f92fae3fSSascha Wildner 			    "discard PREP from %s, old seq no %u < %u",
1459f92fae3fSSascha Wildner 			    ether_sprintf(prep->prep_targetaddr),
146032176cfdSRui Paulo 			    prep->prep_targetseq, hr->hr_seq);
14614f655ef5SMatthew Dillon #else
14624f655ef5SMatthew Dillon 			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
14634f655ef5SMatthew Dillon 			    "discard PREP from %6D, old seq no %u < %u",
14644f655ef5SMatthew Dillon 			    prep->prep_targetaddr, ":",
14654f655ef5SMatthew Dillon 			    prep->prep_targetseq, hr->hr_seq);
14664f655ef5SMatthew Dillon #endif
146732176cfdSRui Paulo 			return;
1468085ff963SMatthew Dillon 		} else if (HWMP_SEQ_LEQ(prep->prep_targetseq, hr->hr_seq) &&
1469085ff963SMatthew Dillon 		    metric > rt->rt_metric) {
14704f655ef5SMatthew Dillon #if defined(__DragonFly__)
1471085ff963SMatthew Dillon 			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1472f92fae3fSSascha Wildner 			    "discard PREP from %s, new metric %u > %u",
1473f92fae3fSSascha Wildner 			    ether_sprintf(prep->prep_targetaddr),
1474085ff963SMatthew Dillon 			    metric, rt->rt_metric);
14754f655ef5SMatthew Dillon #else
14764f655ef5SMatthew Dillon 			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
14774f655ef5SMatthew Dillon 			    "discard PREP from %6D, new metric %u > %u",
14784f655ef5SMatthew Dillon 			    prep->prep_targetaddr, ":",
14794f655ef5SMatthew Dillon 			    metric, rt->rt_metric);
14804f655ef5SMatthew Dillon #endif
1481085ff963SMatthew Dillon 			return;
148232176cfdSRui Paulo 		}
1483085ff963SMatthew Dillon 	}
148432176cfdSRui Paulo 
14854f655ef5SMatthew Dillon #if defined(__DragonFly__)
148632176cfdSRui Paulo 	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1487f92fae3fSSascha Wildner 	    "%s path to %s, hopcount %d:%d metric %d:%d",
1488085ff963SMatthew Dillon 	    rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID ?
1489085ff963SMatthew Dillon 	    "prefer" : "update",
1490f92fae3fSSascha Wildner 	    ether_sprintf(prep->prep_targetaddr),
1491085ff963SMatthew Dillon 	    rt->rt_nhops, prep->prep_hopcount + 1,
1492085ff963SMatthew Dillon 	    rt->rt_metric, metric);
14934f655ef5SMatthew Dillon #else
14944f655ef5SMatthew Dillon 	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
14954f655ef5SMatthew Dillon 	    "%s path to %6D, hopcount %d:%d metric %d:%d",
14964f655ef5SMatthew Dillon 	    rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID ?
14974f655ef5SMatthew Dillon 	    "prefer" : "update",
14984f655ef5SMatthew Dillon 	    prep->prep_targetaddr, ":",
14994f655ef5SMatthew Dillon 	    rt->rt_nhops, prep->prep_hopcount + 1,
15004f655ef5SMatthew Dillon 	    rt->rt_metric, metric);
15014f655ef5SMatthew Dillon #endif
1502085ff963SMatthew Dillon 
1503085ff963SMatthew Dillon 	hr->hr_seq = prep->prep_targetseq;
1504085ff963SMatthew Dillon 	hr->hr_preqretries = 0;
1505085ff963SMatthew Dillon 	IEEE80211_ADDR_COPY(rt->rt_nexthop, ni->ni_macaddr);
1506085ff963SMatthew Dillon 	rt->rt_metric = metric;
1507085ff963SMatthew Dillon 	rt->rt_nhops = prep->prep_hopcount + 1;
1508085ff963SMatthew Dillon 	ieee80211_mesh_rt_update(rt, prep->prep_lifetime);
1509085ff963SMatthew Dillon 	if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_DISCOVER) {
1510085ff963SMatthew Dillon 		/* discovery complete */
1511085ff963SMatthew Dillon 		rt->rt_flags &= ~IEEE80211_MESHRT_FLAGS_DISCOVER;
1512085ff963SMatthew Dillon 	}
1513085ff963SMatthew Dillon 	rt->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID; /* mark valid */
1514085ff963SMatthew Dillon 
1515085ff963SMatthew Dillon 	/* Update forwarding information to TA if metric improves */
1516085ff963SMatthew Dillon 	hwmp_update_transmitter(vap, ni, "PREP");
1517085ff963SMatthew Dillon 
1518085ff963SMatthew Dillon 	/*
1519085ff963SMatthew Dillon 	 * If it's NOT for us, propagate the PREP
1520085ff963SMatthew Dillon 	 */
1521085ff963SMatthew Dillon 	if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, prep->prep_origaddr) &&
1522085ff963SMatthew Dillon 	    prep->prep_ttl > 1 &&
1523085ff963SMatthew Dillon 	    prep->prep_hopcount < hs->hs_maxhops) {
1524085ff963SMatthew Dillon 		struct ieee80211_meshprep_ie pprep; /* propagated PREP */
1525085ff963SMatthew Dillon 		/*
1526085ff963SMatthew Dillon 		 * NB: We should already have setup the path to orig
1527085ff963SMatthew Dillon 		 * mesh STA when we propagated PREQ to target mesh STA,
1528085ff963SMatthew Dillon 		 * no PREP is generated without a corresponding PREQ.
1529085ff963SMatthew Dillon 		 * XXX: for now just ignore.
1530085ff963SMatthew Dillon 		 */
1531085ff963SMatthew Dillon 		if (rtorig == NULL) {
15324f655ef5SMatthew Dillon #if defined(__DragonFly__)
1533085ff963SMatthew Dillon 			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1534f92fae3fSSascha Wildner 			    "received PREP for an unknown orig(%s)",
1535f92fae3fSSascha Wildner 			    ether_sprintf(prep->prep_origaddr));
15364f655ef5SMatthew Dillon #else
15374f655ef5SMatthew Dillon 			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
15384f655ef5SMatthew Dillon 			    "received PREP for an unknown orig(%6D)",
15394f655ef5SMatthew Dillon 			    prep->prep_origaddr, ":");
15404f655ef5SMatthew Dillon #endif
1541085ff963SMatthew Dillon 			return;
1542085ff963SMatthew Dillon 		}
1543085ff963SMatthew Dillon 
15444f655ef5SMatthew Dillon #if defined(__DragonFly__)
1545085ff963SMatthew Dillon 		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1546f92fae3fSSascha Wildner 		    "propagate PREP from %s",
1547f92fae3fSSascha Wildner 		    ether_sprintf(prep->prep_targetaddr));
15484f655ef5SMatthew Dillon #else
15494f655ef5SMatthew Dillon 		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
15504f655ef5SMatthew Dillon 		    "propagate PREP from %6D",
15514f655ef5SMatthew Dillon 		    prep->prep_targetaddr, ":");
15524f655ef5SMatthew Dillon #endif
155332176cfdSRui Paulo 
155432176cfdSRui Paulo 		memcpy(&pprep, prep, sizeof(pprep));
155532176cfdSRui Paulo 		pprep.prep_hopcount += 1;
155632176cfdSRui Paulo 		pprep.prep_ttl -= 1;
155732176cfdSRui Paulo 		pprep.prep_metric += ms->ms_pmetric->mpm_metric(ni);
1558085ff963SMatthew Dillon 		hwmp_send_prep(vap, rtorig->rt_nexthop, &pprep);
1559085ff963SMatthew Dillon 
1560085ff963SMatthew Dillon 		/* precursor list for the Target Mesh STA Address is updated */
156132176cfdSRui Paulo 	}
1562085ff963SMatthew Dillon 
156332176cfdSRui Paulo 	/*
1564085ff963SMatthew Dillon 	 * Check if we received a PREP w/ AE and store target external address.
1565085ff963SMatthew Dillon 	 * We may store target external address if recevied PREP w/ AE
1566085ff963SMatthew Dillon 	 * and we are not final destination
156732176cfdSRui Paulo 	 */
1568085ff963SMatthew Dillon 	if (prep->prep_flags & IEEE80211_MESHPREP_FLAGS_AE) {
1569085ff963SMatthew Dillon 		rtext = ieee80211_mesh_rt_find(vap,
1570085ff963SMatthew Dillon 			prep->prep_target_ext_addr);
1571085ff963SMatthew Dillon 		if (rtext == NULL) {
1572085ff963SMatthew Dillon 			rtext = ieee80211_mesh_rt_add(vap,
1573085ff963SMatthew Dillon 				prep->prep_target_ext_addr);
1574085ff963SMatthew Dillon 			if (rtext == NULL) {
15754f655ef5SMatthew Dillon #if defined(__DragonFly__)
157632176cfdSRui Paulo 				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1577f92fae3fSSascha Wildner 				    "unable to add PREP path to proxy %s",
1578f92fae3fSSascha Wildner 				    ether_sprintf(prep->prep_targetaddr));
15794f655ef5SMatthew Dillon #else
15804f655ef5SMatthew Dillon 				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
15814f655ef5SMatthew Dillon 				    "unable to add PREP path to proxy %6D",
15824f655ef5SMatthew Dillon 				    prep->prep_targetaddr, ":");
15834f655ef5SMatthew Dillon #endif
1584085ff963SMatthew Dillon 				vap->iv_stats.is_mesh_rtaddfailed++;
1585085ff963SMatthew Dillon 				return;
158632176cfdSRui Paulo 			}
1587085ff963SMatthew Dillon 		}
15884f655ef5SMatthew Dillon #if defined(__DragonFly__)
158932176cfdSRui Paulo 		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1590f92fae3fSSascha Wildner 		    "%s path to %s, hopcount %d:%d metric %d:%d",
1591085ff963SMatthew Dillon 		    rtext->rt_flags & IEEE80211_MESHRT_FLAGS_VALID ?
1592085ff963SMatthew Dillon 		    "prefer" : "update",
1593f92fae3fSSascha Wildner 		    ether_sprintf(prep->prep_target_ext_addr),
1594085ff963SMatthew Dillon 		    rtext->rt_nhops, prep->prep_hopcount + 1,
1595085ff963SMatthew Dillon 		    rtext->rt_metric, metric);
15964f655ef5SMatthew Dillon #else
15974f655ef5SMatthew Dillon 		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
15984f655ef5SMatthew Dillon 		    "%s path to %6D, hopcount %d:%d metric %d:%d",
15994f655ef5SMatthew Dillon 		    rtext->rt_flags & IEEE80211_MESHRT_FLAGS_VALID ?
16004f655ef5SMatthew Dillon 		    "prefer" : "update",
16014f655ef5SMatthew Dillon 		    prep->prep_target_ext_addr, ":",
16024f655ef5SMatthew Dillon 		    rtext->rt_nhops, prep->prep_hopcount + 1,
16034f655ef5SMatthew Dillon 		    rtext->rt_metric, metric);
16044f655ef5SMatthew Dillon #endif
1605085ff963SMatthew Dillon 
1606085ff963SMatthew Dillon 		rtext->rt_flags = IEEE80211_MESHRT_FLAGS_PROXY |
1607085ff963SMatthew Dillon 			IEEE80211_MESHRT_FLAGS_VALID;
1608085ff963SMatthew Dillon 		IEEE80211_ADDR_COPY(rtext->rt_dest,
1609085ff963SMatthew Dillon 		    prep->prep_target_ext_addr);
1610085ff963SMatthew Dillon 		IEEE80211_ADDR_COPY(rtext->rt_mesh_gate,
1611085ff963SMatthew Dillon 		    prep->prep_targetaddr);
1612085ff963SMatthew Dillon 		IEEE80211_ADDR_COPY(rtext->rt_nexthop, wh->i_addr2);
1613085ff963SMatthew Dillon 		rtext->rt_metric = metric;
1614085ff963SMatthew Dillon 		rtext->rt_lifetime = prep->prep_lifetime;
1615085ff963SMatthew Dillon 		rtext->rt_nhops = prep->prep_hopcount + 1;
1616085ff963SMatthew Dillon 		rtext->rt_ext_seq = prep->prep_origseq; /* new proxy seq */
1617085ff963SMatthew Dillon 		/*
1618085ff963SMatthew Dillon 		 * XXX: proxy entries have no HWMP priv data,
1619085ff963SMatthew Dillon 		 * nullify them to be sure?
1620085ff963SMatthew Dillon 		 */
162132176cfdSRui Paulo 	}
162232176cfdSRui Paulo 	/*
162332176cfdSRui Paulo 	 * Check for frames queued awaiting path discovery.
162432176cfdSRui Paulo 	 * XXX probably can tell exactly and avoid remove call
162532176cfdSRui Paulo 	 * NB: hash may have false matches, if so they will get
162632176cfdSRui Paulo 	 *     stuck back on the stageq because there won't be
162732176cfdSRui Paulo 	 *     a path.
162832176cfdSRui Paulo 	 */
1629085ff963SMatthew Dillon 	addr = prep->prep_flags & IEEE80211_MESHPREP_FLAGS_AE ?
1630085ff963SMatthew Dillon 	    prep->prep_target_ext_addr : prep->prep_targetaddr;
163132176cfdSRui Paulo 	m = ieee80211_ageq_remove(&ic->ic_stageq,
163232176cfdSRui Paulo 	    (struct ieee80211_node *)(uintptr_t)
1633085ff963SMatthew Dillon 	    ieee80211_mac_hash(ic, addr)); /* either dest or ext_dest */
1634085ff963SMatthew Dillon 
1635085ff963SMatthew Dillon 	/*
1636085ff963SMatthew Dillon 	 * All frames in the stageq here should be non-M_ENCAP; or things
1637085ff963SMatthew Dillon 	 * will get very unhappy.
1638085ff963SMatthew Dillon 	 */
163932176cfdSRui Paulo 	for (; m != NULL; m = next) {
164032176cfdSRui Paulo 		next = m->m_nextpkt;
164132176cfdSRui Paulo 		m->m_nextpkt = NULL;
164232176cfdSRui Paulo 		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
164332176cfdSRui Paulo 		    "flush queued frame %p len %d", m, m->m_pkthdr.len);
1644085ff963SMatthew Dillon 		/*
1645085ff963SMatthew Dillon 		 * If the mbuf has M_ENCAP set, ensure we free it.
1646085ff963SMatthew Dillon 		 * Note that after if_transmit() is called, m is invalid.
1647085ff963SMatthew Dillon 		 */
1648085ff963SMatthew Dillon 		(void) ieee80211_vap_xmitpkt(vap, m);
164932176cfdSRui Paulo 	}
1650085ff963SMatthew Dillon #undef	IS_PROXY
1651085ff963SMatthew Dillon #undef	PROXIED_BY_US
165232176cfdSRui Paulo }
165332176cfdSRui Paulo 
165432176cfdSRui Paulo static int
hwmp_send_prep(struct ieee80211vap * vap,const uint8_t da[IEEE80211_ADDR_LEN],struct ieee80211_meshprep_ie * prep)1655085ff963SMatthew Dillon hwmp_send_prep(struct ieee80211vap *vap,
165632176cfdSRui Paulo     const uint8_t da[IEEE80211_ADDR_LEN],
165732176cfdSRui Paulo     struct ieee80211_meshprep_ie *prep)
165832176cfdSRui Paulo {
165932176cfdSRui Paulo 	/* NB: there's no PREP minimum interval. */
166032176cfdSRui Paulo 
166132176cfdSRui Paulo 	/*
166232176cfdSRui Paulo 	 * mesh prep action frame format
166332176cfdSRui Paulo 	 *     [6] da
166432176cfdSRui Paulo 	 *     [6] sa
166532176cfdSRui Paulo 	 *     [6] addr3 = sa
166632176cfdSRui Paulo 	 *     [1] action
166732176cfdSRui Paulo 	 *     [1] category
166832176cfdSRui Paulo 	 *     [tlv] mesh path reply
166932176cfdSRui Paulo 	 */
167032176cfdSRui Paulo 	prep->prep_ie = IEEE80211_ELEMID_MESHPREP;
1671085ff963SMatthew Dillon 	prep->prep_len = prep->prep_flags & IEEE80211_MESHPREP_FLAGS_AE ?
1672085ff963SMatthew Dillon 	    IEEE80211_MESHPREP_BASE_SZ_AE : IEEE80211_MESHPREP_BASE_SZ;
1673085ff963SMatthew Dillon 	return hwmp_send_action(vap, da, (uint8_t *)prep, prep->prep_len + 2);
167432176cfdSRui Paulo }
167532176cfdSRui Paulo 
167632176cfdSRui Paulo #define	PERR_DFLAGS(n)	perr.perr_dests[n].dest_flags
167732176cfdSRui Paulo #define	PERR_DADDR(n)	perr.perr_dests[n].dest_addr
167832176cfdSRui Paulo #define	PERR_DSEQ(n)	perr.perr_dests[n].dest_seq
167932176cfdSRui Paulo #define	PERR_DRCODE(n)	perr.perr_dests[n].dest_rcode
168032176cfdSRui Paulo static void
hwmp_peerdown(struct ieee80211_node * ni)168132176cfdSRui Paulo hwmp_peerdown(struct ieee80211_node *ni)
168232176cfdSRui Paulo {
168332176cfdSRui Paulo 	struct ieee80211vap *vap = ni->ni_vap;
168432176cfdSRui Paulo 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
168532176cfdSRui Paulo 	struct ieee80211_meshperr_ie perr;
168632176cfdSRui Paulo 	struct ieee80211_mesh_route *rt;
168732176cfdSRui Paulo 	struct ieee80211_hwmp_route *hr;
168832176cfdSRui Paulo 
168932176cfdSRui Paulo 	rt = ieee80211_mesh_rt_find(vap, ni->ni_macaddr);
169032176cfdSRui Paulo 	if (rt == NULL)
169132176cfdSRui Paulo 		return;
169232176cfdSRui Paulo 	hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
169332176cfdSRui Paulo 	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
169432176cfdSRui Paulo 	    "%s", "delete route entry");
169532176cfdSRui Paulo 	perr.perr_ttl = ms->ms_ttl;
169632176cfdSRui Paulo 	perr.perr_ndests = 1;
169732176cfdSRui Paulo 	PERR_DFLAGS(0) = 0;
169832176cfdSRui Paulo 	if (hr->hr_seq == 0)
169932176cfdSRui Paulo 		PERR_DFLAGS(0) |= IEEE80211_MESHPERR_DFLAGS_USN;
170032176cfdSRui Paulo 	PERR_DFLAGS(0) |= IEEE80211_MESHPERR_DFLAGS_RC;
170132176cfdSRui Paulo 	IEEE80211_ADDR_COPY(PERR_DADDR(0), rt->rt_dest);
1702085ff963SMatthew Dillon 	PERR_DSEQ(0) = ++hr->hr_seq;
170332176cfdSRui Paulo 	PERR_DRCODE(0) = IEEE80211_REASON_MESH_PERR_DEST_UNREACH;
170432176cfdSRui Paulo 	/* NB: flush everything passing through peer */
170532176cfdSRui Paulo 	ieee80211_mesh_rt_flush_peer(vap, ni->ni_macaddr);
1706085ff963SMatthew Dillon 	hwmp_send_perr(vap, broadcastaddr, &perr);
170732176cfdSRui Paulo }
170832176cfdSRui Paulo #undef	PERR_DFLAGS
170932176cfdSRui Paulo #undef	PERR_DADDR
171032176cfdSRui Paulo #undef	PERR_DSEQ
171132176cfdSRui Paulo #undef	PERR_DRCODE
171232176cfdSRui Paulo 
171332176cfdSRui Paulo #define	PERR_DFLAGS(n)		perr->perr_dests[n].dest_flags
171432176cfdSRui Paulo #define	PERR_DADDR(n)		perr->perr_dests[n].dest_addr
171532176cfdSRui Paulo #define	PERR_DSEQ(n)		perr->perr_dests[n].dest_seq
1716085ff963SMatthew Dillon #define	PERR_DEXTADDR(n)	perr->perr_dests[n].dest_ext_addr
171732176cfdSRui Paulo #define	PERR_DRCODE(n)		perr->perr_dests[n].dest_rcode
171832176cfdSRui Paulo static void
hwmp_recv_perr(struct ieee80211vap * vap,struct ieee80211_node * ni,const struct ieee80211_frame * wh,const struct ieee80211_meshperr_ie * perr)171932176cfdSRui Paulo hwmp_recv_perr(struct ieee80211vap *vap, struct ieee80211_node *ni,
172032176cfdSRui Paulo     const struct ieee80211_frame *wh, const struct ieee80211_meshperr_ie *perr)
172132176cfdSRui Paulo {
172232176cfdSRui Paulo 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
172332176cfdSRui Paulo 	struct ieee80211_mesh_route *rt = NULL;
1724085ff963SMatthew Dillon 	struct ieee80211_mesh_route *rt_ext = NULL;
172532176cfdSRui Paulo 	struct ieee80211_hwmp_route *hr;
1726085ff963SMatthew Dillon 	struct ieee80211_meshperr_ie *pperr = NULL;
1727085ff963SMatthew Dillon 	int i, j = 0, forward = 0;
1728085ff963SMatthew Dillon 
17294f655ef5SMatthew Dillon #if defined(__DragonFly__)
1730085ff963SMatthew Dillon 	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1731f92fae3fSSascha Wildner 	    "received PERR from %s", ether_sprintf(wh->i_addr2));
17324f655ef5SMatthew Dillon #else
17334f655ef5SMatthew Dillon 	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
17344f655ef5SMatthew Dillon 	    "received PERR from %6D", wh->i_addr2, ":");
17354f655ef5SMatthew Dillon #endif
173632176cfdSRui Paulo 
173732176cfdSRui Paulo 	/*
1738085ff963SMatthew Dillon 	 * if forwarding is true, prepare pperr
173932176cfdSRui Paulo 	 */
1740085ff963SMatthew Dillon 	if (ms->ms_flags & IEEE80211_MESHFLAGS_FWD) {
1741085ff963SMatthew Dillon 		forward = 1;
17424f655ef5SMatthew Dillon #if defined(__DragonFly__)
1743085ff963SMatthew Dillon 		pperr = kmalloc(sizeof(*perr) + 31*sizeof(*perr->perr_dests),
1744085ff963SMatthew Dillon 		    M_80211_MESH_PERR, M_INTWAIT); /* XXX: magic number, 32 err dests */
17454f655ef5SMatthew Dillon #else
17464f655ef5SMatthew Dillon 		pperr = IEEE80211_MALLOC(sizeof(*perr) + 31*sizeof(*perr->perr_dests),
17474f655ef5SMatthew Dillon 		    M_80211_MESH_PERR, IEEE80211_M_NOWAIT); /* XXX: magic number, 32 err dests */
17484f655ef5SMatthew Dillon #endif
1749085ff963SMatthew Dillon 	}
1750085ff963SMatthew Dillon 
175132176cfdSRui Paulo 	/*
1752085ff963SMatthew Dillon 	 * Acceptance criteria: check if we have forwarding information
1753085ff963SMatthew Dillon 	 * stored about destination, and that nexthop == TA of this PERR.
1754085ff963SMatthew Dillon 	 * NB: we also build a new PERR to propagate in case we should forward.
175532176cfdSRui Paulo 	 */
175632176cfdSRui Paulo 	for (i = 0; i < perr->perr_ndests; i++) {
175732176cfdSRui Paulo 		rt = ieee80211_mesh_rt_find(vap, PERR_DADDR(i));
175832176cfdSRui Paulo 		if (rt == NULL)
175932176cfdSRui Paulo 			continue;
1760085ff963SMatthew Dillon 		if (!IEEE80211_ADDR_EQ(rt->rt_nexthop, wh->i_addr2))
1761085ff963SMatthew Dillon 			continue;
1762085ff963SMatthew Dillon 
1763085ff963SMatthew Dillon 		/* found and accepted a PERR ndest element, process it... */
1764085ff963SMatthew Dillon 		if (forward)
1765085ff963SMatthew Dillon 			memcpy(&pperr->perr_dests[j], &perr->perr_dests[i],
1766085ff963SMatthew Dillon 			    sizeof(*perr->perr_dests));
176732176cfdSRui Paulo 		hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
1768085ff963SMatthew Dillon 		switch(PERR_DFLAGS(i)) {
1769085ff963SMatthew Dillon 		case (IEEE80211_REASON_MESH_PERR_NO_FI):
1770085ff963SMatthew Dillon 			if (PERR_DSEQ(i) == 0) {
1771085ff963SMatthew Dillon 				hr->hr_seq++;
1772085ff963SMatthew Dillon 				if (forward) {
1773085ff963SMatthew Dillon 					pperr->perr_dests[j].dest_seq =
1774085ff963SMatthew Dillon 					    hr->hr_seq;
177532176cfdSRui Paulo 				}
1776085ff963SMatthew Dillon 			} else {
1777085ff963SMatthew Dillon 				hr->hr_seq = PERR_DSEQ(i);
177832176cfdSRui Paulo 			}
1779085ff963SMatthew Dillon 			rt->rt_flags &= ~IEEE80211_MESHRT_FLAGS_VALID;
1780085ff963SMatthew Dillon 			j++;
1781085ff963SMatthew Dillon 			break;
1782085ff963SMatthew Dillon 		case (IEEE80211_REASON_MESH_PERR_DEST_UNREACH):
1783085ff963SMatthew Dillon 			if(HWMP_SEQ_GT(PERR_DSEQ(i), hr->hr_seq)) {
1784085ff963SMatthew Dillon 				hr->hr_seq = PERR_DSEQ(i);
1785085ff963SMatthew Dillon 				rt->rt_flags &= ~IEEE80211_MESHRT_FLAGS_VALID;
1786085ff963SMatthew Dillon 				j++;
1787085ff963SMatthew Dillon 			}
1788085ff963SMatthew Dillon 			break;
1789085ff963SMatthew Dillon 		case (IEEE80211_REASON_MESH_PERR_NO_PROXY):
1790085ff963SMatthew Dillon 			rt_ext = ieee80211_mesh_rt_find(vap, PERR_DEXTADDR(i));
1791085ff963SMatthew Dillon 			if (rt_ext != NULL) {
1792085ff963SMatthew Dillon 				rt_ext->rt_flags &=
1793085ff963SMatthew Dillon 				    ~IEEE80211_MESHRT_FLAGS_VALID;
1794085ff963SMatthew Dillon 				j++;
1795085ff963SMatthew Dillon 			}
1796085ff963SMatthew Dillon 			break;
1797085ff963SMatthew Dillon 		default:
1798085ff963SMatthew Dillon 			IEEE80211_DISCARD(vap, IEEE80211_MSG_HWMP, wh, NULL,
1799085ff963SMatthew Dillon 			    "PERR, unknown reason code %u\n", PERR_DFLAGS(i));
1800085ff963SMatthew Dillon 			goto done; /* XXX: stats?? */
1801085ff963SMatthew Dillon 		}
1802085ff963SMatthew Dillon 		ieee80211_mesh_rt_flush_peer(vap, PERR_DADDR(i));
1803085ff963SMatthew Dillon 		KASSERT(j < 32, ("PERR, error ndest >= 32 (%u)", j));
1804085ff963SMatthew Dillon 	}
1805085ff963SMatthew Dillon 	if (j == 0) {
1806085ff963SMatthew Dillon 		IEEE80211_DISCARD(vap, IEEE80211_MSG_HWMP, wh, NULL, "%s",
1807085ff963SMatthew Dillon 		    "PERR not accepted");
1808085ff963SMatthew Dillon 		goto done; /* XXX: stats?? */
1809085ff963SMatthew Dillon 	}
1810085ff963SMatthew Dillon 
181132176cfdSRui Paulo 	/*
181232176cfdSRui Paulo 	 * Propagate the PERR if we previously found it on our routing table.
181332176cfdSRui Paulo 	 */
181432176cfdSRui Paulo 	if (forward && perr->perr_ttl > 1) {
18154f655ef5SMatthew Dillon #if defined(__DragonFly__)
181632176cfdSRui Paulo 		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
1817f92fae3fSSascha Wildner 		    "propagate PERR from %s", ether_sprintf(wh->i_addr2));
18184f655ef5SMatthew Dillon #else
18194f655ef5SMatthew Dillon 		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
18204f655ef5SMatthew Dillon 		    "propagate PERR from %6D", wh->i_addr2, ":");
18214f655ef5SMatthew Dillon #endif
1822085ff963SMatthew Dillon 		pperr->perr_ndests = j;
1823085ff963SMatthew Dillon 		pperr->perr_ttl--;
1824085ff963SMatthew Dillon 		hwmp_send_perr(vap, broadcastaddr, pperr);
182532176cfdSRui Paulo 	}
1826085ff963SMatthew Dillon done:
1827085ff963SMatthew Dillon 	if (pperr != NULL)
18284f655ef5SMatthew Dillon 		IEEE80211_FREE(pperr, M_80211_MESH_PERR);
182932176cfdSRui Paulo }
1830085ff963SMatthew Dillon #undef	PERR_DFLAGS
1831085ff963SMatthew Dillon #undef	PERR_DADDR
183232176cfdSRui Paulo #undef	PERR_DSEQ
1833085ff963SMatthew Dillon #undef	PERR_DEXTADDR
1834085ff963SMatthew Dillon #undef	PERR_DRCODE
183532176cfdSRui Paulo 
183632176cfdSRui Paulo static int
hwmp_send_perr(struct ieee80211vap * vap,const uint8_t da[IEEE80211_ADDR_LEN],struct ieee80211_meshperr_ie * perr)1837085ff963SMatthew Dillon hwmp_send_perr(struct ieee80211vap *vap,
183832176cfdSRui Paulo     const uint8_t da[IEEE80211_ADDR_LEN],
183932176cfdSRui Paulo     struct ieee80211_meshperr_ie *perr)
184032176cfdSRui Paulo {
1841085ff963SMatthew Dillon 	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
1842085ff963SMatthew Dillon 	int i;
1843085ff963SMatthew Dillon 	uint8_t length = 0;
184432176cfdSRui Paulo 
184532176cfdSRui Paulo 	/*
184632176cfdSRui Paulo 	 * Enforce PERR interval.
184732176cfdSRui Paulo 	 */
184832176cfdSRui Paulo 	if (ratecheck(&hs->hs_lastperr, &ieee80211_hwmp_perrminint) == 0)
184932176cfdSRui Paulo 		return EALREADY;
185032176cfdSRui Paulo 	getmicrouptime(&hs->hs_lastperr);
185132176cfdSRui Paulo 
185232176cfdSRui Paulo 	/*
185332176cfdSRui Paulo 	 * mesh perr action frame format
185432176cfdSRui Paulo 	 *     [6] da
185532176cfdSRui Paulo 	 *     [6] sa
185632176cfdSRui Paulo 	 *     [6] addr3 = sa
185732176cfdSRui Paulo 	 *     [1] action
185832176cfdSRui Paulo 	 *     [1] category
185932176cfdSRui Paulo 	 *     [tlv] mesh path error
186032176cfdSRui Paulo 	 */
186132176cfdSRui Paulo 	perr->perr_ie = IEEE80211_ELEMID_MESHPERR;
1862085ff963SMatthew Dillon 	length = IEEE80211_MESHPERR_BASE_SZ;
1863085ff963SMatthew Dillon 	for (i = 0; i<perr->perr_ndests; i++) {
1864085ff963SMatthew Dillon 		if (perr->perr_dests[i].dest_flags &
1865085ff963SMatthew Dillon 		    IEEE80211_MESHPERR_FLAGS_AE) {
1866085ff963SMatthew Dillon 			length += IEEE80211_MESHPERR_DEST_SZ_AE;
1867085ff963SMatthew Dillon 			continue ;
186832176cfdSRui Paulo 		}
1869085ff963SMatthew Dillon 		length += IEEE80211_MESHPERR_DEST_SZ;
1870085ff963SMatthew Dillon 	}
1871085ff963SMatthew Dillon 	perr->perr_len =length;
1872085ff963SMatthew Dillon 	return hwmp_send_action(vap, da, (uint8_t *)perr, perr->perr_len+2);
1873085ff963SMatthew Dillon }
1874085ff963SMatthew Dillon 
1875085ff963SMatthew Dillon /*
1876085ff963SMatthew Dillon  * Called from the rest of the net80211 code (mesh code for example).
1877085ff963SMatthew Dillon  * NB: IEEE80211_REASON_MESH_PERR_DEST_UNREACH can be trigger by the fact that
1878085ff963SMatthew Dillon  * a mesh STA is unable to forward an MSDU/MMPDU to a next-hop mesh STA.
1879085ff963SMatthew Dillon  */
1880085ff963SMatthew Dillon #define	PERR_DFLAGS(n)		perr.perr_dests[n].dest_flags
1881085ff963SMatthew Dillon #define	PERR_DADDR(n)		perr.perr_dests[n].dest_addr
1882085ff963SMatthew Dillon #define	PERR_DSEQ(n)		perr.perr_dests[n].dest_seq
1883085ff963SMatthew Dillon #define	PERR_DEXTADDR(n)	perr.perr_dests[n].dest_ext_addr
1884085ff963SMatthew Dillon #define	PERR_DRCODE(n)		perr.perr_dests[n].dest_rcode
1885085ff963SMatthew Dillon static void
hwmp_senderror(struct ieee80211vap * vap,const uint8_t addr[IEEE80211_ADDR_LEN],struct ieee80211_mesh_route * rt,int rcode)1886085ff963SMatthew Dillon hwmp_senderror(struct ieee80211vap *vap,
1887085ff963SMatthew Dillon     const uint8_t addr[IEEE80211_ADDR_LEN],
1888085ff963SMatthew Dillon     struct ieee80211_mesh_route *rt, int rcode)
1889085ff963SMatthew Dillon {
1890085ff963SMatthew Dillon 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
1891085ff963SMatthew Dillon 	struct ieee80211_hwmp_route *hr = NULL;
1892085ff963SMatthew Dillon 	struct ieee80211_meshperr_ie perr;
1893085ff963SMatthew Dillon 
1894085ff963SMatthew Dillon 	if (rt != NULL)
1895085ff963SMatthew Dillon 		hr = IEEE80211_MESH_ROUTE_PRIV(rt,
1896085ff963SMatthew Dillon 		    struct ieee80211_hwmp_route);
1897085ff963SMatthew Dillon 
1898085ff963SMatthew Dillon 	perr.perr_ndests = 1;
1899085ff963SMatthew Dillon 	perr.perr_ttl = ms->ms_ttl;
1900085ff963SMatthew Dillon 	PERR_DFLAGS(0) = 0;
1901085ff963SMatthew Dillon 	PERR_DRCODE(0) = rcode;
1902085ff963SMatthew Dillon 
1903085ff963SMatthew Dillon 	switch (rcode) {
1904085ff963SMatthew Dillon 	case IEEE80211_REASON_MESH_PERR_NO_FI:
1905085ff963SMatthew Dillon 		IEEE80211_ADDR_COPY(PERR_DADDR(0), addr);
1906085ff963SMatthew Dillon 		PERR_DSEQ(0) = 0; /* reserved */
1907085ff963SMatthew Dillon 		break;
1908085ff963SMatthew Dillon 	case IEEE80211_REASON_MESH_PERR_NO_PROXY:
1909085ff963SMatthew Dillon 		KASSERT(rt != NULL, ("no proxy info for sending PERR"));
1910085ff963SMatthew Dillon 		KASSERT(rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY,
1911085ff963SMatthew Dillon 		    ("route is not marked proxy"));
1912085ff963SMatthew Dillon 		PERR_DFLAGS(0) |= IEEE80211_MESHPERR_FLAGS_AE;
1913085ff963SMatthew Dillon 		IEEE80211_ADDR_COPY(PERR_DADDR(0), vap->iv_myaddr);
1914085ff963SMatthew Dillon 		PERR_DSEQ(0) = rt->rt_ext_seq;
1915085ff963SMatthew Dillon 		IEEE80211_ADDR_COPY(PERR_DEXTADDR(0), addr);
1916085ff963SMatthew Dillon 		break;
1917085ff963SMatthew Dillon 	case IEEE80211_REASON_MESH_PERR_DEST_UNREACH:
1918085ff963SMatthew Dillon 		KASSERT(rt != NULL, ("no route info for sending PERR"));
1919085ff963SMatthew Dillon 		IEEE80211_ADDR_COPY(PERR_DADDR(0), addr);
1920085ff963SMatthew Dillon 		PERR_DSEQ(0) = hr->hr_seq;
1921085ff963SMatthew Dillon 		break;
1922085ff963SMatthew Dillon 	default:
1923085ff963SMatthew Dillon 		KASSERT(0, ("unknown reason code for HWMP PERR (%u)", rcode));
1924085ff963SMatthew Dillon 	}
1925085ff963SMatthew Dillon 	hwmp_send_perr(vap, broadcastaddr, &perr);
1926085ff963SMatthew Dillon }
1927085ff963SMatthew Dillon #undef	PERR_DFLAGS
1928085ff963SMatthew Dillon #undef	PEER_DADDR
1929085ff963SMatthew Dillon #undef	PERR_DSEQ
1930085ff963SMatthew Dillon #undef	PERR_DEXTADDR
1931085ff963SMatthew Dillon #undef	PERR_DRCODE
193232176cfdSRui Paulo 
193332176cfdSRui Paulo static void
hwmp_recv_rann(struct ieee80211vap * vap,struct ieee80211_node * ni,const struct ieee80211_frame * wh,const struct ieee80211_meshrann_ie * rann)193432176cfdSRui Paulo hwmp_recv_rann(struct ieee80211vap *vap, struct ieee80211_node *ni,
193532176cfdSRui Paulo     const struct ieee80211_frame *wh, const struct ieee80211_meshrann_ie *rann)
193632176cfdSRui Paulo {
193732176cfdSRui Paulo 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
193832176cfdSRui Paulo 	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
193932176cfdSRui Paulo 	struct ieee80211_mesh_route *rt = NULL;
194032176cfdSRui Paulo 	struct ieee80211_hwmp_route *hr;
1941085ff963SMatthew Dillon 	struct ieee80211_meshpreq_ie preq;
194232176cfdSRui Paulo 	struct ieee80211_meshrann_ie prann;
1943085ff963SMatthew Dillon 	uint32_t metric = 0;
194432176cfdSRui Paulo 
1945085ff963SMatthew Dillon 	if (IEEE80211_ADDR_EQ(rann->rann_addr, vap->iv_myaddr))
194632176cfdSRui Paulo 		return;
194732176cfdSRui Paulo 
194832176cfdSRui Paulo 	rt = ieee80211_mesh_rt_find(vap, rann->rann_addr);
1949085ff963SMatthew Dillon 	if (rt != NULL && rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) {
1950085ff963SMatthew Dillon 		hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
1951085ff963SMatthew Dillon 
1952085ff963SMatthew Dillon 		/* Acceptance criteria: if RANN.seq < stored seq, discard RANN */
1953085ff963SMatthew Dillon 		if (HWMP_SEQ_LT(rann->rann_seq, hr->hr_seq)) {
1954085ff963SMatthew Dillon 			IEEE80211_DISCARD(vap, IEEE80211_MSG_HWMP, wh, NULL,
1955085ff963SMatthew Dillon 			"RANN seq %u < %u", rann->rann_seq, hr->hr_seq);
195632176cfdSRui Paulo 			return;
195732176cfdSRui Paulo 		}
1958085ff963SMatthew Dillon 
1959085ff963SMatthew Dillon 		/* Acceptance criteria: if RANN.seq == stored seq AND
1960085ff963SMatthew Dillon 		* RANN.metric > stored metric, discard RANN */
1961085ff963SMatthew Dillon 		if (HWMP_SEQ_EQ(rann->rann_seq, hr->hr_seq) &&
1962085ff963SMatthew Dillon 		rann->rann_metric > rt->rt_metric) {
1963085ff963SMatthew Dillon 			IEEE80211_DISCARD(vap, IEEE80211_MSG_HWMP, wh, NULL,
1964085ff963SMatthew Dillon 			"RANN metric %u > %u", rann->rann_metric, rt->rt_metric);
1965085ff963SMatthew Dillon 			return;
1966085ff963SMatthew Dillon 		}
1967085ff963SMatthew Dillon 	}
1968085ff963SMatthew Dillon 
1969085ff963SMatthew Dillon 	/* RANN ACCEPTED */
1970085ff963SMatthew Dillon 
1971085ff963SMatthew Dillon 	ieee80211_hwmp_rannint = rann->rann_interval; /* XXX: mtx lock? */
1972085ff963SMatthew Dillon 	metric = rann->rann_metric + ms->ms_pmetric->mpm_metric(ni);
1973085ff963SMatthew Dillon 
1974085ff963SMatthew Dillon 	if (rt == NULL) {
1975085ff963SMatthew Dillon 		rt = ieee80211_mesh_rt_add(vap, rann->rann_addr);
1976085ff963SMatthew Dillon 		if (rt == NULL) {
19774f655ef5SMatthew Dillon #if defined(__DragonFly__)
1978085ff963SMatthew Dillon 			IEEE80211_DISCARD(vap, IEEE80211_MSG_HWMP, wh, NULL,
1979f92fae3fSSascha Wildner 			    "unable to add mac for RANN root %s",
1980f92fae3fSSascha Wildner 			    ether_sprintf(rann->rann_addr));
19814f655ef5SMatthew Dillon #else
19824f655ef5SMatthew Dillon 			IEEE80211_DISCARD(vap, IEEE80211_MSG_HWMP, wh, NULL,
19834f655ef5SMatthew Dillon 			    "unable to add mac for RANN root %6D",
19844f655ef5SMatthew Dillon 			    rann->rann_addr, ":");
19854f655ef5SMatthew Dillon #endif
1986085ff963SMatthew Dillon 			    vap->iv_stats.is_mesh_rtaddfailed++;
1987085ff963SMatthew Dillon 			return;
1988085ff963SMatthew Dillon 		}
1989085ff963SMatthew Dillon 	}
199032176cfdSRui Paulo 	hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
1991085ff963SMatthew Dillon 	/* Check if root is a mesh gate, mark it */
1992085ff963SMatthew Dillon 	if (rann->rann_flags & IEEE80211_MESHRANN_FLAGS_GATE) {
1993085ff963SMatthew Dillon 		struct ieee80211_mesh_gate_route *gr;
1994085ff963SMatthew Dillon 
1995085ff963SMatthew Dillon 		rt->rt_flags |= IEEE80211_MESHRT_FLAGS_GATE;
1996085ff963SMatthew Dillon 		gr = ieee80211_mesh_mark_gate(vap, rann->rann_addr,
1997085ff963SMatthew Dillon 			rt);
1998085ff963SMatthew Dillon 		gr->gr_lastseq = 0; /* NOT GANN */
1999085ff963SMatthew Dillon 	}
2000085ff963SMatthew Dillon 	/* discovery timeout */
2001085ff963SMatthew Dillon 	ieee80211_mesh_rt_update(rt,
2002085ff963SMatthew Dillon 	    ticks_to_msecs(ieee80211_hwmp_roottimeout));
2003085ff963SMatthew Dillon 
2004085ff963SMatthew Dillon 	preq.preq_flags = IEEE80211_MESHPREQ_FLAGS_AM;
2005085ff963SMatthew Dillon 	preq.preq_hopcount = 0;
2006085ff963SMatthew Dillon 	preq.preq_ttl = ms->ms_ttl;
2007085ff963SMatthew Dillon 	preq.preq_id = 0; /* reserved */
2008085ff963SMatthew Dillon 	IEEE80211_ADDR_COPY(preq.preq_origaddr, vap->iv_myaddr);
2009085ff963SMatthew Dillon 	preq.preq_origseq = ++hs->hs_seq;
2010085ff963SMatthew Dillon 	preq.preq_lifetime = ieee80211_hwmp_roottimeout;
2011085ff963SMatthew Dillon 	preq.preq_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
2012085ff963SMatthew Dillon 	preq.preq_tcount = 1;
2013085ff963SMatthew Dillon 	preq.preq_targets[0].target_flags = IEEE80211_MESHPREQ_TFLAGS_TO;
2014085ff963SMatthew Dillon 	/* NB: IEEE80211_MESHPREQ_TFLAGS_USN = 0 implicitly implied */
2015085ff963SMatthew Dillon 	IEEE80211_ADDR_COPY(preq.preq_targets[0].target_addr, rann->rann_addr);
2016085ff963SMatthew Dillon 	preq.preq_targets[0].target_seq = rann->rann_seq;
2017085ff963SMatthew Dillon 	/* XXX: if rootconfint have not passed, we built this preq in vain */
2018085ff963SMatthew Dillon 	hwmp_send_preq(vap, wh->i_addr2, &preq, &hr->hr_lastrootconf,
2019085ff963SMatthew Dillon 	    &ieee80211_hwmp_rootconfint);
2020085ff963SMatthew Dillon 
2021085ff963SMatthew Dillon 	/* propagate a RANN */
2022085ff963SMatthew Dillon 	if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID &&
2023085ff963SMatthew Dillon 	    rann->rann_ttl > 1 &&
2024085ff963SMatthew Dillon 	    ms->ms_flags & IEEE80211_MESHFLAGS_FWD) {
202532176cfdSRui Paulo 		hr->hr_seq = rann->rann_seq;
202632176cfdSRui Paulo 		memcpy(&prann, rann, sizeof(prann));
202732176cfdSRui Paulo 		prann.rann_hopcount += 1;
202832176cfdSRui Paulo 		prann.rann_ttl -= 1;
202932176cfdSRui Paulo 		prann.rann_metric += ms->ms_pmetric->mpm_metric(ni);
2030085ff963SMatthew Dillon 		hwmp_send_rann(vap, broadcastaddr, &prann);
203132176cfdSRui Paulo 	}
203232176cfdSRui Paulo }
203332176cfdSRui Paulo 
203432176cfdSRui Paulo static int
hwmp_send_rann(struct ieee80211vap * vap,const uint8_t da[IEEE80211_ADDR_LEN],struct ieee80211_meshrann_ie * rann)2035085ff963SMatthew Dillon hwmp_send_rann(struct ieee80211vap *vap,
203632176cfdSRui Paulo     const uint8_t da[IEEE80211_ADDR_LEN],
203732176cfdSRui Paulo     struct ieee80211_meshrann_ie *rann)
203832176cfdSRui Paulo {
203932176cfdSRui Paulo 	/*
204032176cfdSRui Paulo 	 * mesh rann action frame format
204132176cfdSRui Paulo 	 *     [6] da
204232176cfdSRui Paulo 	 *     [6] sa
204332176cfdSRui Paulo 	 *     [6] addr3 = sa
204432176cfdSRui Paulo 	 *     [1] action
204532176cfdSRui Paulo 	 *     [1] category
204632176cfdSRui Paulo 	 *     [tlv] root annoucement
204732176cfdSRui Paulo 	 */
204832176cfdSRui Paulo 	rann->rann_ie = IEEE80211_ELEMID_MESHRANN;
2049085ff963SMatthew Dillon 	rann->rann_len = IEEE80211_MESHRANN_BASE_SZ;
2050085ff963SMatthew Dillon 	return hwmp_send_action(vap, da, (uint8_t *)rann, rann->rann_len + 2);
205132176cfdSRui Paulo }
205232176cfdSRui Paulo 
205332176cfdSRui Paulo #define	PREQ_TFLAGS(n)	preq.preq_targets[n].target_flags
205432176cfdSRui Paulo #define	PREQ_TADDR(n)	preq.preq_targets[n].target_addr
205532176cfdSRui Paulo #define	PREQ_TSEQ(n)	preq.preq_targets[n].target_seq
2056085ff963SMatthew Dillon static void
hwmp_rediscover_cb(void * arg)2057085ff963SMatthew Dillon hwmp_rediscover_cb(void *arg)
2058085ff963SMatthew Dillon {
2059085ff963SMatthew Dillon 	struct ieee80211_mesh_route *rt = arg;
2060085ff963SMatthew Dillon 	struct ieee80211vap *vap = rt->rt_vap;
2061085ff963SMatthew Dillon 	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
2062085ff963SMatthew Dillon 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
2063085ff963SMatthew Dillon 	struct ieee80211_hwmp_route *hr;
2064085ff963SMatthew Dillon 	struct ieee80211_meshpreq_ie preq; /* Optimize: storing first preq? */
2065085ff963SMatthew Dillon 
2066085ff963SMatthew Dillon 	if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID))
2067085ff963SMatthew Dillon 		return ; /* nothing to do */
2068085ff963SMatthew Dillon 
2069085ff963SMatthew Dillon 	hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
2070085ff963SMatthew Dillon 	if (hr->hr_preqretries >=
2071085ff963SMatthew Dillon 		ieee80211_hwmp_maxpreq_retries) {
2072085ff963SMatthew Dillon 		IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ANY,
2073085ff963SMatthew Dillon 			rt->rt_dest, "%s",
2074085ff963SMatthew Dillon 			"max number of discovery, send queued frames to GATE");
2075085ff963SMatthew Dillon 		ieee80211_mesh_forward_to_gates(vap, rt);
2076085ff963SMatthew Dillon 		vap->iv_stats.is_mesh_fwd_nopath++;
2077085ff963SMatthew Dillon 		return ; /* XXX: flush queue? */
2078085ff963SMatthew Dillon 	}
2079085ff963SMatthew Dillon 
2080085ff963SMatthew Dillon 	hr->hr_preqretries++;
2081085ff963SMatthew Dillon 
2082085ff963SMatthew Dillon 
2083085ff963SMatthew Dillon 	IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP, rt->rt_dest,
2084085ff963SMatthew Dillon 	    "start path rediscovery , target seq %u", hr->hr_seq);
2085085ff963SMatthew Dillon 	/*
2086085ff963SMatthew Dillon 	 * Try to discover the path for this node.
2087085ff963SMatthew Dillon 	 * Group addressed PREQ Case A
2088085ff963SMatthew Dillon 	 */
2089085ff963SMatthew Dillon 	preq.preq_flags = 0;
2090085ff963SMatthew Dillon 	preq.preq_hopcount = 0;
2091085ff963SMatthew Dillon 	preq.preq_ttl = ms->ms_ttl;
2092085ff963SMatthew Dillon 	preq.preq_id = ++hs->hs_preqid;
2093085ff963SMatthew Dillon 	IEEE80211_ADDR_COPY(preq.preq_origaddr, vap->iv_myaddr);
2094085ff963SMatthew Dillon 	preq.preq_origseq = hr->hr_origseq;
2095085ff963SMatthew Dillon 	preq.preq_lifetime = ticks_to_msecs(ieee80211_hwmp_pathtimeout);
2096085ff963SMatthew Dillon 	preq.preq_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
2097085ff963SMatthew Dillon 	preq.preq_tcount = 1;
2098085ff963SMatthew Dillon 	IEEE80211_ADDR_COPY(PREQ_TADDR(0), rt->rt_dest);
2099085ff963SMatthew Dillon 	PREQ_TFLAGS(0) = 0;
2100085ff963SMatthew Dillon 	if (ieee80211_hwmp_targetonly)
2101085ff963SMatthew Dillon 		PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_TO;
2102085ff963SMatthew Dillon 	PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_USN;
2103085ff963SMatthew Dillon 	PREQ_TSEQ(0) = 0; /* RESERVED when USN flag is set */
2104085ff963SMatthew Dillon 	/* XXX check return value */
2105085ff963SMatthew Dillon 	hwmp_send_preq(vap, broadcastaddr, &preq, &hr->hr_lastpreq,
2106085ff963SMatthew Dillon 	    &ieee80211_hwmp_preqminint);
2107085ff963SMatthew Dillon 	callout_reset(&rt->rt_discovery,
2108085ff963SMatthew Dillon 		ieee80211_hwmp_net_diameter_traversaltime * 2,
2109085ff963SMatthew Dillon 		hwmp_rediscover_cb, rt);
2110085ff963SMatthew Dillon }
2111085ff963SMatthew Dillon 
211232176cfdSRui Paulo static struct ieee80211_node *
hwmp_discover(struct ieee80211vap * vap,const uint8_t dest[IEEE80211_ADDR_LEN],struct mbuf * m)211332176cfdSRui Paulo hwmp_discover(struct ieee80211vap *vap,
211432176cfdSRui Paulo     const uint8_t dest[IEEE80211_ADDR_LEN], struct mbuf *m)
211532176cfdSRui Paulo {
211632176cfdSRui Paulo 	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
211732176cfdSRui Paulo 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
211832176cfdSRui Paulo 	struct ieee80211_mesh_route *rt = NULL;
211932176cfdSRui Paulo 	struct ieee80211_hwmp_route *hr;
212032176cfdSRui Paulo 	struct ieee80211_meshpreq_ie preq;
212132176cfdSRui Paulo 	struct ieee80211_node *ni;
212232176cfdSRui Paulo 	int sendpreq = 0;
212332176cfdSRui Paulo 
212432176cfdSRui Paulo 	KASSERT(vap->iv_opmode == IEEE80211_M_MBSS,
212532176cfdSRui Paulo 	    ("not a mesh vap, opmode %d", vap->iv_opmode));
212632176cfdSRui Paulo 
212732176cfdSRui Paulo 	KASSERT(!IEEE80211_ADDR_EQ(vap->iv_myaddr, dest),
212832176cfdSRui Paulo 	    ("%s: discovering self!", __func__));
212932176cfdSRui Paulo 
213032176cfdSRui Paulo 	ni = NULL;
213132176cfdSRui Paulo 	if (!IEEE80211_IS_MULTICAST(dest)) {
213232176cfdSRui Paulo 		rt = ieee80211_mesh_rt_find(vap, dest);
213332176cfdSRui Paulo 		if (rt == NULL) {
213432176cfdSRui Paulo 			rt = ieee80211_mesh_rt_add(vap, dest);
213532176cfdSRui Paulo 			if (rt == NULL) {
21364f655ef5SMatthew Dillon #if defined(__DragonFly__)
213732176cfdSRui Paulo 				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP,
2138f92fae3fSSascha Wildner 				    ni, "unable to add discovery path to %s",
2139f92fae3fSSascha Wildner 				    ether_sprintf(dest));
21404f655ef5SMatthew Dillon #else
21414f655ef5SMatthew Dillon 				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP,
21424f655ef5SMatthew Dillon 				    ni, "unable to add discovery path to %6D",
21434f655ef5SMatthew Dillon 				    dest, ":");
21444f655ef5SMatthew Dillon #endif
214532176cfdSRui Paulo 				vap->iv_stats.is_mesh_rtaddfailed++;
214632176cfdSRui Paulo 				goto done;
214732176cfdSRui Paulo 			}
214832176cfdSRui Paulo 		}
214932176cfdSRui Paulo 		hr = IEEE80211_MESH_ROUTE_PRIV(rt,
215032176cfdSRui Paulo 		    struct ieee80211_hwmp_route);
2151085ff963SMatthew Dillon 		if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_DISCOVER) {
2152085ff963SMatthew Dillon 			IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP, dest,
2153085ff963SMatthew Dillon 			    "%s", "already discovering queue frame until path found");
2154085ff963SMatthew Dillon 			sendpreq = 1;
2155085ff963SMatthew Dillon 			goto done;
2156085ff963SMatthew Dillon 		}
215732176cfdSRui Paulo 		if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0) {
2158085ff963SMatthew Dillon 			if (hr->hr_lastdiscovery != 0 &&
2159085ff963SMatthew Dillon 			    (ticks - hr->hr_lastdiscovery <
2160085ff963SMatthew Dillon 			    (ieee80211_hwmp_net_diameter_traversaltime * 2))) {
2161085ff963SMatthew Dillon 				IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
2162085ff963SMatthew Dillon 			            dest, NULL, "%s",
2163085ff963SMatthew Dillon 				    "too frequent discovery requeust");
2164085ff963SMatthew Dillon 				sendpreq = 1;
2165085ff963SMatthew Dillon 				goto done;
2166085ff963SMatthew Dillon 			}
2167085ff963SMatthew Dillon 			hr->hr_lastdiscovery = ticks;
2168085ff963SMatthew Dillon 			if (hr->hr_preqretries >=
2169085ff963SMatthew Dillon 			    ieee80211_hwmp_maxpreq_retries) {
2170085ff963SMatthew Dillon 				IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
2171085ff963SMatthew Dillon 			            dest, NULL, "%s",
2172085ff963SMatthew Dillon 				    "no valid path , max number of discovery");
2173085ff963SMatthew Dillon 				vap->iv_stats.is_mesh_fwd_nopath++;
2174085ff963SMatthew Dillon 				goto done;
2175085ff963SMatthew Dillon 			}
2176085ff963SMatthew Dillon 			rt->rt_flags = IEEE80211_MESHRT_FLAGS_DISCOVER;
2177085ff963SMatthew Dillon 			hr->hr_preqretries++;
217832176cfdSRui Paulo 			if (hr->hr_origseq == 0)
217932176cfdSRui Paulo 				hr->hr_origseq = ++hs->hs_seq;
218032176cfdSRui Paulo 			rt->rt_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
218132176cfdSRui Paulo 			sendpreq = 1;
2182085ff963SMatthew Dillon 			IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP, dest,
2183085ff963SMatthew Dillon 			    "start path discovery (src %s), target seq %u",
2184085ff963SMatthew Dillon 			    m == NULL ? "<none>" : ether_sprintf(
2185085ff963SMatthew Dillon 			    mtod(m, struct ether_header *)->ether_shost),
2186085ff963SMatthew Dillon 			    hr->hr_seq);
218732176cfdSRui Paulo 			/*
218832176cfdSRui Paulo 			 * Try to discover the path for this node.
2189085ff963SMatthew Dillon 			 * Group addressed PREQ Case A
219032176cfdSRui Paulo 			 */
219132176cfdSRui Paulo 			preq.preq_flags = 0;
219232176cfdSRui Paulo 			preq.preq_hopcount = 0;
219332176cfdSRui Paulo 			preq.preq_ttl = ms->ms_ttl;
219432176cfdSRui Paulo 			preq.preq_id = ++hs->hs_preqid;
219532176cfdSRui Paulo 			IEEE80211_ADDR_COPY(preq.preq_origaddr, vap->iv_myaddr);
219632176cfdSRui Paulo 			preq.preq_origseq = hr->hr_origseq;
2197085ff963SMatthew Dillon 			preq.preq_lifetime =
2198085ff963SMatthew Dillon 			    ticks_to_msecs(ieee80211_hwmp_pathtimeout);
2199085ff963SMatthew Dillon 			preq.preq_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
220032176cfdSRui Paulo 			preq.preq_tcount = 1;
220132176cfdSRui Paulo 			IEEE80211_ADDR_COPY(PREQ_TADDR(0), dest);
220232176cfdSRui Paulo 			PREQ_TFLAGS(0) = 0;
220332176cfdSRui Paulo 			if (ieee80211_hwmp_targetonly)
220432176cfdSRui Paulo 				PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_TO;
220532176cfdSRui Paulo 			PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_USN;
2206085ff963SMatthew Dillon 			PREQ_TSEQ(0) = 0; /* RESERVED when USN flag is set */
220732176cfdSRui Paulo 			/* XXX check return value */
2208085ff963SMatthew Dillon 			hwmp_send_preq(vap, broadcastaddr, &preq,
2209085ff963SMatthew Dillon 			    &hr->hr_lastpreq, &ieee80211_hwmp_preqminint);
2210085ff963SMatthew Dillon 			callout_reset(&rt->rt_discovery,
2211085ff963SMatthew Dillon 			    ieee80211_hwmp_net_diameter_traversaltime * 2,
2212085ff963SMatthew Dillon 			    hwmp_rediscover_cb, rt);
221332176cfdSRui Paulo 		}
221432176cfdSRui Paulo 		if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID)
221532176cfdSRui Paulo 			ni = ieee80211_find_txnode(vap, rt->rt_nexthop);
221632176cfdSRui Paulo 	} else {
221732176cfdSRui Paulo 		ni = ieee80211_find_txnode(vap, dest);
221832176cfdSRui Paulo 		/* NB: if null then we leak mbuf */
221932176cfdSRui Paulo 		KASSERT(ni != NULL, ("leak mcast frame"));
222032176cfdSRui Paulo 		return ni;
222132176cfdSRui Paulo 	}
222232176cfdSRui Paulo done:
222332176cfdSRui Paulo 	if (ni == NULL && m != NULL) {
222432176cfdSRui Paulo 		if (sendpreq) {
222532176cfdSRui Paulo 			struct ieee80211com *ic = vap->iv_ic;
222632176cfdSRui Paulo 			/*
222732176cfdSRui Paulo 			 * Queue packet for transmit when path discovery
222832176cfdSRui Paulo 			 * completes.  If discovery never completes the
222932176cfdSRui Paulo 			 * frame will be flushed by way of the aging timer.
223032176cfdSRui Paulo 			 */
223132176cfdSRui Paulo 			IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP, dest,
223232176cfdSRui Paulo 			    "%s", "queue frame until path found");
223332176cfdSRui Paulo 			m->m_pkthdr.rcvif = (void *)(uintptr_t)
223432176cfdSRui Paulo 			    ieee80211_mac_hash(ic, dest);
223532176cfdSRui Paulo 			/* XXX age chosen randomly */
223632176cfdSRui Paulo 			ieee80211_ageq_append(&ic->ic_stageq, m,
223732176cfdSRui Paulo 			    IEEE80211_INACT_WAIT);
223832176cfdSRui Paulo 		} else {
223932176cfdSRui Paulo 			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_HWMP,
224032176cfdSRui Paulo 			    dest, NULL, "%s", "no valid path to this node");
224132176cfdSRui Paulo 			m_freem(m);
224232176cfdSRui Paulo 		}
224332176cfdSRui Paulo 	}
224432176cfdSRui Paulo 	return ni;
224532176cfdSRui Paulo }
224632176cfdSRui Paulo #undef	PREQ_TFLAGS
224732176cfdSRui Paulo #undef	PREQ_TADDR
224832176cfdSRui Paulo #undef	PREQ_TSEQ
224932176cfdSRui Paulo 
225032176cfdSRui Paulo static int
hwmp_ioctl_get80211(struct ieee80211vap * vap,struct ieee80211req * ireq)225132176cfdSRui Paulo hwmp_ioctl_get80211(struct ieee80211vap *vap, struct ieee80211req *ireq)
225232176cfdSRui Paulo {
225332176cfdSRui Paulo 	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
225432176cfdSRui Paulo 	int error;
225532176cfdSRui Paulo 
225632176cfdSRui Paulo 	if (vap->iv_opmode != IEEE80211_M_MBSS)
225732176cfdSRui Paulo 		return ENOSYS;
225832176cfdSRui Paulo 	error = 0;
225932176cfdSRui Paulo 	switch (ireq->i_type) {
226032176cfdSRui Paulo 	case IEEE80211_IOC_HWMP_ROOTMODE:
226132176cfdSRui Paulo 		ireq->i_val = hs->hs_rootmode;
226232176cfdSRui Paulo 		break;
226332176cfdSRui Paulo 	case IEEE80211_IOC_HWMP_MAXHOPS:
226432176cfdSRui Paulo 		ireq->i_val = hs->hs_maxhops;
226532176cfdSRui Paulo 		break;
226632176cfdSRui Paulo 	default:
226732176cfdSRui Paulo 		return ENOSYS;
226832176cfdSRui Paulo 	}
226932176cfdSRui Paulo 	return error;
227032176cfdSRui Paulo }
227132176cfdSRui Paulo IEEE80211_IOCTL_GET(hwmp, hwmp_ioctl_get80211);
227232176cfdSRui Paulo 
227332176cfdSRui Paulo static int
hwmp_ioctl_set80211(struct ieee80211vap * vap,struct ieee80211req * ireq)227432176cfdSRui Paulo hwmp_ioctl_set80211(struct ieee80211vap *vap, struct ieee80211req *ireq)
227532176cfdSRui Paulo {
227632176cfdSRui Paulo 	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
227732176cfdSRui Paulo 	int error;
227832176cfdSRui Paulo 
227932176cfdSRui Paulo 	if (vap->iv_opmode != IEEE80211_M_MBSS)
228032176cfdSRui Paulo 		return ENOSYS;
228132176cfdSRui Paulo 	error = 0;
228232176cfdSRui Paulo 	switch (ireq->i_type) {
228332176cfdSRui Paulo 	case IEEE80211_IOC_HWMP_ROOTMODE:
228432176cfdSRui Paulo 		if (ireq->i_val < 0 || ireq->i_val > 3)
228532176cfdSRui Paulo 			return EINVAL;
228632176cfdSRui Paulo 		hs->hs_rootmode = ireq->i_val;
228732176cfdSRui Paulo 		hwmp_rootmode_setup(vap);
228832176cfdSRui Paulo 		break;
228932176cfdSRui Paulo 	case IEEE80211_IOC_HWMP_MAXHOPS:
229032176cfdSRui Paulo 		if (ireq->i_val <= 0 || ireq->i_val > 255)
229132176cfdSRui Paulo 			return EINVAL;
229232176cfdSRui Paulo 		hs->hs_maxhops = ireq->i_val;
229332176cfdSRui Paulo 		break;
229432176cfdSRui Paulo 	default:
229532176cfdSRui Paulo 		return ENOSYS;
229632176cfdSRui Paulo 	}
229732176cfdSRui Paulo 	return error;
229832176cfdSRui Paulo }
229932176cfdSRui Paulo IEEE80211_IOCTL_SET(hwmp, hwmp_ioctl_set80211);
2300