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