xref: /dflybsd-src/sys/netproto/802_11/wlan/ieee80211_hwmp.c (revision fcaa651d6f09e572728e5023bf68ddcd3f2c2ef8)
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  *
2932176cfdSRui Paulo  * $FreeBSD: head/sys/net80211/ieee80211_hwmp.c 198581 2009-10-29 12:19:10Z rpaulo $
3032176cfdSRui Paulo  * $DragonFly$
3132176cfdSRui Paulo  */
3232176cfdSRui Paulo 
3332176cfdSRui Paulo /*
3432176cfdSRui Paulo  * IEEE 802.11s Hybrid Wireless Mesh Protocol, HWMP.
3532176cfdSRui Paulo  *
3632176cfdSRui Paulo  * Based on March 2009, D3.0 802.11s draft spec.
3732176cfdSRui Paulo  */
3832176cfdSRui Paulo #include "opt_inet.h"
3932176cfdSRui Paulo #include "opt_wlan.h"
4032176cfdSRui Paulo 
4132176cfdSRui Paulo #include <sys/param.h>
4232176cfdSRui Paulo #include <sys/systm.h>
4332176cfdSRui Paulo #include <sys/mbuf.h>
4432176cfdSRui Paulo #include <sys/malloc.h>
4532176cfdSRui Paulo #include <sys/kernel.h>
4632176cfdSRui Paulo 
4732176cfdSRui Paulo #include <sys/socket.h>
4832176cfdSRui Paulo #include <sys/sockio.h>
4932176cfdSRui Paulo #include <sys/endian.h>
5032176cfdSRui Paulo #include <sys/errno.h>
5132176cfdSRui Paulo #include <sys/proc.h>
5232176cfdSRui Paulo #include <sys/sysctl.h>
5332176cfdSRui Paulo 
5432176cfdSRui Paulo #include <net/if.h>
5532176cfdSRui Paulo #include <net/if_media.h>
5632176cfdSRui Paulo #include <net/if_llc.h>
5732176cfdSRui Paulo #include <net/ethernet.h>
5832176cfdSRui Paulo #include <net/route.h>
5932176cfdSRui Paulo 
6032176cfdSRui Paulo #include <net/bpf.h>
6132176cfdSRui Paulo 
6232176cfdSRui Paulo #include <netproto/802_11/ieee80211_var.h>
6332176cfdSRui Paulo #include <netproto/802_11/ieee80211_action.h>
6432176cfdSRui Paulo #include <netproto/802_11/ieee80211_input.h>
6532176cfdSRui Paulo #include <netproto/802_11/ieee80211_mesh.h>
6632176cfdSRui Paulo 
6732176cfdSRui Paulo static void	hwmp_vattach(struct ieee80211vap *);
6832176cfdSRui Paulo static void	hwmp_vdetach(struct ieee80211vap *);
6932176cfdSRui Paulo static int	hwmp_newstate(struct ieee80211vap *,
7032176cfdSRui Paulo 		    enum ieee80211_state, int);
7132176cfdSRui Paulo static int	hwmp_send_action(struct ieee80211_node *,
7232176cfdSRui Paulo 		    const uint8_t [IEEE80211_ADDR_LEN],
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 *);
8432176cfdSRui Paulo static void	hwmp_rootmode_cb(void *);
8532176cfdSRui Paulo 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 *);
8932176cfdSRui Paulo static int	hwmp_send_preq(struct ieee80211_node *,
9032176cfdSRui Paulo 		    const uint8_t [IEEE80211_ADDR_LEN],
9132176cfdSRui Paulo 		    const uint8_t [IEEE80211_ADDR_LEN],
9232176cfdSRui Paulo 		    struct ieee80211_meshpreq_ie *);
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 *);
9632176cfdSRui Paulo static int	hwmp_send_prep(struct ieee80211_node *,
9732176cfdSRui Paulo 		    const uint8_t [IEEE80211_ADDR_LEN],
9832176cfdSRui Paulo 		    const uint8_t [IEEE80211_ADDR_LEN],
9932176cfdSRui Paulo 		    struct ieee80211_meshprep_ie *);
10032176cfdSRui Paulo static void	hwmp_recv_perr(struct ieee80211vap *, struct ieee80211_node *,
10132176cfdSRui Paulo 		    const struct ieee80211_frame *,
10232176cfdSRui Paulo 		    const struct ieee80211_meshperr_ie *);
10332176cfdSRui Paulo static int	hwmp_send_perr(struct ieee80211_node *,
10432176cfdSRui Paulo 		    const uint8_t [IEEE80211_ADDR_LEN],
10532176cfdSRui Paulo 		    const uint8_t [IEEE80211_ADDR_LEN],
10632176cfdSRui Paulo 		    struct ieee80211_meshperr_ie *);
10732176cfdSRui Paulo static void	hwmp_recv_rann(struct ieee80211vap *, struct ieee80211_node *,
10832176cfdSRui Paulo 		   const struct ieee80211_frame *,
10932176cfdSRui Paulo 		   const struct ieee80211_meshrann_ie *);
11032176cfdSRui Paulo static int	hwmp_send_rann(struct ieee80211_node *,
11132176cfdSRui Paulo 		    const uint8_t [IEEE80211_ADDR_LEN],
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 /* unalligned little endian access */
12332176cfdSRui Paulo #define LE_WRITE_2(p, v) do {				\
12432176cfdSRui Paulo 	((uint8_t *)(p))[0] = (v) & 0xff;		\
12532176cfdSRui Paulo 	((uint8_t *)(p))[1] = ((v) >> 8) & 0xff;	\
12632176cfdSRui Paulo } while (0)
12732176cfdSRui Paulo #define LE_WRITE_4(p, v) do {				\
12832176cfdSRui Paulo 	((uint8_t *)(p))[0] = (v) & 0xff;		\
12932176cfdSRui Paulo 	((uint8_t *)(p))[1] = ((v) >> 8) & 0xff;	\
13032176cfdSRui Paulo 	((uint8_t *)(p))[2] = ((v) >> 16) & 0xff;	\
13132176cfdSRui Paulo 	((uint8_t *)(p))[3] = ((v) >> 24) & 0xff;	\
13232176cfdSRui Paulo } while (0)
13332176cfdSRui Paulo 
13432176cfdSRui Paulo 
13532176cfdSRui Paulo /* NB: the Target Address set in a Proactive PREQ is the broadcast address. */
13632176cfdSRui Paulo static const uint8_t	broadcastaddr[IEEE80211_ADDR_LEN] =
13732176cfdSRui Paulo 	{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
13832176cfdSRui Paulo 
13932176cfdSRui Paulo typedef uint32_t ieee80211_hwmp_seq;
14032176cfdSRui Paulo #define	HWMP_SEQ_LT(a, b)	((int32_t)((a)-(b)) < 0)
14132176cfdSRui Paulo #define	HWMP_SEQ_LEQ(a, b)	((int32_t)((a)-(b)) <= 0)
14232176cfdSRui Paulo #define	HWMP_SEQ_GT(a, b)	((int32_t)((a)-(b)) > 0)
14332176cfdSRui Paulo #define	HWMP_SEQ_GEQ(a, b)	((int32_t)((a)-(b)) >= 0)
14432176cfdSRui Paulo 
14532176cfdSRui Paulo /*
14632176cfdSRui Paulo  * Private extension of ieee80211_mesh_route.
14732176cfdSRui Paulo  */
14832176cfdSRui Paulo struct ieee80211_hwmp_route {
14932176cfdSRui Paulo 	ieee80211_hwmp_seq	hr_seq;		/* last HWMP seq seen from dst*/
15032176cfdSRui Paulo 	ieee80211_hwmp_seq	hr_preqid;	/* last PREQ ID seen from dst */
15132176cfdSRui Paulo 	ieee80211_hwmp_seq	hr_origseq;	/* seq. no. on our latest PREQ*/
15232176cfdSRui Paulo 	int			hr_preqretries;
15332176cfdSRui Paulo };
15432176cfdSRui Paulo struct ieee80211_hwmp_state {
15532176cfdSRui Paulo 	ieee80211_hwmp_seq	hs_seq;		/* next seq to be used */
15632176cfdSRui Paulo 	ieee80211_hwmp_seq	hs_preqid;	/* next PREQ ID to be used */
15732176cfdSRui Paulo 	struct timeval		hs_lastpreq;	/* last time we sent a PREQ */
15832176cfdSRui Paulo 	struct timeval		hs_lastperr;	/* last time we sent a PERR */
15932176cfdSRui Paulo 	int			hs_rootmode;	/* proactive HWMP */
16032176cfdSRui Paulo 	struct callout		hs_roottimer;
16132176cfdSRui Paulo 	uint8_t			hs_maxhops;	/* max hop count */
16232176cfdSRui Paulo };
16332176cfdSRui Paulo 
16432176cfdSRui Paulo SYSCTL_NODE(_net_wlan, OID_AUTO, hwmp, CTLFLAG_RD, 0,
16532176cfdSRui Paulo     "IEEE 802.11s HWMP parameters");
16632176cfdSRui Paulo static int	ieee80211_hwmp_targetonly = 0;
16732176cfdSRui Paulo SYSCTL_INT(_net_wlan_hwmp, OID_AUTO, targetonly, CTLTYPE_INT | CTLFLAG_RW,
16832176cfdSRui Paulo     &ieee80211_hwmp_targetonly, 0, "Set TO bit on generated PREQs");
16932176cfdSRui Paulo static int	ieee80211_hwmp_replyforward = 1;
17032176cfdSRui Paulo SYSCTL_INT(_net_wlan_hwmp, OID_AUTO, replyforward, CTLTYPE_INT | CTLFLAG_RW,
17132176cfdSRui Paulo     &ieee80211_hwmp_replyforward, 0, "Set RF bit on generated PREQs");
17232176cfdSRui Paulo static int	ieee80211_hwmp_pathtimeout = -1;
17332176cfdSRui Paulo SYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, pathlifetime, CTLTYPE_INT | CTLFLAG_RW,
17432176cfdSRui Paulo     &ieee80211_hwmp_pathtimeout, 0, ieee80211_sysctl_msecs_ticks, "I",
17532176cfdSRui Paulo     "path entry lifetime (ms)");
17632176cfdSRui Paulo static int	ieee80211_hwmp_roottimeout = -1;
17732176cfdSRui Paulo SYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, roottimeout, CTLTYPE_INT | CTLFLAG_RW,
17832176cfdSRui Paulo     &ieee80211_hwmp_roottimeout, 0, ieee80211_sysctl_msecs_ticks, "I",
17932176cfdSRui Paulo     "root PREQ timeout (ms)");
18032176cfdSRui Paulo static int	ieee80211_hwmp_rootint = -1;
18132176cfdSRui Paulo SYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, rootint, CTLTYPE_INT | CTLFLAG_RW,
18232176cfdSRui Paulo     &ieee80211_hwmp_rootint, 0, ieee80211_sysctl_msecs_ticks, "I",
18332176cfdSRui Paulo     "root interval (ms)");
18432176cfdSRui Paulo static int	ieee80211_hwmp_rannint = -1;
18532176cfdSRui Paulo SYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, rannint, CTLTYPE_INT | CTLFLAG_RW,
18632176cfdSRui Paulo     &ieee80211_hwmp_rannint, 0, ieee80211_sysctl_msecs_ticks, "I",
18732176cfdSRui Paulo     "root announcement interval (ms)");
18832176cfdSRui Paulo 
18932176cfdSRui Paulo #define	IEEE80211_HWMP_DEFAULT_MAXHOPS	31
19032176cfdSRui Paulo 
19132176cfdSRui Paulo static	ieee80211_recv_action_func hwmp_recv_action_meshpath;
19232176cfdSRui Paulo 
19332176cfdSRui Paulo static struct ieee80211_mesh_proto_path mesh_proto_hwmp = {
19432176cfdSRui Paulo 	.mpp_descr	= "HWMP",
19532176cfdSRui Paulo 	.mpp_ie		= IEEE80211_MESHCONF_PATH_HWMP,
19632176cfdSRui Paulo 	.mpp_discover	= hwmp_discover,
19732176cfdSRui Paulo 	.mpp_peerdown	= hwmp_peerdown,
19832176cfdSRui Paulo 	.mpp_vattach	= hwmp_vattach,
19932176cfdSRui Paulo 	.mpp_vdetach	= hwmp_vdetach,
20032176cfdSRui Paulo 	.mpp_newstate	= hwmp_newstate,
20132176cfdSRui Paulo 	.mpp_privlen	= sizeof(struct ieee80211_hwmp_route),
20232176cfdSRui Paulo };
20332176cfdSRui Paulo SYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, inact, CTLTYPE_INT | CTLFLAG_RW,
20432176cfdSRui Paulo 	&mesh_proto_hwmp.mpp_inact, 0, ieee80211_sysctl_msecs_ticks, "I",
20532176cfdSRui Paulo 	"mesh route inactivity timeout (ms)");
20632176cfdSRui Paulo 
20732176cfdSRui Paulo 
20832176cfdSRui Paulo static void
20932176cfdSRui Paulo ieee80211_hwmp_init(void)
21032176cfdSRui Paulo {
21132176cfdSRui Paulo 	ieee80211_hwmp_pathtimeout = msecs_to_ticks(5*1000);
21232176cfdSRui Paulo 	ieee80211_hwmp_roottimeout = msecs_to_ticks(5*1000);
21332176cfdSRui Paulo 	ieee80211_hwmp_rootint = msecs_to_ticks(2*1000);
21432176cfdSRui Paulo 	ieee80211_hwmp_rannint = msecs_to_ticks(1*1000);
21532176cfdSRui Paulo 
21632176cfdSRui Paulo 	/*
21732176cfdSRui Paulo 	 * Register action frame handler.
21832176cfdSRui Paulo 	 */
21932176cfdSRui Paulo 	ieee80211_recv_action_register(IEEE80211_ACTION_CAT_MESHPATH,
22032176cfdSRui Paulo 	    IEEE80211_ACTION_MESHPATH_SEL, hwmp_recv_action_meshpath);
22132176cfdSRui Paulo 
22232176cfdSRui Paulo 	/* NB: default is 5 secs per spec */
22332176cfdSRui Paulo 	mesh_proto_hwmp.mpp_inact = msecs_to_ticks(5*1000);
22432176cfdSRui Paulo 
22532176cfdSRui Paulo 	/*
22632176cfdSRui Paulo 	 * Register HWMP.
22732176cfdSRui Paulo 	 */
22832176cfdSRui Paulo 	ieee80211_mesh_register_proto_path(&mesh_proto_hwmp);
22932176cfdSRui Paulo }
23032176cfdSRui Paulo SYSINIT(wlan_hwmp, SI_SUB_DRIVERS, SI_ORDER_SECOND, ieee80211_hwmp_init, NULL);
23132176cfdSRui Paulo 
23232176cfdSRui Paulo void
23332176cfdSRui Paulo hwmp_vattach(struct ieee80211vap *vap)
23432176cfdSRui Paulo {
23532176cfdSRui Paulo 	struct ieee80211_hwmp_state *hs;
23632176cfdSRui Paulo 
23732176cfdSRui Paulo 	KASSERT(vap->iv_opmode == IEEE80211_M_MBSS,
23832176cfdSRui Paulo 	    ("not a mesh vap, opmode %d", vap->iv_opmode));
23932176cfdSRui Paulo 
24032176cfdSRui Paulo 	hs = kmalloc(sizeof(struct ieee80211_hwmp_state), M_80211_VAP,
241*fcaa651dSRui Paulo 	    M_INTWAIT | M_ZERO);
24232176cfdSRui Paulo 	if (hs == NULL) {
24332176cfdSRui Paulo 		kprintf("%s: couldn't alloc HWMP state\n", __func__);
24432176cfdSRui Paulo 		return;
24532176cfdSRui Paulo 	}
24632176cfdSRui Paulo 	hs->hs_maxhops = IEEE80211_HWMP_DEFAULT_MAXHOPS;
24734a60cf6SRui Paulo 	callout_init_mp(&hs->hs_roottimer);
24832176cfdSRui Paulo 	vap->iv_hwmp = hs;
24932176cfdSRui Paulo }
25032176cfdSRui Paulo 
25132176cfdSRui Paulo void
25232176cfdSRui Paulo hwmp_vdetach(struct ieee80211vap *vap)
25332176cfdSRui Paulo {
25432176cfdSRui Paulo 	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
25532176cfdSRui Paulo 
256dfcf81fdSRui Paulo 	callout_stop(&hs->hs_roottimer);
25732176cfdSRui Paulo 	kfree(vap->iv_hwmp, M_80211_VAP);
25832176cfdSRui Paulo 	vap->iv_hwmp = NULL;
25932176cfdSRui Paulo }
26032176cfdSRui Paulo 
26132176cfdSRui Paulo int
26232176cfdSRui Paulo hwmp_newstate(struct ieee80211vap *vap, enum ieee80211_state ostate, int arg)
26332176cfdSRui Paulo {
26432176cfdSRui Paulo 	enum ieee80211_state nstate = vap->iv_state;
26532176cfdSRui Paulo 	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
26632176cfdSRui Paulo 
26732176cfdSRui Paulo 	IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n",
26832176cfdSRui Paulo 	    __func__, ieee80211_state_name[ostate],
26932176cfdSRui Paulo 	    ieee80211_state_name[nstate], arg);
27032176cfdSRui Paulo 
27132176cfdSRui Paulo 	if (nstate != IEEE80211_S_RUN && ostate == IEEE80211_S_RUN)
272dfcf81fdSRui Paulo 		callout_stop(&hs->hs_roottimer);
27332176cfdSRui Paulo 	if (nstate == IEEE80211_S_RUN)
27432176cfdSRui Paulo 		hwmp_rootmode_setup(vap);
27532176cfdSRui Paulo 	return 0;
27632176cfdSRui Paulo }
27732176cfdSRui Paulo 
27832176cfdSRui Paulo static int
27932176cfdSRui Paulo hwmp_recv_action_meshpath(struct ieee80211_node *ni,
28032176cfdSRui Paulo 	const struct ieee80211_frame *wh,
28132176cfdSRui Paulo 	const uint8_t *frm, const uint8_t *efrm)
28232176cfdSRui Paulo {
28332176cfdSRui Paulo 	struct ieee80211vap *vap = ni->ni_vap;
28432176cfdSRui Paulo 	struct ieee80211_meshpreq_ie preq;
28532176cfdSRui Paulo 	struct ieee80211_meshprep_ie prep;
28632176cfdSRui Paulo 	struct ieee80211_meshperr_ie perr;
28732176cfdSRui Paulo 	struct ieee80211_meshrann_ie rann;
28832176cfdSRui Paulo 	const uint8_t *iefrm = frm + 2; /* action + code */
28932176cfdSRui Paulo 	int found = 0;
29032176cfdSRui Paulo 
29132176cfdSRui Paulo 	while (efrm - iefrm > 1) {
29232176cfdSRui Paulo 		IEEE80211_VERIFY_LENGTH(efrm - iefrm, iefrm[1] + 2, return 0);
29332176cfdSRui Paulo 		switch (*iefrm) {
29432176cfdSRui Paulo 		case IEEE80211_ELEMID_MESHPREQ:
29532176cfdSRui Paulo 		{
29632176cfdSRui Paulo 			const struct ieee80211_meshpreq_ie *mpreq =
29732176cfdSRui Paulo 			    (const struct ieee80211_meshpreq_ie *) iefrm;
29832176cfdSRui Paulo 			/* XXX > 1 target */
29932176cfdSRui Paulo 			if (mpreq->preq_len !=
30032176cfdSRui Paulo 			    sizeof(struct ieee80211_meshpreq_ie) - 2) {
30132176cfdSRui Paulo 				IEEE80211_DISCARD(vap,
30232176cfdSRui Paulo 				    IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
30332176cfdSRui Paulo 				    wh, NULL, "%s", "PREQ with wrong len");
30432176cfdSRui Paulo 				vap->iv_stats.is_rx_mgtdiscard++;
30532176cfdSRui Paulo 				break;
30632176cfdSRui Paulo 			}
30732176cfdSRui Paulo 			memcpy(&preq, mpreq, sizeof(preq));
30832176cfdSRui Paulo 			preq.preq_id = LE_READ_4(&mpreq->preq_id);
30932176cfdSRui Paulo 			preq.preq_origseq = LE_READ_4(&mpreq->preq_origseq);
31032176cfdSRui Paulo 			preq.preq_lifetime = LE_READ_4(&mpreq->preq_lifetime);
31132176cfdSRui Paulo 			preq.preq_metric = LE_READ_4(&mpreq->preq_metric);
31232176cfdSRui Paulo 			preq.preq_targets[0].target_seq =
31332176cfdSRui Paulo 			    LE_READ_4(&mpreq->preq_targets[0].target_seq);
31432176cfdSRui Paulo 			hwmp_recv_preq(vap, ni, wh, &preq);
31532176cfdSRui Paulo 			found++;
31632176cfdSRui Paulo 			break;
31732176cfdSRui Paulo 		}
31832176cfdSRui Paulo 		case IEEE80211_ELEMID_MESHPREP:
31932176cfdSRui Paulo 		{
32032176cfdSRui Paulo 			const struct ieee80211_meshprep_ie *mprep =
32132176cfdSRui Paulo 			    (const struct ieee80211_meshprep_ie *) iefrm;
32232176cfdSRui Paulo 			if (mprep->prep_len !=
32332176cfdSRui Paulo 			    sizeof(struct ieee80211_meshprep_ie) - 2) {
32432176cfdSRui Paulo 				IEEE80211_DISCARD(vap,
32532176cfdSRui Paulo 				    IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
32632176cfdSRui Paulo 				    wh, NULL, "%s", "PREP with wrong len");
32732176cfdSRui Paulo 				vap->iv_stats.is_rx_mgtdiscard++;
32832176cfdSRui Paulo 				break;
32932176cfdSRui Paulo 			}
33032176cfdSRui Paulo 			memcpy(&prep, mprep, sizeof(prep));
33132176cfdSRui Paulo 			prep.prep_targetseq = LE_READ_4(&mprep->prep_targetseq);
33232176cfdSRui Paulo 			prep.prep_lifetime = LE_READ_4(&mprep->prep_lifetime);
33332176cfdSRui Paulo 			prep.prep_metric = LE_READ_4(&mprep->prep_metric);
33432176cfdSRui Paulo 			prep.prep_origseq = LE_READ_4(&mprep->prep_origseq);
33532176cfdSRui Paulo 			hwmp_recv_prep(vap, ni, wh, &prep);
33632176cfdSRui Paulo 			found++;
33732176cfdSRui Paulo 			break;
33832176cfdSRui Paulo 		}
33932176cfdSRui Paulo 		case IEEE80211_ELEMID_MESHPERR:
34032176cfdSRui Paulo 		{
34132176cfdSRui Paulo 			const struct ieee80211_meshperr_ie *mperr =
34232176cfdSRui Paulo 			    (const struct ieee80211_meshperr_ie *) iefrm;
34332176cfdSRui Paulo 			/* XXX > 1 target */
34432176cfdSRui Paulo 			if (mperr->perr_len !=
34532176cfdSRui Paulo 			    sizeof(struct ieee80211_meshperr_ie) - 2) {
34632176cfdSRui Paulo 				IEEE80211_DISCARD(vap,
34732176cfdSRui Paulo 				    IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
34832176cfdSRui Paulo 				    wh, NULL, "%s", "PERR with wrong len");
34932176cfdSRui Paulo 				vap->iv_stats.is_rx_mgtdiscard++;
35032176cfdSRui Paulo 				break;
35132176cfdSRui Paulo 			}
35232176cfdSRui Paulo 			memcpy(&perr, mperr, sizeof(perr));
35332176cfdSRui Paulo 			perr.perr_dests[0].dest_seq =
35432176cfdSRui Paulo 			    LE_READ_4(&mperr->perr_dests[0].dest_seq);
35532176cfdSRui Paulo 			hwmp_recv_perr(vap, ni, wh, &perr);
35632176cfdSRui Paulo 			found++;
35732176cfdSRui Paulo 			break;
35832176cfdSRui Paulo 		}
35932176cfdSRui Paulo 		case IEEE80211_ELEMID_MESHRANN:
36032176cfdSRui Paulo 		{
36132176cfdSRui Paulo 			const struct ieee80211_meshrann_ie *mrann =
36232176cfdSRui Paulo 			    (const struct ieee80211_meshrann_ie *) iefrm;
36332176cfdSRui Paulo 			if (mrann->rann_len !=
36432176cfdSRui Paulo 			    sizeof(struct ieee80211_meshrann_ie) - 2) {
36532176cfdSRui Paulo 				IEEE80211_DISCARD(vap,
36632176cfdSRui Paulo 				    IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
36732176cfdSRui Paulo 				    wh, NULL, "%s", "RAN with wrong len");
36832176cfdSRui Paulo 				vap->iv_stats.is_rx_mgtdiscard++;
36932176cfdSRui Paulo 				return 1;
37032176cfdSRui Paulo 			}
37132176cfdSRui Paulo 			memcpy(&rann, mrann, sizeof(rann));
37232176cfdSRui Paulo 			rann.rann_seq = LE_READ_4(&mrann->rann_seq);
37332176cfdSRui Paulo 			rann.rann_metric = LE_READ_4(&mrann->rann_metric);
37432176cfdSRui Paulo 			hwmp_recv_rann(vap, ni, wh, &rann);
37532176cfdSRui Paulo 			found++;
37632176cfdSRui Paulo 			break;
37732176cfdSRui Paulo 		}
37832176cfdSRui Paulo 		}
37932176cfdSRui Paulo 		iefrm += iefrm[1] + 2;
38032176cfdSRui Paulo 	}
38132176cfdSRui Paulo 	if (!found) {
38232176cfdSRui Paulo 		IEEE80211_DISCARD(vap,
38332176cfdSRui Paulo 		    IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
38432176cfdSRui Paulo 		    wh, NULL, "%s", "PATH SEL action without IE");
38532176cfdSRui Paulo 		vap->iv_stats.is_rx_mgtdiscard++;
38632176cfdSRui Paulo 	}
38732176cfdSRui Paulo 	return 0;
38832176cfdSRui Paulo }
38932176cfdSRui Paulo 
39032176cfdSRui Paulo static int
39132176cfdSRui Paulo hwmp_send_action(struct ieee80211_node *ni,
39232176cfdSRui Paulo     const uint8_t sa[IEEE80211_ADDR_LEN],
39332176cfdSRui Paulo     const uint8_t da[IEEE80211_ADDR_LEN],
39432176cfdSRui Paulo     uint8_t *ie, size_t len)
39532176cfdSRui Paulo {
39632176cfdSRui Paulo 	struct ieee80211vap *vap = ni->ni_vap;
39732176cfdSRui Paulo 	struct ieee80211com *ic = ni->ni_ic;
39832176cfdSRui Paulo 	struct ieee80211_bpf_params params;
39932176cfdSRui Paulo 	struct mbuf *m;
40032176cfdSRui Paulo 	uint8_t *frm;
40132176cfdSRui Paulo 
40232176cfdSRui Paulo 	if (vap->iv_state == IEEE80211_S_CAC) {
40332176cfdSRui Paulo 		IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, ni,
40432176cfdSRui Paulo 		    "block %s frame in CAC state", "HWMP action");
40532176cfdSRui Paulo 		vap->iv_stats.is_tx_badstate++;
40632176cfdSRui Paulo 		return EIO;	/* XXX */
40732176cfdSRui Paulo 	}
40832176cfdSRui Paulo 
40932176cfdSRui Paulo 	KASSERT(ni != NULL, ("null node"));
41032176cfdSRui Paulo 	/*
41132176cfdSRui Paulo 	 * Hold a reference on the node so it doesn't go away until after
41232176cfdSRui Paulo 	 * the xmit is complete all the way in the driver.  On error we
41332176cfdSRui Paulo 	 * will remove our reference.
41432176cfdSRui Paulo 	 */
41532176cfdSRui Paulo #ifdef IEEE80211_DEBUG_REFCNT
41632176cfdSRui Paulo 	IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
4176168f72eSRui Paulo 	    "ieee80211_ref_node (%s:%u) %p<%6D> refcnt %d\n",
41832176cfdSRui Paulo 	    __func__, __LINE__,
4196168f72eSRui Paulo 	    ni, ni->ni_macaddr, ":",
42032176cfdSRui Paulo 	    ieee80211_node_refcnt(ni)+1);
42132176cfdSRui Paulo #endif
42232176cfdSRui Paulo 	ieee80211_ref_node(ni);
42332176cfdSRui Paulo 
42432176cfdSRui Paulo 	m = ieee80211_getmgtframe(&frm,
42532176cfdSRui Paulo 	    ic->ic_headroom + sizeof(struct ieee80211_frame),
42632176cfdSRui Paulo 	    sizeof(struct ieee80211_action) + len
42732176cfdSRui Paulo 	);
42832176cfdSRui Paulo 	if (m == NULL) {
42932176cfdSRui Paulo 		ieee80211_free_node(ni);
43032176cfdSRui Paulo 		vap->iv_stats.is_tx_nobuf++;
43132176cfdSRui Paulo 		return ENOMEM;
43232176cfdSRui Paulo 	}
43332176cfdSRui Paulo 	*frm++ = IEEE80211_ACTION_CAT_MESHPATH;
43432176cfdSRui Paulo 	*frm++ = IEEE80211_ACTION_MESHPATH_SEL;
43532176cfdSRui Paulo 	switch (*ie) {
43632176cfdSRui Paulo 	case IEEE80211_ELEMID_MESHPREQ:
43732176cfdSRui Paulo 		frm = hwmp_add_meshpreq(frm,
43832176cfdSRui Paulo 		    (struct ieee80211_meshpreq_ie *)ie);
43932176cfdSRui Paulo 		break;
44032176cfdSRui Paulo 	case IEEE80211_ELEMID_MESHPREP:
44132176cfdSRui Paulo 		frm = hwmp_add_meshprep(frm,
44232176cfdSRui Paulo 		    (struct ieee80211_meshprep_ie *)ie);
44332176cfdSRui Paulo 		break;
44432176cfdSRui Paulo 	case IEEE80211_ELEMID_MESHPERR:
44532176cfdSRui Paulo 		frm = hwmp_add_meshperr(frm,
44632176cfdSRui Paulo 		    (struct ieee80211_meshperr_ie *)ie);
44732176cfdSRui Paulo 		break;
44832176cfdSRui Paulo 	case IEEE80211_ELEMID_MESHRANN:
44932176cfdSRui Paulo 		frm = hwmp_add_meshrann(frm,
45032176cfdSRui Paulo 		    (struct ieee80211_meshrann_ie *)ie);
45132176cfdSRui Paulo 		break;
45232176cfdSRui Paulo 	}
45332176cfdSRui Paulo 
45432176cfdSRui Paulo 	m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
45532176cfdSRui Paulo 	M_PREPEND(m, sizeof(struct ieee80211_frame), MB_DONTWAIT);
45632176cfdSRui Paulo 	if (m == NULL) {
45732176cfdSRui Paulo 		ieee80211_free_node(ni);
45832176cfdSRui Paulo 		vap->iv_stats.is_tx_nobuf++;
45932176cfdSRui Paulo 		return ENOMEM;
46032176cfdSRui Paulo 	}
46132176cfdSRui Paulo 	ieee80211_send_setup(ni, m,
46232176cfdSRui Paulo 	    IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_ACTION,
46332176cfdSRui Paulo 	    IEEE80211_NONQOS_TID, sa, da, sa);
46432176cfdSRui Paulo 
46532176cfdSRui Paulo 	m->m_flags |= M_ENCAP;		/* mark encapsulated */
46632176cfdSRui Paulo 	IEEE80211_NODE_STAT(ni, tx_mgmt);
46732176cfdSRui Paulo 
46832176cfdSRui Paulo 	memset(&params, 0, sizeof(params));
46932176cfdSRui Paulo 	params.ibp_pri = WME_AC_VO;
47032176cfdSRui Paulo 	params.ibp_rate0 = ni->ni_txparms->mgmtrate;
47132176cfdSRui Paulo 	if (IEEE80211_IS_MULTICAST(da))
47232176cfdSRui Paulo 		params.ibp_try0 = 1;
47332176cfdSRui Paulo 	else
47432176cfdSRui Paulo 		params.ibp_try0 = ni->ni_txparms->maxretry;
47532176cfdSRui Paulo 	params.ibp_power = ni->ni_txpower;
47632176cfdSRui Paulo 	return ic->ic_raw_xmit(ni, m, &params);
47732176cfdSRui Paulo }
47832176cfdSRui Paulo 
47932176cfdSRui Paulo #define ADDSHORT(frm, v) do {		\
48032176cfdSRui Paulo 	frm[0] = (v) & 0xff;		\
48132176cfdSRui Paulo 	frm[1] = (v) >> 8;		\
48232176cfdSRui Paulo 	frm += 2;			\
48332176cfdSRui Paulo } while (0)
48432176cfdSRui Paulo #define ADDWORD(frm, v) do {		\
48532176cfdSRui Paulo 	LE_WRITE_4(frm, v);		\
48632176cfdSRui Paulo 	frm += 4;			\
48732176cfdSRui Paulo } while (0)
48832176cfdSRui Paulo /*
48932176cfdSRui Paulo  * Add a Mesh Path Request IE to a frame.
49032176cfdSRui Paulo  */
49132176cfdSRui Paulo static uint8_t *
49232176cfdSRui Paulo hwmp_add_meshpreq(uint8_t *frm, const struct ieee80211_meshpreq_ie *preq)
49332176cfdSRui Paulo {
49432176cfdSRui Paulo 	int i;
49532176cfdSRui Paulo 
49632176cfdSRui Paulo 	*frm++ = IEEE80211_ELEMID_MESHPREQ;
49732176cfdSRui Paulo 	*frm++ = sizeof(struct ieee80211_meshpreq_ie) - 2 +
49832176cfdSRui Paulo 	    (preq->preq_tcount - 1) * sizeof(*preq->preq_targets);
49932176cfdSRui Paulo 	*frm++ = preq->preq_flags;
50032176cfdSRui Paulo 	*frm++ = preq->preq_hopcount;
50132176cfdSRui Paulo 	*frm++ = preq->preq_ttl;
50232176cfdSRui Paulo 	ADDWORD(frm, preq->preq_id);
50332176cfdSRui Paulo 	IEEE80211_ADDR_COPY(frm, preq->preq_origaddr); frm += 6;
50432176cfdSRui Paulo 	ADDWORD(frm, preq->preq_origseq);
50532176cfdSRui Paulo 	ADDWORD(frm, preq->preq_lifetime);
50632176cfdSRui Paulo 	ADDWORD(frm, preq->preq_metric);
50732176cfdSRui Paulo 	*frm++ = preq->preq_tcount;
50832176cfdSRui Paulo 	for (i = 0; i < preq->preq_tcount; i++) {
50932176cfdSRui Paulo 		*frm++ = preq->preq_targets[i].target_flags;
51032176cfdSRui Paulo 		IEEE80211_ADDR_COPY(frm, preq->preq_targets[i].target_addr);
51132176cfdSRui Paulo 		frm += 6;
51232176cfdSRui Paulo 		ADDWORD(frm, preq->preq_targets[i].target_seq);
51332176cfdSRui Paulo 	}
51432176cfdSRui Paulo 	return frm;
51532176cfdSRui Paulo }
51632176cfdSRui Paulo 
51732176cfdSRui Paulo /*
51832176cfdSRui Paulo  * Add a Mesh Path Reply IE to a frame.
51932176cfdSRui Paulo  */
52032176cfdSRui Paulo static uint8_t *
52132176cfdSRui Paulo hwmp_add_meshprep(uint8_t *frm, const struct ieee80211_meshprep_ie *prep)
52232176cfdSRui Paulo {
52332176cfdSRui Paulo 	*frm++ = IEEE80211_ELEMID_MESHPREP;
52432176cfdSRui Paulo 	*frm++ = sizeof(struct ieee80211_meshprep_ie) - 2;
52532176cfdSRui Paulo 	*frm++ = prep->prep_flags;
52632176cfdSRui Paulo 	*frm++ = prep->prep_hopcount;
52732176cfdSRui Paulo 	*frm++ = prep->prep_ttl;
52832176cfdSRui Paulo 	IEEE80211_ADDR_COPY(frm, prep->prep_targetaddr); frm += 6;
52932176cfdSRui Paulo 	ADDWORD(frm, prep->prep_targetseq);
53032176cfdSRui Paulo 	ADDWORD(frm, prep->prep_lifetime);
53132176cfdSRui Paulo 	ADDWORD(frm, prep->prep_metric);
53232176cfdSRui Paulo 	IEEE80211_ADDR_COPY(frm, prep->prep_origaddr); frm += 6;
53332176cfdSRui Paulo 	ADDWORD(frm, prep->prep_origseq);
53432176cfdSRui Paulo 	return frm;
53532176cfdSRui Paulo }
53632176cfdSRui Paulo 
53732176cfdSRui Paulo /*
53832176cfdSRui Paulo  * Add a Mesh Path Error IE to a frame.
53932176cfdSRui Paulo  */
54032176cfdSRui Paulo static uint8_t *
54132176cfdSRui Paulo hwmp_add_meshperr(uint8_t *frm, const struct ieee80211_meshperr_ie *perr)
54232176cfdSRui Paulo {
54332176cfdSRui Paulo 	int i;
54432176cfdSRui Paulo 
54532176cfdSRui Paulo 	*frm++ = IEEE80211_ELEMID_MESHPERR;
54632176cfdSRui Paulo 	*frm++ = sizeof(struct ieee80211_meshperr_ie) - 2 +
54732176cfdSRui Paulo 	    (perr->perr_ndests - 1) * sizeof(*perr->perr_dests);
54832176cfdSRui Paulo 	*frm++ = perr->perr_ttl;
54932176cfdSRui Paulo 	*frm++ = perr->perr_ndests;
55032176cfdSRui Paulo 	for (i = 0; i < perr->perr_ndests; i++) {
55132176cfdSRui Paulo 		*frm++ = perr->perr_dests[i].dest_flags;
55232176cfdSRui Paulo 		IEEE80211_ADDR_COPY(frm, perr->perr_dests[i].dest_addr);
55332176cfdSRui Paulo 		frm += 6;
55432176cfdSRui Paulo 		ADDWORD(frm, perr->perr_dests[i].dest_seq);
55532176cfdSRui Paulo 		ADDSHORT(frm, perr->perr_dests[i].dest_rcode);
55632176cfdSRui Paulo 	}
55732176cfdSRui Paulo 	return frm;
55832176cfdSRui Paulo }
55932176cfdSRui Paulo 
56032176cfdSRui Paulo /*
56132176cfdSRui Paulo  * Add a Root Annoucement IE to a frame.
56232176cfdSRui Paulo  */
56332176cfdSRui Paulo static uint8_t *
56432176cfdSRui Paulo hwmp_add_meshrann(uint8_t *frm, const struct ieee80211_meshrann_ie *rann)
56532176cfdSRui Paulo {
56632176cfdSRui Paulo 	*frm++ = IEEE80211_ELEMID_MESHRANN;
56732176cfdSRui Paulo 	*frm++ = sizeof(struct ieee80211_meshrann_ie) - 2;
56832176cfdSRui Paulo 	*frm++ = rann->rann_flags;
56932176cfdSRui Paulo 	*frm++ = rann->rann_hopcount;
57032176cfdSRui Paulo 	*frm++ = rann->rann_ttl;
57132176cfdSRui Paulo 	IEEE80211_ADDR_COPY(frm, rann->rann_addr); frm += 6;
57232176cfdSRui Paulo 	ADDWORD(frm, rann->rann_seq);
57332176cfdSRui Paulo 	ADDWORD(frm, rann->rann_metric);
57432176cfdSRui Paulo 	return frm;
57532176cfdSRui Paulo }
57632176cfdSRui Paulo 
57732176cfdSRui Paulo static void
57832176cfdSRui Paulo hwmp_rootmode_setup(struct ieee80211vap *vap)
57932176cfdSRui Paulo {
58032176cfdSRui Paulo 	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
58132176cfdSRui Paulo 
58232176cfdSRui Paulo 	switch (hs->hs_rootmode) {
58332176cfdSRui Paulo 	case IEEE80211_HWMP_ROOTMODE_DISABLED:
584dfcf81fdSRui Paulo 		callout_stop(&hs->hs_roottimer);
58532176cfdSRui Paulo 		break;
58632176cfdSRui Paulo 	case IEEE80211_HWMP_ROOTMODE_NORMAL:
58732176cfdSRui Paulo 	case IEEE80211_HWMP_ROOTMODE_PROACTIVE:
58832176cfdSRui Paulo 		callout_reset(&hs->hs_roottimer, ieee80211_hwmp_rootint,
58932176cfdSRui Paulo 		    hwmp_rootmode_cb, vap);
59032176cfdSRui Paulo 		break;
59132176cfdSRui Paulo 	case IEEE80211_HWMP_ROOTMODE_RANN:
59232176cfdSRui Paulo 		callout_reset(&hs->hs_roottimer, ieee80211_hwmp_rannint,
59332176cfdSRui Paulo 		    hwmp_rootmode_rann_cb, vap);
59432176cfdSRui Paulo 		break;
59532176cfdSRui Paulo 	}
59632176cfdSRui Paulo }
59732176cfdSRui Paulo 
59832176cfdSRui Paulo /*
59932176cfdSRui Paulo  * Send a broadcast Path Request to find all nodes on the mesh. We are
60032176cfdSRui Paulo  * called when the vap is configured as a HWMP root node.
60132176cfdSRui Paulo  */
60232176cfdSRui Paulo #define	PREQ_TFLAGS(n)	preq.preq_targets[n].target_flags
60332176cfdSRui Paulo #define	PREQ_TADDR(n)	preq.preq_targets[n].target_addr
60432176cfdSRui Paulo #define	PREQ_TSEQ(n)	preq.preq_targets[n].target_seq
60532176cfdSRui Paulo static void
60632176cfdSRui Paulo hwmp_rootmode_cb(void *arg)
60732176cfdSRui Paulo {
60832176cfdSRui Paulo 	struct ieee80211vap *vap = (struct ieee80211vap *)arg;
60932176cfdSRui Paulo 	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
61032176cfdSRui Paulo 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
61132176cfdSRui Paulo 	struct ieee80211_meshpreq_ie preq;
61232176cfdSRui Paulo 
61332176cfdSRui Paulo 	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, vap->iv_bss,
61432176cfdSRui Paulo 	    "%s", "send broadcast PREQ");
61532176cfdSRui Paulo 
61632176cfdSRui Paulo 	preq.preq_flags = IEEE80211_MESHPREQ_FLAGS_AM;
61732176cfdSRui Paulo 	if (ms->ms_flags & IEEE80211_MESHFLAGS_PORTAL)
61832176cfdSRui Paulo 		preq.preq_flags |= IEEE80211_MESHPREQ_FLAGS_PR;
61932176cfdSRui Paulo 	if (hs->hs_rootmode == IEEE80211_HWMP_ROOTMODE_PROACTIVE)
62032176cfdSRui Paulo 		preq.preq_flags |= IEEE80211_MESHPREQ_FLAGS_PP;
62132176cfdSRui Paulo 	preq.preq_hopcount = 0;
62232176cfdSRui Paulo 	preq.preq_ttl = ms->ms_ttl;
62332176cfdSRui Paulo 	preq.preq_id = ++hs->hs_preqid;
62432176cfdSRui Paulo 	IEEE80211_ADDR_COPY(preq.preq_origaddr, vap->iv_myaddr);
62532176cfdSRui Paulo 	preq.preq_origseq = ++hs->hs_seq;
62632176cfdSRui Paulo 	preq.preq_lifetime = ticks_to_msecs(ieee80211_hwmp_roottimeout);
62732176cfdSRui Paulo 	preq.preq_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
62832176cfdSRui Paulo 	preq.preq_tcount = 1;
62932176cfdSRui Paulo 	IEEE80211_ADDR_COPY(PREQ_TADDR(0), broadcastaddr);
63032176cfdSRui Paulo 	PREQ_TFLAGS(0) = IEEE80211_MESHPREQ_TFLAGS_TO |
63132176cfdSRui Paulo 	    IEEE80211_MESHPREQ_TFLAGS_RF;
63232176cfdSRui Paulo 	PREQ_TSEQ(0) = 0;
63332176cfdSRui Paulo 	vap->iv_stats.is_hwmp_rootreqs++;
63432176cfdSRui Paulo 	hwmp_send_preq(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &preq);
63532176cfdSRui Paulo 	hwmp_rootmode_setup(vap);
63632176cfdSRui Paulo }
63732176cfdSRui Paulo #undef	PREQ_TFLAGS
63832176cfdSRui Paulo #undef	PREQ_TADDR
63932176cfdSRui Paulo #undef	PREQ_TSEQ
64032176cfdSRui Paulo 
64132176cfdSRui Paulo /*
64232176cfdSRui Paulo  * Send a Root Annoucement (RANN) to find all the nodes on the mesh. We are
64332176cfdSRui Paulo  * called when the vap is configured as a HWMP RANN root node.
64432176cfdSRui Paulo  */
64532176cfdSRui Paulo static void
64632176cfdSRui Paulo hwmp_rootmode_rann_cb(void *arg)
64732176cfdSRui Paulo {
64832176cfdSRui Paulo 	struct ieee80211vap *vap = (struct ieee80211vap *)arg;
64932176cfdSRui Paulo 	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
65032176cfdSRui Paulo 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
65132176cfdSRui Paulo 	struct ieee80211_meshrann_ie rann;
65232176cfdSRui Paulo 
65332176cfdSRui Paulo 	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, vap->iv_bss,
65432176cfdSRui Paulo 	    "%s", "send broadcast RANN");
65532176cfdSRui Paulo 
65632176cfdSRui Paulo 	rann.rann_flags = 0;
65732176cfdSRui Paulo 	if (ms->ms_flags & IEEE80211_MESHFLAGS_PORTAL)
65832176cfdSRui Paulo 		rann.rann_flags |= IEEE80211_MESHRANN_FLAGS_PR;
65932176cfdSRui Paulo 	rann.rann_hopcount = 0;
66032176cfdSRui Paulo 	rann.rann_ttl = ms->ms_ttl;
66132176cfdSRui Paulo 	IEEE80211_ADDR_COPY(rann.rann_addr, vap->iv_myaddr);
66232176cfdSRui Paulo 	rann.rann_seq = ++hs->hs_seq;
66332176cfdSRui Paulo 	rann.rann_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
66432176cfdSRui Paulo 
66532176cfdSRui Paulo 	vap->iv_stats.is_hwmp_rootrann++;
66632176cfdSRui Paulo 	hwmp_send_rann(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &rann);
66732176cfdSRui Paulo 	hwmp_rootmode_setup(vap);
66832176cfdSRui Paulo }
66932176cfdSRui Paulo 
67032176cfdSRui Paulo #define	PREQ_TFLAGS(n)	preq->preq_targets[n].target_flags
67132176cfdSRui Paulo #define	PREQ_TADDR(n)	preq->preq_targets[n].target_addr
67232176cfdSRui Paulo #define	PREQ_TSEQ(n)	preq->preq_targets[n].target_seq
67332176cfdSRui Paulo static void
67432176cfdSRui Paulo hwmp_recv_preq(struct ieee80211vap *vap, struct ieee80211_node *ni,
67532176cfdSRui Paulo     const struct ieee80211_frame *wh, const struct ieee80211_meshpreq_ie *preq)
67632176cfdSRui Paulo {
67732176cfdSRui Paulo 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
67832176cfdSRui Paulo 	struct ieee80211_mesh_route *rt = NULL;
67932176cfdSRui Paulo 	struct ieee80211_mesh_route *rtorig = NULL;
68032176cfdSRui Paulo 	struct ieee80211_hwmp_route *hrorig;
68132176cfdSRui Paulo 	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
68232176cfdSRui Paulo 	struct ieee80211_meshprep_ie prep;
68332176cfdSRui Paulo 
68432176cfdSRui Paulo 	if (ni == vap->iv_bss ||
68532176cfdSRui Paulo 	    ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED)
68632176cfdSRui Paulo 		return;
68732176cfdSRui Paulo 	/*
68832176cfdSRui Paulo 	 * Ignore PREQs from us. Could happen because someone forward it
68932176cfdSRui Paulo 	 * back to us.
69032176cfdSRui Paulo 	 */
69132176cfdSRui Paulo 	if (IEEE80211_ADDR_EQ(vap->iv_myaddr, preq->preq_origaddr))
69232176cfdSRui Paulo 		return;
69332176cfdSRui Paulo 
69432176cfdSRui Paulo 	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
6956168f72eSRui Paulo 	    "received PREQ, source %6D", preq->preq_origaddr, ":");
69632176cfdSRui Paulo 
69732176cfdSRui Paulo 	/*
69832176cfdSRui Paulo 	 * Acceptance criteria: if the PREQ is not for us and
69932176cfdSRui Paulo 	 * forwarding is disabled, discard this PREQ.
70032176cfdSRui Paulo 	 */
70132176cfdSRui Paulo 	if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0)) &&
70232176cfdSRui Paulo 	    !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) {
70332176cfdSRui Paulo 		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_HWMP,
70432176cfdSRui Paulo 		    preq->preq_origaddr, NULL, "%s", "not accepting PREQ");
70532176cfdSRui Paulo 		return;
70632176cfdSRui Paulo 	}
70732176cfdSRui Paulo 	rtorig = ieee80211_mesh_rt_find(vap, preq->preq_origaddr);
70832176cfdSRui Paulo 	if (rtorig == NULL)
70932176cfdSRui Paulo 		rtorig = ieee80211_mesh_rt_add(vap, preq->preq_origaddr);
71032176cfdSRui Paulo 	hrorig = IEEE80211_MESH_ROUTE_PRIV(rtorig, struct ieee80211_hwmp_route);
71132176cfdSRui Paulo 	/*
71232176cfdSRui Paulo 	 * Sequence number validation.
71332176cfdSRui Paulo 	 */
71432176cfdSRui Paulo 	if (HWMP_SEQ_LEQ(preq->preq_id, hrorig->hr_preqid) &&
71532176cfdSRui Paulo 	    HWMP_SEQ_LEQ(preq->preq_origseq, hrorig->hr_seq)) {
71632176cfdSRui Paulo 		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
7176168f72eSRui Paulo 		    "discard PREQ from %6D, old seq no %u <= %u",
7186168f72eSRui Paulo 		    preq->preq_origaddr, ":",
71932176cfdSRui Paulo 		    preq->preq_origseq, hrorig->hr_seq);
72032176cfdSRui Paulo 		return;
72132176cfdSRui Paulo 	}
72232176cfdSRui Paulo 	hrorig->hr_preqid = preq->preq_id;
72332176cfdSRui Paulo 	hrorig->hr_seq = preq->preq_origseq;
72432176cfdSRui Paulo 
72532176cfdSRui Paulo 	/*
72632176cfdSRui Paulo 	 * Check if the PREQ is addressed to us.
72732176cfdSRui Paulo 	 */
72832176cfdSRui Paulo 	if (IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0))) {
72932176cfdSRui Paulo 		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
7306168f72eSRui Paulo 		    "reply to %6D", preq->preq_origaddr, ":");
73132176cfdSRui Paulo 		/*
73232176cfdSRui Paulo 		 * Build and send a PREP frame.
73332176cfdSRui Paulo 		 */
73432176cfdSRui Paulo 		prep.prep_flags = 0;
73532176cfdSRui Paulo 		prep.prep_hopcount = 0;
73632176cfdSRui Paulo 		prep.prep_ttl = ms->ms_ttl;
73732176cfdSRui Paulo 		IEEE80211_ADDR_COPY(prep.prep_targetaddr, vap->iv_myaddr);
73832176cfdSRui Paulo 		prep.prep_targetseq = ++hs->hs_seq;
73932176cfdSRui Paulo 		prep.prep_lifetime = preq->preq_lifetime;
74032176cfdSRui Paulo 		prep.prep_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
74132176cfdSRui Paulo 		IEEE80211_ADDR_COPY(prep.prep_origaddr, preq->preq_origaddr);
74232176cfdSRui Paulo 		prep.prep_origseq = preq->preq_origseq;
74332176cfdSRui Paulo 		hwmp_send_prep(ni, vap->iv_myaddr, wh->i_addr2, &prep);
74432176cfdSRui Paulo 		/*
74532176cfdSRui Paulo 		 * Build the reverse path, if we don't have it already.
74632176cfdSRui Paulo 		 */
74732176cfdSRui Paulo 		rt = ieee80211_mesh_rt_find(vap, preq->preq_origaddr);
74832176cfdSRui Paulo 		if (rt == NULL)
74932176cfdSRui Paulo 			hwmp_discover(vap, preq->preq_origaddr, NULL);
75032176cfdSRui Paulo 		else if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0)
75132176cfdSRui Paulo 			hwmp_discover(vap, rt->rt_dest, NULL);
75232176cfdSRui Paulo 		return;
75332176cfdSRui Paulo 	}
75432176cfdSRui Paulo 	/*
75532176cfdSRui Paulo 	 * Proactive PREQ: reply with a proactive PREP to the
75632176cfdSRui Paulo 	 * root STA if requested.
75732176cfdSRui Paulo 	 */
75832176cfdSRui Paulo 	if (IEEE80211_ADDR_EQ(PREQ_TADDR(0), broadcastaddr) &&
75932176cfdSRui Paulo 	    (PREQ_TFLAGS(0) &
76032176cfdSRui Paulo 	    ((IEEE80211_MESHPREQ_TFLAGS_TO|IEEE80211_MESHPREQ_TFLAGS_RF) ==
76132176cfdSRui Paulo 	    (IEEE80211_MESHPREQ_TFLAGS_TO|IEEE80211_MESHPREQ_TFLAGS_RF)))) {
76232176cfdSRui Paulo 		uint8_t rootmac[IEEE80211_ADDR_LEN];
76332176cfdSRui Paulo 
76432176cfdSRui Paulo 		IEEE80211_ADDR_COPY(rootmac, preq->preq_origaddr);
76532176cfdSRui Paulo 		rt = ieee80211_mesh_rt_find(vap, rootmac);
76632176cfdSRui Paulo 		if (rt == NULL) {
76732176cfdSRui Paulo 			rt = ieee80211_mesh_rt_add(vap, rootmac);
76832176cfdSRui Paulo 			if (rt == NULL) {
76932176cfdSRui Paulo 				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
7706168f72eSRui Paulo 				    "unable to add root mesh path to %6D",
7716168f72eSRui Paulo 				    rootmac, ":");
77232176cfdSRui Paulo 				vap->iv_stats.is_mesh_rtaddfailed++;
77332176cfdSRui Paulo 				return;
77432176cfdSRui Paulo 			}
77532176cfdSRui Paulo 		}
77632176cfdSRui Paulo 		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
7776168f72eSRui Paulo 		    "root mesh station @ %6D", rootmac, ":");
77832176cfdSRui Paulo 
77932176cfdSRui Paulo 		/*
78032176cfdSRui Paulo 		 * Reply with a PREP if we don't have a path to the root
78132176cfdSRui Paulo 		 * or if the root sent us a proactive PREQ.
78232176cfdSRui Paulo 		 */
78332176cfdSRui Paulo 		if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0 ||
78432176cfdSRui Paulo 		    (preq->preq_flags & IEEE80211_MESHPREQ_FLAGS_PP)) {
78532176cfdSRui Paulo 			prep.prep_flags = 0;
78632176cfdSRui Paulo 			prep.prep_hopcount = 0;
78732176cfdSRui Paulo 			prep.prep_ttl = ms->ms_ttl;
78832176cfdSRui Paulo 			IEEE80211_ADDR_COPY(prep.prep_origaddr, rootmac);
78932176cfdSRui Paulo 			prep.prep_origseq = preq->preq_origseq;
79032176cfdSRui Paulo 			prep.prep_lifetime = preq->preq_lifetime;
79132176cfdSRui Paulo 			prep.prep_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
79232176cfdSRui Paulo 			IEEE80211_ADDR_COPY(prep.prep_targetaddr,
79332176cfdSRui Paulo 			    vap->iv_myaddr);
79432176cfdSRui Paulo 			prep.prep_targetseq = ++hs->hs_seq;
79532176cfdSRui Paulo 			hwmp_send_prep(vap->iv_bss, vap->iv_myaddr,
79632176cfdSRui Paulo 			    broadcastaddr, &prep);
79732176cfdSRui Paulo 		}
79832176cfdSRui Paulo 		hwmp_discover(vap, rootmac, NULL);
79932176cfdSRui Paulo 		return;
80032176cfdSRui Paulo 	}
80132176cfdSRui Paulo 	rt = ieee80211_mesh_rt_find(vap, PREQ_TADDR(0));
80232176cfdSRui Paulo 
80332176cfdSRui Paulo 	/*
80432176cfdSRui Paulo 	 * Forwarding and Intermediate reply for PREQs with 1 target.
80532176cfdSRui Paulo 	 */
80632176cfdSRui Paulo 	if (preq->preq_tcount == 1) {
80732176cfdSRui Paulo 		struct ieee80211_meshpreq_ie ppreq; /* propagated PREQ */
80832176cfdSRui Paulo 
80932176cfdSRui Paulo 		memcpy(&ppreq, preq, sizeof(ppreq));
81032176cfdSRui Paulo 		/*
81132176cfdSRui Paulo 		 * We have a valid route to this node.
81232176cfdSRui Paulo 		 */
81332176cfdSRui Paulo 		if (rt != NULL &&
81432176cfdSRui Paulo 		    (rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID)) {
81532176cfdSRui Paulo 			if (preq->preq_ttl > 1 &&
81632176cfdSRui Paulo 			    preq->preq_hopcount < hs->hs_maxhops) {
81732176cfdSRui Paulo 				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
8186168f72eSRui Paulo 				    "forward PREQ from %6D",
8196168f72eSRui Paulo 				    preq->preq_origaddr, ":");
82032176cfdSRui Paulo 				/*
82132176cfdSRui Paulo 				 * Propagate the original PREQ.
82232176cfdSRui Paulo 				 */
82332176cfdSRui Paulo 				ppreq.preq_hopcount += 1;
82432176cfdSRui Paulo 				ppreq.preq_ttl -= 1;
82532176cfdSRui Paulo 				ppreq.preq_metric +=
82632176cfdSRui Paulo 				    ms->ms_pmetric->mpm_metric(ni);
82732176cfdSRui Paulo 				/*
82832176cfdSRui Paulo 				 * Set TO and unset RF bits because we are going
82932176cfdSRui Paulo 				 * to send a PREP next.
83032176cfdSRui Paulo 				 */
83132176cfdSRui Paulo 				ppreq.preq_targets[0].target_flags |=
83232176cfdSRui Paulo 				    IEEE80211_MESHPREQ_TFLAGS_TO;
83332176cfdSRui Paulo 				ppreq.preq_targets[0].target_flags &=
83432176cfdSRui Paulo 				    ~IEEE80211_MESHPREQ_TFLAGS_RF;
83532176cfdSRui Paulo 				hwmp_send_preq(ni, vap->iv_myaddr,
83632176cfdSRui Paulo 				    broadcastaddr, &ppreq);
83732176cfdSRui Paulo 			}
83832176cfdSRui Paulo 			/*
83932176cfdSRui Paulo 			 * Check if we can send an intermediate Path Reply,
84032176cfdSRui Paulo 			 * i.e., Target Only bit is not set.
84132176cfdSRui Paulo 			 */
84232176cfdSRui Paulo 	    		if (!(PREQ_TFLAGS(0) & IEEE80211_MESHPREQ_TFLAGS_TO)) {
84332176cfdSRui Paulo 				struct ieee80211_meshprep_ie prep;
84432176cfdSRui Paulo 
84532176cfdSRui Paulo 				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
8466168f72eSRui Paulo 				    "intermediate reply for PREQ from %6D",
8476168f72eSRui Paulo 				    preq->preq_origaddr, ":");
84832176cfdSRui Paulo 				prep.prep_flags = 0;
84932176cfdSRui Paulo 				prep.prep_hopcount = rt->rt_nhops + 1;
85032176cfdSRui Paulo 				prep.prep_ttl = ms->ms_ttl;
85132176cfdSRui Paulo 				IEEE80211_ADDR_COPY(&prep.prep_targetaddr,
85232176cfdSRui Paulo 				    PREQ_TADDR(0));
85332176cfdSRui Paulo 				prep.prep_targetseq = hrorig->hr_seq;
85432176cfdSRui Paulo 				prep.prep_lifetime = preq->preq_lifetime;
85532176cfdSRui Paulo 				prep.prep_metric = rt->rt_metric +
85632176cfdSRui Paulo 				    ms->ms_pmetric->mpm_metric(ni);
85732176cfdSRui Paulo 				IEEE80211_ADDR_COPY(&prep.prep_origaddr,
85832176cfdSRui Paulo 				    preq->preq_origaddr);
85932176cfdSRui Paulo 				prep.prep_origseq = hrorig->hr_seq;
86032176cfdSRui Paulo 				hwmp_send_prep(ni, vap->iv_myaddr,
86132176cfdSRui Paulo 				    broadcastaddr, &prep);
86232176cfdSRui Paulo 			}
86332176cfdSRui Paulo 		/*
86432176cfdSRui Paulo 		 * We have no information about this path,
86532176cfdSRui Paulo 		 * propagate the PREQ.
86632176cfdSRui Paulo 		 */
86732176cfdSRui Paulo 		} else if (preq->preq_ttl > 1 &&
86832176cfdSRui Paulo 		    preq->preq_hopcount < hs->hs_maxhops) {
86932176cfdSRui Paulo 			if (rt == NULL) {
87032176cfdSRui Paulo 				rt = ieee80211_mesh_rt_add(vap, PREQ_TADDR(0));
87132176cfdSRui Paulo 				if (rt == NULL) {
87232176cfdSRui Paulo 					IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP,
8736168f72eSRui Paulo 					    ni, "unable to add PREQ path to %6D",
8746168f72eSRui Paulo 					    PREQ_TADDR(0), ":");
87532176cfdSRui Paulo 					vap->iv_stats.is_mesh_rtaddfailed++;
87632176cfdSRui Paulo 					return;
87732176cfdSRui Paulo 				}
87832176cfdSRui Paulo 			}
87932176cfdSRui Paulo 			rt->rt_metric = preq->preq_metric;
88032176cfdSRui Paulo 			rt->rt_lifetime = preq->preq_lifetime;
88132176cfdSRui Paulo 			hrorig = IEEE80211_MESH_ROUTE_PRIV(rt,
88232176cfdSRui Paulo 			    struct ieee80211_hwmp_route);
88332176cfdSRui Paulo 			hrorig->hr_seq = preq->preq_origseq;
88432176cfdSRui Paulo 			hrorig->hr_preqid = preq->preq_id;
88532176cfdSRui Paulo 
88632176cfdSRui Paulo 			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
8876168f72eSRui Paulo 			    "forward PREQ from %6D",
8886168f72eSRui Paulo 			    preq->preq_origaddr, ":");
88932176cfdSRui Paulo 			ppreq.preq_hopcount += 1;
89032176cfdSRui Paulo 			ppreq.preq_ttl -= 1;
89132176cfdSRui Paulo 			ppreq.preq_metric += ms->ms_pmetric->mpm_metric(ni);
89232176cfdSRui Paulo 			hwmp_send_preq(ni, vap->iv_myaddr, broadcastaddr,
89332176cfdSRui Paulo 			    &ppreq);
89432176cfdSRui Paulo 		}
89532176cfdSRui Paulo 	}
89632176cfdSRui Paulo 
89732176cfdSRui Paulo }
89832176cfdSRui Paulo #undef	PREQ_TFLAGS
89932176cfdSRui Paulo #undef	PREQ_TADDR
90032176cfdSRui Paulo #undef	PREQ_TSEQ
90132176cfdSRui Paulo 
90232176cfdSRui Paulo static int
90332176cfdSRui Paulo hwmp_send_preq(struct ieee80211_node *ni,
90432176cfdSRui Paulo     const uint8_t sa[IEEE80211_ADDR_LEN],
90532176cfdSRui Paulo     const uint8_t da[IEEE80211_ADDR_LEN],
90632176cfdSRui Paulo     struct ieee80211_meshpreq_ie *preq)
90732176cfdSRui Paulo {
90832176cfdSRui Paulo 	struct ieee80211_hwmp_state *hs = ni->ni_vap->iv_hwmp;
90932176cfdSRui Paulo 
91032176cfdSRui Paulo 	/*
91132176cfdSRui Paulo 	 * Enforce PREQ interval.
91232176cfdSRui Paulo 	 */
91332176cfdSRui Paulo 	if (ratecheck(&hs->hs_lastpreq, &ieee80211_hwmp_preqminint) == 0)
91432176cfdSRui Paulo 		return EALREADY;
91532176cfdSRui Paulo 	getmicrouptime(&hs->hs_lastpreq);
91632176cfdSRui Paulo 
91732176cfdSRui Paulo 	/*
91832176cfdSRui Paulo 	 * mesh preq action frame format
91932176cfdSRui Paulo 	 *     [6] da
92032176cfdSRui Paulo 	 *     [6] sa
92132176cfdSRui Paulo 	 *     [6] addr3 = sa
92232176cfdSRui Paulo 	 *     [1] action
92332176cfdSRui Paulo 	 *     [1] category
92432176cfdSRui Paulo 	 *     [tlv] mesh path request
92532176cfdSRui Paulo 	 */
92632176cfdSRui Paulo 	preq->preq_ie = IEEE80211_ELEMID_MESHPREQ;
92732176cfdSRui Paulo 	return hwmp_send_action(ni, sa, da, (uint8_t *)preq,
92832176cfdSRui Paulo 	    sizeof(struct ieee80211_meshpreq_ie));
92932176cfdSRui Paulo }
93032176cfdSRui Paulo 
93132176cfdSRui Paulo static void
93232176cfdSRui Paulo hwmp_recv_prep(struct ieee80211vap *vap, struct ieee80211_node *ni,
93332176cfdSRui Paulo     const struct ieee80211_frame *wh, const struct ieee80211_meshprep_ie *prep)
93432176cfdSRui Paulo {
93532176cfdSRui Paulo 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
93632176cfdSRui Paulo 	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
93732176cfdSRui Paulo 	struct ieee80211_mesh_route *rt = NULL;
93832176cfdSRui Paulo 	struct ieee80211_hwmp_route *hr;
93932176cfdSRui Paulo 	struct ieee80211com *ic = vap->iv_ic;
94032176cfdSRui Paulo 	struct ifnet *ifp = vap->iv_ifp;
94132176cfdSRui Paulo 	struct mbuf *m, *next;
94232176cfdSRui Paulo 
94332176cfdSRui Paulo 	/*
94432176cfdSRui Paulo 	 * Acceptance criteria: if the corresponding PREQ was not generated
94532176cfdSRui Paulo 	 * by us and forwarding is disabled, discard this PREP.
94632176cfdSRui Paulo 	 */
94732176cfdSRui Paulo 	if (ni == vap->iv_bss ||
94832176cfdSRui Paulo 	    ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED)
94932176cfdSRui Paulo 		return;
95032176cfdSRui Paulo 	if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, prep->prep_origaddr) &&
95132176cfdSRui Paulo 	    !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD))
95232176cfdSRui Paulo 		return;
95332176cfdSRui Paulo 
95432176cfdSRui Paulo 	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
9556168f72eSRui Paulo 	    "received PREP from %6D", prep->prep_targetaddr, ":");
95632176cfdSRui Paulo 
95732176cfdSRui Paulo 	rt = ieee80211_mesh_rt_find(vap, prep->prep_targetaddr);
95832176cfdSRui Paulo 	if (rt == NULL) {
95932176cfdSRui Paulo 		/*
96032176cfdSRui Paulo 		 * If we have no entry this could be a reply to a root PREQ.
96132176cfdSRui Paulo 		 */
96232176cfdSRui Paulo 		if (hs->hs_rootmode != IEEE80211_HWMP_ROOTMODE_DISABLED) {
96332176cfdSRui Paulo 			rt = ieee80211_mesh_rt_add(vap, prep->prep_targetaddr);
96432176cfdSRui Paulo 			if (rt == NULL) {
96532176cfdSRui Paulo 				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP,
9666168f72eSRui Paulo 				    ni, "unable to add PREP path to %6D",
9676168f72eSRui Paulo 				    prep->prep_targetaddr, ":");
96832176cfdSRui Paulo 				vap->iv_stats.is_mesh_rtaddfailed++;
96932176cfdSRui Paulo 				return;
97032176cfdSRui Paulo 			}
97132176cfdSRui Paulo 			IEEE80211_ADDR_COPY(rt->rt_nexthop, wh->i_addr2);
97232176cfdSRui Paulo 			rt->rt_nhops = prep->prep_hopcount;
97332176cfdSRui Paulo 			rt->rt_lifetime = prep->prep_lifetime;
97432176cfdSRui Paulo 			rt->rt_metric = prep->prep_metric;
97532176cfdSRui Paulo 			rt->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID;
97632176cfdSRui Paulo 			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
9776168f72eSRui Paulo 			    "add root path to %6D nhops %d metric %d (PREP)",
9786168f72eSRui Paulo 			    prep->prep_targetaddr, ":",
97932176cfdSRui Paulo 			    rt->rt_nhops, rt->rt_metric);
98032176cfdSRui Paulo 			return;
98132176cfdSRui Paulo 		}
98232176cfdSRui Paulo 		return;
98332176cfdSRui Paulo 	}
98432176cfdSRui Paulo 	/*
98532176cfdSRui Paulo 	 * Sequence number validation.
98632176cfdSRui Paulo 	 */
98732176cfdSRui Paulo 	hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
98832176cfdSRui Paulo 	if (HWMP_SEQ_LEQ(prep->prep_targetseq, hr->hr_seq)) {
98932176cfdSRui Paulo 		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
9906168f72eSRui Paulo 		    "discard PREP from %6D, old seq no %u <= %u",
9916168f72eSRui Paulo 		    prep->prep_targetaddr, ":",
99232176cfdSRui Paulo 		    prep->prep_targetseq, hr->hr_seq);
99332176cfdSRui Paulo 		return;
99432176cfdSRui Paulo 	}
99532176cfdSRui Paulo 	hr->hr_seq = prep->prep_targetseq;
99632176cfdSRui Paulo 	/*
99732176cfdSRui Paulo 	 * If it's NOT for us, propagate the PREP.
99832176cfdSRui Paulo 	 */
99932176cfdSRui Paulo 	if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, prep->prep_origaddr) &&
100032176cfdSRui Paulo 	    prep->prep_ttl > 1 && prep->prep_hopcount < hs->hs_maxhops) {
100132176cfdSRui Paulo 		struct ieee80211_meshprep_ie pprep; /* propagated PREP */
100232176cfdSRui Paulo 
100332176cfdSRui Paulo 		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
10046168f72eSRui Paulo 		    "propagate PREP from %6D",
10056168f72eSRui Paulo 		    prep->prep_targetaddr, ":");
100632176cfdSRui Paulo 
100732176cfdSRui Paulo 		memcpy(&pprep, prep, sizeof(pprep));
100832176cfdSRui Paulo 		pprep.prep_hopcount += 1;
100932176cfdSRui Paulo 		pprep.prep_ttl -= 1;
101032176cfdSRui Paulo 		pprep.prep_metric += ms->ms_pmetric->mpm_metric(ni);
101132176cfdSRui Paulo 		IEEE80211_ADDR_COPY(pprep.prep_targetaddr, vap->iv_myaddr);
101232176cfdSRui Paulo 		hwmp_send_prep(ni, vap->iv_myaddr, broadcastaddr, &pprep);
101332176cfdSRui Paulo 	}
101432176cfdSRui Paulo 	hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
101532176cfdSRui Paulo 	if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY) {
101632176cfdSRui Paulo 		/* NB: never clobber a proxy entry */;
101732176cfdSRui Paulo 		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
10186168f72eSRui Paulo 		    "discard PREP for %6D, route is marked PROXY",
10196168f72eSRui Paulo 		    prep->prep_targetaddr, ":");
102032176cfdSRui Paulo 		vap->iv_stats.is_hwmp_proxy++;
102132176cfdSRui Paulo 	} else if (prep->prep_origseq == hr->hr_origseq) {
102232176cfdSRui Paulo 		/*
102332176cfdSRui Paulo 		 * Check if we already have a path to this node.
102432176cfdSRui Paulo 		 * If we do, check if this path reply contains a
102532176cfdSRui Paulo 		 * better route.
102632176cfdSRui Paulo 		 */
102732176cfdSRui Paulo 		if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0 ||
102832176cfdSRui Paulo 		    (prep->prep_hopcount < rt->rt_nhops ||
102932176cfdSRui Paulo 		     prep->prep_metric < rt->rt_metric)) {
103032176cfdSRui Paulo 			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
10316168f72eSRui Paulo 			    "%s path to %6D, hopcount %d:%d metric %d:%d",
103232176cfdSRui Paulo 			    rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID ?
103332176cfdSRui Paulo 				"prefer" : "update",
10346168f72eSRui Paulo 			    prep->prep_origaddr, ":",
103532176cfdSRui Paulo 			    rt->rt_nhops, prep->prep_hopcount,
103632176cfdSRui Paulo 			    rt->rt_metric, prep->prep_metric);
103732176cfdSRui Paulo 			IEEE80211_ADDR_COPY(rt->rt_nexthop, wh->i_addr2);
103832176cfdSRui Paulo 			rt->rt_nhops = prep->prep_hopcount;
103932176cfdSRui Paulo 			rt->rt_lifetime = prep->prep_lifetime;
104032176cfdSRui Paulo 			rt->rt_metric = prep->prep_metric;
104132176cfdSRui Paulo 			rt->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID;
104232176cfdSRui Paulo 		} else {
104332176cfdSRui Paulo 			IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
10446168f72eSRui Paulo 			    "ignore PREP for %6D, hopcount %d:%d metric %d:%d",
10456168f72eSRui Paulo 			    prep->prep_targetaddr, ":",
104632176cfdSRui Paulo 			    rt->rt_nhops, prep->prep_hopcount,
104732176cfdSRui Paulo 			    rt->rt_metric, prep->prep_metric);
104832176cfdSRui Paulo 		}
104932176cfdSRui Paulo 	} else {
105032176cfdSRui Paulo 		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
10516168f72eSRui Paulo 		    "discard PREP for %6D, wrong seqno %u != %u",
10526168f72eSRui Paulo 		    prep->prep_targetaddr, ":", prep->prep_origseq,
105332176cfdSRui Paulo 		    hr->hr_seq);
105432176cfdSRui Paulo 		vap->iv_stats.is_hwmp_wrongseq++;
105532176cfdSRui Paulo 	}
105632176cfdSRui Paulo 	/*
105732176cfdSRui Paulo 	 * Check for frames queued awaiting path discovery.
105832176cfdSRui Paulo 	 * XXX probably can tell exactly and avoid remove call
105932176cfdSRui Paulo 	 * NB: hash may have false matches, if so they will get
106032176cfdSRui Paulo 	 *     stuck back on the stageq because there won't be
106132176cfdSRui Paulo 	 *     a path.
106232176cfdSRui Paulo 	 */
106332176cfdSRui Paulo 	m = ieee80211_ageq_remove(&ic->ic_stageq,
106432176cfdSRui Paulo 	    (struct ieee80211_node *)(uintptr_t)
106532176cfdSRui Paulo 		ieee80211_mac_hash(ic, rt->rt_dest));
106632176cfdSRui Paulo 	for (; m != NULL; m = next) {
106732176cfdSRui Paulo 		next = m->m_nextpkt;
106832176cfdSRui Paulo 		m->m_nextpkt = NULL;
106932176cfdSRui Paulo 		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
107032176cfdSRui Paulo 		    "flush queued frame %p len %d", m, m->m_pkthdr.len);
1071*fcaa651dSRui Paulo 		ieee80211_handoff(ifp, m);
107232176cfdSRui Paulo 	}
107332176cfdSRui Paulo }
107432176cfdSRui Paulo 
107532176cfdSRui Paulo static int
107632176cfdSRui Paulo hwmp_send_prep(struct ieee80211_node *ni,
107732176cfdSRui Paulo     const uint8_t sa[IEEE80211_ADDR_LEN],
107832176cfdSRui Paulo     const uint8_t da[IEEE80211_ADDR_LEN],
107932176cfdSRui Paulo     struct ieee80211_meshprep_ie *prep)
108032176cfdSRui Paulo {
108132176cfdSRui Paulo 	/* NB: there's no PREP minimum interval. */
108232176cfdSRui Paulo 
108332176cfdSRui Paulo 	/*
108432176cfdSRui Paulo 	 * mesh prep action frame format
108532176cfdSRui Paulo 	 *     [6] da
108632176cfdSRui Paulo 	 *     [6] sa
108732176cfdSRui Paulo 	 *     [6] addr3 = sa
108832176cfdSRui Paulo 	 *     [1] action
108932176cfdSRui Paulo 	 *     [1] category
109032176cfdSRui Paulo 	 *     [tlv] mesh path reply
109132176cfdSRui Paulo 	 */
109232176cfdSRui Paulo 	prep->prep_ie = IEEE80211_ELEMID_MESHPREP;
109332176cfdSRui Paulo 	return hwmp_send_action(ni, sa, da, (uint8_t *)prep,
109432176cfdSRui Paulo 	    sizeof(struct ieee80211_meshprep_ie));
109532176cfdSRui Paulo }
109632176cfdSRui Paulo 
109732176cfdSRui Paulo #define	PERR_DFLAGS(n)	perr.perr_dests[n].dest_flags
109832176cfdSRui Paulo #define	PERR_DADDR(n)	perr.perr_dests[n].dest_addr
109932176cfdSRui Paulo #define	PERR_DSEQ(n)	perr.perr_dests[n].dest_seq
110032176cfdSRui Paulo #define	PERR_DRCODE(n)	perr.perr_dests[n].dest_rcode
110132176cfdSRui Paulo static void
110232176cfdSRui Paulo hwmp_peerdown(struct ieee80211_node *ni)
110332176cfdSRui Paulo {
110432176cfdSRui Paulo 	struct ieee80211vap *vap = ni->ni_vap;
110532176cfdSRui Paulo 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
110632176cfdSRui Paulo 	struct ieee80211_meshperr_ie perr;
110732176cfdSRui Paulo 	struct ieee80211_mesh_route *rt;
110832176cfdSRui Paulo 	struct ieee80211_hwmp_route *hr;
110932176cfdSRui Paulo 
111032176cfdSRui Paulo 	rt = ieee80211_mesh_rt_find(vap, ni->ni_macaddr);
111132176cfdSRui Paulo 	if (rt == NULL)
111232176cfdSRui Paulo 		return;
111332176cfdSRui Paulo 	hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
111432176cfdSRui Paulo 	IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
111532176cfdSRui Paulo 	    "%s", "delete route entry");
111632176cfdSRui Paulo 	perr.perr_ttl = ms->ms_ttl;
111732176cfdSRui Paulo 	perr.perr_ndests = 1;
111832176cfdSRui Paulo 	PERR_DFLAGS(0) = 0;
111932176cfdSRui Paulo 	if (hr->hr_seq == 0)
112032176cfdSRui Paulo 		PERR_DFLAGS(0) |= IEEE80211_MESHPERR_DFLAGS_USN;
112132176cfdSRui Paulo 	PERR_DFLAGS(0) |= IEEE80211_MESHPERR_DFLAGS_RC;
112232176cfdSRui Paulo 	IEEE80211_ADDR_COPY(PERR_DADDR(0), rt->rt_dest);
112332176cfdSRui Paulo 	PERR_DSEQ(0) = hr->hr_seq;
112432176cfdSRui Paulo 	PERR_DRCODE(0) = IEEE80211_REASON_MESH_PERR_DEST_UNREACH;
112532176cfdSRui Paulo 	/* NB: flush everything passing through peer */
112632176cfdSRui Paulo 	ieee80211_mesh_rt_flush_peer(vap, ni->ni_macaddr);
112732176cfdSRui Paulo 	hwmp_send_perr(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &perr);
112832176cfdSRui Paulo }
112932176cfdSRui Paulo #undef	PERR_DFLAGS
113032176cfdSRui Paulo #undef	PERR_DADDR
113132176cfdSRui Paulo #undef	PERR_DSEQ
113232176cfdSRui Paulo #undef	PERR_DRCODE
113332176cfdSRui Paulo 
113432176cfdSRui Paulo #define	PERR_DFLAGS(n)	perr->perr_dests[n].dest_flags
113532176cfdSRui Paulo #define	PERR_DADDR(n)	perr->perr_dests[n].dest_addr
113632176cfdSRui Paulo #define	PERR_DSEQ(n)	perr->perr_dests[n].dest_seq
113732176cfdSRui Paulo #define	PERR_DRCODE(n)	perr->perr_dests[n].dest_rcode
113832176cfdSRui Paulo static void
113932176cfdSRui Paulo hwmp_recv_perr(struct ieee80211vap *vap, struct ieee80211_node *ni,
114032176cfdSRui Paulo     const struct ieee80211_frame *wh, const struct ieee80211_meshperr_ie *perr)
114132176cfdSRui Paulo {
114232176cfdSRui Paulo 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
114332176cfdSRui Paulo 	struct ieee80211_mesh_route *rt = NULL;
114432176cfdSRui Paulo 	struct ieee80211_hwmp_route *hr;
114532176cfdSRui Paulo  	struct ieee80211_meshperr_ie pperr;
114632176cfdSRui Paulo 	int i, forward = 0;
114732176cfdSRui Paulo 
114832176cfdSRui Paulo 	/*
114932176cfdSRui Paulo 	 * Acceptance criteria: check if we received a PERR from a
115032176cfdSRui Paulo 	 * neighbor and forwarding is enabled.
115132176cfdSRui Paulo 	 */
115232176cfdSRui Paulo 	if (ni == vap->iv_bss ||
115332176cfdSRui Paulo 	    ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED ||
115432176cfdSRui Paulo 	    !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD))
115532176cfdSRui Paulo 		return;
115632176cfdSRui Paulo 	/*
115732176cfdSRui Paulo 	 * Find all routing entries that match and delete them.
115832176cfdSRui Paulo 	 */
115932176cfdSRui Paulo 	for (i = 0; i < perr->perr_ndests; i++) {
116032176cfdSRui Paulo 		rt = ieee80211_mesh_rt_find(vap, PERR_DADDR(i));
116132176cfdSRui Paulo 		if (rt == NULL)
116232176cfdSRui Paulo 			continue;
116332176cfdSRui Paulo 		hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
116432176cfdSRui Paulo 		if (!(PERR_DFLAGS(0) & IEEE80211_MESHPERR_DFLAGS_USN) &&
116532176cfdSRui Paulo 		    HWMP_SEQ_GEQ(PERR_DSEQ(i), hr->hr_seq)) {
116632176cfdSRui Paulo 			ieee80211_mesh_rt_del(vap, rt->rt_dest);
116732176cfdSRui Paulo 			ieee80211_mesh_rt_flush_peer(vap, rt->rt_dest);
116832176cfdSRui Paulo 			rt = NULL;
116932176cfdSRui Paulo 			forward = 1;
117032176cfdSRui Paulo 		}
117132176cfdSRui Paulo 	}
117232176cfdSRui Paulo 	/*
117332176cfdSRui Paulo 	 * Propagate the PERR if we previously found it on our routing table.
117432176cfdSRui Paulo 	 * XXX handle ndest > 1
117532176cfdSRui Paulo 	 */
117632176cfdSRui Paulo 	if (forward && perr->perr_ttl > 1) {
117732176cfdSRui Paulo 		IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
11786168f72eSRui Paulo 		    "propagate PERR from %6D", wh->i_addr2, ":");
117932176cfdSRui Paulo 		memcpy(&pperr, perr, sizeof(*perr));
118032176cfdSRui Paulo 		pperr.perr_ttl--;
118132176cfdSRui Paulo 		hwmp_send_perr(vap->iv_bss, vap->iv_myaddr, broadcastaddr,
118232176cfdSRui Paulo 		    &pperr);
118332176cfdSRui Paulo 	}
118432176cfdSRui Paulo }
118532176cfdSRui Paulo #undef	PEER_DADDR
118632176cfdSRui Paulo #undef	PERR_DSEQ
118732176cfdSRui Paulo 
118832176cfdSRui Paulo static int
118932176cfdSRui Paulo hwmp_send_perr(struct ieee80211_node *ni,
119032176cfdSRui Paulo     const uint8_t sa[IEEE80211_ADDR_LEN],
119132176cfdSRui Paulo     const uint8_t da[IEEE80211_ADDR_LEN],
119232176cfdSRui Paulo     struct ieee80211_meshperr_ie *perr)
119332176cfdSRui Paulo {
119432176cfdSRui Paulo 	struct ieee80211_hwmp_state *hs = ni->ni_vap->iv_hwmp;
119532176cfdSRui Paulo 
119632176cfdSRui Paulo 	/*
119732176cfdSRui Paulo 	 * Enforce PERR interval.
119832176cfdSRui Paulo 	 */
119932176cfdSRui Paulo 	if (ratecheck(&hs->hs_lastperr, &ieee80211_hwmp_perrminint) == 0)
120032176cfdSRui Paulo 		return EALREADY;
120132176cfdSRui Paulo 	getmicrouptime(&hs->hs_lastperr);
120232176cfdSRui Paulo 
120332176cfdSRui Paulo 	/*
120432176cfdSRui Paulo 	 * mesh perr action frame format
120532176cfdSRui Paulo 	 *     [6] da
120632176cfdSRui Paulo 	 *     [6] sa
120732176cfdSRui Paulo 	 *     [6] addr3 = sa
120832176cfdSRui Paulo 	 *     [1] action
120932176cfdSRui Paulo 	 *     [1] category
121032176cfdSRui Paulo 	 *     [tlv] mesh path error
121132176cfdSRui Paulo 	 */
121232176cfdSRui Paulo 	perr->perr_ie = IEEE80211_ELEMID_MESHPERR;
121332176cfdSRui Paulo 	return hwmp_send_action(ni, sa, da, (uint8_t *)perr,
121432176cfdSRui Paulo 	    sizeof(struct ieee80211_meshperr_ie));
121532176cfdSRui Paulo }
121632176cfdSRui Paulo 
121732176cfdSRui Paulo static void
121832176cfdSRui Paulo hwmp_recv_rann(struct ieee80211vap *vap, struct ieee80211_node *ni,
121932176cfdSRui Paulo     const struct ieee80211_frame *wh, const struct ieee80211_meshrann_ie *rann)
122032176cfdSRui Paulo {
122132176cfdSRui Paulo 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
122232176cfdSRui Paulo 	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
122332176cfdSRui Paulo 	struct ieee80211_mesh_route *rt = NULL;
122432176cfdSRui Paulo 	struct ieee80211_hwmp_route *hr;
122532176cfdSRui Paulo 	struct ieee80211_meshrann_ie prann;
122632176cfdSRui Paulo 
122732176cfdSRui Paulo 	if (ni == vap->iv_bss ||
122832176cfdSRui Paulo 	    ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED ||
122932176cfdSRui Paulo 	    IEEE80211_ADDR_EQ(rann->rann_addr, vap->iv_myaddr))
123032176cfdSRui Paulo 		return;
123132176cfdSRui Paulo 
123232176cfdSRui Paulo 	rt = ieee80211_mesh_rt_find(vap, rann->rann_addr);
123332176cfdSRui Paulo 	/*
123432176cfdSRui Paulo 	 * Discover the path to the root mesh STA.
123532176cfdSRui Paulo 	 * If we already know it, propagate the RANN element.
123632176cfdSRui Paulo 	 */
123732176cfdSRui Paulo 	if (rt == NULL) {
123832176cfdSRui Paulo 		hwmp_discover(vap, rann->rann_addr, NULL);
123932176cfdSRui Paulo 		return;
124032176cfdSRui Paulo 	}
124132176cfdSRui Paulo 	hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
124232176cfdSRui Paulo 	if (HWMP_SEQ_GT(rann->rann_seq, hr->hr_seq)) {
124332176cfdSRui Paulo 		hr->hr_seq = rann->rann_seq;
124432176cfdSRui Paulo 		if (rann->rann_ttl > 1 &&
124532176cfdSRui Paulo 		    rann->rann_hopcount < hs->hs_maxhops &&
124632176cfdSRui Paulo 		    (ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) {
124732176cfdSRui Paulo 			memcpy(&prann, rann, sizeof(prann));
124832176cfdSRui Paulo 			prann.rann_hopcount += 1;
124932176cfdSRui Paulo 			prann.rann_ttl -= 1;
125032176cfdSRui Paulo 			prann.rann_metric += ms->ms_pmetric->mpm_metric(ni);
125132176cfdSRui Paulo 			hwmp_send_rann(vap->iv_bss, vap->iv_myaddr,
125232176cfdSRui Paulo 			    broadcastaddr, &prann);
125332176cfdSRui Paulo 		}
125432176cfdSRui Paulo 	}
125532176cfdSRui Paulo }
125632176cfdSRui Paulo 
125732176cfdSRui Paulo static int
125832176cfdSRui Paulo hwmp_send_rann(struct ieee80211_node *ni,
125932176cfdSRui Paulo     const uint8_t sa[IEEE80211_ADDR_LEN],
126032176cfdSRui Paulo     const uint8_t da[IEEE80211_ADDR_LEN],
126132176cfdSRui Paulo     struct ieee80211_meshrann_ie *rann)
126232176cfdSRui Paulo {
126332176cfdSRui Paulo 	/*
126432176cfdSRui Paulo 	 * mesh rann action frame format
126532176cfdSRui Paulo 	 *     [6] da
126632176cfdSRui Paulo 	 *     [6] sa
126732176cfdSRui Paulo 	 *     [6] addr3 = sa
126832176cfdSRui Paulo 	 *     [1] action
126932176cfdSRui Paulo 	 *     [1] category
127032176cfdSRui Paulo 	 *     [tlv] root annoucement
127132176cfdSRui Paulo 	 */
127232176cfdSRui Paulo 	rann->rann_ie = IEEE80211_ELEMID_MESHRANN;
127332176cfdSRui Paulo 	return hwmp_send_action(ni, sa, da, (uint8_t *)rann,
127432176cfdSRui Paulo 	    sizeof(struct ieee80211_meshrann_ie));
127532176cfdSRui Paulo }
127632176cfdSRui Paulo 
127732176cfdSRui Paulo #define	PREQ_TFLAGS(n)	preq.preq_targets[n].target_flags
127832176cfdSRui Paulo #define	PREQ_TADDR(n)	preq.preq_targets[n].target_addr
127932176cfdSRui Paulo #define	PREQ_TSEQ(n)	preq.preq_targets[n].target_seq
128032176cfdSRui Paulo static struct ieee80211_node *
128132176cfdSRui Paulo hwmp_discover(struct ieee80211vap *vap,
128232176cfdSRui Paulo     const uint8_t dest[IEEE80211_ADDR_LEN], struct mbuf *m)
128332176cfdSRui Paulo {
128432176cfdSRui Paulo 	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
128532176cfdSRui Paulo 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
128632176cfdSRui Paulo 	struct ieee80211_mesh_route *rt = NULL;
128732176cfdSRui Paulo 	struct ieee80211_hwmp_route *hr;
128832176cfdSRui Paulo 	struct ieee80211_meshpreq_ie preq;
128932176cfdSRui Paulo 	struct ieee80211_node *ni;
129032176cfdSRui Paulo 	int sendpreq = 0;
129132176cfdSRui Paulo 
129232176cfdSRui Paulo 	KASSERT(vap->iv_opmode == IEEE80211_M_MBSS,
129332176cfdSRui Paulo 	    ("not a mesh vap, opmode %d", vap->iv_opmode));
129432176cfdSRui Paulo 
129532176cfdSRui Paulo 	KASSERT(!IEEE80211_ADDR_EQ(vap->iv_myaddr, dest),
129632176cfdSRui Paulo 	    ("%s: discovering self!", __func__));
129732176cfdSRui Paulo 
129832176cfdSRui Paulo 	ni = NULL;
129932176cfdSRui Paulo 	if (!IEEE80211_IS_MULTICAST(dest)) {
130032176cfdSRui Paulo 		rt = ieee80211_mesh_rt_find(vap, dest);
130132176cfdSRui Paulo 		if (rt == NULL) {
130232176cfdSRui Paulo 			rt = ieee80211_mesh_rt_add(vap, dest);
130332176cfdSRui Paulo 			if (rt == NULL) {
130432176cfdSRui Paulo 				IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP,
13056168f72eSRui Paulo 				    ni, "unable to add discovery path to %6D",
13066168f72eSRui Paulo 				    dest, ":");
130732176cfdSRui Paulo 				vap->iv_stats.is_mesh_rtaddfailed++;
130832176cfdSRui Paulo 				goto done;
130932176cfdSRui Paulo 			}
131032176cfdSRui Paulo 		}
131132176cfdSRui Paulo 		hr = IEEE80211_MESH_ROUTE_PRIV(rt,
131232176cfdSRui Paulo 		    struct ieee80211_hwmp_route);
131332176cfdSRui Paulo 		if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0) {
131432176cfdSRui Paulo 			if (hr->hr_origseq == 0)
131532176cfdSRui Paulo 				hr->hr_origseq = ++hs->hs_seq;
131632176cfdSRui Paulo 			rt->rt_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
131732176cfdSRui Paulo 			rt->rt_lifetime =
131832176cfdSRui Paulo 			    ticks_to_msecs(ieee80211_hwmp_pathtimeout);
131932176cfdSRui Paulo 			/* XXX check preq retries */
132032176cfdSRui Paulo 			sendpreq = 1;
1321ea86af0dSRui Paulo 			if (m != NULL) {
1322ea86af0dSRui Paulo 				IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP,
1323ea86af0dSRui Paulo 				    dest, "%s",
1324ea86af0dSRui Paulo 				    "start path discovery (src <none>)");
1325ea86af0dSRui Paulo 			} else {
1326ea86af0dSRui Paulo 				IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP,
1327ea86af0dSRui Paulo 				    dest,
13286168f72eSRui Paulo 				    "start path discovery (src %6D)",
13296168f72eSRui Paulo 				    mtod(m, struct ether_header *)->ether_shost,
13306168f72eSRui Paulo 				    ":");
1331ea86af0dSRui Paulo 			}
133232176cfdSRui Paulo 			/*
133332176cfdSRui Paulo 			 * Try to discover the path for this node.
133432176cfdSRui Paulo 			 */
133532176cfdSRui Paulo 			preq.preq_flags = 0;
133632176cfdSRui Paulo 			preq.preq_hopcount = 0;
133732176cfdSRui Paulo 			preq.preq_ttl = ms->ms_ttl;
133832176cfdSRui Paulo 			preq.preq_id = ++hs->hs_preqid;
133932176cfdSRui Paulo 			IEEE80211_ADDR_COPY(preq.preq_origaddr, vap->iv_myaddr);
134032176cfdSRui Paulo 			preq.preq_origseq = hr->hr_origseq;
134132176cfdSRui Paulo 			preq.preq_lifetime = rt->rt_lifetime;
134232176cfdSRui Paulo 			preq.preq_metric = rt->rt_metric;
134332176cfdSRui Paulo 			preq.preq_tcount = 1;
134432176cfdSRui Paulo 			IEEE80211_ADDR_COPY(PREQ_TADDR(0), dest);
134532176cfdSRui Paulo 			PREQ_TFLAGS(0) = 0;
134632176cfdSRui Paulo 			if (ieee80211_hwmp_targetonly)
134732176cfdSRui Paulo 				PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_TO;
134832176cfdSRui Paulo 			if (ieee80211_hwmp_replyforward)
134932176cfdSRui Paulo 				PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_RF;
135032176cfdSRui Paulo 			PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_USN;
135132176cfdSRui Paulo 			PREQ_TSEQ(0) = 0;
135232176cfdSRui Paulo 			/* XXX check return value */
135332176cfdSRui Paulo 			hwmp_send_preq(vap->iv_bss, vap->iv_myaddr,
135432176cfdSRui Paulo 			    broadcastaddr, &preq);
135532176cfdSRui Paulo 		}
135632176cfdSRui Paulo 		if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID)
135732176cfdSRui Paulo 			ni = ieee80211_find_txnode(vap, rt->rt_nexthop);
135832176cfdSRui Paulo 	} else {
135932176cfdSRui Paulo 		ni = ieee80211_find_txnode(vap, dest);
136032176cfdSRui Paulo 		/* NB: if null then we leak mbuf */
136132176cfdSRui Paulo 		KASSERT(ni != NULL, ("leak mcast frame"));
136232176cfdSRui Paulo 		return ni;
136332176cfdSRui Paulo 	}
136432176cfdSRui Paulo done:
136532176cfdSRui Paulo 	if (ni == NULL && m != NULL) {
136632176cfdSRui Paulo 		if (sendpreq) {
136732176cfdSRui Paulo 			struct ieee80211com *ic = vap->iv_ic;
136832176cfdSRui Paulo 			/*
136932176cfdSRui Paulo 			 * Queue packet for transmit when path discovery
137032176cfdSRui Paulo 			 * completes.  If discovery never completes the
137132176cfdSRui Paulo 			 * frame will be flushed by way of the aging timer.
137232176cfdSRui Paulo 			 */
137332176cfdSRui Paulo 			IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP, dest,
137432176cfdSRui Paulo 			    "%s", "queue frame until path found");
137532176cfdSRui Paulo 			m->m_pkthdr.rcvif = (void *)(uintptr_t)
137632176cfdSRui Paulo 			    ieee80211_mac_hash(ic, dest);
137732176cfdSRui Paulo 			/* XXX age chosen randomly */
137832176cfdSRui Paulo 			ieee80211_ageq_append(&ic->ic_stageq, m,
137932176cfdSRui Paulo 			    IEEE80211_INACT_WAIT);
138032176cfdSRui Paulo 		} else {
138132176cfdSRui Paulo 			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_HWMP,
138232176cfdSRui Paulo 			    dest, NULL, "%s", "no valid path to this node");
138332176cfdSRui Paulo 			m_freem(m);
138432176cfdSRui Paulo 		}
138532176cfdSRui Paulo 	}
138632176cfdSRui Paulo 	return ni;
138732176cfdSRui Paulo }
138832176cfdSRui Paulo #undef	PREQ_TFLAGS
138932176cfdSRui Paulo #undef	PREQ_TADDR
139032176cfdSRui Paulo #undef	PREQ_TSEQ
139132176cfdSRui Paulo 
139232176cfdSRui Paulo static int
139332176cfdSRui Paulo hwmp_ioctl_get80211(struct ieee80211vap *vap, struct ieee80211req *ireq)
139432176cfdSRui Paulo {
139532176cfdSRui Paulo 	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
139632176cfdSRui Paulo 	int error;
139732176cfdSRui Paulo 
139832176cfdSRui Paulo 	if (vap->iv_opmode != IEEE80211_M_MBSS)
139932176cfdSRui Paulo 		return ENOSYS;
140032176cfdSRui Paulo 	error = 0;
140132176cfdSRui Paulo 	switch (ireq->i_type) {
140232176cfdSRui Paulo 	case IEEE80211_IOC_HWMP_ROOTMODE:
140332176cfdSRui Paulo 		ireq->i_val = hs->hs_rootmode;
140432176cfdSRui Paulo 		break;
140532176cfdSRui Paulo 	case IEEE80211_IOC_HWMP_MAXHOPS:
140632176cfdSRui Paulo 		ireq->i_val = hs->hs_maxhops;
140732176cfdSRui Paulo 		break;
140832176cfdSRui Paulo 	default:
140932176cfdSRui Paulo 		return ENOSYS;
141032176cfdSRui Paulo 	}
141132176cfdSRui Paulo 	return error;
141232176cfdSRui Paulo }
141332176cfdSRui Paulo IEEE80211_IOCTL_GET(hwmp, hwmp_ioctl_get80211);
141432176cfdSRui Paulo 
141532176cfdSRui Paulo static int
141632176cfdSRui Paulo hwmp_ioctl_set80211(struct ieee80211vap *vap, struct ieee80211req *ireq)
141732176cfdSRui Paulo {
141832176cfdSRui Paulo 	struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
141932176cfdSRui Paulo 	int error;
142032176cfdSRui Paulo 
142132176cfdSRui Paulo 	if (vap->iv_opmode != IEEE80211_M_MBSS)
142232176cfdSRui Paulo 		return ENOSYS;
142332176cfdSRui Paulo 	error = 0;
142432176cfdSRui Paulo 	switch (ireq->i_type) {
142532176cfdSRui Paulo 	case IEEE80211_IOC_HWMP_ROOTMODE:
142632176cfdSRui Paulo 		if (ireq->i_val < 0 || ireq->i_val > 3)
142732176cfdSRui Paulo 			return EINVAL;
142832176cfdSRui Paulo 		hs->hs_rootmode = ireq->i_val;
142932176cfdSRui Paulo 		hwmp_rootmode_setup(vap);
143032176cfdSRui Paulo 		break;
143132176cfdSRui Paulo 	case IEEE80211_IOC_HWMP_MAXHOPS:
143232176cfdSRui Paulo 		if (ireq->i_val <= 0 || ireq->i_val > 255)
143332176cfdSRui Paulo 			return EINVAL;
143432176cfdSRui Paulo 		hs->hs_maxhops = ireq->i_val;
143532176cfdSRui Paulo 		break;
143632176cfdSRui Paulo 	default:
143732176cfdSRui Paulo 		return ENOSYS;
143832176cfdSRui Paulo 	}
143932176cfdSRui Paulo 	return error;
144032176cfdSRui Paulo }
144132176cfdSRui Paulo IEEE80211_IOCTL_SET(hwmp, hwmp_ioctl_set80211);
1442