xref: /dflybsd-src/sys/netproto/802_11/wlan/ieee80211_mesh.c (revision b5523eac31a95e6876e05e20e6fe836ec3a45202)
132176cfdSRui Paulo /*-
232176cfdSRui Paulo  * Copyright (c) 2009 The FreeBSD Foundation
332176cfdSRui Paulo  * All rights reserved.
432176cfdSRui Paulo  *
532176cfdSRui Paulo  * This software was developed by Rui Paulo under sponsorship from the
632176cfdSRui Paulo  * FreeBSD Foundation.
732176cfdSRui Paulo  *
832176cfdSRui Paulo  * Redistribution and use in source and binary forms, with or without
932176cfdSRui Paulo  * modification, are permitted provided that the following conditions
1032176cfdSRui Paulo  * are met:
1132176cfdSRui Paulo  * 1. Redistributions of source code must retain the above copyright
1232176cfdSRui Paulo  *    notice, this list of conditions and the following disclaimer.
1332176cfdSRui Paulo  * 2. Redistributions in binary form must reproduce the above copyright
1432176cfdSRui Paulo  *    notice, this list of conditions and the following disclaimer in the
1532176cfdSRui Paulo  *    documentation and/or other materials provided with the distribution.
1632176cfdSRui Paulo  *
1732176cfdSRui Paulo  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1832176cfdSRui Paulo  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1932176cfdSRui Paulo  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2032176cfdSRui Paulo  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2132176cfdSRui Paulo  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2232176cfdSRui Paulo  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2332176cfdSRui Paulo  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2432176cfdSRui Paulo  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2532176cfdSRui Paulo  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2632176cfdSRui Paulo  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2732176cfdSRui Paulo  * SUCH DAMAGE.
2832176cfdSRui Paulo  */
29085ff963SMatthew Dillon #include <sys/cdefs.h>
30085ff963SMatthew Dillon #ifdef __FreeBSD__
31085ff963SMatthew Dillon __FBSDID("$FreeBSD$");
32085ff963SMatthew Dillon #endif
3332176cfdSRui Paulo 
3432176cfdSRui Paulo /*
3532176cfdSRui Paulo  * IEEE 802.11s Mesh Point (MBSS) support.
3632176cfdSRui Paulo  *
3732176cfdSRui Paulo  * Based on March 2009, D3.0 802.11s draft spec.
3832176cfdSRui Paulo  */
3932176cfdSRui Paulo #include "opt_inet.h"
4032176cfdSRui Paulo #include "opt_wlan.h"
4132176cfdSRui Paulo 
4232176cfdSRui Paulo #include <sys/param.h>
4332176cfdSRui Paulo #include <sys/systm.h>
4432176cfdSRui Paulo #include <sys/mbuf.h>
4532176cfdSRui Paulo #include <sys/malloc.h>
4632176cfdSRui Paulo #include <sys/kernel.h>
4732176cfdSRui Paulo 
4832176cfdSRui Paulo #include <sys/socket.h>
4932176cfdSRui Paulo #include <sys/sockio.h>
5032176cfdSRui Paulo #include <sys/endian.h>
5132176cfdSRui Paulo #include <sys/errno.h>
5232176cfdSRui Paulo #include <sys/proc.h>
5332176cfdSRui Paulo #include <sys/sysctl.h>
5432176cfdSRui Paulo 
55085ff963SMatthew Dillon #include <net/bpf.h>
5632176cfdSRui Paulo #include <net/if.h>
57085ff963SMatthew Dillon #include <net/if_var.h>
5832176cfdSRui Paulo #include <net/if_media.h>
5932176cfdSRui Paulo #include <net/if_llc.h>
6032176cfdSRui Paulo #include <net/ethernet.h>
6132176cfdSRui Paulo 
6232176cfdSRui Paulo #include <netproto/802_11/ieee80211_var.h>
6332176cfdSRui Paulo #include <netproto/802_11/ieee80211_action.h>
64085ff963SMatthew Dillon #ifdef IEEE80211_SUPPORT_SUPERG
65085ff963SMatthew Dillon #include <netproto/802_11/ieee80211_superg.h>
66085ff963SMatthew Dillon #endif
6732176cfdSRui Paulo #include <netproto/802_11/ieee80211_input.h>
6832176cfdSRui Paulo #include <netproto/802_11/ieee80211_mesh.h>
6932176cfdSRui Paulo 
7032176cfdSRui Paulo static void	mesh_rt_flush_invalid(struct ieee80211vap *);
7132176cfdSRui Paulo static int	mesh_select_proto_path(struct ieee80211vap *, const char *);
7232176cfdSRui Paulo static int	mesh_select_proto_metric(struct ieee80211vap *, const char *);
7332176cfdSRui Paulo static void	mesh_vattach(struct ieee80211vap *);
7432176cfdSRui Paulo static int	mesh_newstate(struct ieee80211vap *, enum ieee80211_state, int);
75085ff963SMatthew Dillon static void	mesh_rt_cleanup_cb(void *);
76085ff963SMatthew Dillon static void	mesh_gatemode_setup(struct ieee80211vap *);
77085ff963SMatthew Dillon static void	mesh_gatemode_cb(void *);
7832176cfdSRui Paulo static void	mesh_linkchange(struct ieee80211_node *,
7932176cfdSRui Paulo 		    enum ieee80211_mesh_mlstate);
8032176cfdSRui Paulo static void	mesh_checkid(void *, struct ieee80211_node *);
8132176cfdSRui Paulo static uint32_t	mesh_generateid(struct ieee80211vap *);
8232176cfdSRui Paulo static int	mesh_checkpseq(struct ieee80211vap *,
8332176cfdSRui Paulo 		    const uint8_t [IEEE80211_ADDR_LEN], uint32_t);
84085ff963SMatthew Dillon static void	mesh_transmit_to_gate(struct ieee80211vap *, struct mbuf *,
85085ff963SMatthew Dillon 		    struct ieee80211_mesh_route *);
8632176cfdSRui Paulo static void	mesh_forward(struct ieee80211vap *, struct mbuf *,
8732176cfdSRui Paulo 		    const struct ieee80211_meshcntl *);
8832176cfdSRui Paulo static int	mesh_input(struct ieee80211_node *, struct mbuf *, int, int);
8932176cfdSRui Paulo static void	mesh_recv_mgmt(struct ieee80211_node *, struct mbuf *, int,
9032176cfdSRui Paulo 		    int, int);
91085ff963SMatthew Dillon static void	mesh_recv_ctl(struct ieee80211_node *, struct mbuf *, int);
9232176cfdSRui Paulo static void	mesh_peer_timeout_setup(struct ieee80211_node *);
9332176cfdSRui Paulo static void	mesh_peer_timeout_backoff(struct ieee80211_node *);
94085ff963SMatthew Dillon static void	mesh_peer_timeout_cb(void *);
9532176cfdSRui Paulo static __inline void
9632176cfdSRui Paulo 		mesh_peer_timeout_stop(struct ieee80211_node *);
9732176cfdSRui Paulo static int	mesh_verify_meshid(struct ieee80211vap *, const uint8_t *);
9832176cfdSRui Paulo static int	mesh_verify_meshconf(struct ieee80211vap *, const uint8_t *);
9932176cfdSRui Paulo static int	mesh_verify_meshpeer(struct ieee80211vap *, uint8_t,
10032176cfdSRui Paulo     		    const uint8_t *);
10132176cfdSRui Paulo uint32_t	mesh_airtime_calc(struct ieee80211_node *);
10232176cfdSRui Paulo 
10332176cfdSRui Paulo /*
10432176cfdSRui Paulo  * Timeout values come from the specification and are in milliseconds.
10532176cfdSRui Paulo  */
106085ff963SMatthew Dillon static SYSCTL_NODE(_net_wlan, OID_AUTO, mesh, CTLFLAG_RD, 0,
10732176cfdSRui Paulo     "IEEE 802.11s parameters");
108085ff963SMatthew Dillon static int	ieee80211_mesh_gateint = -1;
109085ff963SMatthew Dillon SYSCTL_PROC(_net_wlan_mesh, OID_AUTO, gateint, CTLTYPE_INT | CTLFLAG_RW,
110085ff963SMatthew Dillon     &ieee80211_mesh_gateint, 0, ieee80211_sysctl_msecs_ticks, "I",
111085ff963SMatthew Dillon     "mesh gate interval (ms)");
11232176cfdSRui Paulo static int ieee80211_mesh_retrytimeout = -1;
11332176cfdSRui Paulo SYSCTL_PROC(_net_wlan_mesh, OID_AUTO, retrytimeout, CTLTYPE_INT | CTLFLAG_RW,
11432176cfdSRui Paulo     &ieee80211_mesh_retrytimeout, 0, ieee80211_sysctl_msecs_ticks, "I",
11532176cfdSRui Paulo     "Retry timeout (msec)");
11632176cfdSRui Paulo static int ieee80211_mesh_holdingtimeout = -1;
117085ff963SMatthew Dillon 
11832176cfdSRui Paulo SYSCTL_PROC(_net_wlan_mesh, OID_AUTO, holdingtimeout, CTLTYPE_INT | CTLFLAG_RW,
11932176cfdSRui Paulo     &ieee80211_mesh_holdingtimeout, 0, ieee80211_sysctl_msecs_ticks, "I",
12032176cfdSRui Paulo     "Holding state timeout (msec)");
12132176cfdSRui Paulo static int ieee80211_mesh_confirmtimeout = -1;
12232176cfdSRui Paulo SYSCTL_PROC(_net_wlan_mesh, OID_AUTO, confirmtimeout, CTLTYPE_INT | CTLFLAG_RW,
12332176cfdSRui Paulo     &ieee80211_mesh_confirmtimeout, 0, ieee80211_sysctl_msecs_ticks, "I",
12432176cfdSRui Paulo     "Confirm state timeout (msec)");
125085ff963SMatthew Dillon static int ieee80211_mesh_backofftimeout = -1;
126085ff963SMatthew Dillon SYSCTL_PROC(_net_wlan_mesh, OID_AUTO, backofftimeout, CTLTYPE_INT | CTLFLAG_RW,
127085ff963SMatthew Dillon     &ieee80211_mesh_backofftimeout, 0, ieee80211_sysctl_msecs_ticks, "I",
128085ff963SMatthew Dillon     "Backoff timeout (msec). This is to throutles peering forever when "
129085ff963SMatthew Dillon     "not receving answer or is rejected by a neighbor");
13032176cfdSRui Paulo static int ieee80211_mesh_maxretries = 2;
131085ff963SMatthew Dillon SYSCTL_INT(_net_wlan_mesh, OID_AUTO, maxretries, CTLFLAG_RW,
13232176cfdSRui Paulo     &ieee80211_mesh_maxretries, 0,
13332176cfdSRui Paulo     "Maximum retries during peer link establishment");
134085ff963SMatthew Dillon static int ieee80211_mesh_maxholding = 2;
135085ff963SMatthew Dillon SYSCTL_INT(_net_wlan_mesh, OID_AUTO, maxholding, CTLFLAG_RW,
136085ff963SMatthew Dillon     &ieee80211_mesh_maxholding, 0,
137085ff963SMatthew Dillon     "Maximum times we are allowed to transition to HOLDING state before "
138085ff963SMatthew Dillon     "backinoff during peer link establishment");
13932176cfdSRui Paulo 
14032176cfdSRui Paulo static const uint8_t broadcastaddr[IEEE80211_ADDR_LEN] =
14132176cfdSRui Paulo 	{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
14232176cfdSRui Paulo 
14332176cfdSRui Paulo static	ieee80211_recv_action_func mesh_recv_action_meshpeering_open;
14432176cfdSRui Paulo static	ieee80211_recv_action_func mesh_recv_action_meshpeering_confirm;
14532176cfdSRui Paulo static	ieee80211_recv_action_func mesh_recv_action_meshpeering_close;
146085ff963SMatthew Dillon static	ieee80211_recv_action_func mesh_recv_action_meshlmetric;
147085ff963SMatthew Dillon static	ieee80211_recv_action_func mesh_recv_action_meshgate;
14832176cfdSRui Paulo 
14932176cfdSRui Paulo static	ieee80211_send_action_func mesh_send_action_meshpeering_open;
15032176cfdSRui Paulo static	ieee80211_send_action_func mesh_send_action_meshpeering_confirm;
15132176cfdSRui Paulo static	ieee80211_send_action_func mesh_send_action_meshpeering_close;
152085ff963SMatthew Dillon static	ieee80211_send_action_func mesh_send_action_meshlmetric;
153085ff963SMatthew Dillon static	ieee80211_send_action_func mesh_send_action_meshgate;
15432176cfdSRui Paulo 
15532176cfdSRui Paulo static const struct ieee80211_mesh_proto_metric mesh_metric_airtime = {
15632176cfdSRui Paulo 	.mpm_descr	= "AIRTIME",
15732176cfdSRui Paulo 	.mpm_ie		= IEEE80211_MESHCONF_METRIC_AIRTIME,
15832176cfdSRui Paulo 	.mpm_metric	= mesh_airtime_calc,
15932176cfdSRui Paulo };
16032176cfdSRui Paulo 
16132176cfdSRui Paulo static struct ieee80211_mesh_proto_path		mesh_proto_paths[4];
16232176cfdSRui Paulo static struct ieee80211_mesh_proto_metric	mesh_proto_metrics[4];
16332176cfdSRui Paulo 
164085ff963SMatthew Dillon #if defined(__DragonFly__)
165085ff963SMatthew Dillon 
166085ff963SMatthew Dillon #define	RT_ENTRY_LOCK(rt)	lockmgr(&(rt)->rt_lock, LK_EXCLUSIVE)
167085ff963SMatthew Dillon #define	RT_ENTRY_LOCK_ASSERT(rt) KKASSERT(lockstatus(&(rt)->rt_lock, curthread) == LK_EXCLUSIVE)
168085ff963SMatthew Dillon #define	RT_ENTRY_UNLOCK(rt)	lockmgr(&(rt)->rt_lock, LK_RELEASE)
169085ff963SMatthew Dillon 
170085ff963SMatthew Dillon #define	MESH_RT_LOCK(ms)	lockmgr(&(ms)->ms_rt_lock, LK_EXCLUSIVE)
171085ff963SMatthew Dillon #define	MESH_RT_LOCK_ASSERT(ms)	KKASSERT(lockstatus(&(ms)->ms_rt_lock, curthread) == LK_EXCLUSIVE)
172085ff963SMatthew Dillon #define	MESH_RT_UNLOCK(ms)	lockmgr(&(ms)->ms_rt_lock, LK_RELEASE)
173085ff963SMatthew Dillon 
174085ff963SMatthew Dillon #else
175085ff963SMatthew Dillon 
176085ff963SMatthew Dillon #define	RT_ENTRY_LOCK(rt)	mtx_lock(&(rt)->rt_lock)
177085ff963SMatthew Dillon #define	RT_ENTRY_LOCK_ASSERT(rt) mtx_assert(&(rt)->rt_lock, MA_OWNED)
178085ff963SMatthew Dillon #define	RT_ENTRY_UNLOCK(rt)	mtx_unlock(&(rt)->rt_lock)
179085ff963SMatthew Dillon 
180085ff963SMatthew Dillon #define	MESH_RT_LOCK(ms)	mtx_lock(&(ms)->ms_rt_lock)
181085ff963SMatthew Dillon #define	MESH_RT_LOCK_ASSERT(ms)	mtx_assert(&(ms)->ms_rt_lock, MA_OWNED)
182085ff963SMatthew Dillon #define	MESH_RT_UNLOCK(ms)	mtx_unlock(&(ms)->ms_rt_lock)
183085ff963SMatthew Dillon 
184085ff963SMatthew Dillon #endif
185085ff963SMatthew Dillon 
186085ff963SMatthew Dillon MALLOC_DEFINE(M_80211_MESH_PREQ, "80211preq", "802.11 MESH Path Request frame");
187085ff963SMatthew Dillon MALLOC_DEFINE(M_80211_MESH_PREP, "80211prep", "802.11 MESH Path Reply frame");
188085ff963SMatthew Dillon MALLOC_DEFINE(M_80211_MESH_PERR, "80211perr", "802.11 MESH Path Error frame");
189085ff963SMatthew Dillon 
190085ff963SMatthew Dillon /* The longer one of the lifetime should be stored as new lifetime */
191085ff963SMatthew Dillon #define MESH_ROUTE_LIFETIME_MAX(a, b)	(a > b ? a : b)
192085ff963SMatthew Dillon 
193085ff963SMatthew Dillon MALLOC_DEFINE(M_80211_MESH_RT, "80211mesh_rt", "802.11s routing table");
194085ff963SMatthew Dillon MALLOC_DEFINE(M_80211_MESH_GT_RT, "80211mesh_gt", "802.11s known gates table");
19532176cfdSRui Paulo 
19632176cfdSRui Paulo /*
19732176cfdSRui Paulo  * Helper functions to manipulate the Mesh routing table.
19832176cfdSRui Paulo  */
19932176cfdSRui Paulo 
20032176cfdSRui Paulo static struct ieee80211_mesh_route *
20132176cfdSRui Paulo mesh_rt_find_locked(struct ieee80211_mesh_state *ms,
20232176cfdSRui Paulo     const uint8_t dest[IEEE80211_ADDR_LEN])
20332176cfdSRui Paulo {
20432176cfdSRui Paulo 	struct ieee80211_mesh_route *rt;
20532176cfdSRui Paulo 
206085ff963SMatthew Dillon 	MESH_RT_LOCK_ASSERT(ms);
207085ff963SMatthew Dillon 
20832176cfdSRui Paulo 	TAILQ_FOREACH(rt, &ms->ms_routes, rt_next) {
20932176cfdSRui Paulo 		if (IEEE80211_ADDR_EQ(dest, rt->rt_dest))
21032176cfdSRui Paulo 			return rt;
21132176cfdSRui Paulo 	}
21232176cfdSRui Paulo 	return NULL;
21332176cfdSRui Paulo }
21432176cfdSRui Paulo 
21532176cfdSRui Paulo static struct ieee80211_mesh_route *
216085ff963SMatthew Dillon mesh_rt_add_locked(struct ieee80211vap *vap,
21732176cfdSRui Paulo     const uint8_t dest[IEEE80211_ADDR_LEN])
21832176cfdSRui Paulo {
219085ff963SMatthew Dillon 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
22032176cfdSRui Paulo 	struct ieee80211_mesh_route *rt;
22132176cfdSRui Paulo 
22232176cfdSRui Paulo 	KASSERT(!IEEE80211_ADDR_EQ(broadcastaddr, dest),
22332176cfdSRui Paulo 	    ("%s: adding broadcast to the routing table", __func__));
22432176cfdSRui Paulo 
225085ff963SMatthew Dillon 	MESH_RT_LOCK_ASSERT(ms);
226085ff963SMatthew Dillon 
22732176cfdSRui Paulo 	rt = kmalloc(ALIGN(sizeof(struct ieee80211_mesh_route)) +
228fcaa651dSRui Paulo 	    ms->ms_ppath->mpp_privlen, M_80211_MESH_RT, M_INTWAIT | M_ZERO);
22932176cfdSRui Paulo 	if (rt != NULL) {
230085ff963SMatthew Dillon 		rt->rt_vap = vap;
23132176cfdSRui Paulo 		IEEE80211_ADDR_COPY(rt->rt_dest, dest);
23232176cfdSRui Paulo 		rt->rt_priv = (void *)ALIGN(&rt[1]);
233085ff963SMatthew Dillon #if defined(__DragonFly__)
234085ff963SMatthew Dillon 		lockinit(&rt->rt_lock, "MBSS_RT", 0, 0);
235085ff963SMatthew Dillon #else
236085ff963SMatthew Dillon 		mtx_init(&rt->rt_lock, "MBSS_RT", "802.11s route entry", MTX_DEF);
237085ff963SMatthew Dillon #endif
238085ff963SMatthew Dillon 		callout_init_mp(&rt->rt_discovery);
239085ff963SMatthew Dillon 		rt->rt_updtime = ticks;	/* create time */
24032176cfdSRui Paulo 		TAILQ_INSERT_TAIL(&ms->ms_routes, rt, rt_next);
24132176cfdSRui Paulo 	}
24232176cfdSRui Paulo 	return rt;
24332176cfdSRui Paulo }
24432176cfdSRui Paulo 
24532176cfdSRui Paulo struct ieee80211_mesh_route *
24632176cfdSRui Paulo ieee80211_mesh_rt_find(struct ieee80211vap *vap,
24732176cfdSRui Paulo     const uint8_t dest[IEEE80211_ADDR_LEN])
24832176cfdSRui Paulo {
24932176cfdSRui Paulo 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
25032176cfdSRui Paulo 	struct ieee80211_mesh_route *rt;
25132176cfdSRui Paulo 
252085ff963SMatthew Dillon 	MESH_RT_LOCK(ms);
25332176cfdSRui Paulo 	rt = mesh_rt_find_locked(ms, dest);
254085ff963SMatthew Dillon 	MESH_RT_UNLOCK(ms);
25532176cfdSRui Paulo 	return rt;
25632176cfdSRui Paulo }
25732176cfdSRui Paulo 
25832176cfdSRui Paulo struct ieee80211_mesh_route *
25932176cfdSRui Paulo ieee80211_mesh_rt_add(struct ieee80211vap *vap,
26032176cfdSRui Paulo     const uint8_t dest[IEEE80211_ADDR_LEN])
26132176cfdSRui Paulo {
26232176cfdSRui Paulo 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
26332176cfdSRui Paulo 	struct ieee80211_mesh_route *rt;
26432176cfdSRui Paulo 
26532176cfdSRui Paulo 	KASSERT(ieee80211_mesh_rt_find(vap, dest) == NULL,
26632176cfdSRui Paulo 	    ("%s: duplicate entry in the routing table", __func__));
26732176cfdSRui Paulo 	KASSERT(!IEEE80211_ADDR_EQ(vap->iv_myaddr, dest),
26832176cfdSRui Paulo 	    ("%s: adding self to the routing table", __func__));
26932176cfdSRui Paulo 
270085ff963SMatthew Dillon 	MESH_RT_LOCK(ms);
271085ff963SMatthew Dillon 	rt = mesh_rt_add_locked(vap, dest);
272085ff963SMatthew Dillon 	MESH_RT_UNLOCK(ms);
27332176cfdSRui Paulo 	return rt;
27432176cfdSRui Paulo }
27532176cfdSRui Paulo 
27632176cfdSRui Paulo /*
277085ff963SMatthew Dillon  * Update the route lifetime and returns the updated lifetime.
278085ff963SMatthew Dillon  * If new_lifetime is zero and route is timedout it will be invalidated.
279085ff963SMatthew Dillon  * new_lifetime is in msec
280085ff963SMatthew Dillon  */
281085ff963SMatthew Dillon int
282085ff963SMatthew Dillon ieee80211_mesh_rt_update(struct ieee80211_mesh_route *rt, int new_lifetime)
283085ff963SMatthew Dillon {
284085ff963SMatthew Dillon 	int timesince, now;
285085ff963SMatthew Dillon 	uint32_t lifetime = 0;
286085ff963SMatthew Dillon 
287085ff963SMatthew Dillon 	KASSERT(rt != NULL, ("route is NULL"));
288085ff963SMatthew Dillon 
289085ff963SMatthew Dillon 	now = ticks;
290085ff963SMatthew Dillon 	RT_ENTRY_LOCK(rt);
291085ff963SMatthew Dillon 
292085ff963SMatthew Dillon 	/* dont clobber a proxy entry gated by us */
293085ff963SMatthew Dillon 	if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY && rt->rt_nhops == 0) {
294085ff963SMatthew Dillon 		RT_ENTRY_UNLOCK(rt);
295085ff963SMatthew Dillon 		return rt->rt_lifetime;
296085ff963SMatthew Dillon 	}
297085ff963SMatthew Dillon 
298085ff963SMatthew Dillon 	timesince = ticks_to_msecs(now - rt->rt_updtime);
299085ff963SMatthew Dillon 	rt->rt_updtime = now;
300085ff963SMatthew Dillon 	if (timesince >= rt->rt_lifetime) {
301085ff963SMatthew Dillon 		if (new_lifetime != 0) {
302085ff963SMatthew Dillon 			rt->rt_lifetime = new_lifetime;
303085ff963SMatthew Dillon 		}
304085ff963SMatthew Dillon 		else {
305085ff963SMatthew Dillon 			rt->rt_flags &= ~IEEE80211_MESHRT_FLAGS_VALID;
306085ff963SMatthew Dillon 			rt->rt_lifetime = 0;
307085ff963SMatthew Dillon 		}
308085ff963SMatthew Dillon 	} else {
309085ff963SMatthew Dillon 		/* update what is left of lifetime */
310085ff963SMatthew Dillon 		rt->rt_lifetime = rt->rt_lifetime - timesince;
311085ff963SMatthew Dillon 		rt->rt_lifetime  = MESH_ROUTE_LIFETIME_MAX(
312085ff963SMatthew Dillon 			new_lifetime, rt->rt_lifetime);
313085ff963SMatthew Dillon 	}
314085ff963SMatthew Dillon 	lifetime = rt->rt_lifetime;
315085ff963SMatthew Dillon 	RT_ENTRY_UNLOCK(rt);
316085ff963SMatthew Dillon 
317085ff963SMatthew Dillon 	return lifetime;
318085ff963SMatthew Dillon }
319085ff963SMatthew Dillon 
320085ff963SMatthew Dillon /*
32132176cfdSRui Paulo  * Add a proxy route (as needed) for the specified destination.
32232176cfdSRui Paulo  */
32332176cfdSRui Paulo void
32432176cfdSRui Paulo ieee80211_mesh_proxy_check(struct ieee80211vap *vap,
32532176cfdSRui Paulo     const uint8_t dest[IEEE80211_ADDR_LEN])
32632176cfdSRui Paulo {
32732176cfdSRui Paulo 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
32832176cfdSRui Paulo 	struct ieee80211_mesh_route *rt;
32932176cfdSRui Paulo 
330085ff963SMatthew Dillon 	MESH_RT_LOCK(ms);
33132176cfdSRui Paulo 	rt = mesh_rt_find_locked(ms, dest);
33232176cfdSRui Paulo 	if (rt == NULL) {
333085ff963SMatthew Dillon 		rt = mesh_rt_add_locked(vap, dest);
33432176cfdSRui Paulo 		if (rt == NULL) {
33532176cfdSRui Paulo 			IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, dest,
33632176cfdSRui Paulo 			    "%s", "unable to add proxy entry");
33732176cfdSRui Paulo 			vap->iv_stats.is_mesh_rtaddfailed++;
33832176cfdSRui Paulo 		} else {
33932176cfdSRui Paulo 			IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, dest,
34032176cfdSRui Paulo 			    "%s", "add proxy entry");
341085ff963SMatthew Dillon 			IEEE80211_ADDR_COPY(rt->rt_mesh_gate, vap->iv_myaddr);
34232176cfdSRui Paulo 			IEEE80211_ADDR_COPY(rt->rt_nexthop, vap->iv_myaddr);
34332176cfdSRui Paulo 			rt->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID
34432176cfdSRui Paulo 				     |  IEEE80211_MESHRT_FLAGS_PROXY;
34532176cfdSRui Paulo 		}
34632176cfdSRui Paulo 	} else if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0) {
347085ff963SMatthew Dillon 		KASSERT(rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY,
348085ff963SMatthew Dillon 		    ("no proxy flag for poxy entry"));
34932176cfdSRui Paulo 		struct ieee80211com *ic = vap->iv_ic;
35032176cfdSRui Paulo 		/*
35132176cfdSRui Paulo 		 * Fix existing entry created by received frames from
35232176cfdSRui Paulo 		 * stations that have some memory of dest.  We also
35332176cfdSRui Paulo 		 * flush any frames held on the staging queue; delivering
35432176cfdSRui Paulo 		 * them is too much trouble right now.
35532176cfdSRui Paulo 		 */
35632176cfdSRui Paulo 		IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, dest,
35732176cfdSRui Paulo 		    "%s", "fix proxy entry");
35832176cfdSRui Paulo 		IEEE80211_ADDR_COPY(rt->rt_nexthop, vap->iv_myaddr);
35932176cfdSRui Paulo 		rt->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID
36032176cfdSRui Paulo 			     |  IEEE80211_MESHRT_FLAGS_PROXY;
36132176cfdSRui Paulo 		/* XXX belongs in hwmp */
36232176cfdSRui Paulo 		ieee80211_ageq_drain_node(&ic->ic_stageq,
36332176cfdSRui Paulo 		   (void *)(uintptr_t) ieee80211_mac_hash(ic, dest));
36432176cfdSRui Paulo 		/* XXX stat? */
36532176cfdSRui Paulo 	}
366085ff963SMatthew Dillon 	MESH_RT_UNLOCK(ms);
36732176cfdSRui Paulo }
36832176cfdSRui Paulo 
36932176cfdSRui Paulo static __inline void
37032176cfdSRui Paulo mesh_rt_del(struct ieee80211_mesh_state *ms, struct ieee80211_mesh_route *rt)
37132176cfdSRui Paulo {
37232176cfdSRui Paulo 	TAILQ_REMOVE(&ms->ms_routes, rt, rt_next);
373085ff963SMatthew Dillon 	/*
374085ff963SMatthew Dillon 	 * Grab the lock before destroying it, to be sure no one else
375085ff963SMatthew Dillon 	 * is holding the route.
376085ff963SMatthew Dillon 	 */
377085ff963SMatthew Dillon 	RT_ENTRY_LOCK(rt);
378085ff963SMatthew Dillon 	callout_drain(&rt->rt_discovery);
379085ff963SMatthew Dillon #if defined(__DragonFly__)
380085ff963SMatthew Dillon 	lockuninit(&rt->rt_lock);
381085ff963SMatthew Dillon #else
382085ff963SMatthew Dillon 	mtx_destroy(&rt->rt_lock);
383085ff963SMatthew Dillon #endif
38432176cfdSRui Paulo 	kfree(rt, M_80211_MESH_RT);
38532176cfdSRui Paulo }
38632176cfdSRui Paulo 
38732176cfdSRui Paulo void
38832176cfdSRui Paulo ieee80211_mesh_rt_del(struct ieee80211vap *vap,
38932176cfdSRui Paulo     const uint8_t dest[IEEE80211_ADDR_LEN])
39032176cfdSRui Paulo {
39132176cfdSRui Paulo 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
39232176cfdSRui Paulo 	struct ieee80211_mesh_route *rt, *next;
39332176cfdSRui Paulo 
394085ff963SMatthew Dillon 	MESH_RT_LOCK(ms);
395085ff963SMatthew Dillon 	TAILQ_FOREACH_SAFE(rt, &ms->ms_routes, rt_next, next) {
39632176cfdSRui Paulo 		if (IEEE80211_ADDR_EQ(rt->rt_dest, dest)) {
397085ff963SMatthew Dillon 			if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY) {
398085ff963SMatthew Dillon 				ms->ms_ppath->mpp_senderror(vap, dest, rt,
399085ff963SMatthew Dillon 				    IEEE80211_REASON_MESH_PERR_NO_PROXY);
400085ff963SMatthew Dillon 			} else {
401085ff963SMatthew Dillon 				ms->ms_ppath->mpp_senderror(vap, dest, rt,
402085ff963SMatthew Dillon 				    IEEE80211_REASON_MESH_PERR_DEST_UNREACH);
403085ff963SMatthew Dillon 			}
40432176cfdSRui Paulo 			mesh_rt_del(ms, rt);
405085ff963SMatthew Dillon 			MESH_RT_UNLOCK(ms);
40632176cfdSRui Paulo 			return;
40732176cfdSRui Paulo 		}
40832176cfdSRui Paulo 	}
409085ff963SMatthew Dillon 	MESH_RT_UNLOCK(ms);
41032176cfdSRui Paulo }
41132176cfdSRui Paulo 
41232176cfdSRui Paulo void
41332176cfdSRui Paulo ieee80211_mesh_rt_flush(struct ieee80211vap *vap)
41432176cfdSRui Paulo {
41532176cfdSRui Paulo 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
41632176cfdSRui Paulo 	struct ieee80211_mesh_route *rt, *next;
41732176cfdSRui Paulo 
41832176cfdSRui Paulo 	if (ms == NULL)
41932176cfdSRui Paulo 		return;
420085ff963SMatthew Dillon 	MESH_RT_LOCK(ms);
421085ff963SMatthew Dillon 	TAILQ_FOREACH_SAFE(rt, &ms->ms_routes, rt_next, next)
42232176cfdSRui Paulo 		mesh_rt_del(ms, rt);
423085ff963SMatthew Dillon 	MESH_RT_UNLOCK(ms);
42432176cfdSRui Paulo }
42532176cfdSRui Paulo 
42632176cfdSRui Paulo void
42732176cfdSRui Paulo ieee80211_mesh_rt_flush_peer(struct ieee80211vap *vap,
42832176cfdSRui Paulo     const uint8_t peer[IEEE80211_ADDR_LEN])
42932176cfdSRui Paulo {
43032176cfdSRui Paulo 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
43132176cfdSRui Paulo 	struct ieee80211_mesh_route *rt, *next;
43232176cfdSRui Paulo 
433085ff963SMatthew Dillon 	MESH_RT_LOCK(ms);
434085ff963SMatthew Dillon 	TAILQ_FOREACH_SAFE(rt, &ms->ms_routes, rt_next, next) {
43532176cfdSRui Paulo 		if (IEEE80211_ADDR_EQ(rt->rt_nexthop, peer))
43632176cfdSRui Paulo 			mesh_rt_del(ms, rt);
43732176cfdSRui Paulo 	}
438085ff963SMatthew Dillon 	MESH_RT_UNLOCK(ms);
43932176cfdSRui Paulo }
44032176cfdSRui Paulo 
44132176cfdSRui Paulo /*
44232176cfdSRui Paulo  * Flush expired routing entries, i.e. those in invalid state for
44332176cfdSRui Paulo  * some time.
44432176cfdSRui Paulo  */
44532176cfdSRui Paulo static void
44632176cfdSRui Paulo mesh_rt_flush_invalid(struct ieee80211vap *vap)
44732176cfdSRui Paulo {
44832176cfdSRui Paulo 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
44932176cfdSRui Paulo 	struct ieee80211_mesh_route *rt, *next;
45032176cfdSRui Paulo 
45132176cfdSRui Paulo 	if (ms == NULL)
45232176cfdSRui Paulo 		return;
453085ff963SMatthew Dillon 	MESH_RT_LOCK(ms);
454085ff963SMatthew Dillon 	TAILQ_FOREACH_SAFE(rt, &ms->ms_routes, rt_next, next) {
455085ff963SMatthew Dillon 		/* Discover paths will be deleted by their own callout */
456085ff963SMatthew Dillon 		if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_DISCOVER)
457085ff963SMatthew Dillon 			continue;
458085ff963SMatthew Dillon 		ieee80211_mesh_rt_update(rt, 0);
459085ff963SMatthew Dillon 		if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0)
46032176cfdSRui Paulo 			mesh_rt_del(ms, rt);
46132176cfdSRui Paulo 	}
462085ff963SMatthew Dillon 	MESH_RT_UNLOCK(ms);
46332176cfdSRui Paulo }
46432176cfdSRui Paulo 
465085ff963SMatthew Dillon #define	N(a)	(sizeof(a) / sizeof(a[0]))
46632176cfdSRui Paulo int
46732176cfdSRui Paulo ieee80211_mesh_register_proto_path(const struct ieee80211_mesh_proto_path *mpp)
46832176cfdSRui Paulo {
46932176cfdSRui Paulo 	int i, firstempty = -1;
47032176cfdSRui Paulo 
471085ff963SMatthew Dillon 	for (i = 0; i < N(mesh_proto_paths); i++) {
47232176cfdSRui Paulo 		if (strncmp(mpp->mpp_descr, mesh_proto_paths[i].mpp_descr,
47332176cfdSRui Paulo 		    IEEE80211_MESH_PROTO_DSZ) == 0)
47432176cfdSRui Paulo 			return EEXIST;
47532176cfdSRui Paulo 		if (!mesh_proto_paths[i].mpp_active && firstempty == -1)
47632176cfdSRui Paulo 			firstempty = i;
47732176cfdSRui Paulo 	}
47832176cfdSRui Paulo 	if (firstempty < 0)
47932176cfdSRui Paulo 		return ENOSPC;
48032176cfdSRui Paulo 	memcpy(&mesh_proto_paths[firstempty], mpp, sizeof(*mpp));
48132176cfdSRui Paulo 	mesh_proto_paths[firstempty].mpp_active = 1;
48232176cfdSRui Paulo 	return 0;
48332176cfdSRui Paulo }
48432176cfdSRui Paulo 
48532176cfdSRui Paulo int
48632176cfdSRui Paulo ieee80211_mesh_register_proto_metric(const struct
48732176cfdSRui Paulo     ieee80211_mesh_proto_metric *mpm)
48832176cfdSRui Paulo {
48932176cfdSRui Paulo 	int i, firstempty = -1;
49032176cfdSRui Paulo 
491085ff963SMatthew Dillon 	for (i = 0; i < N(mesh_proto_metrics); i++) {
49232176cfdSRui Paulo 		if (strncmp(mpm->mpm_descr, mesh_proto_metrics[i].mpm_descr,
49332176cfdSRui Paulo 		    IEEE80211_MESH_PROTO_DSZ) == 0)
49432176cfdSRui Paulo 			return EEXIST;
49532176cfdSRui Paulo 		if (!mesh_proto_metrics[i].mpm_active && firstempty == -1)
49632176cfdSRui Paulo 			firstempty = i;
49732176cfdSRui Paulo 	}
49832176cfdSRui Paulo 	if (firstempty < 0)
49932176cfdSRui Paulo 		return ENOSPC;
50032176cfdSRui Paulo 	memcpy(&mesh_proto_metrics[firstempty], mpm, sizeof(*mpm));
50132176cfdSRui Paulo 	mesh_proto_metrics[firstempty].mpm_active = 1;
50232176cfdSRui Paulo 	return 0;
50332176cfdSRui Paulo }
50432176cfdSRui Paulo 
50532176cfdSRui Paulo static int
50632176cfdSRui Paulo mesh_select_proto_path(struct ieee80211vap *vap, const char *name)
50732176cfdSRui Paulo {
50832176cfdSRui Paulo 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
50932176cfdSRui Paulo 	int i;
51032176cfdSRui Paulo 
511085ff963SMatthew Dillon 	for (i = 0; i < N(mesh_proto_paths); i++) {
51232176cfdSRui Paulo 		if (strcasecmp(mesh_proto_paths[i].mpp_descr, name) == 0) {
51332176cfdSRui Paulo 			ms->ms_ppath = &mesh_proto_paths[i];
51432176cfdSRui Paulo 			return 0;
51532176cfdSRui Paulo 		}
51632176cfdSRui Paulo 	}
51732176cfdSRui Paulo 	return ENOENT;
51832176cfdSRui Paulo }
51932176cfdSRui Paulo 
52032176cfdSRui Paulo static int
52132176cfdSRui Paulo mesh_select_proto_metric(struct ieee80211vap *vap, const char *name)
52232176cfdSRui Paulo {
52332176cfdSRui Paulo 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
52432176cfdSRui Paulo 	int i;
52532176cfdSRui Paulo 
526085ff963SMatthew Dillon 	for (i = 0; i < N(mesh_proto_metrics); i++) {
52732176cfdSRui Paulo 		if (strcasecmp(mesh_proto_metrics[i].mpm_descr, name) == 0) {
52832176cfdSRui Paulo 			ms->ms_pmetric = &mesh_proto_metrics[i];
52932176cfdSRui Paulo 			return 0;
53032176cfdSRui Paulo 		}
53132176cfdSRui Paulo 	}
53232176cfdSRui Paulo 	return ENOENT;
53332176cfdSRui Paulo }
534085ff963SMatthew Dillon #undef	N
535085ff963SMatthew Dillon 
536085ff963SMatthew Dillon static void
537085ff963SMatthew Dillon mesh_gatemode_setup(struct ieee80211vap *vap)
538085ff963SMatthew Dillon {
539085ff963SMatthew Dillon 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
540085ff963SMatthew Dillon 
541085ff963SMatthew Dillon 	/*
542085ff963SMatthew Dillon 	 * NB: When a mesh gate is running as a ROOT it shall
543085ff963SMatthew Dillon 	 * not send out periodic GANNs but instead mark the
544085ff963SMatthew Dillon 	 * mesh gate flag for the corresponding proactive PREQ
545085ff963SMatthew Dillon 	 * and RANN frames.
546085ff963SMatthew Dillon 	 */
547085ff963SMatthew Dillon 	if (ms->ms_flags & IEEE80211_MESHFLAGS_ROOT ||
548085ff963SMatthew Dillon 	    (ms->ms_flags & IEEE80211_MESHFLAGS_GATE) == 0) {
549085ff963SMatthew Dillon 		callout_drain(&ms->ms_gatetimer);
550085ff963SMatthew Dillon 		return ;
551085ff963SMatthew Dillon 	}
552085ff963SMatthew Dillon 	callout_reset(&ms->ms_gatetimer, ieee80211_mesh_gateint,
553085ff963SMatthew Dillon 	    mesh_gatemode_cb, vap);
554085ff963SMatthew Dillon }
555085ff963SMatthew Dillon 
556085ff963SMatthew Dillon static void
557085ff963SMatthew Dillon mesh_gatemode_cb(void *arg)
558085ff963SMatthew Dillon {
559085ff963SMatthew Dillon 	struct ieee80211vap *vap = (struct ieee80211vap *)arg;
560085ff963SMatthew Dillon 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
561085ff963SMatthew Dillon 	struct ieee80211_meshgann_ie gann;
562085ff963SMatthew Dillon 
563085ff963SMatthew Dillon 	gann.gann_flags = 0; /* Reserved */
564085ff963SMatthew Dillon 	gann.gann_hopcount = 0;
565085ff963SMatthew Dillon 	gann.gann_ttl = ms->ms_ttl;
566085ff963SMatthew Dillon 	IEEE80211_ADDR_COPY(gann.gann_addr, vap->iv_myaddr);
567085ff963SMatthew Dillon 	gann.gann_seq = ms->ms_gateseq++;
568085ff963SMatthew Dillon 	gann.gann_interval = ieee80211_mesh_gateint;
569085ff963SMatthew Dillon 
570085ff963SMatthew Dillon 	IEEE80211_NOTE(vap, IEEE80211_MSG_MESH, vap->iv_bss,
571085ff963SMatthew Dillon 	    "send broadcast GANN (seq %u)", gann.gann_seq);
572085ff963SMatthew Dillon 
573085ff963SMatthew Dillon 	ieee80211_send_action(vap->iv_bss, IEEE80211_ACTION_CAT_MESH,
574085ff963SMatthew Dillon 	    IEEE80211_ACTION_MESH_GANN, &gann);
575085ff963SMatthew Dillon 	mesh_gatemode_setup(vap);
576085ff963SMatthew Dillon }
57732176cfdSRui Paulo 
57832176cfdSRui Paulo static void
57932176cfdSRui Paulo ieee80211_mesh_init(void)
58032176cfdSRui Paulo {
58132176cfdSRui Paulo 
58232176cfdSRui Paulo 	memset(mesh_proto_paths, 0, sizeof(mesh_proto_paths));
58332176cfdSRui Paulo 	memset(mesh_proto_metrics, 0, sizeof(mesh_proto_metrics));
58432176cfdSRui Paulo 
58532176cfdSRui Paulo 	/*
58632176cfdSRui Paulo 	 * Setup mesh parameters that depends on the clock frequency.
58732176cfdSRui Paulo 	 */
588085ff963SMatthew Dillon 	ieee80211_mesh_gateint = msecs_to_ticks(10000);
58932176cfdSRui Paulo 	ieee80211_mesh_retrytimeout = msecs_to_ticks(40);
59032176cfdSRui Paulo 	ieee80211_mesh_holdingtimeout = msecs_to_ticks(40);
59132176cfdSRui Paulo 	ieee80211_mesh_confirmtimeout = msecs_to_ticks(40);
592085ff963SMatthew Dillon 	ieee80211_mesh_backofftimeout = msecs_to_ticks(5000);
59332176cfdSRui Paulo 
59432176cfdSRui Paulo 	/*
59532176cfdSRui Paulo 	 * Register action frame handlers.
59632176cfdSRui Paulo 	 */
597085ff963SMatthew Dillon 	ieee80211_recv_action_register(IEEE80211_ACTION_CAT_SELF_PROT,
59832176cfdSRui Paulo 	    IEEE80211_ACTION_MESHPEERING_OPEN,
59932176cfdSRui Paulo 	    mesh_recv_action_meshpeering_open);
600085ff963SMatthew Dillon 	ieee80211_recv_action_register(IEEE80211_ACTION_CAT_SELF_PROT,
60132176cfdSRui Paulo 	    IEEE80211_ACTION_MESHPEERING_CONFIRM,
60232176cfdSRui Paulo 	    mesh_recv_action_meshpeering_confirm);
603085ff963SMatthew Dillon 	ieee80211_recv_action_register(IEEE80211_ACTION_CAT_SELF_PROT,
60432176cfdSRui Paulo 	    IEEE80211_ACTION_MESHPEERING_CLOSE,
60532176cfdSRui Paulo 	    mesh_recv_action_meshpeering_close);
606085ff963SMatthew Dillon 	ieee80211_recv_action_register(IEEE80211_ACTION_CAT_MESH,
607085ff963SMatthew Dillon 	    IEEE80211_ACTION_MESH_LMETRIC, mesh_recv_action_meshlmetric);
608085ff963SMatthew Dillon 	ieee80211_recv_action_register(IEEE80211_ACTION_CAT_MESH,
609085ff963SMatthew Dillon 	    IEEE80211_ACTION_MESH_GANN, mesh_recv_action_meshgate);
61032176cfdSRui Paulo 
611085ff963SMatthew Dillon 	ieee80211_send_action_register(IEEE80211_ACTION_CAT_SELF_PROT,
61232176cfdSRui Paulo 	    IEEE80211_ACTION_MESHPEERING_OPEN,
61332176cfdSRui Paulo 	    mesh_send_action_meshpeering_open);
614085ff963SMatthew Dillon 	ieee80211_send_action_register(IEEE80211_ACTION_CAT_SELF_PROT,
61532176cfdSRui Paulo 	    IEEE80211_ACTION_MESHPEERING_CONFIRM,
61632176cfdSRui Paulo 	    mesh_send_action_meshpeering_confirm);
617085ff963SMatthew Dillon 	ieee80211_send_action_register(IEEE80211_ACTION_CAT_SELF_PROT,
61832176cfdSRui Paulo 	    IEEE80211_ACTION_MESHPEERING_CLOSE,
61932176cfdSRui Paulo 	    mesh_send_action_meshpeering_close);
620085ff963SMatthew Dillon 	ieee80211_send_action_register(IEEE80211_ACTION_CAT_MESH,
621085ff963SMatthew Dillon 	    IEEE80211_ACTION_MESH_LMETRIC,
622085ff963SMatthew Dillon 	    mesh_send_action_meshlmetric);
623085ff963SMatthew Dillon 	ieee80211_send_action_register(IEEE80211_ACTION_CAT_MESH,
624085ff963SMatthew Dillon 	    IEEE80211_ACTION_MESH_GANN,
625085ff963SMatthew Dillon 	    mesh_send_action_meshgate);
62632176cfdSRui Paulo 
62732176cfdSRui Paulo 	/*
62832176cfdSRui Paulo 	 * Register Airtime Link Metric.
62932176cfdSRui Paulo 	 */
63032176cfdSRui Paulo 	ieee80211_mesh_register_proto_metric(&mesh_metric_airtime);
63132176cfdSRui Paulo 
63232176cfdSRui Paulo }
63332176cfdSRui Paulo SYSINIT(wlan_mesh, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_mesh_init, NULL);
63432176cfdSRui Paulo 
63532176cfdSRui Paulo void
63632176cfdSRui Paulo ieee80211_mesh_attach(struct ieee80211com *ic)
63732176cfdSRui Paulo {
63832176cfdSRui Paulo 	ic->ic_vattach[IEEE80211_M_MBSS] = mesh_vattach;
63932176cfdSRui Paulo }
64032176cfdSRui Paulo 
64132176cfdSRui Paulo void
64232176cfdSRui Paulo ieee80211_mesh_detach(struct ieee80211com *ic)
64332176cfdSRui Paulo {
64432176cfdSRui Paulo }
64532176cfdSRui Paulo 
64632176cfdSRui Paulo static void
64732176cfdSRui Paulo mesh_vdetach_peers(void *arg, struct ieee80211_node *ni)
64832176cfdSRui Paulo {
64932176cfdSRui Paulo 	struct ieee80211com *ic = ni->ni_ic;
65032176cfdSRui Paulo 	uint16_t args[3];
65132176cfdSRui Paulo 
65232176cfdSRui Paulo 	if (ni->ni_mlstate == IEEE80211_NODE_MESH_ESTABLISHED) {
65332176cfdSRui Paulo 		args[0] = ni->ni_mlpid;
65432176cfdSRui Paulo 		args[1] = ni->ni_mllid;
65532176cfdSRui Paulo 		args[2] = IEEE80211_REASON_PEER_LINK_CANCELED;
65632176cfdSRui Paulo 		ieee80211_send_action(ni,
657085ff963SMatthew Dillon 		    IEEE80211_ACTION_CAT_SELF_PROT,
65832176cfdSRui Paulo 		    IEEE80211_ACTION_MESHPEERING_CLOSE,
65932176cfdSRui Paulo 		    args);
66032176cfdSRui Paulo 	}
661085ff963SMatthew Dillon 	callout_drain(&ni->ni_mltimer);
66232176cfdSRui Paulo 	/* XXX belongs in hwmp */
66332176cfdSRui Paulo 	ieee80211_ageq_drain_node(&ic->ic_stageq,
66432176cfdSRui Paulo 	   (void *)(uintptr_t) ieee80211_mac_hash(ic, ni->ni_macaddr));
66532176cfdSRui Paulo }
66632176cfdSRui Paulo 
66732176cfdSRui Paulo static void
66832176cfdSRui Paulo mesh_vdetach(struct ieee80211vap *vap)
66932176cfdSRui Paulo {
67032176cfdSRui Paulo 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
67132176cfdSRui Paulo 
672085ff963SMatthew Dillon 	callout_drain(&ms->ms_cleantimer);
67332176cfdSRui Paulo 	ieee80211_iterate_nodes(&vap->iv_ic->ic_sta, mesh_vdetach_peers,
67432176cfdSRui Paulo 	    NULL);
67532176cfdSRui Paulo 	ieee80211_mesh_rt_flush(vap);
676085ff963SMatthew Dillon #if defined(__DragonFly__)
677085ff963SMatthew Dillon 	lockuninit(&ms->ms_rt_lock);
678085ff963SMatthew Dillon #else
679085ff963SMatthew Dillon 	mtx_destroy(&ms->ms_rt_lock);
680085ff963SMatthew Dillon #endif
68132176cfdSRui Paulo 	ms->ms_ppath->mpp_vdetach(vap);
68232176cfdSRui Paulo 	kfree(vap->iv_mesh, M_80211_VAP);
68332176cfdSRui Paulo 	vap->iv_mesh = NULL;
68432176cfdSRui Paulo }
68532176cfdSRui Paulo 
68632176cfdSRui Paulo static void
68732176cfdSRui Paulo mesh_vattach(struct ieee80211vap *vap)
68832176cfdSRui Paulo {
68932176cfdSRui Paulo 	struct ieee80211_mesh_state *ms;
69032176cfdSRui Paulo 	vap->iv_newstate = mesh_newstate;
69132176cfdSRui Paulo 	vap->iv_input = mesh_input;
69232176cfdSRui Paulo 	vap->iv_opdetach = mesh_vdetach;
69332176cfdSRui Paulo 	vap->iv_recv_mgmt = mesh_recv_mgmt;
694085ff963SMatthew Dillon 	vap->iv_recv_ctl = mesh_recv_ctl;
69532176cfdSRui Paulo 	ms = kmalloc(sizeof(struct ieee80211_mesh_state), M_80211_VAP,
696fcaa651dSRui Paulo 	    M_INTWAIT | M_ZERO);
697085ff963SMatthew Dillon 	if (ms == NULL) {
698085ff963SMatthew Dillon 		kprintf("%s: couldn't alloc MBSS state\n", __func__);
699085ff963SMatthew Dillon 		return;
700085ff963SMatthew Dillon 	}
70132176cfdSRui Paulo 	vap->iv_mesh = ms;
70232176cfdSRui Paulo 	ms->ms_seq = 0;
70332176cfdSRui Paulo 	ms->ms_flags = (IEEE80211_MESHFLAGS_AP | IEEE80211_MESHFLAGS_FWD);
70432176cfdSRui Paulo 	ms->ms_ttl = IEEE80211_MESH_DEFAULT_TTL;
705085ff963SMatthew Dillon 	TAILQ_INIT(&ms->ms_known_gates);
70632176cfdSRui Paulo 	TAILQ_INIT(&ms->ms_routes);
707085ff963SMatthew Dillon #if defined(__DragonFly__)
708085ff963SMatthew Dillon 	lockinit(&ms->ms_rt_lock, "MBSS", 0, 0);
709085ff963SMatthew Dillon #else
710085ff963SMatthew Dillon 	mtx_init(&ms->ms_rt_lock, "MBSS", "802.11s routing table", MTX_DEF);
711085ff963SMatthew Dillon #endif
71234a60cf6SRui Paulo 	callout_init_mp(&ms->ms_cleantimer);
713085ff963SMatthew Dillon 	callout_init_mp(&ms->ms_gatetimer);
714085ff963SMatthew Dillon 	ms->ms_gateseq = 0;
71532176cfdSRui Paulo 	mesh_select_proto_metric(vap, "AIRTIME");
71632176cfdSRui Paulo 	KASSERT(ms->ms_pmetric, ("ms_pmetric == NULL"));
71732176cfdSRui Paulo 	mesh_select_proto_path(vap, "HWMP");
71832176cfdSRui Paulo 	KASSERT(ms->ms_ppath, ("ms_ppath == NULL"));
71932176cfdSRui Paulo 	ms->ms_ppath->mpp_vattach(vap);
72032176cfdSRui Paulo }
72132176cfdSRui Paulo 
72232176cfdSRui Paulo /*
72332176cfdSRui Paulo  * IEEE80211_M_MBSS vap state machine handler.
72432176cfdSRui Paulo  */
72532176cfdSRui Paulo static int
72632176cfdSRui Paulo mesh_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
72732176cfdSRui Paulo {
72832176cfdSRui Paulo 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
72932176cfdSRui Paulo 	struct ieee80211com *ic = vap->iv_ic;
730085ff963SMatthew Dillon 	struct ieee80211_node *ni;
73132176cfdSRui Paulo 	enum ieee80211_state ostate;
732085ff963SMatthew Dillon 
733085ff963SMatthew Dillon 	IEEE80211_LOCK_ASSERT(ic);
73432176cfdSRui Paulo 
73532176cfdSRui Paulo 	ostate = vap->iv_state;
73632176cfdSRui Paulo 	IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n",
73732176cfdSRui Paulo 	    __func__, ieee80211_state_name[ostate],
73832176cfdSRui Paulo 	    ieee80211_state_name[nstate], arg);
73932176cfdSRui Paulo 	vap->iv_state = nstate;		/* state transition */
74032176cfdSRui Paulo 	if (ostate != IEEE80211_S_SCAN)
74132176cfdSRui Paulo 		ieee80211_cancel_scan(vap);	/* background scan */
742085ff963SMatthew Dillon 	ni = vap->iv_bss;			/* NB: no reference held */
743085ff963SMatthew Dillon 	if (nstate != IEEE80211_S_RUN && ostate == IEEE80211_S_RUN) {
744085ff963SMatthew Dillon 		callout_drain(&ms->ms_cleantimer);
745085ff963SMatthew Dillon 		callout_drain(&ms->ms_gatetimer);
746085ff963SMatthew Dillon 	}
74732176cfdSRui Paulo 	switch (nstate) {
74832176cfdSRui Paulo 	case IEEE80211_S_INIT:
74932176cfdSRui Paulo 		switch (ostate) {
75032176cfdSRui Paulo 		case IEEE80211_S_SCAN:
75132176cfdSRui Paulo 			ieee80211_cancel_scan(vap);
75232176cfdSRui Paulo 			break;
75332176cfdSRui Paulo 		case IEEE80211_S_CAC:
75432176cfdSRui Paulo 			ieee80211_dfs_cac_stop(vap);
75532176cfdSRui Paulo 			break;
75632176cfdSRui Paulo 		case IEEE80211_S_RUN:
75732176cfdSRui Paulo 			ieee80211_iterate_nodes(&ic->ic_sta,
75832176cfdSRui Paulo 			    mesh_vdetach_peers, NULL);
75932176cfdSRui Paulo 			break;
76032176cfdSRui Paulo 		default:
76132176cfdSRui Paulo 			break;
76232176cfdSRui Paulo 		}
76332176cfdSRui Paulo 		if (ostate != IEEE80211_S_INIT) {
76432176cfdSRui Paulo 			/* NB: optimize INIT -> INIT case */
76532176cfdSRui Paulo 			ieee80211_reset_bss(vap);
76632176cfdSRui Paulo 			ieee80211_mesh_rt_flush(vap);
76732176cfdSRui Paulo 		}
76832176cfdSRui Paulo 		break;
76932176cfdSRui Paulo 	case IEEE80211_S_SCAN:
77032176cfdSRui Paulo 		switch (ostate) {
77132176cfdSRui Paulo 		case IEEE80211_S_INIT:
77232176cfdSRui Paulo 			if (vap->iv_des_chan != IEEE80211_CHAN_ANYC &&
77332176cfdSRui Paulo 			    !IEEE80211_IS_CHAN_RADAR(vap->iv_des_chan) &&
77432176cfdSRui Paulo 			    ms->ms_idlen != 0) {
77532176cfdSRui Paulo 				/*
77632176cfdSRui Paulo 				 * Already have a channel and a mesh ID; bypass
77732176cfdSRui Paulo 				 * the scan and startup immediately.
77832176cfdSRui Paulo 				 */
77932176cfdSRui Paulo 				ieee80211_create_ibss(vap, vap->iv_des_chan);
78032176cfdSRui Paulo 				break;
78132176cfdSRui Paulo 			}
78232176cfdSRui Paulo 			/*
78332176cfdSRui Paulo 			 * Initiate a scan.  We can come here as a result
78432176cfdSRui Paulo 			 * of an IEEE80211_IOC_SCAN_REQ too in which case
78532176cfdSRui Paulo 			 * the vap will be marked with IEEE80211_FEXT_SCANREQ
78632176cfdSRui Paulo 			 * and the scan request parameters will be present
78732176cfdSRui Paulo 			 * in iv_scanreq.  Otherwise we do the default.
78832176cfdSRui Paulo 			*/
78932176cfdSRui Paulo 			if (vap->iv_flags_ext & IEEE80211_FEXT_SCANREQ) {
79032176cfdSRui Paulo 				ieee80211_check_scan(vap,
79132176cfdSRui Paulo 				    vap->iv_scanreq_flags,
79232176cfdSRui Paulo 				    vap->iv_scanreq_duration,
79332176cfdSRui Paulo 				    vap->iv_scanreq_mindwell,
79432176cfdSRui Paulo 				    vap->iv_scanreq_maxdwell,
79532176cfdSRui Paulo 				    vap->iv_scanreq_nssid, vap->iv_scanreq_ssid);
79632176cfdSRui Paulo 				vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANREQ;
79732176cfdSRui Paulo 			} else
79832176cfdSRui Paulo 				ieee80211_check_scan_current(vap);
79932176cfdSRui Paulo 			break;
80032176cfdSRui Paulo 		default:
80132176cfdSRui Paulo 			break;
80232176cfdSRui Paulo 		}
80332176cfdSRui Paulo 		break;
80432176cfdSRui Paulo 	case IEEE80211_S_CAC:
80532176cfdSRui Paulo 		/*
80632176cfdSRui Paulo 		 * Start CAC on a DFS channel.  We come here when starting
80732176cfdSRui Paulo 		 * a bss on a DFS channel (see ieee80211_create_ibss).
80832176cfdSRui Paulo 		 */
80932176cfdSRui Paulo 		ieee80211_dfs_cac_start(vap);
81032176cfdSRui Paulo 		break;
81132176cfdSRui Paulo 	case IEEE80211_S_RUN:
81232176cfdSRui Paulo 		switch (ostate) {
81332176cfdSRui Paulo 		case IEEE80211_S_INIT:
81432176cfdSRui Paulo 			/*
81532176cfdSRui Paulo 			 * Already have a channel; bypass the
81632176cfdSRui Paulo 			 * scan and startup immediately.
81732176cfdSRui Paulo 			 * Note that ieee80211_create_ibss will call
81832176cfdSRui Paulo 			 * back to do a RUN->RUN state change.
81932176cfdSRui Paulo 			 */
82032176cfdSRui Paulo 			ieee80211_create_ibss(vap,
82132176cfdSRui Paulo 			    ieee80211_ht_adjust_channel(ic,
82232176cfdSRui Paulo 				ic->ic_curchan, vap->iv_flags_ht));
82332176cfdSRui Paulo 			/* NB: iv_bss is changed on return */
82432176cfdSRui Paulo 			break;
82532176cfdSRui Paulo 		case IEEE80211_S_CAC:
82632176cfdSRui Paulo 			/*
82732176cfdSRui Paulo 			 * NB: This is the normal state change when CAC
82832176cfdSRui Paulo 			 * expires and no radar was detected; no need to
82932176cfdSRui Paulo 			 * clear the CAC timer as it's already expired.
83032176cfdSRui Paulo 			 */
83132176cfdSRui Paulo 			/* fall thru... */
83232176cfdSRui Paulo 		case IEEE80211_S_CSA:
83332176cfdSRui Paulo #if 0
83432176cfdSRui Paulo 			/*
83532176cfdSRui Paulo 			 * Shorten inactivity timer of associated stations
83632176cfdSRui Paulo 			 * to weed out sta's that don't follow a CSA.
83732176cfdSRui Paulo 			 */
83832176cfdSRui Paulo 			ieee80211_iterate_nodes(&ic->ic_sta, sta_csa, vap);
83932176cfdSRui Paulo #endif
84032176cfdSRui Paulo 			/*
84132176cfdSRui Paulo 			 * Update bss node channel to reflect where
84232176cfdSRui Paulo 			 * we landed after CSA.
84332176cfdSRui Paulo 			 */
84432176cfdSRui Paulo 			ieee80211_node_set_chan(vap->iv_bss,
84532176cfdSRui Paulo 			    ieee80211_ht_adjust_channel(ic, ic->ic_curchan,
84632176cfdSRui Paulo 				ieee80211_htchanflags(vap->iv_bss->ni_chan)));
84732176cfdSRui Paulo 			/* XXX bypass debug msgs */
84832176cfdSRui Paulo 			break;
84932176cfdSRui Paulo 		case IEEE80211_S_SCAN:
85032176cfdSRui Paulo 		case IEEE80211_S_RUN:
85132176cfdSRui Paulo #ifdef IEEE80211_DEBUG
85232176cfdSRui Paulo 			if (ieee80211_msg_debug(vap)) {
85332176cfdSRui Paulo 				struct ieee80211_node *ni = vap->iv_bss;
85432176cfdSRui Paulo 				ieee80211_note(vap,
8551e290df3SAntonio Huete Jimenez 				    "synchronized with %s meshid ",
856085ff963SMatthew Dillon 				    ether_sprintf(ni->ni_meshid));
85732176cfdSRui Paulo 				ieee80211_print_essid(ni->ni_meshid,
85832176cfdSRui Paulo 				    ni->ni_meshidlen);
85932176cfdSRui Paulo 				/* XXX MCS/HT */
8606168f72eSRui Paulo 				kprintf(" channel %d\n",
86132176cfdSRui Paulo 				    ieee80211_chan2ieee(ic, ic->ic_curchan));
86232176cfdSRui Paulo 			}
86332176cfdSRui Paulo #endif
86432176cfdSRui Paulo 			break;
86532176cfdSRui Paulo 		default:
86632176cfdSRui Paulo 			break;
86732176cfdSRui Paulo 		}
86832176cfdSRui Paulo 		ieee80211_node_authorize(vap->iv_bss);
86932176cfdSRui Paulo 		callout_reset(&ms->ms_cleantimer, ms->ms_ppath->mpp_inact,
870085ff963SMatthew Dillon                     mesh_rt_cleanup_cb, vap);
871085ff963SMatthew Dillon 		mesh_gatemode_setup(vap);
87232176cfdSRui Paulo 		break;
87332176cfdSRui Paulo 	default:
87432176cfdSRui Paulo 		break;
87532176cfdSRui Paulo 	}
87632176cfdSRui Paulo 	/* NB: ostate not nstate */
87732176cfdSRui Paulo 	ms->ms_ppath->mpp_newstate(vap, ostate, arg);
87832176cfdSRui Paulo 	return 0;
87932176cfdSRui Paulo }
88032176cfdSRui Paulo 
88132176cfdSRui Paulo static void
882085ff963SMatthew Dillon mesh_rt_cleanup_cb(void *arg)
88332176cfdSRui Paulo {
88432176cfdSRui Paulo 	struct ieee80211vap *vap = arg;
88532176cfdSRui Paulo 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
88632176cfdSRui Paulo 
88732176cfdSRui Paulo 	mesh_rt_flush_invalid(vap);
88832176cfdSRui Paulo 	callout_reset(&ms->ms_cleantimer, ms->ms_ppath->mpp_inact,
889085ff963SMatthew Dillon 	    mesh_rt_cleanup_cb, vap);
890085ff963SMatthew Dillon }
891085ff963SMatthew Dillon 
892085ff963SMatthew Dillon /*
893085ff963SMatthew Dillon  * Mark a mesh STA as gate and return a pointer to it.
894085ff963SMatthew Dillon  * If this is first time, we create a new gate route.
895085ff963SMatthew Dillon  * Always update the path route to this mesh gate.
896085ff963SMatthew Dillon  */
897085ff963SMatthew Dillon struct ieee80211_mesh_gate_route *
898085ff963SMatthew Dillon ieee80211_mesh_mark_gate(struct ieee80211vap *vap, const uint8_t *addr,
899085ff963SMatthew Dillon     struct ieee80211_mesh_route *rt)
900085ff963SMatthew Dillon {
901085ff963SMatthew Dillon 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
902085ff963SMatthew Dillon 	struct ieee80211_mesh_gate_route *gr = NULL, *next;
903085ff963SMatthew Dillon 	int found = 0;
904085ff963SMatthew Dillon 
905085ff963SMatthew Dillon 	MESH_RT_LOCK(ms);
906085ff963SMatthew Dillon 	TAILQ_FOREACH_SAFE(gr, &ms->ms_known_gates, gr_next, next) {
907085ff963SMatthew Dillon 		if (IEEE80211_ADDR_EQ(gr->gr_addr, addr)) {
908085ff963SMatthew Dillon 			found = 1;
909085ff963SMatthew Dillon 			break;
910085ff963SMatthew Dillon 		}
911085ff963SMatthew Dillon 	}
912085ff963SMatthew Dillon 
913085ff963SMatthew Dillon 	if (!found) {
914085ff963SMatthew Dillon 		/* New mesh gate add it to known table. */
915085ff963SMatthew Dillon 		IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, addr,
916085ff963SMatthew Dillon 		    "%s", "stored new gate information from pro-PREQ.");
917085ff963SMatthew Dillon 		gr = kmalloc(ALIGN(sizeof(struct ieee80211_mesh_gate_route)),
918085ff963SMatthew Dillon 		    M_80211_MESH_GT_RT, M_INTWAIT | M_ZERO);
919085ff963SMatthew Dillon 		IEEE80211_ADDR_COPY(gr->gr_addr, addr);
920085ff963SMatthew Dillon 		TAILQ_INSERT_TAIL(&ms->ms_known_gates, gr, gr_next);
921085ff963SMatthew Dillon 	}
922085ff963SMatthew Dillon 	gr->gr_route = rt;
923085ff963SMatthew Dillon 	/* TODO: link from path route to gate route */
924085ff963SMatthew Dillon 	MESH_RT_UNLOCK(ms);
925085ff963SMatthew Dillon 
926085ff963SMatthew Dillon 	return gr;
92732176cfdSRui Paulo }
92832176cfdSRui Paulo 
92932176cfdSRui Paulo 
93032176cfdSRui Paulo /*
93132176cfdSRui Paulo  * Helper function to note the Mesh Peer Link FSM change.
93232176cfdSRui Paulo  */
93332176cfdSRui Paulo static void
93432176cfdSRui Paulo mesh_linkchange(struct ieee80211_node *ni, enum ieee80211_mesh_mlstate state)
93532176cfdSRui Paulo {
93632176cfdSRui Paulo 	struct ieee80211vap *vap = ni->ni_vap;
93732176cfdSRui Paulo 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
93832176cfdSRui Paulo #ifdef IEEE80211_DEBUG
93932176cfdSRui Paulo 	static const char *meshlinkstates[] = {
94032176cfdSRui Paulo 		[IEEE80211_NODE_MESH_IDLE]		= "IDLE",
94132176cfdSRui Paulo 		[IEEE80211_NODE_MESH_OPENSNT]		= "OPEN SENT",
94232176cfdSRui Paulo 		[IEEE80211_NODE_MESH_OPENRCV]		= "OPEN RECEIVED",
94332176cfdSRui Paulo 		[IEEE80211_NODE_MESH_CONFIRMRCV]	= "CONFIRM RECEIVED",
94432176cfdSRui Paulo 		[IEEE80211_NODE_MESH_ESTABLISHED]	= "ESTABLISHED",
94532176cfdSRui Paulo 		[IEEE80211_NODE_MESH_HOLDING]		= "HOLDING"
94632176cfdSRui Paulo 	};
94732176cfdSRui Paulo #endif
94832176cfdSRui Paulo 	IEEE80211_NOTE(vap, IEEE80211_MSG_MESH,
94932176cfdSRui Paulo 	    ni, "peer link: %s -> %s",
95032176cfdSRui Paulo 	    meshlinkstates[ni->ni_mlstate], meshlinkstates[state]);
95132176cfdSRui Paulo 
95232176cfdSRui Paulo 	/* track neighbor count */
95332176cfdSRui Paulo 	if (state == IEEE80211_NODE_MESH_ESTABLISHED &&
95432176cfdSRui Paulo 	    ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED) {
95532176cfdSRui Paulo 		KASSERT(ms->ms_neighbors < 65535, ("neighbor count overflow"));
95632176cfdSRui Paulo 		ms->ms_neighbors++;
95732176cfdSRui Paulo 		ieee80211_beacon_notify(vap, IEEE80211_BEACON_MESHCONF);
95832176cfdSRui Paulo 	} else if (ni->ni_mlstate == IEEE80211_NODE_MESH_ESTABLISHED &&
95932176cfdSRui Paulo 	    state != IEEE80211_NODE_MESH_ESTABLISHED) {
96032176cfdSRui Paulo 		KASSERT(ms->ms_neighbors > 0, ("neighbor count 0"));
96132176cfdSRui Paulo 		ms->ms_neighbors--;
96232176cfdSRui Paulo 		ieee80211_beacon_notify(vap, IEEE80211_BEACON_MESHCONF);
96332176cfdSRui Paulo 	}
96432176cfdSRui Paulo 	ni->ni_mlstate = state;
96532176cfdSRui Paulo 	switch (state) {
96632176cfdSRui Paulo 	case IEEE80211_NODE_MESH_HOLDING:
96732176cfdSRui Paulo 		ms->ms_ppath->mpp_peerdown(ni);
96832176cfdSRui Paulo 		break;
96932176cfdSRui Paulo 	case IEEE80211_NODE_MESH_ESTABLISHED:
97032176cfdSRui Paulo 		ieee80211_mesh_discover(vap, ni->ni_macaddr, NULL);
97132176cfdSRui Paulo 		break;
97232176cfdSRui Paulo 	default:
97332176cfdSRui Paulo 		break;
97432176cfdSRui Paulo 	}
97532176cfdSRui Paulo }
97632176cfdSRui Paulo 
97732176cfdSRui Paulo /*
97832176cfdSRui Paulo  * Helper function to generate a unique local ID required for mesh
97932176cfdSRui Paulo  * peer establishment.
98032176cfdSRui Paulo  */
98132176cfdSRui Paulo static void
98232176cfdSRui Paulo mesh_checkid(void *arg, struct ieee80211_node *ni)
98332176cfdSRui Paulo {
98432176cfdSRui Paulo 	uint16_t *r = arg;
98532176cfdSRui Paulo 
98632176cfdSRui Paulo 	if (*r == ni->ni_mllid)
98732176cfdSRui Paulo 		*(uint16_t *)arg = 0;
98832176cfdSRui Paulo }
98932176cfdSRui Paulo 
99032176cfdSRui Paulo static uint32_t
99132176cfdSRui Paulo mesh_generateid(struct ieee80211vap *vap)
99232176cfdSRui Paulo {
99332176cfdSRui Paulo 	int maxiter = 4;
99432176cfdSRui Paulo 	uint16_t r;
99532176cfdSRui Paulo 
99632176cfdSRui Paulo 	do {
99732176cfdSRui Paulo 		get_random_bytes(&r, 2);
99832176cfdSRui Paulo 		ieee80211_iterate_nodes(&vap->iv_ic->ic_sta, mesh_checkid, &r);
99932176cfdSRui Paulo 		maxiter--;
100032176cfdSRui Paulo 	} while (r == 0 && maxiter > 0);
100132176cfdSRui Paulo 	return r;
100232176cfdSRui Paulo }
100332176cfdSRui Paulo 
100432176cfdSRui Paulo /*
100532176cfdSRui Paulo  * Verifies if we already received this packet by checking its
100632176cfdSRui Paulo  * sequence number.
100732176cfdSRui Paulo  * Returns 0 if the frame is to be accepted, 1 otherwise.
100832176cfdSRui Paulo  */
100932176cfdSRui Paulo static int
101032176cfdSRui Paulo mesh_checkpseq(struct ieee80211vap *vap,
101132176cfdSRui Paulo     const uint8_t source[IEEE80211_ADDR_LEN], uint32_t seq)
101232176cfdSRui Paulo {
101332176cfdSRui Paulo 	struct ieee80211_mesh_route *rt;
101432176cfdSRui Paulo 
101532176cfdSRui Paulo 	rt = ieee80211_mesh_rt_find(vap, source);
101632176cfdSRui Paulo 	if (rt == NULL) {
101732176cfdSRui Paulo 		rt = ieee80211_mesh_rt_add(vap, source);
101832176cfdSRui Paulo 		if (rt == NULL) {
101932176cfdSRui Paulo 			IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, source,
102032176cfdSRui Paulo 			    "%s", "add mcast route failed");
102132176cfdSRui Paulo 			vap->iv_stats.is_mesh_rtaddfailed++;
102232176cfdSRui Paulo 			return 1;
102332176cfdSRui Paulo 		}
102432176cfdSRui Paulo 		IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, source,
102532176cfdSRui Paulo 		    "add mcast route, mesh seqno %d", seq);
102632176cfdSRui Paulo 		rt->rt_lastmseq = seq;
102732176cfdSRui Paulo 		return 0;
102832176cfdSRui Paulo 	}
102932176cfdSRui Paulo 	if (IEEE80211_MESH_SEQ_GEQ(rt->rt_lastmseq, seq)) {
103032176cfdSRui Paulo 		return 1;
103132176cfdSRui Paulo 	} else {
103232176cfdSRui Paulo 		rt->rt_lastmseq = seq;
103332176cfdSRui Paulo 		return 0;
103432176cfdSRui Paulo 	}
103532176cfdSRui Paulo }
103632176cfdSRui Paulo 
103732176cfdSRui Paulo /*
103832176cfdSRui Paulo  * Iterate the routing table and locate the next hop.
103932176cfdSRui Paulo  */
1040085ff963SMatthew Dillon struct ieee80211_node *
1041085ff963SMatthew Dillon ieee80211_mesh_find_txnode(struct ieee80211vap *vap,
104232176cfdSRui Paulo     const uint8_t dest[IEEE80211_ADDR_LEN])
104332176cfdSRui Paulo {
104432176cfdSRui Paulo 	struct ieee80211_mesh_route *rt;
104532176cfdSRui Paulo 
104632176cfdSRui Paulo 	rt = ieee80211_mesh_rt_find(vap, dest);
104732176cfdSRui Paulo 	if (rt == NULL)
104832176cfdSRui Paulo 		return NULL;
1049085ff963SMatthew Dillon 	if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0) {
105032176cfdSRui Paulo 		IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, dest,
1051085ff963SMatthew Dillon 		    "%s: !valid, flags 0x%x", __func__, rt->rt_flags);
105232176cfdSRui Paulo 		/* XXX stat */
105332176cfdSRui Paulo 		return NULL;
105432176cfdSRui Paulo 	}
1055085ff963SMatthew Dillon 	if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY) {
1056085ff963SMatthew Dillon 		rt = ieee80211_mesh_rt_find(vap, rt->rt_mesh_gate);
1057085ff963SMatthew Dillon 		if (rt == NULL) return NULL;
1058085ff963SMatthew Dillon 		if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0) {
1059085ff963SMatthew Dillon 			IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, dest,
1060085ff963SMatthew Dillon 			    "%s: meshgate !valid, flags 0x%x", __func__,
1061085ff963SMatthew Dillon 			    rt->rt_flags);
1062085ff963SMatthew Dillon 			/* XXX stat */
1063085ff963SMatthew Dillon 			return NULL;
1064085ff963SMatthew Dillon 		}
1065085ff963SMatthew Dillon 	}
106632176cfdSRui Paulo 	return ieee80211_find_txnode(vap, rt->rt_nexthop);
106732176cfdSRui Paulo }
106832176cfdSRui Paulo 
1069085ff963SMatthew Dillon static void
1070085ff963SMatthew Dillon mesh_transmit_to_gate(struct ieee80211vap *vap, struct mbuf *m,
1071085ff963SMatthew Dillon     struct ieee80211_mesh_route *rt_gate)
1072085ff963SMatthew Dillon {
1073085ff963SMatthew Dillon 	struct ifnet *ifp = vap->iv_ifp;
1074085ff963SMatthew Dillon 	struct ieee80211_node *ni;
1075085ff963SMatthew Dillon 
1076085ff963SMatthew Dillon 	IEEE80211_TX_UNLOCK_ASSERT(vap->iv_ic);
1077085ff963SMatthew Dillon 
1078085ff963SMatthew Dillon 	ni = ieee80211_mesh_find_txnode(vap, rt_gate->rt_dest);
1079085ff963SMatthew Dillon 	if (ni == NULL) {
1080085ff963SMatthew Dillon 		IFNET_STAT_INC(ifp, oerrors, 1);
1081085ff963SMatthew Dillon 		m_freem(m);
1082085ff963SMatthew Dillon 		return;
1083085ff963SMatthew Dillon 	}
1084085ff963SMatthew Dillon 
1085085ff963SMatthew Dillon 	/*
1086085ff963SMatthew Dillon 	 * Send through the VAP packet transmit path.
1087085ff963SMatthew Dillon 	 * This consumes the node ref grabbed above and
1088085ff963SMatthew Dillon 	 * the mbuf, regardless of whether there's a problem
1089085ff963SMatthew Dillon 	 * or not.
1090085ff963SMatthew Dillon 	 */
1091085ff963SMatthew Dillon 	(void) ieee80211_vap_pkt_send_dest(vap, m, ni);
1092085ff963SMatthew Dillon }
1093085ff963SMatthew Dillon 
1094085ff963SMatthew Dillon /*
1095085ff963SMatthew Dillon  * Forward the queued frames to known valid mesh gates.
1096085ff963SMatthew Dillon  * Assume destination to be outside the MBSS (i.e. proxy entry),
1097085ff963SMatthew Dillon  * If no valid mesh gates are known silently discard queued frames.
1098085ff963SMatthew Dillon  * After transmitting frames to all known valid mesh gates, this route
1099085ff963SMatthew Dillon  * will be marked invalid, and a new path discovery will happen in the hopes
1100085ff963SMatthew Dillon  * that (at least) one of the mesh gates have a new proxy entry for us to use.
1101085ff963SMatthew Dillon  */
1102085ff963SMatthew Dillon void
1103085ff963SMatthew Dillon ieee80211_mesh_forward_to_gates(struct ieee80211vap *vap,
1104085ff963SMatthew Dillon     struct ieee80211_mesh_route *rt_dest)
1105085ff963SMatthew Dillon {
1106085ff963SMatthew Dillon 	struct ieee80211com *ic = vap->iv_ic;
1107085ff963SMatthew Dillon 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
1108085ff963SMatthew Dillon 	struct ieee80211_mesh_route *rt_gate;
1109085ff963SMatthew Dillon 	struct ieee80211_mesh_gate_route *gr = NULL, *gr_next;
1110085ff963SMatthew Dillon 	struct mbuf *m, *mcopy, *next;
1111085ff963SMatthew Dillon 
1112085ff963SMatthew Dillon 	IEEE80211_TX_UNLOCK_ASSERT(ic);
1113085ff963SMatthew Dillon 
1114085ff963SMatthew Dillon 	KASSERT( rt_dest->rt_flags == IEEE80211_MESHRT_FLAGS_DISCOVER,
1115085ff963SMatthew Dillon 	    ("Route is not marked with IEEE80211_MESHRT_FLAGS_DISCOVER"));
1116085ff963SMatthew Dillon 
1117085ff963SMatthew Dillon 	/* XXX: send to more than one valid mash gate */
1118085ff963SMatthew Dillon 	MESH_RT_LOCK(ms);
1119085ff963SMatthew Dillon 
1120085ff963SMatthew Dillon 	m = ieee80211_ageq_remove(&ic->ic_stageq,
1121085ff963SMatthew Dillon 	    (struct ieee80211_node *)(uintptr_t)
1122085ff963SMatthew Dillon 	    ieee80211_mac_hash(ic, rt_dest->rt_dest));
1123085ff963SMatthew Dillon 
1124085ff963SMatthew Dillon 	TAILQ_FOREACH_SAFE(gr, &ms->ms_known_gates, gr_next, gr_next) {
1125085ff963SMatthew Dillon 		rt_gate = gr->gr_route;
1126085ff963SMatthew Dillon 		if (rt_gate == NULL) {
1127085ff963SMatthew Dillon 			IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP,
1128085ff963SMatthew Dillon 				rt_dest->rt_dest,
1129085ff963SMatthew Dillon 				"mesh gate with no path %6D",
1130085ff963SMatthew Dillon 				gr->gr_addr, ":");
1131085ff963SMatthew Dillon 			continue;
1132085ff963SMatthew Dillon 		}
1133085ff963SMatthew Dillon 		if ((rt_gate->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0)
1134085ff963SMatthew Dillon 			continue;
1135085ff963SMatthew Dillon 		KASSERT(rt_gate->rt_flags & IEEE80211_MESHRT_FLAGS_GATE,
1136085ff963SMatthew Dillon 		    ("route not marked as a mesh gate"));
1137085ff963SMatthew Dillon 		KASSERT((rt_gate->rt_flags &
1138085ff963SMatthew Dillon 			IEEE80211_MESHRT_FLAGS_PROXY) == 0,
1139085ff963SMatthew Dillon 			("found mesh gate that is also marked porxy"));
1140085ff963SMatthew Dillon 		/*
1141085ff963SMatthew Dillon 		 * convert route to a proxy route gated by the current
1142085ff963SMatthew Dillon 		 * mesh gate, this is needed so encap can built data
1143085ff963SMatthew Dillon 		 * frame with correct address.
1144085ff963SMatthew Dillon 		 */
1145085ff963SMatthew Dillon 		rt_dest->rt_flags = IEEE80211_MESHRT_FLAGS_PROXY |
1146085ff963SMatthew Dillon 			IEEE80211_MESHRT_FLAGS_VALID;
1147085ff963SMatthew Dillon 		rt_dest->rt_ext_seq = 1; /* random value */
1148085ff963SMatthew Dillon 		IEEE80211_ADDR_COPY(rt_dest->rt_mesh_gate, rt_gate->rt_dest);
1149085ff963SMatthew Dillon 		IEEE80211_ADDR_COPY(rt_dest->rt_nexthop, rt_gate->rt_nexthop);
1150085ff963SMatthew Dillon 		rt_dest->rt_metric = rt_gate->rt_metric;
1151085ff963SMatthew Dillon 		rt_dest->rt_nhops = rt_gate->rt_nhops;
1152085ff963SMatthew Dillon 		ieee80211_mesh_rt_update(rt_dest, ms->ms_ppath->mpp_inact);
1153085ff963SMatthew Dillon 		MESH_RT_UNLOCK(ms);
1154085ff963SMatthew Dillon 		/* XXX: lock?? */
1155*b5523eacSSascha Wildner 		mcopy = m_dup(m, M_NOWAIT);
1156085ff963SMatthew Dillon 		for (; mcopy != NULL; mcopy = next) {
1157085ff963SMatthew Dillon 			next = mcopy->m_nextpkt;
1158085ff963SMatthew Dillon 			mcopy->m_nextpkt = NULL;
1159085ff963SMatthew Dillon 			IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP,
1160085ff963SMatthew Dillon 			    rt_dest->rt_dest,
1161085ff963SMatthew Dillon 			    "flush queued frame %p len %d", mcopy,
1162085ff963SMatthew Dillon 			    mcopy->m_pkthdr.len);
1163085ff963SMatthew Dillon 			mesh_transmit_to_gate(vap, mcopy, rt_gate);
1164085ff963SMatthew Dillon 		}
1165085ff963SMatthew Dillon 		MESH_RT_LOCK(ms);
1166085ff963SMatthew Dillon 	}
1167085ff963SMatthew Dillon 	rt_dest->rt_flags = 0; /* Mark invalid */
1168085ff963SMatthew Dillon 	m_freem(m);
1169085ff963SMatthew Dillon 	MESH_RT_UNLOCK(ms);
1170085ff963SMatthew Dillon }
1171085ff963SMatthew Dillon 
117232176cfdSRui Paulo /*
117332176cfdSRui Paulo  * Forward the specified frame.
117432176cfdSRui Paulo  * Decrement the TTL and set TA to our MAC address.
117532176cfdSRui Paulo  */
117632176cfdSRui Paulo static void
117732176cfdSRui Paulo mesh_forward(struct ieee80211vap *vap, struct mbuf *m,
117832176cfdSRui Paulo     const struct ieee80211_meshcntl *mc)
117932176cfdSRui Paulo {
118032176cfdSRui Paulo 	struct ieee80211com *ic = vap->iv_ic;
118132176cfdSRui Paulo 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
118232176cfdSRui Paulo 	struct ifnet *ifp = vap->iv_ifp;
118332176cfdSRui Paulo 	const struct ieee80211_frame *wh =
118432176cfdSRui Paulo 	    mtod(m, const struct ieee80211_frame *);
118532176cfdSRui Paulo 	struct mbuf *mcopy;
118632176cfdSRui Paulo 	struct ieee80211_meshcntl *mccopy;
118732176cfdSRui Paulo 	struct ieee80211_frame *whcopy;
118832176cfdSRui Paulo 	struct ieee80211_node *ni;
118932176cfdSRui Paulo 	int err;
119032176cfdSRui Paulo 
1191085ff963SMatthew Dillon 	/* This is called from the RX path - don't hold this lock */
1192085ff963SMatthew Dillon 	IEEE80211_TX_UNLOCK_ASSERT(ic);
1193085ff963SMatthew Dillon 
1194085ff963SMatthew Dillon 	/*
1195085ff963SMatthew Dillon 	 * mesh ttl of 1 means we are the last one receving it,
1196085ff963SMatthew Dillon 	 * according to amendment we decrement and then check if
1197085ff963SMatthew Dillon 	 * 0, if so we dont forward.
1198085ff963SMatthew Dillon 	 */
1199085ff963SMatthew Dillon 	if (mc->mc_ttl < 1) {
120032176cfdSRui Paulo 		IEEE80211_NOTE_FRAME(vap, IEEE80211_MSG_MESH, wh,
1201085ff963SMatthew Dillon 		    "%s", "frame not fwd'd, ttl 1");
120232176cfdSRui Paulo 		vap->iv_stats.is_mesh_fwd_ttl++;
120332176cfdSRui Paulo 		return;
120432176cfdSRui Paulo 	}
120532176cfdSRui Paulo 	if (!(ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) {
120632176cfdSRui Paulo 		IEEE80211_NOTE_FRAME(vap, IEEE80211_MSG_MESH, wh,
120732176cfdSRui Paulo 		    "%s", "frame not fwd'd, fwding disabled");
120832176cfdSRui Paulo 		vap->iv_stats.is_mesh_fwd_disabled++;
120932176cfdSRui Paulo 		return;
121032176cfdSRui Paulo 	}
1211*b5523eacSSascha Wildner 	mcopy = m_dup(m, M_NOWAIT);
121232176cfdSRui Paulo 	if (mcopy == NULL) {
121332176cfdSRui Paulo 		IEEE80211_NOTE_FRAME(vap, IEEE80211_MSG_MESH, wh,
121432176cfdSRui Paulo 		    "%s", "frame not fwd'd, cannot dup");
121532176cfdSRui Paulo 		vap->iv_stats.is_mesh_fwd_nobuf++;
1216d40991efSSepherosa Ziehau 		IFNET_STAT_INC(ifp, oerrors, 1);
121732176cfdSRui Paulo 		return;
121832176cfdSRui Paulo 	}
121932176cfdSRui Paulo 	mcopy = m_pullup(mcopy, ieee80211_hdrspace(ic, wh) +
122032176cfdSRui Paulo 	    sizeof(struct ieee80211_meshcntl));
122132176cfdSRui Paulo 	if (mcopy == NULL) {
122232176cfdSRui Paulo 		IEEE80211_NOTE_FRAME(vap, IEEE80211_MSG_MESH, wh,
122332176cfdSRui Paulo 		    "%s", "frame not fwd'd, too short");
122432176cfdSRui Paulo 		vap->iv_stats.is_mesh_fwd_tooshort++;
1225d40991efSSepherosa Ziehau 		IFNET_STAT_INC(ifp, oerrors, 1);
122632176cfdSRui Paulo 		m_freem(mcopy);
122732176cfdSRui Paulo 		return;
122832176cfdSRui Paulo 	}
122932176cfdSRui Paulo 	whcopy = mtod(mcopy, struct ieee80211_frame *);
123032176cfdSRui Paulo 	mccopy = (struct ieee80211_meshcntl *)
123132176cfdSRui Paulo 	    (mtod(mcopy, uint8_t *) + ieee80211_hdrspace(ic, wh));
123232176cfdSRui Paulo 	/* XXX clear other bits? */
123332176cfdSRui Paulo 	whcopy->i_fc[1] &= ~IEEE80211_FC1_RETRY;
123432176cfdSRui Paulo 	IEEE80211_ADDR_COPY(whcopy->i_addr2, vap->iv_myaddr);
123532176cfdSRui Paulo 	if (IEEE80211_IS_MULTICAST(wh->i_addr1)) {
123632176cfdSRui Paulo 		ni = ieee80211_ref_node(vap->iv_bss);
123732176cfdSRui Paulo 		mcopy->m_flags |= M_MCAST;
123832176cfdSRui Paulo 	} else {
1239085ff963SMatthew Dillon 		ni = ieee80211_mesh_find_txnode(vap, whcopy->i_addr3);
124032176cfdSRui Paulo 		if (ni == NULL) {
1241085ff963SMatthew Dillon 			/*
1242085ff963SMatthew Dillon 			 * [Optional] any of the following three actions:
1243085ff963SMatthew Dillon 			 * o silently discard
1244085ff963SMatthew Dillon 			 * o trigger a path discovery
1245085ff963SMatthew Dillon 			 * o inform TA that meshDA is unknown.
1246085ff963SMatthew Dillon 			 */
124732176cfdSRui Paulo 			IEEE80211_NOTE_FRAME(vap, IEEE80211_MSG_MESH, wh,
124832176cfdSRui Paulo 			    "%s", "frame not fwd'd, no path");
1249085ff963SMatthew Dillon 			ms->ms_ppath->mpp_senderror(vap, whcopy->i_addr3, NULL,
1250085ff963SMatthew Dillon 			    IEEE80211_REASON_MESH_PERR_NO_FI);
125132176cfdSRui Paulo 			vap->iv_stats.is_mesh_fwd_nopath++;
125232176cfdSRui Paulo 			m_freem(mcopy);
125332176cfdSRui Paulo 			return;
125432176cfdSRui Paulo 		}
125532176cfdSRui Paulo 		IEEE80211_ADDR_COPY(whcopy->i_addr1, ni->ni_macaddr);
125632176cfdSRui Paulo 	}
125732176cfdSRui Paulo 	KASSERT(mccopy->mc_ttl > 0, ("%s called with wrong ttl", __func__));
125832176cfdSRui Paulo 	mccopy->mc_ttl--;
125932176cfdSRui Paulo 
126032176cfdSRui Paulo 	/* XXX calculate priority so drivers can find the tx queue */
126132176cfdSRui Paulo 	M_WME_SETAC(mcopy, WME_AC_BE);
126232176cfdSRui Paulo 
126332176cfdSRui Paulo 	/* XXX do we know m_nextpkt is NULL? */
126432176cfdSRui Paulo 	mcopy->m_pkthdr.rcvif = (void *) ni;
1265085ff963SMatthew Dillon 
1266085ff963SMatthew Dillon 	/*
1267085ff963SMatthew Dillon 	 * XXX this bypasses all of the VAP TX handling; it passes frames
1268085ff963SMatthew Dillon 	 * directly to the parent interface.
1269085ff963SMatthew Dillon 	 *
1270085ff963SMatthew Dillon 	 * Because of this, there's no TX lock being held as there's no
1271085ff963SMatthew Dillon 	 * encaps state being used.
1272085ff963SMatthew Dillon 	 *
1273085ff963SMatthew Dillon 	 * Doing a direct parent transmit may not be the correct thing
1274085ff963SMatthew Dillon 	 * to do here; we'll have to re-think this soon.
1275085ff963SMatthew Dillon 	 */
1276085ff963SMatthew Dillon 	IEEE80211_TX_LOCK(ic);
1277085ff963SMatthew Dillon 	err = ieee80211_parent_xmitpkt(ic, mcopy);
1278085ff963SMatthew Dillon 	IEEE80211_TX_UNLOCK(ic);
127932176cfdSRui Paulo 	if (err != 0) {
128032176cfdSRui Paulo 		/* NB: IFQ_HANDOFF reclaims mbuf */
128132176cfdSRui Paulo 		ieee80211_free_node(ni);
128232176cfdSRui Paulo 	} else {
1283d40991efSSepherosa Ziehau 		IFNET_STAT_INC(ifp, opackets, 1);
128432176cfdSRui Paulo 	}
128532176cfdSRui Paulo }
128632176cfdSRui Paulo 
128732176cfdSRui Paulo static struct mbuf *
128832176cfdSRui Paulo mesh_decap(struct ieee80211vap *vap, struct mbuf *m, int hdrlen, int meshdrlen)
128932176cfdSRui Paulo {
129032176cfdSRui Paulo #define	WHDIR(wh)	((wh)->i_fc[1] & IEEE80211_FC1_DIR_MASK)
1291085ff963SMatthew Dillon #define	MC01(mc)	((const struct ieee80211_meshcntl_ae01 *)mc)
129232176cfdSRui Paulo 	uint8_t b[sizeof(struct ieee80211_qosframe_addr4) +
1293085ff963SMatthew Dillon 		  sizeof(struct ieee80211_meshcntl_ae10)];
129432176cfdSRui Paulo 	const struct ieee80211_qosframe_addr4 *wh;
129532176cfdSRui Paulo 	const struct ieee80211_meshcntl_ae10 *mc;
129632176cfdSRui Paulo 	struct ether_header *eh;
129732176cfdSRui Paulo 	struct llc *llc;
129832176cfdSRui Paulo 	int ae;
129932176cfdSRui Paulo 
130032176cfdSRui Paulo 	if (m->m_len < hdrlen + sizeof(*llc) &&
130132176cfdSRui Paulo 	    (m = m_pullup(m, hdrlen + sizeof(*llc))) == NULL) {
130232176cfdSRui Paulo 		IEEE80211_DPRINTF(vap, IEEE80211_MSG_ANY,
130332176cfdSRui Paulo 		    "discard data frame: %s", "m_pullup failed");
130432176cfdSRui Paulo 		vap->iv_stats.is_rx_tooshort++;
130532176cfdSRui Paulo 		return NULL;
130632176cfdSRui Paulo 	}
130732176cfdSRui Paulo 	memcpy(b, mtod(m, caddr_t), hdrlen);
130832176cfdSRui Paulo 	wh = (const struct ieee80211_qosframe_addr4 *)&b[0];
130932176cfdSRui Paulo 	mc = (const struct ieee80211_meshcntl_ae10 *)&b[hdrlen - meshdrlen];
131032176cfdSRui Paulo 	KASSERT(WHDIR(wh) == IEEE80211_FC1_DIR_FROMDS ||
131132176cfdSRui Paulo 		WHDIR(wh) == IEEE80211_FC1_DIR_DSTODS,
131232176cfdSRui Paulo 	    ("bogus dir, fc 0x%x:0x%x", wh->i_fc[0], wh->i_fc[1]));
131332176cfdSRui Paulo 
131432176cfdSRui Paulo 	llc = (struct llc *)(mtod(m, caddr_t) + hdrlen);
131532176cfdSRui Paulo 	if (llc->llc_dsap == LLC_SNAP_LSAP && llc->llc_ssap == LLC_SNAP_LSAP &&
131632176cfdSRui Paulo 	    llc->llc_control == LLC_UI && llc->llc_snap.org_code[0] == 0 &&
131732176cfdSRui Paulo 	    llc->llc_snap.org_code[1] == 0 && llc->llc_snap.org_code[2] == 0 &&
1318085ff963SMatthew Dillon 	    /* NB: preserve AppleTalk frames that have a native SNAP hdr */
1319085ff963SMatthew Dillon 	    !(llc->llc_snap.ether_type == htons(ETHERTYPE_AARP) ||
1320085ff963SMatthew Dillon 	      llc->llc_snap.ether_type == htons(ETHERTYPE_IPX))) {
132132176cfdSRui Paulo 		m_adj(m, hdrlen + sizeof(struct llc) - sizeof(*eh));
132232176cfdSRui Paulo 		llc = NULL;
132332176cfdSRui Paulo 	} else {
132432176cfdSRui Paulo 		m_adj(m, hdrlen - sizeof(*eh));
132532176cfdSRui Paulo 	}
132632176cfdSRui Paulo 	eh = mtod(m, struct ether_header *);
1327085ff963SMatthew Dillon 	ae = mc->mc_flags & IEEE80211_MESH_AE_MASK;
132832176cfdSRui Paulo 	if (WHDIR(wh) == IEEE80211_FC1_DIR_FROMDS) {
132932176cfdSRui Paulo 		IEEE80211_ADDR_COPY(eh->ether_dhost, wh->i_addr1);
1330085ff963SMatthew Dillon 		if (ae == IEEE80211_MESH_AE_00) {
133132176cfdSRui Paulo 			IEEE80211_ADDR_COPY(eh->ether_shost, wh->i_addr3);
1332085ff963SMatthew Dillon 		} else if (ae == IEEE80211_MESH_AE_01) {
1333085ff963SMatthew Dillon 			IEEE80211_ADDR_COPY(eh->ether_shost,
1334085ff963SMatthew Dillon 			    MC01(mc)->mc_addr4);
133532176cfdSRui Paulo 		} else {
133632176cfdSRui Paulo 			IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
133732176cfdSRui Paulo 			    (const struct ieee80211_frame *)wh, NULL,
133832176cfdSRui Paulo 			    "bad AE %d", ae);
133932176cfdSRui Paulo 			vap->iv_stats.is_mesh_badae++;
134032176cfdSRui Paulo 			m_freem(m);
134132176cfdSRui Paulo 			return NULL;
134232176cfdSRui Paulo 		}
134332176cfdSRui Paulo 	} else {
1344085ff963SMatthew Dillon 		if (ae == IEEE80211_MESH_AE_00) {
134532176cfdSRui Paulo 			IEEE80211_ADDR_COPY(eh->ether_dhost, wh->i_addr3);
134632176cfdSRui Paulo 			IEEE80211_ADDR_COPY(eh->ether_shost, wh->i_addr4);
1347085ff963SMatthew Dillon 		} else if (ae == IEEE80211_MESH_AE_10) {
1348085ff963SMatthew Dillon 			IEEE80211_ADDR_COPY(eh->ether_dhost, mc->mc_addr5);
1349085ff963SMatthew Dillon 			IEEE80211_ADDR_COPY(eh->ether_shost, mc->mc_addr6);
135032176cfdSRui Paulo 		} else {
135132176cfdSRui Paulo 			IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
135232176cfdSRui Paulo 			    (const struct ieee80211_frame *)wh, NULL,
135332176cfdSRui Paulo 			    "bad AE %d", ae);
135432176cfdSRui Paulo 			vap->iv_stats.is_mesh_badae++;
135532176cfdSRui Paulo 			m_freem(m);
135632176cfdSRui Paulo 			return NULL;
135732176cfdSRui Paulo 		}
135832176cfdSRui Paulo 	}
1359085ff963SMatthew Dillon #ifndef __NO_STRICT_ALIGNMENT
136032176cfdSRui Paulo 	if (!ALIGNED_POINTER(mtod(m, caddr_t) + sizeof(*eh), uint32_t)) {
136132176cfdSRui Paulo 		m = ieee80211_realign(vap, m, sizeof(*eh));
136232176cfdSRui Paulo 		if (m == NULL)
136332176cfdSRui Paulo 			return NULL;
136432176cfdSRui Paulo 	}
1365085ff963SMatthew Dillon #endif /* !__NO_STRICT_ALIGNMENT */
136632176cfdSRui Paulo 	if (llc != NULL) {
136732176cfdSRui Paulo 		eh = mtod(m, struct ether_header *);
136832176cfdSRui Paulo 		eh->ether_type = htons(m->m_pkthdr.len - sizeof(*eh));
136932176cfdSRui Paulo 	}
137032176cfdSRui Paulo 	return m;
137132176cfdSRui Paulo #undef	WDIR
1372085ff963SMatthew Dillon #undef	MC01
137332176cfdSRui Paulo }
137432176cfdSRui Paulo 
137532176cfdSRui Paulo /*
137632176cfdSRui Paulo  * Return non-zero if the unicast mesh data frame should be processed
137732176cfdSRui Paulo  * locally.  Frames that are not proxy'd have our address, otherwise
137832176cfdSRui Paulo  * we need to consult the routing table to look for a proxy entry.
137932176cfdSRui Paulo  */
138032176cfdSRui Paulo static __inline int
138132176cfdSRui Paulo mesh_isucastforme(struct ieee80211vap *vap, const struct ieee80211_frame *wh,
138232176cfdSRui Paulo     const struct ieee80211_meshcntl *mc)
138332176cfdSRui Paulo {
138432176cfdSRui Paulo 	int ae = mc->mc_flags & 3;
138532176cfdSRui Paulo 
138632176cfdSRui Paulo 	KASSERT((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS,
138732176cfdSRui Paulo 	    ("bad dir 0x%x:0x%x", wh->i_fc[0], wh->i_fc[1]));
1388085ff963SMatthew Dillon 	KASSERT(ae == IEEE80211_MESH_AE_00 || ae == IEEE80211_MESH_AE_10,
1389085ff963SMatthew Dillon 	    ("bad AE %d", ae));
1390085ff963SMatthew Dillon 	if (ae == IEEE80211_MESH_AE_10) {	/* ucast w/ proxy */
139132176cfdSRui Paulo 		const struct ieee80211_meshcntl_ae10 *mc10 =
139232176cfdSRui Paulo 		    (const struct ieee80211_meshcntl_ae10 *) mc;
139332176cfdSRui Paulo 		struct ieee80211_mesh_route *rt =
1394085ff963SMatthew Dillon 		    ieee80211_mesh_rt_find(vap, mc10->mc_addr5);
139532176cfdSRui Paulo 		/* check for proxy route to ourself */
139632176cfdSRui Paulo 		return (rt != NULL &&
139732176cfdSRui Paulo 		    (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY));
139832176cfdSRui Paulo 	} else					/* ucast w/o proxy */
139932176cfdSRui Paulo 		return IEEE80211_ADDR_EQ(wh->i_addr3, vap->iv_myaddr);
140032176cfdSRui Paulo }
140132176cfdSRui Paulo 
1402085ff963SMatthew Dillon /*
1403085ff963SMatthew Dillon  * Verifies transmitter, updates lifetime, precursor list and forwards data.
1404085ff963SMatthew Dillon  * > 0 means we have forwarded data and no need to process locally
1405085ff963SMatthew Dillon  * == 0 means we want to process locally (and we may have forwarded data
1406085ff963SMatthew Dillon  * < 0 means there was an error and data should be discarded
1407085ff963SMatthew Dillon  */
1408085ff963SMatthew Dillon static int
1409085ff963SMatthew Dillon mesh_recv_indiv_data_to_fwrd(struct ieee80211vap *vap, struct mbuf *m,
1410085ff963SMatthew Dillon     struct ieee80211_frame *wh, const struct ieee80211_meshcntl *mc)
1411085ff963SMatthew Dillon {
1412085ff963SMatthew Dillon 	struct ieee80211_qosframe_addr4 *qwh;
1413085ff963SMatthew Dillon 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
1414085ff963SMatthew Dillon 	struct ieee80211_mesh_route *rt_meshda, *rt_meshsa;
1415085ff963SMatthew Dillon 
1416085ff963SMatthew Dillon 	/* This is called from the RX path - don't hold this lock */
1417085ff963SMatthew Dillon 	IEEE80211_TX_UNLOCK_ASSERT(vap->iv_ic);
1418085ff963SMatthew Dillon 
1419085ff963SMatthew Dillon 	qwh = (struct ieee80211_qosframe_addr4 *)wh;
1420085ff963SMatthew Dillon 
1421085ff963SMatthew Dillon 	/*
1422085ff963SMatthew Dillon 	 * TODO:
1423085ff963SMatthew Dillon 	 * o verify addr2 is  a legitimate transmitter
1424085ff963SMatthew Dillon 	 * o lifetime of precursor of addr3 (addr2) is max(init, curr)
1425085ff963SMatthew Dillon 	 * o lifetime of precursor of addr4 (nexthop) is max(init, curr)
1426085ff963SMatthew Dillon 	 */
1427085ff963SMatthew Dillon 
1428085ff963SMatthew Dillon 	/* set lifetime of addr3 (meshDA) to initial value */
1429085ff963SMatthew Dillon 	rt_meshda = ieee80211_mesh_rt_find(vap, qwh->i_addr3);
1430085ff963SMatthew Dillon 	if (rt_meshda == NULL) {
1431085ff963SMatthew Dillon 		IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, qwh->i_addr2,
1432085ff963SMatthew Dillon 		    "no route to meshDA(%6D)", qwh->i_addr3, ":");
1433085ff963SMatthew Dillon 		/*
1434085ff963SMatthew Dillon 		 * [Optional] any of the following three actions:
1435085ff963SMatthew Dillon 		 * o silently discard 				[X]
1436085ff963SMatthew Dillon 		 * o trigger a path discovery			[ ]
1437085ff963SMatthew Dillon 		 * o inform TA that meshDA is unknown.		[ ]
1438085ff963SMatthew Dillon 		 */
1439085ff963SMatthew Dillon 		/* XXX: stats */
1440085ff963SMatthew Dillon 		return (-1);
1441085ff963SMatthew Dillon 	}
1442085ff963SMatthew Dillon 
1443085ff963SMatthew Dillon 	ieee80211_mesh_rt_update(rt_meshda, ticks_to_msecs(
1444085ff963SMatthew Dillon 	    ms->ms_ppath->mpp_inact));
1445085ff963SMatthew Dillon 
1446085ff963SMatthew Dillon 	/* set lifetime of addr4 (meshSA) to initial value */
1447085ff963SMatthew Dillon 	rt_meshsa = ieee80211_mesh_rt_find(vap, qwh->i_addr4);
1448085ff963SMatthew Dillon 	KASSERT(rt_meshsa != NULL, ("no route"));
1449085ff963SMatthew Dillon 	ieee80211_mesh_rt_update(rt_meshsa, ticks_to_msecs(
1450085ff963SMatthew Dillon 	    ms->ms_ppath->mpp_inact));
1451085ff963SMatthew Dillon 
1452085ff963SMatthew Dillon 	mesh_forward(vap, m, mc);
1453085ff963SMatthew Dillon 	return (1); /* dont process locally */
1454085ff963SMatthew Dillon }
1455085ff963SMatthew Dillon 
1456085ff963SMatthew Dillon /*
1457085ff963SMatthew Dillon  * Verifies transmitter, updates lifetime, precursor list and process data
1458085ff963SMatthew Dillon  * locally, if data is proxy with AE = 10 it could mean data should go
1459085ff963SMatthew Dillon  * on another mesh path or data should be forwarded to the DS.
1460085ff963SMatthew Dillon  *
1461085ff963SMatthew Dillon  * > 0 means we have forwarded data and no need to process locally
1462085ff963SMatthew Dillon  * == 0 means we want to process locally (and we may have forwarded data
1463085ff963SMatthew Dillon  * < 0 means there was an error and data should be discarded
1464085ff963SMatthew Dillon  */
1465085ff963SMatthew Dillon static int
1466085ff963SMatthew Dillon mesh_recv_indiv_data_to_me(struct ieee80211vap *vap, struct mbuf *m,
1467085ff963SMatthew Dillon     struct ieee80211_frame *wh, const struct ieee80211_meshcntl *mc)
1468085ff963SMatthew Dillon {
1469085ff963SMatthew Dillon 	struct ieee80211_qosframe_addr4 *qwh;
1470085ff963SMatthew Dillon 	const struct ieee80211_meshcntl_ae10 *mc10;
1471085ff963SMatthew Dillon 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
1472085ff963SMatthew Dillon 	struct ieee80211_mesh_route *rt;
1473085ff963SMatthew Dillon 	int ae;
1474085ff963SMatthew Dillon 
1475085ff963SMatthew Dillon 	/* This is called from the RX path - don't hold this lock */
1476085ff963SMatthew Dillon 	IEEE80211_TX_UNLOCK_ASSERT(vap->iv_ic);
1477085ff963SMatthew Dillon 
1478085ff963SMatthew Dillon 	qwh = (struct ieee80211_qosframe_addr4 *)wh;
1479085ff963SMatthew Dillon 	mc10 = (const struct ieee80211_meshcntl_ae10 *)mc;
1480085ff963SMatthew Dillon 
1481085ff963SMatthew Dillon 	/*
1482085ff963SMatthew Dillon 	 * TODO:
1483085ff963SMatthew Dillon 	 * o verify addr2 is  a legitimate transmitter
1484085ff963SMatthew Dillon 	 * o lifetime of precursor entry is max(init, curr)
1485085ff963SMatthew Dillon 	 */
1486085ff963SMatthew Dillon 
1487085ff963SMatthew Dillon 	/* set lifetime of addr4 (meshSA) to initial value */
1488085ff963SMatthew Dillon 	rt = ieee80211_mesh_rt_find(vap, qwh->i_addr4);
1489085ff963SMatthew Dillon 	KASSERT(rt != NULL, ("no route"));
1490085ff963SMatthew Dillon 	ieee80211_mesh_rt_update(rt, ticks_to_msecs(ms->ms_ppath->mpp_inact));
1491085ff963SMatthew Dillon 	rt = NULL;
1492085ff963SMatthew Dillon 
1493085ff963SMatthew Dillon 	ae = mc10->mc_flags & IEEE80211_MESH_AE_MASK;
1494085ff963SMatthew Dillon 	KASSERT(ae == IEEE80211_MESH_AE_00 ||
1495085ff963SMatthew Dillon 	    ae == IEEE80211_MESH_AE_10, ("bad AE %d", ae));
1496085ff963SMatthew Dillon 	if (ae == IEEE80211_MESH_AE_10) {
1497085ff963SMatthew Dillon 		if (IEEE80211_ADDR_EQ(mc10->mc_addr5, qwh->i_addr3)) {
1498085ff963SMatthew Dillon 			return (0); /* process locally */
1499085ff963SMatthew Dillon 		}
1500085ff963SMatthew Dillon 
1501085ff963SMatthew Dillon 		rt =  ieee80211_mesh_rt_find(vap, mc10->mc_addr5);
1502085ff963SMatthew Dillon 		if (rt != NULL &&
1503085ff963SMatthew Dillon 		    (rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) &&
1504085ff963SMatthew Dillon 		    (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY) == 0) {
1505085ff963SMatthew Dillon 			/*
1506085ff963SMatthew Dillon 			 * Forward on another mesh-path, according to
1507085ff963SMatthew Dillon 			 * amendment as specified in 9.32.4.1
1508085ff963SMatthew Dillon 			 */
1509085ff963SMatthew Dillon 			IEEE80211_ADDR_COPY(qwh->i_addr3, mc10->mc_addr5);
1510085ff963SMatthew Dillon 			mesh_forward(vap, m,
1511085ff963SMatthew Dillon 			    (const struct ieee80211_meshcntl *)mc10);
1512085ff963SMatthew Dillon 			return (1); /* dont process locally */
1513085ff963SMatthew Dillon 		}
1514085ff963SMatthew Dillon 		/*
1515085ff963SMatthew Dillon 		 * All other cases: forward of MSDUs from the MBSS to DS indiv.
1516085ff963SMatthew Dillon 		 * addressed according to 13.11.3.2.
1517085ff963SMatthew Dillon 		 */
1518085ff963SMatthew Dillon 		IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_OUTPUT, qwh->i_addr2,
1519085ff963SMatthew Dillon 		    "forward frame to DS, SA(%6D) DA(%6D)",
1520085ff963SMatthew Dillon 		    mc10->mc_addr6, ":", mc10->mc_addr5, ":");
1521085ff963SMatthew Dillon 	}
1522085ff963SMatthew Dillon 	return (0); /* process locally */
1523085ff963SMatthew Dillon }
1524085ff963SMatthew Dillon 
1525085ff963SMatthew Dillon /*
1526085ff963SMatthew Dillon  * Try to forward the group addressed data on to other mesh STAs, and
1527085ff963SMatthew Dillon  * also to the DS.
1528085ff963SMatthew Dillon  *
1529085ff963SMatthew Dillon  * > 0 means we have forwarded data and no need to process locally
1530085ff963SMatthew Dillon  * == 0 means we want to process locally (and we may have forwarded data
1531085ff963SMatthew Dillon  * < 0 means there was an error and data should be discarded
1532085ff963SMatthew Dillon  */
1533085ff963SMatthew Dillon static int
1534085ff963SMatthew Dillon mesh_recv_group_data(struct ieee80211vap *vap, struct mbuf *m,
1535085ff963SMatthew Dillon     struct ieee80211_frame *wh, const struct ieee80211_meshcntl *mc)
1536085ff963SMatthew Dillon {
1537085ff963SMatthew Dillon #define	MC01(mc)	((const struct ieee80211_meshcntl_ae01 *)mc)
1538085ff963SMatthew Dillon 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
1539085ff963SMatthew Dillon 
1540085ff963SMatthew Dillon 	/* This is called from the RX path - don't hold this lock */
1541085ff963SMatthew Dillon 	IEEE80211_TX_UNLOCK_ASSERT(vap->iv_ic);
1542085ff963SMatthew Dillon 
1543085ff963SMatthew Dillon 	mesh_forward(vap, m, mc);
1544085ff963SMatthew Dillon 
1545085ff963SMatthew Dillon 	if(mc->mc_ttl > 0) {
1546085ff963SMatthew Dillon 		if (mc->mc_flags & IEEE80211_MESH_AE_01) {
1547085ff963SMatthew Dillon 			/*
1548085ff963SMatthew Dillon 			 * Forward of MSDUs from the MBSS to DS group addressed
1549085ff963SMatthew Dillon 			 * (according to 13.11.3.2)
1550085ff963SMatthew Dillon 			 * This happens by delivering the packet, and a bridge
1551085ff963SMatthew Dillon 			 * will sent it on another port member.
1552085ff963SMatthew Dillon 			 */
1553085ff963SMatthew Dillon 			if (ms->ms_flags & IEEE80211_MESHFLAGS_GATE &&
1554085ff963SMatthew Dillon 			    ms->ms_flags & IEEE80211_MESHFLAGS_FWD)
1555085ff963SMatthew Dillon 				IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH,
1556085ff963SMatthew Dillon 				    MC01(mc)->mc_addr4, "%s",
1557085ff963SMatthew Dillon 				    "forward from MBSS to the DS");
1558085ff963SMatthew Dillon 		}
1559085ff963SMatthew Dillon 	}
1560085ff963SMatthew Dillon 	return (0); /* process locally */
1561085ff963SMatthew Dillon #undef	MC01
1562085ff963SMatthew Dillon }
1563085ff963SMatthew Dillon 
156432176cfdSRui Paulo static int
156532176cfdSRui Paulo mesh_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
156632176cfdSRui Paulo {
156732176cfdSRui Paulo #define	HAS_SEQ(type)	((type & 0x4) == 0)
1568085ff963SMatthew Dillon #define	MC01(mc)	((const struct ieee80211_meshcntl_ae01 *)mc)
1569085ff963SMatthew Dillon #define	MC10(mc)	((const struct ieee80211_meshcntl_ae10 *)mc)
157032176cfdSRui Paulo 	struct ieee80211vap *vap = ni->ni_vap;
157132176cfdSRui Paulo 	struct ieee80211com *ic = ni->ni_ic;
157232176cfdSRui Paulo 	struct ifnet *ifp = vap->iv_ifp;
157332176cfdSRui Paulo 	struct ieee80211_frame *wh;
157432176cfdSRui Paulo 	const struct ieee80211_meshcntl *mc;
1575085ff963SMatthew Dillon 	int hdrspace, meshdrlen, need_tap, error;
1576085ff963SMatthew Dillon 	uint8_t dir, type, subtype, ae;
157732176cfdSRui Paulo 	uint32_t seq;
1578085ff963SMatthew Dillon 	const uint8_t *addr;
1579085ff963SMatthew Dillon 	uint8_t qos[2];
158032176cfdSRui Paulo 	ieee80211_seq rxseq;
158132176cfdSRui Paulo 
158232176cfdSRui Paulo 	KASSERT(ni != NULL, ("null node"));
158332176cfdSRui Paulo 	ni->ni_inact = ni->ni_inact_reload;
158432176cfdSRui Paulo 
158532176cfdSRui Paulo 	need_tap = 1;			/* mbuf need to be tapped. */
158632176cfdSRui Paulo 	type = -1;			/* undefined */
158732176cfdSRui Paulo 
1588085ff963SMatthew Dillon 	/* This is called from the RX path - don't hold this lock */
1589085ff963SMatthew Dillon 	IEEE80211_TX_UNLOCK_ASSERT(ic);
1590085ff963SMatthew Dillon 
159132176cfdSRui Paulo 	if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) {
159232176cfdSRui Paulo 		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
159332176cfdSRui Paulo 		    ni->ni_macaddr, NULL,
159432176cfdSRui Paulo 		    "too short (1): len %u", m->m_pkthdr.len);
159532176cfdSRui Paulo 		vap->iv_stats.is_rx_tooshort++;
159632176cfdSRui Paulo 		goto out;
159732176cfdSRui Paulo 	}
159832176cfdSRui Paulo 	/*
159932176cfdSRui Paulo 	 * Bit of a cheat here, we use a pointer for a 3-address
160032176cfdSRui Paulo 	 * frame format but don't reference fields past outside
160132176cfdSRui Paulo 	 * ieee80211_frame_min w/o first validating the data is
160232176cfdSRui Paulo 	 * present.
160332176cfdSRui Paulo 	*/
160432176cfdSRui Paulo 	wh = mtod(m, struct ieee80211_frame *);
160532176cfdSRui Paulo 
160632176cfdSRui Paulo 	if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
160732176cfdSRui Paulo 	    IEEE80211_FC0_VERSION_0) {
160832176cfdSRui Paulo 		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
160932176cfdSRui Paulo 		    ni->ni_macaddr, NULL, "wrong version %x", wh->i_fc[0]);
161032176cfdSRui Paulo 		vap->iv_stats.is_rx_badversion++;
161132176cfdSRui Paulo 		goto err;
161232176cfdSRui Paulo 	}
161332176cfdSRui Paulo 	dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
161432176cfdSRui Paulo 	type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
161532176cfdSRui Paulo 	subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
161632176cfdSRui Paulo 	if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
161732176cfdSRui Paulo 		IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi);
161832176cfdSRui Paulo 		ni->ni_noise = nf;
161932176cfdSRui Paulo 		if (HAS_SEQ(type)) {
162032176cfdSRui Paulo 			uint8_t tid = ieee80211_gettid(wh);
162132176cfdSRui Paulo 
162232176cfdSRui Paulo 			if (IEEE80211_QOS_HAS_SEQ(wh) &&
162332176cfdSRui Paulo 			    TID_TO_WME_AC(tid) >= WME_AC_VI)
162432176cfdSRui Paulo 				ic->ic_wme.wme_hipri_traffic++;
162532176cfdSRui Paulo 			rxseq = le16toh(*(uint16_t *)wh->i_seq);
1626085ff963SMatthew Dillon 			if (! ieee80211_check_rxseq(ni, wh)) {
162732176cfdSRui Paulo 				/* duplicate, discard */
162832176cfdSRui Paulo 				IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
162932176cfdSRui Paulo 				    wh->i_addr1, "duplicate",
163032176cfdSRui Paulo 				    "seqno <%u,%u> fragno <%u,%u> tid %u",
163132176cfdSRui Paulo 				    rxseq >> IEEE80211_SEQ_SEQ_SHIFT,
163232176cfdSRui Paulo 				    ni->ni_rxseqs[tid] >>
163332176cfdSRui Paulo 				    IEEE80211_SEQ_SEQ_SHIFT,
163432176cfdSRui Paulo 				    rxseq & IEEE80211_SEQ_FRAG_MASK,
163532176cfdSRui Paulo 				    ni->ni_rxseqs[tid] &
163632176cfdSRui Paulo 				    IEEE80211_SEQ_FRAG_MASK,
163732176cfdSRui Paulo 				    tid);
163832176cfdSRui Paulo 				vap->iv_stats.is_rx_dup++;
163932176cfdSRui Paulo 				IEEE80211_NODE_STAT(ni, rx_dup);
164032176cfdSRui Paulo 				goto out;
164132176cfdSRui Paulo 			}
164232176cfdSRui Paulo 			ni->ni_rxseqs[tid] = rxseq;
164332176cfdSRui Paulo 		}
164432176cfdSRui Paulo 	}
164532176cfdSRui Paulo #ifdef IEEE80211_DEBUG
164632176cfdSRui Paulo 	/*
164732176cfdSRui Paulo 	 * It's easier, but too expensive, to simulate different mesh
164832176cfdSRui Paulo 	 * topologies by consulting the ACL policy very early, so do this
164932176cfdSRui Paulo 	 * only under DEBUG.
165032176cfdSRui Paulo 	 *
165132176cfdSRui Paulo 	 * NB: this check is also done upon peering link initiation.
165232176cfdSRui Paulo 	 */
1653085ff963SMatthew Dillon 	if (vap->iv_acl != NULL && !vap->iv_acl->iac_check(vap, wh)) {
165432176cfdSRui Paulo 		IEEE80211_DISCARD(vap, IEEE80211_MSG_ACL,
165532176cfdSRui Paulo 		    wh, NULL, "%s", "disallowed by ACL");
165632176cfdSRui Paulo 		vap->iv_stats.is_rx_acl++;
165732176cfdSRui Paulo 		goto out;
165832176cfdSRui Paulo 	}
165932176cfdSRui Paulo #endif
166032176cfdSRui Paulo 	switch (type) {
166132176cfdSRui Paulo 	case IEEE80211_FC0_TYPE_DATA:
166232176cfdSRui Paulo 		if (ni == vap->iv_bss)
166332176cfdSRui Paulo 			goto out;
166432176cfdSRui Paulo 		if (ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED) {
166532176cfdSRui Paulo 			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_MESH,
166632176cfdSRui Paulo 			    ni->ni_macaddr, NULL,
166732176cfdSRui Paulo 			    "peer link not yet established (%d)",
166832176cfdSRui Paulo 			    ni->ni_mlstate);
166932176cfdSRui Paulo 			vap->iv_stats.is_mesh_nolink++;
167032176cfdSRui Paulo 			goto out;
167132176cfdSRui Paulo 		}
167232176cfdSRui Paulo 		if (dir != IEEE80211_FC1_DIR_FROMDS &&
167332176cfdSRui Paulo 		    dir != IEEE80211_FC1_DIR_DSTODS) {
167432176cfdSRui Paulo 			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
167532176cfdSRui Paulo 			    wh, "data", "incorrect dir 0x%x", dir);
167632176cfdSRui Paulo 			vap->iv_stats.is_rx_wrongdir++;
167732176cfdSRui Paulo 			goto err;
167832176cfdSRui Paulo 		}
1679085ff963SMatthew Dillon 
1680085ff963SMatthew Dillon 		/* All Mesh data frames are QoS subtype */
1681085ff963SMatthew Dillon 		if (!HAS_SEQ(type)) {
1682085ff963SMatthew Dillon 			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
1683085ff963SMatthew Dillon 			    wh, "data", "incorrect subtype 0x%x", subtype);
1684085ff963SMatthew Dillon 			vap->iv_stats.is_rx_badsubtype++;
1685085ff963SMatthew Dillon 			goto err;
1686085ff963SMatthew Dillon 		}
1687085ff963SMatthew Dillon 
1688085ff963SMatthew Dillon 		/*
1689085ff963SMatthew Dillon 		 * Next up, any fragmentation.
1690085ff963SMatthew Dillon 		 * XXX: we defrag before we even try to forward,
1691085ff963SMatthew Dillon 		 * Mesh Control field is not present in sub-sequent
1692085ff963SMatthew Dillon 		 * fragmented frames. This is in contrast to Draft 4.0.
1693085ff963SMatthew Dillon 		 */
169432176cfdSRui Paulo 		hdrspace = ieee80211_hdrspace(ic, wh);
1695085ff963SMatthew Dillon 		if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
1696085ff963SMatthew Dillon 			m = ieee80211_defrag(ni, m, hdrspace);
1697085ff963SMatthew Dillon 			if (m == NULL) {
1698085ff963SMatthew Dillon 				/* Fragment dropped or frame not complete yet */
1699085ff963SMatthew Dillon 				goto out;
1700085ff963SMatthew Dillon 			}
1701085ff963SMatthew Dillon 		}
1702085ff963SMatthew Dillon 		wh = mtod(m, struct ieee80211_frame *); /* NB: after defrag */
1703085ff963SMatthew Dillon 
1704085ff963SMatthew Dillon 		/*
1705085ff963SMatthew Dillon 		 * Now we have a complete Mesh Data frame.
1706085ff963SMatthew Dillon 		 */
1707085ff963SMatthew Dillon 
1708085ff963SMatthew Dillon 		/*
1709085ff963SMatthew Dillon 		 * Only fromDStoDS data frames use 4 address qos frames
1710085ff963SMatthew Dillon 		 * as specified in amendment. Otherwise addr4 is located
1711085ff963SMatthew Dillon 		 * in the Mesh Control field and a 3 address qos frame
1712085ff963SMatthew Dillon 		 * is used.
1713085ff963SMatthew Dillon 		 */
1714085ff963SMatthew Dillon 		if (IEEE80211_IS_DSTODS(wh))
1715085ff963SMatthew Dillon 			*(uint16_t *)qos = *(uint16_t *)
1716085ff963SMatthew Dillon 			    ((struct ieee80211_qosframe_addr4 *)wh)->i_qos;
1717085ff963SMatthew Dillon 		else
1718085ff963SMatthew Dillon 			*(uint16_t *)qos = *(uint16_t *)
1719085ff963SMatthew Dillon 			    ((struct ieee80211_qosframe *)wh)->i_qos;
1720085ff963SMatthew Dillon 
1721085ff963SMatthew Dillon 		/*
1722085ff963SMatthew Dillon 		 * NB: The mesh STA sets the Mesh Control Present
1723085ff963SMatthew Dillon 		 * subfield to 1 in the Mesh Data frame containing
1724085ff963SMatthew Dillon 		 * an unfragmented MSDU, an A-MSDU, or the first
1725085ff963SMatthew Dillon 		 * fragment of an MSDU.
1726085ff963SMatthew Dillon 		 * After defrag it should always be present.
1727085ff963SMatthew Dillon 		 */
1728085ff963SMatthew Dillon 		if (!(qos[1] & IEEE80211_QOS_MC)) {
1729085ff963SMatthew Dillon 			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_MESH,
1730085ff963SMatthew Dillon 			    ni->ni_macaddr, NULL,
1731085ff963SMatthew Dillon 			    "%s", "Mesh control field not present");
1732085ff963SMatthew Dillon 			vap->iv_stats.is_rx_elem_missing++; /* XXX: kinda */
1733085ff963SMatthew Dillon 			goto err;
1734085ff963SMatthew Dillon 		}
1735085ff963SMatthew Dillon 
1736085ff963SMatthew Dillon 		/* pull up enough to get to the mesh control */
173732176cfdSRui Paulo 		if (m->m_len < hdrspace + sizeof(struct ieee80211_meshcntl) &&
173832176cfdSRui Paulo 		    (m = m_pullup(m, hdrspace +
173932176cfdSRui Paulo 		        sizeof(struct ieee80211_meshcntl))) == NULL) {
174032176cfdSRui Paulo 			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
174132176cfdSRui Paulo 			    ni->ni_macaddr, NULL,
174232176cfdSRui Paulo 			    "data too short: expecting %u", hdrspace);
174332176cfdSRui Paulo 			vap->iv_stats.is_rx_tooshort++;
174432176cfdSRui Paulo 			goto out;		/* XXX */
174532176cfdSRui Paulo 		}
174632176cfdSRui Paulo 		/*
174732176cfdSRui Paulo 		 * Now calculate the full extent of the headers. Note
174832176cfdSRui Paulo 		 * mesh_decap will pull up anything we didn't get
174932176cfdSRui Paulo 		 * above when it strips the 802.11 headers.
175032176cfdSRui Paulo 		 */
175132176cfdSRui Paulo 		mc = (const struct ieee80211_meshcntl *)
175232176cfdSRui Paulo 		    (mtod(m, const uint8_t *) + hdrspace);
1753085ff963SMatthew Dillon 		ae = mc->mc_flags & IEEE80211_MESH_AE_MASK;
175432176cfdSRui Paulo 		meshdrlen = sizeof(struct ieee80211_meshcntl) +
1755085ff963SMatthew Dillon 		    ae * IEEE80211_ADDR_LEN;
175632176cfdSRui Paulo 		hdrspace += meshdrlen;
1757085ff963SMatthew Dillon 
1758085ff963SMatthew Dillon 		/* pull complete hdrspace = ieee80211_hdrspace + meshcontrol */
1759085ff963SMatthew Dillon 		if ((meshdrlen > sizeof(struct ieee80211_meshcntl)) &&
1760085ff963SMatthew Dillon 		    (m->m_len < hdrspace) &&
1761085ff963SMatthew Dillon 		    ((m = m_pullup(m, hdrspace)) == NULL)) {
1762085ff963SMatthew Dillon 			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
1763085ff963SMatthew Dillon 			    ni->ni_macaddr, NULL,
1764085ff963SMatthew Dillon 			    "data too short: expecting %u", hdrspace);
1765085ff963SMatthew Dillon 			vap->iv_stats.is_rx_tooshort++;
1766085ff963SMatthew Dillon 			goto out;		/* XXX */
1767085ff963SMatthew Dillon 		}
1768085ff963SMatthew Dillon 		/* XXX: are we sure there is no reallocating after m_pullup? */
1769085ff963SMatthew Dillon 
177032176cfdSRui Paulo 		seq = LE_READ_4(mc->mc_seq);
177132176cfdSRui Paulo 		if (IEEE80211_IS_MULTICAST(wh->i_addr1))
177232176cfdSRui Paulo 			addr = wh->i_addr3;
1773085ff963SMatthew Dillon 		else if (ae == IEEE80211_MESH_AE_01)
1774085ff963SMatthew Dillon 			addr = MC01(mc)->mc_addr4;
177532176cfdSRui Paulo 		else
177632176cfdSRui Paulo 			addr = ((struct ieee80211_qosframe_addr4 *)wh)->i_addr4;
177732176cfdSRui Paulo 		if (IEEE80211_ADDR_EQ(vap->iv_myaddr, addr)) {
177832176cfdSRui Paulo 			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
177932176cfdSRui Paulo 			    addr, "data", "%s", "not to me");
178032176cfdSRui Paulo 			vap->iv_stats.is_rx_wrongbss++;	/* XXX kinda */
178132176cfdSRui Paulo 			goto out;
178232176cfdSRui Paulo 		}
178332176cfdSRui Paulo 		if (mesh_checkpseq(vap, addr, seq) != 0) {
178432176cfdSRui Paulo 			vap->iv_stats.is_rx_dup++;
178532176cfdSRui Paulo 			goto out;
178632176cfdSRui Paulo 		}
178732176cfdSRui Paulo 
1788085ff963SMatthew Dillon 		/* This code "routes" the frame to the right control path */
178932176cfdSRui Paulo 		if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
1790085ff963SMatthew Dillon 			if (IEEE80211_ADDR_EQ(vap->iv_myaddr, wh->i_addr3))
1791085ff963SMatthew Dillon 				error =
1792085ff963SMatthew Dillon 				    mesh_recv_indiv_data_to_me(vap, m, wh, mc);
1793085ff963SMatthew Dillon 			else if (IEEE80211_IS_MULTICAST(wh->i_addr3))
1794085ff963SMatthew Dillon 				error = mesh_recv_group_data(vap, m, wh, mc);
1795085ff963SMatthew Dillon 			else
1796085ff963SMatthew Dillon 				error = mesh_recv_indiv_data_to_fwrd(vap, m,
1797085ff963SMatthew Dillon 				    wh, mc);
1798085ff963SMatthew Dillon 		} else
1799085ff963SMatthew Dillon 			error = mesh_recv_group_data(vap, m, wh, mc);
1800085ff963SMatthew Dillon 		if (error < 0)
1801085ff963SMatthew Dillon 			goto err;
1802085ff963SMatthew Dillon 		else if (error > 0)
180332176cfdSRui Paulo 			goto out;
180432176cfdSRui Paulo 
180532176cfdSRui Paulo 		if (ieee80211_radiotap_active_vap(vap))
180632176cfdSRui Paulo 			ieee80211_radiotap_rx(vap, m);
180732176cfdSRui Paulo 		need_tap = 0;
180832176cfdSRui Paulo 
180932176cfdSRui Paulo 		/*
181032176cfdSRui Paulo 		 * Finally, strip the 802.11 header.
181132176cfdSRui Paulo 		 */
181232176cfdSRui Paulo 		m = mesh_decap(vap, m, hdrspace, meshdrlen);
181332176cfdSRui Paulo 		if (m == NULL) {
181432176cfdSRui Paulo 			/* XXX mask bit to check for both */
181532176cfdSRui Paulo 			/* don't count Null data frames as errors */
181632176cfdSRui Paulo 			if (subtype == IEEE80211_FC0_SUBTYPE_NODATA ||
181732176cfdSRui Paulo 			    subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL)
181832176cfdSRui Paulo 				goto out;
181932176cfdSRui Paulo 			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
182032176cfdSRui Paulo 			    ni->ni_macaddr, "data", "%s", "decap error");
182132176cfdSRui Paulo 			vap->iv_stats.is_rx_decap++;
182232176cfdSRui Paulo 			IEEE80211_NODE_STAT(ni, rx_decap);
182332176cfdSRui Paulo 			goto err;
182432176cfdSRui Paulo 		}
1825085ff963SMatthew Dillon 		if (qos[0] & IEEE80211_QOS_AMSDU) {
182632176cfdSRui Paulo 			m = ieee80211_decap_amsdu(ni, m);
182732176cfdSRui Paulo 			if (m == NULL)
182832176cfdSRui Paulo 				return IEEE80211_FC0_TYPE_DATA;
182932176cfdSRui Paulo 		}
183032176cfdSRui Paulo 		ieee80211_deliver_data(vap, ni, m);
183132176cfdSRui Paulo 		return type;
183232176cfdSRui Paulo 	case IEEE80211_FC0_TYPE_MGT:
183332176cfdSRui Paulo 		vap->iv_stats.is_rx_mgmt++;
183432176cfdSRui Paulo 		IEEE80211_NODE_STAT(ni, rx_mgmt);
183532176cfdSRui Paulo 		if (dir != IEEE80211_FC1_DIR_NODS) {
183632176cfdSRui Paulo 			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
183732176cfdSRui Paulo 			    wh, "mgt", "incorrect dir 0x%x", dir);
183832176cfdSRui Paulo 			vap->iv_stats.is_rx_wrongdir++;
183932176cfdSRui Paulo 			goto err;
184032176cfdSRui Paulo 		}
184132176cfdSRui Paulo 		if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) {
184232176cfdSRui Paulo 			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
184332176cfdSRui Paulo 			    ni->ni_macaddr, "mgt", "too short: len %u",
184432176cfdSRui Paulo 			    m->m_pkthdr.len);
184532176cfdSRui Paulo 			vap->iv_stats.is_rx_tooshort++;
184632176cfdSRui Paulo 			goto out;
184732176cfdSRui Paulo 		}
184832176cfdSRui Paulo #ifdef IEEE80211_DEBUG
184932176cfdSRui Paulo 		if ((ieee80211_msg_debug(vap) &&
185032176cfdSRui Paulo 		    (vap->iv_ic->ic_flags & IEEE80211_F_SCAN)) ||
185132176cfdSRui Paulo 		    ieee80211_msg_dumppkts(vap)) {
18521e290df3SAntonio Huete Jimenez 			if_printf(ifp, "received %s from %s rssi %d\n",
185332176cfdSRui Paulo 			    ieee80211_mgt_subtype_name[subtype >>
185432176cfdSRui Paulo 			    IEEE80211_FC0_SUBTYPE_SHIFT],
1855085ff963SMatthew Dillon 			    ether_sprintf(wh->i_addr2), rssi);
185632176cfdSRui Paulo 		}
185732176cfdSRui Paulo #endif
1858085ff963SMatthew Dillon 		if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
185932176cfdSRui Paulo 			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
186032176cfdSRui Paulo 			    wh, NULL, "%s", "WEP set but not permitted");
186132176cfdSRui Paulo 			vap->iv_stats.is_rx_mgtdiscard++; /* XXX */
186232176cfdSRui Paulo 			goto out;
186332176cfdSRui Paulo 		}
186432176cfdSRui Paulo 		vap->iv_recv_mgmt(ni, m, subtype, rssi, nf);
186532176cfdSRui Paulo 		goto out;
186632176cfdSRui Paulo 	case IEEE80211_FC0_TYPE_CTL:
186732176cfdSRui Paulo 		vap->iv_stats.is_rx_ctl++;
186832176cfdSRui Paulo 		IEEE80211_NODE_STAT(ni, rx_ctrl);
186932176cfdSRui Paulo 		goto out;
187032176cfdSRui Paulo 	default:
187132176cfdSRui Paulo 		IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
187232176cfdSRui Paulo 		    wh, "bad", "frame type 0x%x", type);
187332176cfdSRui Paulo 		/* should not come here */
187432176cfdSRui Paulo 		break;
187532176cfdSRui Paulo 	}
187632176cfdSRui Paulo err:
1877d40991efSSepherosa Ziehau 	IFNET_STAT_INC(ifp, ierrors, 1);
187832176cfdSRui Paulo out:
187932176cfdSRui Paulo 	if (m != NULL) {
188032176cfdSRui Paulo 		if (need_tap && ieee80211_radiotap_active_vap(vap))
188132176cfdSRui Paulo 			ieee80211_radiotap_rx(vap, m);
188232176cfdSRui Paulo 		m_freem(m);
188332176cfdSRui Paulo 	}
188432176cfdSRui Paulo 	return type;
1885085ff963SMatthew Dillon #undef	HAS_SEQ
1886085ff963SMatthew Dillon #undef	MC01
1887085ff963SMatthew Dillon #undef	MC10
188832176cfdSRui Paulo }
188932176cfdSRui Paulo 
189032176cfdSRui Paulo static void
189132176cfdSRui Paulo mesh_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, int subtype,
189232176cfdSRui Paulo     int rssi, int nf)
189332176cfdSRui Paulo {
189432176cfdSRui Paulo 	struct ieee80211vap *vap = ni->ni_vap;
189532176cfdSRui Paulo 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
189632176cfdSRui Paulo 	struct ieee80211com *ic = ni->ni_ic;
189732176cfdSRui Paulo 	struct ieee80211_frame *wh;
1898085ff963SMatthew Dillon 	struct ieee80211_mesh_route *rt;
189932176cfdSRui Paulo 	uint8_t *frm, *efrm;
190032176cfdSRui Paulo 
190132176cfdSRui Paulo 	wh = mtod(m0, struct ieee80211_frame *);
190232176cfdSRui Paulo 	frm = (uint8_t *)&wh[1];
190332176cfdSRui Paulo 	efrm = mtod(m0, uint8_t *) + m0->m_len;
190432176cfdSRui Paulo 	switch (subtype) {
190532176cfdSRui Paulo 	case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
190632176cfdSRui Paulo 	case IEEE80211_FC0_SUBTYPE_BEACON:
190732176cfdSRui Paulo 	{
190832176cfdSRui Paulo 		struct ieee80211_scanparams scan;
190932176cfdSRui Paulo 		/*
191032176cfdSRui Paulo 		 * We process beacon/probe response
191132176cfdSRui Paulo 		 * frames to discover neighbors.
191232176cfdSRui Paulo 		 */
191332176cfdSRui Paulo 		if (ieee80211_parse_beacon(ni, m0, &scan) != 0)
191432176cfdSRui Paulo 			return;
191532176cfdSRui Paulo 		/*
191632176cfdSRui Paulo 		 * Count frame now that we know it's to be processed.
191732176cfdSRui Paulo 		 */
191832176cfdSRui Paulo 		if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) {
191932176cfdSRui Paulo 			vap->iv_stats.is_rx_beacon++;	/* XXX remove */
192032176cfdSRui Paulo 			IEEE80211_NODE_STAT(ni, rx_beacons);
192132176cfdSRui Paulo 		} else
192232176cfdSRui Paulo 			IEEE80211_NODE_STAT(ni, rx_proberesp);
192332176cfdSRui Paulo 		/*
192432176cfdSRui Paulo 		 * If scanning, just pass information to the scan module.
192532176cfdSRui Paulo 		 */
192632176cfdSRui Paulo 		if (ic->ic_flags & IEEE80211_F_SCAN) {
192732176cfdSRui Paulo 			if (ic->ic_flags_ext & IEEE80211_FEXT_PROBECHAN) {
192832176cfdSRui Paulo 				/*
192932176cfdSRui Paulo 				 * Actively scanning a channel marked passive;
193032176cfdSRui Paulo 				 * send a probe request now that we know there
193132176cfdSRui Paulo 				 * is 802.11 traffic present.
193232176cfdSRui Paulo 				 *
193332176cfdSRui Paulo 				 * XXX check if the beacon we recv'd gives
193432176cfdSRui Paulo 				 * us what we need and suppress the probe req
193532176cfdSRui Paulo 				 */
193632176cfdSRui Paulo 				ieee80211_probe_curchan(vap, 1);
193732176cfdSRui Paulo 				ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN;
193832176cfdSRui Paulo 			}
193932176cfdSRui Paulo 			ieee80211_add_scan(vap, &scan, wh,
194032176cfdSRui Paulo 			    subtype, rssi, nf);
194132176cfdSRui Paulo 			return;
194232176cfdSRui Paulo 		}
194332176cfdSRui Paulo 
194432176cfdSRui Paulo 		/* The rest of this code assumes we are running */
194532176cfdSRui Paulo 		if (vap->iv_state != IEEE80211_S_RUN)
194632176cfdSRui Paulo 			return;
194732176cfdSRui Paulo 		/*
194832176cfdSRui Paulo 		 * Ignore non-mesh STAs.
194932176cfdSRui Paulo 		 */
195032176cfdSRui Paulo 		if ((scan.capinfo &
195132176cfdSRui Paulo 		     (IEEE80211_CAPINFO_ESS|IEEE80211_CAPINFO_IBSS)) ||
195232176cfdSRui Paulo 		    scan.meshid == NULL || scan.meshconf == NULL) {
195332176cfdSRui Paulo 			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
195432176cfdSRui Paulo 			    wh, "beacon", "%s", "not a mesh sta");
195532176cfdSRui Paulo 			vap->iv_stats.is_mesh_wrongmesh++;
195632176cfdSRui Paulo 			return;
195732176cfdSRui Paulo 		}
195832176cfdSRui Paulo 		/*
195932176cfdSRui Paulo 		 * Ignore STAs for other mesh networks.
196032176cfdSRui Paulo 		 */
196132176cfdSRui Paulo 		if (memcmp(scan.meshid+2, ms->ms_id, ms->ms_idlen) != 0 ||
196232176cfdSRui Paulo 		    mesh_verify_meshconf(vap, scan.meshconf)) {
196332176cfdSRui Paulo 			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
196432176cfdSRui Paulo 			    wh, "beacon", "%s", "not for our mesh");
196532176cfdSRui Paulo 			vap->iv_stats.is_mesh_wrongmesh++;
196632176cfdSRui Paulo 			return;
196732176cfdSRui Paulo 		}
196832176cfdSRui Paulo 		/*
196932176cfdSRui Paulo 		 * Peer only based on the current ACL policy.
197032176cfdSRui Paulo 		 */
1971085ff963SMatthew Dillon 		if (vap->iv_acl != NULL && !vap->iv_acl->iac_check(vap, wh)) {
197232176cfdSRui Paulo 			IEEE80211_DISCARD(vap, IEEE80211_MSG_ACL,
197332176cfdSRui Paulo 			    wh, NULL, "%s", "disallowed by ACL");
197432176cfdSRui Paulo 			vap->iv_stats.is_rx_acl++;
197532176cfdSRui Paulo 			return;
197632176cfdSRui Paulo 		}
197732176cfdSRui Paulo 		/*
197832176cfdSRui Paulo 		 * Do neighbor discovery.
197932176cfdSRui Paulo 		 */
198032176cfdSRui Paulo 		if (!IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) {
198132176cfdSRui Paulo 			/*
198232176cfdSRui Paulo 			 * Create a new entry in the neighbor table.
198332176cfdSRui Paulo 			 */
198432176cfdSRui Paulo 			ni = ieee80211_add_neighbor(vap, wh, &scan);
198532176cfdSRui Paulo 		}
198632176cfdSRui Paulo 		/*
198732176cfdSRui Paulo 		 * Automatically peer with discovered nodes if possible.
198832176cfdSRui Paulo 		 */
198932176cfdSRui Paulo 		if (ni != vap->iv_bss &&
1990085ff963SMatthew Dillon 		    (ms->ms_flags & IEEE80211_MESHFLAGS_AP)) {
1991085ff963SMatthew Dillon 			switch (ni->ni_mlstate) {
1992085ff963SMatthew Dillon 			case IEEE80211_NODE_MESH_IDLE:
1993085ff963SMatthew Dillon 			{
199432176cfdSRui Paulo 				uint16_t args[1];
199532176cfdSRui Paulo 
1996085ff963SMatthew Dillon 				/* Wait for backoff callout to reset counter */
1997085ff963SMatthew Dillon 				if (ni->ni_mlhcnt >= ieee80211_mesh_maxholding)
1998085ff963SMatthew Dillon 					return;
1999085ff963SMatthew Dillon 
200032176cfdSRui Paulo 				ni->ni_mlpid = mesh_generateid(vap);
200132176cfdSRui Paulo 				if (ni->ni_mlpid == 0)
200232176cfdSRui Paulo 					return;
200332176cfdSRui Paulo 				mesh_linkchange(ni, IEEE80211_NODE_MESH_OPENSNT);
200432176cfdSRui Paulo 				args[0] = ni->ni_mlpid;
200532176cfdSRui Paulo 				ieee80211_send_action(ni,
2006085ff963SMatthew Dillon 				IEEE80211_ACTION_CAT_SELF_PROT,
200732176cfdSRui Paulo 				IEEE80211_ACTION_MESHPEERING_OPEN, args);
200832176cfdSRui Paulo 				ni->ni_mlrcnt = 0;
200932176cfdSRui Paulo 				mesh_peer_timeout_setup(ni);
2010085ff963SMatthew Dillon 				break;
2011085ff963SMatthew Dillon 			}
2012085ff963SMatthew Dillon 			case IEEE80211_NODE_MESH_ESTABLISHED:
2013085ff963SMatthew Dillon 			{
2014085ff963SMatthew Dillon 				/*
2015085ff963SMatthew Dillon 				 * Valid beacon from a peer mesh STA
2016085ff963SMatthew Dillon 				 * bump TA lifetime
2017085ff963SMatthew Dillon 				 */
2018085ff963SMatthew Dillon 				rt = ieee80211_mesh_rt_find(vap, wh->i_addr2);
2019085ff963SMatthew Dillon 				if(rt != NULL) {
2020085ff963SMatthew Dillon 					ieee80211_mesh_rt_update(rt,
2021085ff963SMatthew Dillon 					    ticks_to_msecs(
2022085ff963SMatthew Dillon 					    ms->ms_ppath->mpp_inact));
2023085ff963SMatthew Dillon 				}
2024085ff963SMatthew Dillon 				break;
2025085ff963SMatthew Dillon 			}
2026085ff963SMatthew Dillon 			default:
2027085ff963SMatthew Dillon 				break; /* ignore */
2028085ff963SMatthew Dillon 			}
202932176cfdSRui Paulo 		}
203032176cfdSRui Paulo 		break;
203132176cfdSRui Paulo 	}
203232176cfdSRui Paulo 	case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
203332176cfdSRui Paulo 	{
203432176cfdSRui Paulo 		uint8_t *ssid, *meshid, *rates, *xrates;
2035085ff963SMatthew Dillon 		uint8_t *sfrm;
203632176cfdSRui Paulo 
203732176cfdSRui Paulo 		if (vap->iv_state != IEEE80211_S_RUN) {
203832176cfdSRui Paulo 			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
203932176cfdSRui Paulo 			    wh, NULL, "wrong state %s",
204032176cfdSRui Paulo 			    ieee80211_state_name[vap->iv_state]);
204132176cfdSRui Paulo 			vap->iv_stats.is_rx_mgtdiscard++;
204232176cfdSRui Paulo 			return;
204332176cfdSRui Paulo 		}
204432176cfdSRui Paulo 		if (IEEE80211_IS_MULTICAST(wh->i_addr2)) {
204532176cfdSRui Paulo 			/* frame must be directed */
204632176cfdSRui Paulo 			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
204732176cfdSRui Paulo 			    wh, NULL, "%s", "not unicast");
204832176cfdSRui Paulo 			vap->iv_stats.is_rx_mgtdiscard++;	/* XXX stat */
204932176cfdSRui Paulo 			return;
205032176cfdSRui Paulo 		}
205132176cfdSRui Paulo 		/*
205232176cfdSRui Paulo 		 * prreq frame format
205332176cfdSRui Paulo 		 *      [tlv] ssid
205432176cfdSRui Paulo 		 *      [tlv] supported rates
205532176cfdSRui Paulo 		 *      [tlv] extended supported rates
205632176cfdSRui Paulo 		 *	[tlv] mesh id
205732176cfdSRui Paulo 		 */
205832176cfdSRui Paulo 		ssid = meshid = rates = xrates = NULL;
2059085ff963SMatthew Dillon 		sfrm = frm;
206032176cfdSRui Paulo 		while (efrm - frm > 1) {
206132176cfdSRui Paulo 			IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return);
206232176cfdSRui Paulo 			switch (*frm) {
206332176cfdSRui Paulo 			case IEEE80211_ELEMID_SSID:
206432176cfdSRui Paulo 				ssid = frm;
206532176cfdSRui Paulo 				break;
206632176cfdSRui Paulo 			case IEEE80211_ELEMID_RATES:
206732176cfdSRui Paulo 				rates = frm;
206832176cfdSRui Paulo 				break;
206932176cfdSRui Paulo 			case IEEE80211_ELEMID_XRATES:
207032176cfdSRui Paulo 				xrates = frm;
207132176cfdSRui Paulo 				break;
207232176cfdSRui Paulo 			case IEEE80211_ELEMID_MESHID:
207332176cfdSRui Paulo 				meshid = frm;
207432176cfdSRui Paulo 				break;
207532176cfdSRui Paulo 			}
2076085ff963SMatthew Dillon 			frm += frm[1] + 2;
207732176cfdSRui Paulo 		}
207832176cfdSRui Paulo 		IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN, return);
207932176cfdSRui Paulo 		IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE, return);
208032176cfdSRui Paulo 		if (xrates != NULL)
208132176cfdSRui Paulo 			IEEE80211_VERIFY_ELEMENT(xrates,
208232176cfdSRui Paulo 			    IEEE80211_RATE_MAXSIZE - rates[1], return);
208332176cfdSRui Paulo 		if (meshid != NULL) {
208432176cfdSRui Paulo 			IEEE80211_VERIFY_ELEMENT(meshid,
208532176cfdSRui Paulo 			    IEEE80211_MESHID_LEN, return);
208632176cfdSRui Paulo 			/* NB: meshid, not ssid */
208732176cfdSRui Paulo 			IEEE80211_VERIFY_SSID(vap->iv_bss, meshid, return);
208832176cfdSRui Paulo 		}
208932176cfdSRui Paulo 
209032176cfdSRui Paulo 		/* XXX find a better class or define it's own */
209132176cfdSRui Paulo 		IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_INPUT, wh->i_addr2,
209232176cfdSRui Paulo 		    "%s", "recv probe req");
209332176cfdSRui Paulo 		/*
209432176cfdSRui Paulo 		 * Some legacy 11b clients cannot hack a complete
209532176cfdSRui Paulo 		 * probe response frame.  When the request includes
209632176cfdSRui Paulo 		 * only a bare-bones rate set, communicate this to
209732176cfdSRui Paulo 		 * the transmit side.
209832176cfdSRui Paulo 		 */
209932176cfdSRui Paulo 		ieee80211_send_proberesp(vap, wh->i_addr2, 0);
210032176cfdSRui Paulo 		break;
210132176cfdSRui Paulo 	}
2102085ff963SMatthew Dillon 
210332176cfdSRui Paulo 	case IEEE80211_FC0_SUBTYPE_ACTION:
2104085ff963SMatthew Dillon 	case IEEE80211_FC0_SUBTYPE_ACTION_NOACK:
210532176cfdSRui Paulo 		if (ni == vap->iv_bss) {
2106085ff963SMatthew Dillon 			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
210732176cfdSRui Paulo 			    wh, NULL, "%s", "unknown node");
210832176cfdSRui Paulo 			vap->iv_stats.is_rx_mgtdiscard++;
2109085ff963SMatthew Dillon 		} else if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, wh->i_addr1) &&
211032176cfdSRui Paulo 		    !IEEE80211_IS_MULTICAST(wh->i_addr1)) {
2111085ff963SMatthew Dillon 			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
2112085ff963SMatthew Dillon 			    wh, NULL, "%s", "not for us");
211332176cfdSRui Paulo 			vap->iv_stats.is_rx_mgtdiscard++;
2114085ff963SMatthew Dillon 		} else if (vap->iv_state != IEEE80211_S_RUN) {
2115085ff963SMatthew Dillon 			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
2116085ff963SMatthew Dillon 			    wh, NULL, "wrong state %s",
2117085ff963SMatthew Dillon 			    ieee80211_state_name[vap->iv_state]);
2118085ff963SMatthew Dillon 			vap->iv_stats.is_rx_mgtdiscard++;
2119085ff963SMatthew Dillon 		} else {
212032176cfdSRui Paulo 			if (ieee80211_parse_action(ni, m0) == 0)
2121085ff963SMatthew Dillon 				(void)ic->ic_recv_action(ni, wh, frm, efrm);
2122085ff963SMatthew Dillon 		}
212332176cfdSRui Paulo 		break;
2124085ff963SMatthew Dillon 
212532176cfdSRui Paulo 	case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
212632176cfdSRui Paulo 	case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
2127085ff963SMatthew Dillon 	case IEEE80211_FC0_SUBTYPE_REASSOC_REQ:
212832176cfdSRui Paulo 	case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
2129085ff963SMatthew Dillon 	case IEEE80211_FC0_SUBTYPE_ATIM:
213032176cfdSRui Paulo 	case IEEE80211_FC0_SUBTYPE_DISASSOC:
2131085ff963SMatthew Dillon 	case IEEE80211_FC0_SUBTYPE_AUTH:
2132085ff963SMatthew Dillon 	case IEEE80211_FC0_SUBTYPE_DEAUTH:
213332176cfdSRui Paulo 		IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
213432176cfdSRui Paulo 		    wh, NULL, "%s", "not handled");
213532176cfdSRui Paulo 		vap->iv_stats.is_rx_mgtdiscard++;
2136085ff963SMatthew Dillon 		break;
2137085ff963SMatthew Dillon 
213832176cfdSRui Paulo 	default:
213932176cfdSRui Paulo 		IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
214032176cfdSRui Paulo 		    wh, "mgt", "subtype 0x%x not handled", subtype);
214132176cfdSRui Paulo 		vap->iv_stats.is_rx_badsubtype++;
214232176cfdSRui Paulo 		break;
214332176cfdSRui Paulo 	}
214432176cfdSRui Paulo }
214532176cfdSRui Paulo 
2146085ff963SMatthew Dillon static void
2147085ff963SMatthew Dillon mesh_recv_ctl(struct ieee80211_node *ni, struct mbuf *m, int subtype)
2148085ff963SMatthew Dillon {
2149085ff963SMatthew Dillon 
2150085ff963SMatthew Dillon 	switch (subtype) {
2151085ff963SMatthew Dillon 	case IEEE80211_FC0_SUBTYPE_BAR:
2152085ff963SMatthew Dillon 		ieee80211_recv_bar(ni, m);
2153085ff963SMatthew Dillon 		break;
2154085ff963SMatthew Dillon 	}
2155085ff963SMatthew Dillon }
2156085ff963SMatthew Dillon 
215732176cfdSRui Paulo /*
2158085ff963SMatthew Dillon  * Parse meshpeering action ie's for MPM frames
215932176cfdSRui Paulo  */
216032176cfdSRui Paulo static const struct ieee80211_meshpeer_ie *
216132176cfdSRui Paulo mesh_parse_meshpeering_action(struct ieee80211_node *ni,
216232176cfdSRui Paulo 	const struct ieee80211_frame *wh,	/* XXX for VERIFY_LENGTH */
216332176cfdSRui Paulo 	const uint8_t *frm, const uint8_t *efrm,
216432176cfdSRui Paulo 	struct ieee80211_meshpeer_ie *mp, uint8_t subtype)
216532176cfdSRui Paulo {
216632176cfdSRui Paulo 	struct ieee80211vap *vap = ni->ni_vap;
216732176cfdSRui Paulo 	const struct ieee80211_meshpeer_ie *mpie;
2168085ff963SMatthew Dillon 	uint16_t args[3];
216932176cfdSRui Paulo 	const uint8_t *meshid, *meshconf, *meshpeer;
2170085ff963SMatthew Dillon 	uint8_t sendclose = 0; /* 1 = MPM frame rejected, close will be sent */
217132176cfdSRui Paulo 
217232176cfdSRui Paulo 	meshid = meshconf = meshpeer = NULL;
217332176cfdSRui Paulo 	while (efrm - frm > 1) {
217432176cfdSRui Paulo 		IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return NULL);
217532176cfdSRui Paulo 		switch (*frm) {
217632176cfdSRui Paulo 		case IEEE80211_ELEMID_MESHID:
217732176cfdSRui Paulo 			meshid = frm;
217832176cfdSRui Paulo 			break;
217932176cfdSRui Paulo 		case IEEE80211_ELEMID_MESHCONF:
218032176cfdSRui Paulo 			meshconf = frm;
218132176cfdSRui Paulo 			break;
218232176cfdSRui Paulo 		case IEEE80211_ELEMID_MESHPEER:
218332176cfdSRui Paulo 			meshpeer = frm;
218432176cfdSRui Paulo 			mpie = (const struct ieee80211_meshpeer_ie *) frm;
218532176cfdSRui Paulo 			memset(mp, 0, sizeof(*mp));
2186085ff963SMatthew Dillon 			mp->peer_len = mpie->peer_len;
2187085ff963SMatthew Dillon 			mp->peer_proto = LE_READ_2(&mpie->peer_proto);
218832176cfdSRui Paulo 			mp->peer_llinkid = LE_READ_2(&mpie->peer_llinkid);
2189085ff963SMatthew Dillon 			switch (subtype) {
2190085ff963SMatthew Dillon 			case IEEE80211_ACTION_MESHPEERING_CONFIRM:
2191085ff963SMatthew Dillon 				mp->peer_linkid =
2192085ff963SMatthew Dillon 				    LE_READ_2(&mpie->peer_linkid);
2193085ff963SMatthew Dillon 				break;
2194085ff963SMatthew Dillon 			case IEEE80211_ACTION_MESHPEERING_CLOSE:
2195085ff963SMatthew Dillon 				/* NB: peer link ID is optional */
2196085ff963SMatthew Dillon 				if (mpie->peer_len ==
2197085ff963SMatthew Dillon 				    (IEEE80211_MPM_BASE_SZ + 2)) {
219832176cfdSRui Paulo 					mp->peer_linkid = 0;
2199085ff963SMatthew Dillon 					mp->peer_rcode =
2200085ff963SMatthew Dillon 					    LE_READ_2(&mpie->peer_linkid);
220132176cfdSRui Paulo 				} else {
2202085ff963SMatthew Dillon 					mp->peer_linkid =
2203085ff963SMatthew Dillon 					    LE_READ_2(&mpie->peer_linkid);
2204085ff963SMatthew Dillon 					mp->peer_rcode =
2205085ff963SMatthew Dillon 					    LE_READ_2(&mpie->peer_rcode);
2206085ff963SMatthew Dillon 				}
2207085ff963SMatthew Dillon 				break;
220832176cfdSRui Paulo 			}
220932176cfdSRui Paulo 			break;
221032176cfdSRui Paulo 		}
221132176cfdSRui Paulo 		frm += frm[1] + 2;
221232176cfdSRui Paulo 	}
221332176cfdSRui Paulo 
221432176cfdSRui Paulo 	/*
2215085ff963SMatthew Dillon 	 * Verify the contents of the frame.
2216085ff963SMatthew Dillon 	 * If it fails validation, close the peer link.
221732176cfdSRui Paulo 	 */
2218085ff963SMatthew Dillon 	if (mesh_verify_meshpeer(vap, subtype, (const uint8_t *)mp)) {
2219085ff963SMatthew Dillon 		sendclose = 1;
2220085ff963SMatthew Dillon 		IEEE80211_DISCARD(vap,
2221085ff963SMatthew Dillon 		    IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH,
2222085ff963SMatthew Dillon 		    wh, NULL, "%s", "MPM validation failed");
2223085ff963SMatthew Dillon 	}
222432176cfdSRui Paulo 
2225085ff963SMatthew Dillon 	/* If meshid is not the same reject any frames type. */
2226085ff963SMatthew Dillon 	if (sendclose == 0 && mesh_verify_meshid(vap, meshid)) {
2227085ff963SMatthew Dillon 		sendclose = 1;
222832176cfdSRui Paulo 		IEEE80211_DISCARD(vap,
222932176cfdSRui Paulo 		    IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH,
223032176cfdSRui Paulo 		    wh, NULL, "%s", "not for our mesh");
2231085ff963SMatthew Dillon 		if (subtype == IEEE80211_ACTION_MESHPEERING_CLOSE) {
2232085ff963SMatthew Dillon 			/*
2233085ff963SMatthew Dillon 			 * Standard not clear about this, if we dont ignore
2234085ff963SMatthew Dillon 			 * there will be an endless loop between nodes sending
2235085ff963SMatthew Dillon 			 * CLOSE frames between each other with wrong meshid.
2236085ff963SMatthew Dillon 			 * Discard and timers will bring FSM to IDLE state.
2237085ff963SMatthew Dillon 			 */
2238085ff963SMatthew Dillon 			return NULL;
2239085ff963SMatthew Dillon 		}
2240085ff963SMatthew Dillon 	}
2241085ff963SMatthew Dillon 
2242085ff963SMatthew Dillon 	/*
2243085ff963SMatthew Dillon 	 * Close frames are accepted if meshid is the same.
2244085ff963SMatthew Dillon 	 * Verify the other two types.
2245085ff963SMatthew Dillon 	 */
2246085ff963SMatthew Dillon 	if (sendclose == 0 && subtype != IEEE80211_ACTION_MESHPEERING_CLOSE &&
2247085ff963SMatthew Dillon 	    mesh_verify_meshconf(vap, meshconf)) {
2248085ff963SMatthew Dillon 		sendclose = 1;
2249085ff963SMatthew Dillon 		IEEE80211_DISCARD(vap,
2250085ff963SMatthew Dillon 		    IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH,
2251085ff963SMatthew Dillon 		    wh, NULL, "%s", "configuration missmatch");
2252085ff963SMatthew Dillon 	}
2253085ff963SMatthew Dillon 
2254085ff963SMatthew Dillon 	if (sendclose) {
225532176cfdSRui Paulo 		vap->iv_stats.is_rx_mgtdiscard++;
225632176cfdSRui Paulo 		switch (ni->ni_mlstate) {
225732176cfdSRui Paulo 		case IEEE80211_NODE_MESH_IDLE:
225832176cfdSRui Paulo 		case IEEE80211_NODE_MESH_ESTABLISHED:
225932176cfdSRui Paulo 		case IEEE80211_NODE_MESH_HOLDING:
226032176cfdSRui Paulo 			/* ignore */
226132176cfdSRui Paulo 			break;
226232176cfdSRui Paulo 		case IEEE80211_NODE_MESH_OPENSNT:
226332176cfdSRui Paulo 		case IEEE80211_NODE_MESH_OPENRCV:
226432176cfdSRui Paulo 		case IEEE80211_NODE_MESH_CONFIRMRCV:
226532176cfdSRui Paulo 			args[0] = ni->ni_mlpid;
226632176cfdSRui Paulo 			args[1] = ni->ni_mllid;
2267085ff963SMatthew Dillon 			/* Reason codes for rejection */
2268085ff963SMatthew Dillon 			switch (subtype) {
2269085ff963SMatthew Dillon 			case IEEE80211_ACTION_MESHPEERING_OPEN:
2270085ff963SMatthew Dillon 				args[2] = IEEE80211_REASON_MESH_CPVIOLATION;
2271085ff963SMatthew Dillon 				break;
2272085ff963SMatthew Dillon 			case IEEE80211_ACTION_MESHPEERING_CONFIRM:
2273085ff963SMatthew Dillon 				args[2] = IEEE80211_REASON_MESH_INCONS_PARAMS;
2274085ff963SMatthew Dillon 				break;
2275085ff963SMatthew Dillon 			}
227632176cfdSRui Paulo 			ieee80211_send_action(ni,
2277085ff963SMatthew Dillon 			    IEEE80211_ACTION_CAT_SELF_PROT,
227832176cfdSRui Paulo 			    IEEE80211_ACTION_MESHPEERING_CLOSE,
227932176cfdSRui Paulo 			    args);
228032176cfdSRui Paulo 			mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING);
228132176cfdSRui Paulo 			mesh_peer_timeout_setup(ni);
228232176cfdSRui Paulo 			break;
228332176cfdSRui Paulo 		}
228432176cfdSRui Paulo 		return NULL;
228532176cfdSRui Paulo 	}
2286085ff963SMatthew Dillon 
228732176cfdSRui Paulo 	return (const struct ieee80211_meshpeer_ie *) mp;
228832176cfdSRui Paulo }
228932176cfdSRui Paulo 
229032176cfdSRui Paulo static int
229132176cfdSRui Paulo mesh_recv_action_meshpeering_open(struct ieee80211_node *ni,
229232176cfdSRui Paulo 	const struct ieee80211_frame *wh,
229332176cfdSRui Paulo 	const uint8_t *frm, const uint8_t *efrm)
229432176cfdSRui Paulo {
229532176cfdSRui Paulo 	struct ieee80211vap *vap = ni->ni_vap;
2296085ff963SMatthew Dillon 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
229732176cfdSRui Paulo 	struct ieee80211_meshpeer_ie ie;
229832176cfdSRui Paulo 	const struct ieee80211_meshpeer_ie *meshpeer;
229932176cfdSRui Paulo 	uint16_t args[3];
230032176cfdSRui Paulo 
230132176cfdSRui Paulo 	/* +2+2 for action + code + capabilites */
230232176cfdSRui Paulo 	meshpeer = mesh_parse_meshpeering_action(ni, wh, frm+2+2, efrm, &ie,
230332176cfdSRui Paulo 	    IEEE80211_ACTION_MESHPEERING_OPEN);
230432176cfdSRui Paulo 	if (meshpeer == NULL) {
230532176cfdSRui Paulo 		return 0;
230632176cfdSRui Paulo 	}
230732176cfdSRui Paulo 
230832176cfdSRui Paulo 	/* XXX move up */
230932176cfdSRui Paulo 	IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, ni,
231032176cfdSRui Paulo 	    "recv PEER OPEN, lid 0x%x", meshpeer->peer_llinkid);
231132176cfdSRui Paulo 
231232176cfdSRui Paulo 	switch (ni->ni_mlstate) {
231332176cfdSRui Paulo 	case IEEE80211_NODE_MESH_IDLE:
2314085ff963SMatthew Dillon 		/* Reject open request if reached our maximum neighbor count */
2315085ff963SMatthew Dillon 		if (ms->ms_neighbors >= IEEE80211_MESH_MAX_NEIGHBORS) {
2316085ff963SMatthew Dillon 			args[0] = meshpeer->peer_llinkid;
2317085ff963SMatthew Dillon 			args[1] = 0;
2318085ff963SMatthew Dillon 			args[2] = IEEE80211_REASON_MESH_MAX_PEERS;
2319085ff963SMatthew Dillon 			ieee80211_send_action(ni,
2320085ff963SMatthew Dillon 			    IEEE80211_ACTION_CAT_SELF_PROT,
2321085ff963SMatthew Dillon 			    IEEE80211_ACTION_MESHPEERING_CLOSE,
2322085ff963SMatthew Dillon 			    args);
2323085ff963SMatthew Dillon 			/* stay in IDLE state */
2324085ff963SMatthew Dillon 			return (0);
2325085ff963SMatthew Dillon 		}
2326085ff963SMatthew Dillon 		/* Open frame accepted */
232732176cfdSRui Paulo 		mesh_linkchange(ni, IEEE80211_NODE_MESH_OPENRCV);
232832176cfdSRui Paulo 		ni->ni_mllid = meshpeer->peer_llinkid;
232932176cfdSRui Paulo 		ni->ni_mlpid = mesh_generateid(vap);
233032176cfdSRui Paulo 		if (ni->ni_mlpid == 0)
233132176cfdSRui Paulo 			return 0;		/* XXX */
233232176cfdSRui Paulo 		args[0] = ni->ni_mlpid;
233332176cfdSRui Paulo 		/* Announce we're open too... */
233432176cfdSRui Paulo 		ieee80211_send_action(ni,
2335085ff963SMatthew Dillon 		    IEEE80211_ACTION_CAT_SELF_PROT,
233632176cfdSRui Paulo 		    IEEE80211_ACTION_MESHPEERING_OPEN, args);
233732176cfdSRui Paulo 		/* ...and confirm the link. */
233832176cfdSRui Paulo 		args[0] = ni->ni_mlpid;
233932176cfdSRui Paulo 		args[1] = ni->ni_mllid;
234032176cfdSRui Paulo 		ieee80211_send_action(ni,
2341085ff963SMatthew Dillon 		    IEEE80211_ACTION_CAT_SELF_PROT,
234232176cfdSRui Paulo 		    IEEE80211_ACTION_MESHPEERING_CONFIRM,
234332176cfdSRui Paulo 		    args);
234432176cfdSRui Paulo 		mesh_peer_timeout_setup(ni);
234532176cfdSRui Paulo 		break;
234632176cfdSRui Paulo 	case IEEE80211_NODE_MESH_OPENRCV:
234732176cfdSRui Paulo 		/* Wrong Link ID */
234832176cfdSRui Paulo 		if (ni->ni_mllid != meshpeer->peer_llinkid) {
234932176cfdSRui Paulo 			args[0] = ni->ni_mllid;
235032176cfdSRui Paulo 			args[1] = ni->ni_mlpid;
235132176cfdSRui Paulo 			args[2] = IEEE80211_REASON_PEER_LINK_CANCELED;
235232176cfdSRui Paulo 			ieee80211_send_action(ni,
2353085ff963SMatthew Dillon 			    IEEE80211_ACTION_CAT_SELF_PROT,
235432176cfdSRui Paulo 			    IEEE80211_ACTION_MESHPEERING_CLOSE,
235532176cfdSRui Paulo 			    args);
235632176cfdSRui Paulo 			mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING);
235732176cfdSRui Paulo 			mesh_peer_timeout_setup(ni);
235832176cfdSRui Paulo 			break;
235932176cfdSRui Paulo 		}
236032176cfdSRui Paulo 		/* Duplicate open, confirm again. */
236132176cfdSRui Paulo 		args[0] = ni->ni_mlpid;
236232176cfdSRui Paulo 		args[1] = ni->ni_mllid;
236332176cfdSRui Paulo 		ieee80211_send_action(ni,
2364085ff963SMatthew Dillon 		    IEEE80211_ACTION_CAT_SELF_PROT,
236532176cfdSRui Paulo 		    IEEE80211_ACTION_MESHPEERING_CONFIRM,
236632176cfdSRui Paulo 		    args);
236732176cfdSRui Paulo 		break;
236832176cfdSRui Paulo 	case IEEE80211_NODE_MESH_OPENSNT:
236932176cfdSRui Paulo 		ni->ni_mllid = meshpeer->peer_llinkid;
237032176cfdSRui Paulo 		mesh_linkchange(ni, IEEE80211_NODE_MESH_OPENRCV);
237132176cfdSRui Paulo 		args[0] = ni->ni_mlpid;
237232176cfdSRui Paulo 		args[1] = ni->ni_mllid;
237332176cfdSRui Paulo 		ieee80211_send_action(ni,
2374085ff963SMatthew Dillon 		    IEEE80211_ACTION_CAT_SELF_PROT,
237532176cfdSRui Paulo 		    IEEE80211_ACTION_MESHPEERING_CONFIRM,
237632176cfdSRui Paulo 		    args);
237732176cfdSRui Paulo 		/* NB: don't setup/clear any timeout */
237832176cfdSRui Paulo 		break;
237932176cfdSRui Paulo 	case IEEE80211_NODE_MESH_CONFIRMRCV:
238032176cfdSRui Paulo 		if (ni->ni_mlpid != meshpeer->peer_linkid ||
238132176cfdSRui Paulo 		    ni->ni_mllid != meshpeer->peer_llinkid) {
238232176cfdSRui Paulo 			args[0] = ni->ni_mlpid;
238332176cfdSRui Paulo 			args[1] = ni->ni_mllid;
238432176cfdSRui Paulo 			args[2] = IEEE80211_REASON_PEER_LINK_CANCELED;
238532176cfdSRui Paulo 			ieee80211_send_action(ni,
2386085ff963SMatthew Dillon 			    IEEE80211_ACTION_CAT_SELF_PROT,
238732176cfdSRui Paulo 			    IEEE80211_ACTION_MESHPEERING_CLOSE,
238832176cfdSRui Paulo 			    args);
238932176cfdSRui Paulo 			mesh_linkchange(ni,
239032176cfdSRui Paulo 			    IEEE80211_NODE_MESH_HOLDING);
239132176cfdSRui Paulo 			mesh_peer_timeout_setup(ni);
239232176cfdSRui Paulo 			break;
239332176cfdSRui Paulo 		}
239432176cfdSRui Paulo 		mesh_linkchange(ni, IEEE80211_NODE_MESH_ESTABLISHED);
239532176cfdSRui Paulo 		ni->ni_mllid = meshpeer->peer_llinkid;
239632176cfdSRui Paulo 		args[0] = ni->ni_mlpid;
239732176cfdSRui Paulo 		args[1] = ni->ni_mllid;
239832176cfdSRui Paulo 		ieee80211_send_action(ni,
2399085ff963SMatthew Dillon 		    IEEE80211_ACTION_CAT_SELF_PROT,
240032176cfdSRui Paulo 		    IEEE80211_ACTION_MESHPEERING_CONFIRM,
240132176cfdSRui Paulo 		    args);
240232176cfdSRui Paulo 		mesh_peer_timeout_stop(ni);
240332176cfdSRui Paulo 		break;
240432176cfdSRui Paulo 	case IEEE80211_NODE_MESH_ESTABLISHED:
240532176cfdSRui Paulo 		if (ni->ni_mllid != meshpeer->peer_llinkid) {
240632176cfdSRui Paulo 			args[0] = ni->ni_mllid;
240732176cfdSRui Paulo 			args[1] = ni->ni_mlpid;
240832176cfdSRui Paulo 			args[2] = IEEE80211_REASON_PEER_LINK_CANCELED;
240932176cfdSRui Paulo 			ieee80211_send_action(ni,
2410085ff963SMatthew Dillon 			    IEEE80211_ACTION_CAT_SELF_PROT,
241132176cfdSRui Paulo 			    IEEE80211_ACTION_MESHPEERING_CLOSE,
241232176cfdSRui Paulo 			    args);
241332176cfdSRui Paulo 			mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING);
241432176cfdSRui Paulo 			mesh_peer_timeout_setup(ni);
241532176cfdSRui Paulo 			break;
241632176cfdSRui Paulo 		}
241732176cfdSRui Paulo 		args[0] = ni->ni_mlpid;
241832176cfdSRui Paulo 		args[1] = ni->ni_mllid;
241932176cfdSRui Paulo 		ieee80211_send_action(ni,
2420085ff963SMatthew Dillon 		    IEEE80211_ACTION_CAT_SELF_PROT,
242132176cfdSRui Paulo 		    IEEE80211_ACTION_MESHPEERING_CONFIRM,
242232176cfdSRui Paulo 		    args);
242332176cfdSRui Paulo 		break;
242432176cfdSRui Paulo 	case IEEE80211_NODE_MESH_HOLDING:
242532176cfdSRui Paulo 		args[0] = ni->ni_mlpid;
242632176cfdSRui Paulo 		args[1] = meshpeer->peer_llinkid;
2427085ff963SMatthew Dillon 		/* Standard not clear about what the reaason code should be */
2428085ff963SMatthew Dillon 		args[2] = IEEE80211_REASON_PEER_LINK_CANCELED;
242932176cfdSRui Paulo 		ieee80211_send_action(ni,
2430085ff963SMatthew Dillon 		    IEEE80211_ACTION_CAT_SELF_PROT,
243132176cfdSRui Paulo 		    IEEE80211_ACTION_MESHPEERING_CLOSE,
243232176cfdSRui Paulo 		    args);
243332176cfdSRui Paulo 		break;
243432176cfdSRui Paulo 	}
243532176cfdSRui Paulo 	return 0;
243632176cfdSRui Paulo }
243732176cfdSRui Paulo 
243832176cfdSRui Paulo static int
243932176cfdSRui Paulo mesh_recv_action_meshpeering_confirm(struct ieee80211_node *ni,
244032176cfdSRui Paulo 	const struct ieee80211_frame *wh,
244132176cfdSRui Paulo 	const uint8_t *frm, const uint8_t *efrm)
244232176cfdSRui Paulo {
244332176cfdSRui Paulo 	struct ieee80211vap *vap = ni->ni_vap;
244432176cfdSRui Paulo 	struct ieee80211_meshpeer_ie ie;
244532176cfdSRui Paulo 	const struct ieee80211_meshpeer_ie *meshpeer;
244632176cfdSRui Paulo 	uint16_t args[3];
244732176cfdSRui Paulo 
244832176cfdSRui Paulo 	/* +2+2+2+2 for action + code + capabilites + status code + AID */
244932176cfdSRui Paulo 	meshpeer = mesh_parse_meshpeering_action(ni, wh, frm+2+2+2+2, efrm, &ie,
245032176cfdSRui Paulo 	    IEEE80211_ACTION_MESHPEERING_CONFIRM);
245132176cfdSRui Paulo 	if (meshpeer == NULL) {
245232176cfdSRui Paulo 		return 0;
245332176cfdSRui Paulo 	}
245432176cfdSRui Paulo 
245532176cfdSRui Paulo 	IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, ni,
245632176cfdSRui Paulo 	    "recv PEER CONFIRM, local id 0x%x, peer id 0x%x",
245732176cfdSRui Paulo 	    meshpeer->peer_llinkid, meshpeer->peer_linkid);
245832176cfdSRui Paulo 
245932176cfdSRui Paulo 	switch (ni->ni_mlstate) {
246032176cfdSRui Paulo 	case IEEE80211_NODE_MESH_OPENRCV:
246132176cfdSRui Paulo 		mesh_linkchange(ni, IEEE80211_NODE_MESH_ESTABLISHED);
246232176cfdSRui Paulo 		mesh_peer_timeout_stop(ni);
246332176cfdSRui Paulo 		break;
246432176cfdSRui Paulo 	case IEEE80211_NODE_MESH_OPENSNT:
246532176cfdSRui Paulo 		mesh_linkchange(ni, IEEE80211_NODE_MESH_CONFIRMRCV);
2466085ff963SMatthew Dillon 		mesh_peer_timeout_setup(ni);
246732176cfdSRui Paulo 		break;
246832176cfdSRui Paulo 	case IEEE80211_NODE_MESH_HOLDING:
246932176cfdSRui Paulo 		args[0] = ni->ni_mlpid;
247032176cfdSRui Paulo 		args[1] = meshpeer->peer_llinkid;
2471085ff963SMatthew Dillon 		/* Standard not clear about what the reaason code should be */
2472085ff963SMatthew Dillon 		args[2] = IEEE80211_REASON_PEER_LINK_CANCELED;
247332176cfdSRui Paulo 		ieee80211_send_action(ni,
2474085ff963SMatthew Dillon 		    IEEE80211_ACTION_CAT_SELF_PROT,
247532176cfdSRui Paulo 		    IEEE80211_ACTION_MESHPEERING_CLOSE,
247632176cfdSRui Paulo 		    args);
247732176cfdSRui Paulo 		break;
247832176cfdSRui Paulo 	case IEEE80211_NODE_MESH_CONFIRMRCV:
247932176cfdSRui Paulo 		if (ni->ni_mllid != meshpeer->peer_llinkid) {
248032176cfdSRui Paulo 			args[0] = ni->ni_mlpid;
248132176cfdSRui Paulo 			args[1] = ni->ni_mllid;
248232176cfdSRui Paulo 			args[2] = IEEE80211_REASON_PEER_LINK_CANCELED;
248332176cfdSRui Paulo 			ieee80211_send_action(ni,
2484085ff963SMatthew Dillon 			    IEEE80211_ACTION_CAT_SELF_PROT,
248532176cfdSRui Paulo 			    IEEE80211_ACTION_MESHPEERING_CLOSE,
248632176cfdSRui Paulo 			    args);
248732176cfdSRui Paulo 			mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING);
248832176cfdSRui Paulo 			mesh_peer_timeout_setup(ni);
248932176cfdSRui Paulo 		}
249032176cfdSRui Paulo 		break;
249132176cfdSRui Paulo 	default:
249232176cfdSRui Paulo 		IEEE80211_DISCARD(vap,
249332176cfdSRui Paulo 		    IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH,
249432176cfdSRui Paulo 		    wh, NULL, "received confirm in invalid state %d",
249532176cfdSRui Paulo 		    ni->ni_mlstate);
249632176cfdSRui Paulo 		vap->iv_stats.is_rx_mgtdiscard++;
249732176cfdSRui Paulo 		break;
249832176cfdSRui Paulo 	}
249932176cfdSRui Paulo 	return 0;
250032176cfdSRui Paulo }
250132176cfdSRui Paulo 
250232176cfdSRui Paulo static int
250332176cfdSRui Paulo mesh_recv_action_meshpeering_close(struct ieee80211_node *ni,
250432176cfdSRui Paulo 	const struct ieee80211_frame *wh,
250532176cfdSRui Paulo 	const uint8_t *frm, const uint8_t *efrm)
250632176cfdSRui Paulo {
2507085ff963SMatthew Dillon 	struct ieee80211_meshpeer_ie ie;
2508085ff963SMatthew Dillon 	const struct ieee80211_meshpeer_ie *meshpeer;
250932176cfdSRui Paulo 	uint16_t args[3];
251032176cfdSRui Paulo 
2511085ff963SMatthew Dillon 	/* +2 for action + code */
2512085ff963SMatthew Dillon 	meshpeer = mesh_parse_meshpeering_action(ni, wh, frm+2, efrm, &ie,
2513085ff963SMatthew Dillon 	    IEEE80211_ACTION_MESHPEERING_CLOSE);
2514085ff963SMatthew Dillon 	if (meshpeer == NULL) {
2515085ff963SMatthew Dillon 		return 0;
2516085ff963SMatthew Dillon 	}
2517085ff963SMatthew Dillon 
2518085ff963SMatthew Dillon 	/*
2519085ff963SMatthew Dillon 	 * XXX: check reason code, for example we could receive
2520085ff963SMatthew Dillon 	 * IEEE80211_REASON_MESH_MAX_PEERS then we should not attempt
2521085ff963SMatthew Dillon 	 * to peer again.
2522085ff963SMatthew Dillon 	 */
2523085ff963SMatthew Dillon 
252432176cfdSRui Paulo 	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH,
252532176cfdSRui Paulo 	    ni, "%s", "recv PEER CLOSE");
252632176cfdSRui Paulo 
252732176cfdSRui Paulo 	switch (ni->ni_mlstate) {
252832176cfdSRui Paulo 	case IEEE80211_NODE_MESH_IDLE:
252932176cfdSRui Paulo 		/* ignore */
253032176cfdSRui Paulo 		break;
253132176cfdSRui Paulo 	case IEEE80211_NODE_MESH_OPENRCV:
253232176cfdSRui Paulo 	case IEEE80211_NODE_MESH_OPENSNT:
253332176cfdSRui Paulo 	case IEEE80211_NODE_MESH_CONFIRMRCV:
253432176cfdSRui Paulo 	case IEEE80211_NODE_MESH_ESTABLISHED:
253532176cfdSRui Paulo 		args[0] = ni->ni_mlpid;
253632176cfdSRui Paulo 		args[1] = ni->ni_mllid;
253732176cfdSRui Paulo 		args[2] = IEEE80211_REASON_MESH_CLOSE_RCVD;
253832176cfdSRui Paulo 		ieee80211_send_action(ni,
2539085ff963SMatthew Dillon 		    IEEE80211_ACTION_CAT_SELF_PROT,
254032176cfdSRui Paulo 		    IEEE80211_ACTION_MESHPEERING_CLOSE,
254132176cfdSRui Paulo 		    args);
254232176cfdSRui Paulo 		mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING);
254332176cfdSRui Paulo 		mesh_peer_timeout_setup(ni);
254432176cfdSRui Paulo 		break;
254532176cfdSRui Paulo 	case IEEE80211_NODE_MESH_HOLDING:
254632176cfdSRui Paulo 		mesh_linkchange(ni, IEEE80211_NODE_MESH_IDLE);
2547085ff963SMatthew Dillon 		mesh_peer_timeout_stop(ni);
254832176cfdSRui Paulo 		break;
254932176cfdSRui Paulo 	}
255032176cfdSRui Paulo 	return 0;
255132176cfdSRui Paulo }
255232176cfdSRui Paulo 
255332176cfdSRui Paulo /*
255432176cfdSRui Paulo  * Link Metric handling.
255532176cfdSRui Paulo  */
255632176cfdSRui Paulo static int
2557085ff963SMatthew Dillon mesh_recv_action_meshlmetric(struct ieee80211_node *ni,
255832176cfdSRui Paulo 	const struct ieee80211_frame *wh,
255932176cfdSRui Paulo 	const uint8_t *frm, const uint8_t *efrm)
256032176cfdSRui Paulo {
2561085ff963SMatthew Dillon 	const struct ieee80211_meshlmetric_ie *ie =
2562085ff963SMatthew Dillon 	    (const struct ieee80211_meshlmetric_ie *)
2563085ff963SMatthew Dillon 	    (frm+2); /* action + code */
2564085ff963SMatthew Dillon 	struct ieee80211_meshlmetric_ie lm_rep;
256532176cfdSRui Paulo 
2566085ff963SMatthew Dillon 	if (ie->lm_flags & IEEE80211_MESH_LMETRIC_FLAGS_REQ) {
2567085ff963SMatthew Dillon 		lm_rep.lm_flags = 0;
2568085ff963SMatthew Dillon 		lm_rep.lm_metric = mesh_airtime_calc(ni);
256932176cfdSRui Paulo 		ieee80211_send_action(ni,
2570085ff963SMatthew Dillon 		    IEEE80211_ACTION_CAT_MESH,
2571085ff963SMatthew Dillon 		    IEEE80211_ACTION_MESH_LMETRIC,
2572085ff963SMatthew Dillon 		    &lm_rep);
2573085ff963SMatthew Dillon 	}
2574085ff963SMatthew Dillon 	/* XXX: else do nothing for now */
257532176cfdSRui Paulo 	return 0;
257632176cfdSRui Paulo }
257732176cfdSRui Paulo 
2578085ff963SMatthew Dillon /*
2579085ff963SMatthew Dillon  * Parse meshgate action ie's for GANN frames.
2580085ff963SMatthew Dillon  * Returns -1 if parsing fails, otherwise 0.
2581085ff963SMatthew Dillon  */
258232176cfdSRui Paulo static int
2583085ff963SMatthew Dillon mesh_parse_meshgate_action(struct ieee80211_node *ni,
2584085ff963SMatthew Dillon     const struct ieee80211_frame *wh,	/* XXX for VERIFY_LENGTH */
2585085ff963SMatthew Dillon     struct ieee80211_meshgann_ie *ie, const uint8_t *frm, const uint8_t *efrm)
2586085ff963SMatthew Dillon {
2587085ff963SMatthew Dillon 	struct ieee80211vap *vap = ni->ni_vap;
2588085ff963SMatthew Dillon 	const struct ieee80211_meshgann_ie *gannie;
2589085ff963SMatthew Dillon 
2590085ff963SMatthew Dillon 	while (efrm - frm > 1) {
2591085ff963SMatthew Dillon 		IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return -1);
2592085ff963SMatthew Dillon 		switch (*frm) {
2593085ff963SMatthew Dillon 		case IEEE80211_ELEMID_MESHGANN:
2594085ff963SMatthew Dillon 			gannie = (const struct ieee80211_meshgann_ie *) frm;
2595085ff963SMatthew Dillon 			memset(ie, 0, sizeof(*ie));
2596085ff963SMatthew Dillon 			ie->gann_ie = gannie->gann_ie;
2597085ff963SMatthew Dillon 			ie->gann_len = gannie->gann_len;
2598085ff963SMatthew Dillon 			ie->gann_flags = gannie->gann_flags;
2599085ff963SMatthew Dillon 			ie->gann_hopcount = gannie->gann_hopcount;
2600085ff963SMatthew Dillon 			ie->gann_ttl = gannie->gann_ttl;
2601085ff963SMatthew Dillon 			IEEE80211_ADDR_COPY(ie->gann_addr, gannie->gann_addr);
2602085ff963SMatthew Dillon 			ie->gann_seq = LE_READ_4(&gannie->gann_seq);
2603085ff963SMatthew Dillon 			ie->gann_interval = LE_READ_2(&gannie->gann_interval);
2604085ff963SMatthew Dillon 			break;
2605085ff963SMatthew Dillon 		}
2606085ff963SMatthew Dillon 		frm += frm[1] + 2;
2607085ff963SMatthew Dillon 	}
2608085ff963SMatthew Dillon 
2609085ff963SMatthew Dillon 	return 0;
2610085ff963SMatthew Dillon }
2611085ff963SMatthew Dillon 
2612085ff963SMatthew Dillon /*
2613085ff963SMatthew Dillon  * Mesh Gate Announcement handling.
2614085ff963SMatthew Dillon  */
2615085ff963SMatthew Dillon static int
2616085ff963SMatthew Dillon mesh_recv_action_meshgate(struct ieee80211_node *ni,
261732176cfdSRui Paulo 	const struct ieee80211_frame *wh,
261832176cfdSRui Paulo 	const uint8_t *frm, const uint8_t *efrm)
261932176cfdSRui Paulo {
2620085ff963SMatthew Dillon 	struct ieee80211vap *vap = ni->ni_vap;
2621085ff963SMatthew Dillon 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
2622085ff963SMatthew Dillon 	struct ieee80211_mesh_gate_route *gr, *next;
2623085ff963SMatthew Dillon 	struct ieee80211_mesh_route *rt_gate;
2624085ff963SMatthew Dillon 	struct ieee80211_meshgann_ie pgann;
2625085ff963SMatthew Dillon 	struct ieee80211_meshgann_ie ie;
2626085ff963SMatthew Dillon 	int found = 0;
2627085ff963SMatthew Dillon 
2628085ff963SMatthew Dillon 	/* +2 for action + code */
2629085ff963SMatthew Dillon 	if (mesh_parse_meshgate_action(ni, wh, &ie, frm+2, efrm) != 0) {
2630085ff963SMatthew Dillon 		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_MESH,
2631085ff963SMatthew Dillon 		    ni->ni_macaddr, NULL, "%s",
2632085ff963SMatthew Dillon 		    "GANN parsing failed");
2633085ff963SMatthew Dillon 		vap->iv_stats.is_rx_mgtdiscard++;
2634085ff963SMatthew Dillon 		return (0);
2635085ff963SMatthew Dillon 	}
2636085ff963SMatthew Dillon 
2637085ff963SMatthew Dillon 	if (IEEE80211_ADDR_EQ(vap->iv_myaddr, ie.gann_addr))
2638085ff963SMatthew Dillon 		return 0;
2639085ff963SMatthew Dillon 
2640085ff963SMatthew Dillon 	IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, ni->ni_macaddr,
2641085ff963SMatthew Dillon 	    "received GANN, meshgate: %6D (seq %u)", ie.gann_addr, ":",
2642085ff963SMatthew Dillon 	    ie.gann_seq);
2643085ff963SMatthew Dillon 
2644085ff963SMatthew Dillon 	if (ms == NULL)
2645085ff963SMatthew Dillon 		return (0);
2646085ff963SMatthew Dillon 	MESH_RT_LOCK(ms);
2647085ff963SMatthew Dillon 	TAILQ_FOREACH_SAFE(gr, &ms->ms_known_gates, gr_next, next) {
2648085ff963SMatthew Dillon 		if (!IEEE80211_ADDR_EQ(gr->gr_addr, ie.gann_addr))
2649085ff963SMatthew Dillon 			continue;
2650085ff963SMatthew Dillon 		if (ie.gann_seq <= gr->gr_lastseq) {
2651085ff963SMatthew Dillon 			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_MESH,
2652085ff963SMatthew Dillon 			    ni->ni_macaddr, NULL,
2653085ff963SMatthew Dillon 			    "GANN old seqno %u <= %u",
2654085ff963SMatthew Dillon 			    ie.gann_seq, gr->gr_lastseq);
2655085ff963SMatthew Dillon 			MESH_RT_UNLOCK(ms);
2656085ff963SMatthew Dillon 			return (0);
2657085ff963SMatthew Dillon 		}
2658085ff963SMatthew Dillon 		/* corresponding mesh gate found & GANN accepted */
2659085ff963SMatthew Dillon 		found = 1;
2660085ff963SMatthew Dillon 		break;
2661085ff963SMatthew Dillon 
2662085ff963SMatthew Dillon 	}
2663085ff963SMatthew Dillon 	if (found == 0) {
2664085ff963SMatthew Dillon 		/* this GANN is from a new mesh Gate add it to known table. */
2665085ff963SMatthew Dillon 		IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, ie.gann_addr,
2666085ff963SMatthew Dillon 		    "stored new GANN information, seq %u.", ie.gann_seq);
2667085ff963SMatthew Dillon 		gr = kmalloc(ALIGN(sizeof(struct ieee80211_mesh_gate_route)),
2668085ff963SMatthew Dillon 		    M_80211_MESH_GT_RT, M_INTWAIT | M_ZERO);
2669085ff963SMatthew Dillon 		IEEE80211_ADDR_COPY(gr->gr_addr, ie.gann_addr);
2670085ff963SMatthew Dillon 		TAILQ_INSERT_TAIL(&ms->ms_known_gates, gr, gr_next);
2671085ff963SMatthew Dillon 	}
2672085ff963SMatthew Dillon 	gr->gr_lastseq = ie.gann_seq;
2673085ff963SMatthew Dillon 
2674085ff963SMatthew Dillon 	/* check if we have a path to this gate */
2675085ff963SMatthew Dillon 	rt_gate = mesh_rt_find_locked(ms, gr->gr_addr);
2676085ff963SMatthew Dillon 	if (rt_gate != NULL &&
2677085ff963SMatthew Dillon 	    rt_gate->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) {
2678085ff963SMatthew Dillon 		gr->gr_route = rt_gate;
2679085ff963SMatthew Dillon 		rt_gate->rt_flags |= IEEE80211_MESHRT_FLAGS_GATE;
2680085ff963SMatthew Dillon 	}
2681085ff963SMatthew Dillon 
2682085ff963SMatthew Dillon 	MESH_RT_UNLOCK(ms);
2683085ff963SMatthew Dillon 
2684085ff963SMatthew Dillon 	/* popagate only if decremented ttl >= 1 && forwarding is enabled */
2685085ff963SMatthew Dillon 	if ((ie.gann_ttl - 1) < 1 && !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD))
2686085ff963SMatthew Dillon 		return 0;
2687085ff963SMatthew Dillon 		pgann.gann_flags = ie.gann_flags; /* Reserved */
2688085ff963SMatthew Dillon 	pgann.gann_hopcount = ie.gann_hopcount + 1;
2689085ff963SMatthew Dillon 	pgann.gann_ttl = ie.gann_ttl - 1;
2690085ff963SMatthew Dillon 	IEEE80211_ADDR_COPY(pgann.gann_addr, ie.gann_addr);
2691085ff963SMatthew Dillon 	pgann.gann_seq = ie.gann_seq;
2692085ff963SMatthew Dillon 	pgann.gann_interval = ie.gann_interval;
2693085ff963SMatthew Dillon 
2694085ff963SMatthew Dillon 	IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, ie.gann_addr,
2695085ff963SMatthew Dillon 	    "%s", "propagate GANN");
2696085ff963SMatthew Dillon 
2697085ff963SMatthew Dillon 	ieee80211_send_action(vap->iv_bss, IEEE80211_ACTION_CAT_MESH,
2698085ff963SMatthew Dillon 	    IEEE80211_ACTION_MESH_GANN, &pgann);
2699085ff963SMatthew Dillon 
270032176cfdSRui Paulo 	return 0;
270132176cfdSRui Paulo }
270232176cfdSRui Paulo 
270332176cfdSRui Paulo static int
2704085ff963SMatthew Dillon mesh_send_action(struct ieee80211_node *ni,
2705085ff963SMatthew Dillon     const uint8_t sa[IEEE80211_ADDR_LEN],
2706085ff963SMatthew Dillon     const uint8_t da[IEEE80211_ADDR_LEN],
2707085ff963SMatthew Dillon     struct mbuf *m)
270832176cfdSRui Paulo {
2709085ff963SMatthew Dillon 	struct ieee80211vap *vap = ni->ni_vap;
2710085ff963SMatthew Dillon 	struct ieee80211com *ic = ni->ni_ic;
271132176cfdSRui Paulo 	struct ieee80211_bpf_params params;
2712085ff963SMatthew Dillon 	struct ieee80211_frame *wh;
2713085ff963SMatthew Dillon 	int ret;
2714085ff963SMatthew Dillon 
2715085ff963SMatthew Dillon 	KASSERT(ni != NULL, ("null node"));
2716085ff963SMatthew Dillon 
2717085ff963SMatthew Dillon 	if (vap->iv_state == IEEE80211_S_CAC) {
2718085ff963SMatthew Dillon 		IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, ni,
2719085ff963SMatthew Dillon 		    "block %s frame in CAC state", "Mesh action");
2720085ff963SMatthew Dillon 		vap->iv_stats.is_tx_badstate++;
2721085ff963SMatthew Dillon 		ieee80211_free_node(ni);
2722085ff963SMatthew Dillon 		m_freem(m);
2723085ff963SMatthew Dillon 		return EIO;		/* XXX */
2724085ff963SMatthew Dillon 	}
2725085ff963SMatthew Dillon 
2726*b5523eacSSascha Wildner 	M_PREPEND(m, sizeof(struct ieee80211_frame), M_NOWAIT);
2727085ff963SMatthew Dillon 	if (m == NULL) {
2728085ff963SMatthew Dillon 		ieee80211_free_node(ni);
2729085ff963SMatthew Dillon 		return ENOMEM;
2730085ff963SMatthew Dillon 	}
2731085ff963SMatthew Dillon 
2732085ff963SMatthew Dillon 	IEEE80211_TX_LOCK(ic);
2733085ff963SMatthew Dillon 	wh = mtod(m, struct ieee80211_frame *);
2734085ff963SMatthew Dillon 	ieee80211_send_setup(ni, m,
2735085ff963SMatthew Dillon 	     IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_ACTION,
2736085ff963SMatthew Dillon 	     IEEE80211_NONQOS_TID, sa, da, sa);
2737085ff963SMatthew Dillon 	m->m_flags |= M_ENCAP;		/* mark encapsulated */
273832176cfdSRui Paulo 
273932176cfdSRui Paulo 	memset(&params, 0, sizeof(params));
274032176cfdSRui Paulo 	params.ibp_pri = WME_AC_VO;
274132176cfdSRui Paulo 	params.ibp_rate0 = ni->ni_txparms->mgmtrate;
2742085ff963SMatthew Dillon 	if (IEEE80211_IS_MULTICAST(da))
2743085ff963SMatthew Dillon 		params.ibp_try0 = 1;
2744085ff963SMatthew Dillon 	else
274532176cfdSRui Paulo 		params.ibp_try0 = ni->ni_txparms->maxretry;
274632176cfdSRui Paulo 	params.ibp_power = ni->ni_txpower;
2747085ff963SMatthew Dillon 
2748085ff963SMatthew Dillon 	IEEE80211_NODE_STAT(ni, tx_mgmt);
2749085ff963SMatthew Dillon 
2750085ff963SMatthew Dillon 	ret = ieee80211_raw_output(vap, ni, m, &params);
2751085ff963SMatthew Dillon 	IEEE80211_TX_UNLOCK(ic);
2752085ff963SMatthew Dillon 	return (ret);
275332176cfdSRui Paulo }
275432176cfdSRui Paulo 
275532176cfdSRui Paulo #define	ADDSHORT(frm, v) do {			\
275632176cfdSRui Paulo 	frm[0] = (v) & 0xff;			\
275732176cfdSRui Paulo 	frm[1] = (v) >> 8;			\
275832176cfdSRui Paulo 	frm += 2;				\
275932176cfdSRui Paulo } while (0)
276032176cfdSRui Paulo #define	ADDWORD(frm, v) do {			\
276132176cfdSRui Paulo 	frm[0] = (v) & 0xff;			\
276232176cfdSRui Paulo 	frm[1] = ((v) >> 8) & 0xff;		\
276332176cfdSRui Paulo 	frm[2] = ((v) >> 16) & 0xff;		\
276432176cfdSRui Paulo 	frm[3] = ((v) >> 24) & 0xff;		\
276532176cfdSRui Paulo 	frm += 4;				\
276632176cfdSRui Paulo } while (0)
276732176cfdSRui Paulo 
276832176cfdSRui Paulo static int
276932176cfdSRui Paulo mesh_send_action_meshpeering_open(struct ieee80211_node *ni,
277032176cfdSRui Paulo 	int category, int action, void *args0)
277132176cfdSRui Paulo {
277232176cfdSRui Paulo 	struct ieee80211vap *vap = ni->ni_vap;
277332176cfdSRui Paulo 	struct ieee80211com *ic = ni->ni_ic;
277432176cfdSRui Paulo 	uint16_t *args = args0;
277532176cfdSRui Paulo 	const struct ieee80211_rateset *rs;
277632176cfdSRui Paulo 	struct mbuf *m;
277732176cfdSRui Paulo 	uint8_t *frm;
277832176cfdSRui Paulo 
277932176cfdSRui Paulo 	IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, ni,
278032176cfdSRui Paulo 	    "send PEER OPEN action: localid 0x%x", args[0]);
278132176cfdSRui Paulo 
278232176cfdSRui Paulo 	IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
27831e290df3SAntonio Huete Jimenez 	    "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__,
2784085ff963SMatthew Dillon 	    ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1);
278532176cfdSRui Paulo 	ieee80211_ref_node(ni);
278632176cfdSRui Paulo 
278732176cfdSRui Paulo 	m = ieee80211_getmgtframe(&frm,
278832176cfdSRui Paulo 	    ic->ic_headroom + sizeof(struct ieee80211_frame),
278932176cfdSRui Paulo 	    sizeof(uint16_t)	/* action+category */
279032176cfdSRui Paulo 	    + sizeof(uint16_t)	/* capabilites */
279132176cfdSRui Paulo 	    + 2 + IEEE80211_RATE_SIZE
279232176cfdSRui Paulo 	    + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
279332176cfdSRui Paulo 	    + 2 + IEEE80211_MESHID_LEN
279432176cfdSRui Paulo 	    + sizeof(struct ieee80211_meshconf_ie)
279532176cfdSRui Paulo 	    + sizeof(struct ieee80211_meshpeer_ie)
279632176cfdSRui Paulo 	);
279732176cfdSRui Paulo 	if (m != NULL) {
279832176cfdSRui Paulo 		/*
279932176cfdSRui Paulo 		 * mesh peer open action frame format:
280032176cfdSRui Paulo 		 *   [1] category
280132176cfdSRui Paulo 		 *   [1] action
280232176cfdSRui Paulo 		 *   [2] capabilities
280332176cfdSRui Paulo 		 *   [tlv] rates
280432176cfdSRui Paulo 		 *   [tlv] xrates
280532176cfdSRui Paulo 		 *   [tlv] mesh id
280632176cfdSRui Paulo 		 *   [tlv] mesh conf
280732176cfdSRui Paulo 		 *   [tlv] mesh peer link mgmt
280832176cfdSRui Paulo 		 */
280932176cfdSRui Paulo 		*frm++ = category;
281032176cfdSRui Paulo 		*frm++ = action;
281132176cfdSRui Paulo 		ADDSHORT(frm, ieee80211_getcapinfo(vap, ni->ni_chan));
281232176cfdSRui Paulo 		rs = ieee80211_get_suprates(ic, ic->ic_curchan);
281332176cfdSRui Paulo 		frm = ieee80211_add_rates(frm, rs);
281432176cfdSRui Paulo 		frm = ieee80211_add_xrates(frm, rs);
281532176cfdSRui Paulo 		frm = ieee80211_add_meshid(frm, vap);
281632176cfdSRui Paulo 		frm = ieee80211_add_meshconf(frm, vap);
2817085ff963SMatthew Dillon 		frm = ieee80211_add_meshpeer(frm, IEEE80211_ACTION_MESHPEERING_OPEN,
281832176cfdSRui Paulo 		    args[0], 0, 0);
281932176cfdSRui Paulo 		m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
2820085ff963SMatthew Dillon 		return mesh_send_action(ni, vap->iv_myaddr, ni->ni_macaddr, m);
282132176cfdSRui Paulo 	} else {
282232176cfdSRui Paulo 		vap->iv_stats.is_tx_nobuf++;
282332176cfdSRui Paulo 		ieee80211_free_node(ni);
282432176cfdSRui Paulo 		return ENOMEM;
282532176cfdSRui Paulo 	}
282632176cfdSRui Paulo }
282732176cfdSRui Paulo 
282832176cfdSRui Paulo static int
282932176cfdSRui Paulo mesh_send_action_meshpeering_confirm(struct ieee80211_node *ni,
283032176cfdSRui Paulo 	int category, int action, void *args0)
283132176cfdSRui Paulo {
283232176cfdSRui Paulo 	struct ieee80211vap *vap = ni->ni_vap;
283332176cfdSRui Paulo 	struct ieee80211com *ic = ni->ni_ic;
283432176cfdSRui Paulo 	uint16_t *args = args0;
283532176cfdSRui Paulo 	const struct ieee80211_rateset *rs;
283632176cfdSRui Paulo 	struct mbuf *m;
283732176cfdSRui Paulo 	uint8_t *frm;
283832176cfdSRui Paulo 
283932176cfdSRui Paulo 	IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, ni,
284032176cfdSRui Paulo 	    "send PEER CONFIRM action: localid 0x%x, peerid 0x%x",
284132176cfdSRui Paulo 	    args[0], args[1]);
284232176cfdSRui Paulo 
284332176cfdSRui Paulo 	IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
28441e290df3SAntonio Huete Jimenez 	    "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__,
2845085ff963SMatthew Dillon 	    ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1);
284632176cfdSRui Paulo 	ieee80211_ref_node(ni);
284732176cfdSRui Paulo 
284832176cfdSRui Paulo 	m = ieee80211_getmgtframe(&frm,
284932176cfdSRui Paulo 	    ic->ic_headroom + sizeof(struct ieee80211_frame),
285032176cfdSRui Paulo 	    sizeof(uint16_t)	/* action+category */
285132176cfdSRui Paulo 	    + sizeof(uint16_t)	/* capabilites */
285232176cfdSRui Paulo 	    + sizeof(uint16_t)	/* status code */
285332176cfdSRui Paulo 	    + sizeof(uint16_t)	/* AID */
285432176cfdSRui Paulo 	    + 2 + IEEE80211_RATE_SIZE
285532176cfdSRui Paulo 	    + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
285632176cfdSRui Paulo 	    + 2 + IEEE80211_MESHID_LEN
285732176cfdSRui Paulo 	    + sizeof(struct ieee80211_meshconf_ie)
285832176cfdSRui Paulo 	    + sizeof(struct ieee80211_meshpeer_ie)
285932176cfdSRui Paulo 	);
286032176cfdSRui Paulo 	if (m != NULL) {
286132176cfdSRui Paulo 		/*
286232176cfdSRui Paulo 		 * mesh peer confirm action frame format:
286332176cfdSRui Paulo 		 *   [1] category
286432176cfdSRui Paulo 		 *   [1] action
286532176cfdSRui Paulo 		 *   [2] capabilities
286632176cfdSRui Paulo 		 *   [2] status code
286732176cfdSRui Paulo 		 *   [2] association id (peer ID)
286832176cfdSRui Paulo 		 *   [tlv] rates
286932176cfdSRui Paulo 		 *   [tlv] xrates
287032176cfdSRui Paulo 		 *   [tlv] mesh id
287132176cfdSRui Paulo 		 *   [tlv] mesh conf
287232176cfdSRui Paulo 		 *   [tlv] mesh peer link mgmt
287332176cfdSRui Paulo 		 */
287432176cfdSRui Paulo 		*frm++ = category;
287532176cfdSRui Paulo 		*frm++ = action;
287632176cfdSRui Paulo 		ADDSHORT(frm, ieee80211_getcapinfo(vap, ni->ni_chan));
287732176cfdSRui Paulo 		ADDSHORT(frm, 0);		/* status code */
287832176cfdSRui Paulo 		ADDSHORT(frm, args[1]);		/* AID */
287932176cfdSRui Paulo 		rs = ieee80211_get_suprates(ic, ic->ic_curchan);
288032176cfdSRui Paulo 		frm = ieee80211_add_rates(frm, rs);
288132176cfdSRui Paulo 		frm = ieee80211_add_xrates(frm, rs);
288232176cfdSRui Paulo 		frm = ieee80211_add_meshid(frm, vap);
288332176cfdSRui Paulo 		frm = ieee80211_add_meshconf(frm, vap);
288432176cfdSRui Paulo 		frm = ieee80211_add_meshpeer(frm,
2885085ff963SMatthew Dillon 		    IEEE80211_ACTION_MESHPEERING_CONFIRM,
288632176cfdSRui Paulo 		    args[0], args[1], 0);
288732176cfdSRui Paulo 		m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
2888085ff963SMatthew Dillon 		return mesh_send_action(ni, vap->iv_myaddr, ni->ni_macaddr, m);
288932176cfdSRui Paulo 	} else {
289032176cfdSRui Paulo 		vap->iv_stats.is_tx_nobuf++;
289132176cfdSRui Paulo 		ieee80211_free_node(ni);
289232176cfdSRui Paulo 		return ENOMEM;
289332176cfdSRui Paulo 	}
289432176cfdSRui Paulo }
289532176cfdSRui Paulo 
289632176cfdSRui Paulo static int
289732176cfdSRui Paulo mesh_send_action_meshpeering_close(struct ieee80211_node *ni,
289832176cfdSRui Paulo 	int category, int action, void *args0)
289932176cfdSRui Paulo {
290032176cfdSRui Paulo 	struct ieee80211vap *vap = ni->ni_vap;
290132176cfdSRui Paulo 	struct ieee80211com *ic = ni->ni_ic;
290232176cfdSRui Paulo 	uint16_t *args = args0;
290332176cfdSRui Paulo 	struct mbuf *m;
290432176cfdSRui Paulo 	uint8_t *frm;
290532176cfdSRui Paulo 
290632176cfdSRui Paulo 	IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, ni,
290732176cfdSRui Paulo 	    "send PEER CLOSE action: localid 0x%x, peerid 0x%x reason %d",
290832176cfdSRui Paulo 	    args[0], args[1], args[2]);
290932176cfdSRui Paulo 
291032176cfdSRui Paulo 	IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
29111e290df3SAntonio Huete Jimenez 	    "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__,
2912085ff963SMatthew Dillon 	    ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1);
291332176cfdSRui Paulo 	ieee80211_ref_node(ni);
291432176cfdSRui Paulo 
291532176cfdSRui Paulo 	m = ieee80211_getmgtframe(&frm,
291632176cfdSRui Paulo 	    ic->ic_headroom + sizeof(struct ieee80211_frame),
291732176cfdSRui Paulo 	    sizeof(uint16_t)	/* action+category */
291832176cfdSRui Paulo 	    + sizeof(uint16_t)	/* reason code */
291932176cfdSRui Paulo 	    + 2 + IEEE80211_MESHID_LEN
292032176cfdSRui Paulo 	    + sizeof(struct ieee80211_meshpeer_ie)
292132176cfdSRui Paulo 	);
292232176cfdSRui Paulo 	if (m != NULL) {
292332176cfdSRui Paulo 		/*
292432176cfdSRui Paulo 		 * mesh peer close action frame format:
292532176cfdSRui Paulo 		 *   [1] category
292632176cfdSRui Paulo 		 *   [1] action
292732176cfdSRui Paulo 		 *   [tlv] mesh id
292832176cfdSRui Paulo 		 *   [tlv] mesh peer link mgmt
292932176cfdSRui Paulo 		 */
293032176cfdSRui Paulo 		*frm++ = category;
293132176cfdSRui Paulo 		*frm++ = action;
293232176cfdSRui Paulo 		frm = ieee80211_add_meshid(frm, vap);
293332176cfdSRui Paulo 		frm = ieee80211_add_meshpeer(frm,
2934085ff963SMatthew Dillon 		    IEEE80211_ACTION_MESHPEERING_CLOSE,
293532176cfdSRui Paulo 		    args[0], args[1], args[2]);
293632176cfdSRui Paulo 		m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
2937085ff963SMatthew Dillon 		return mesh_send_action(ni, vap->iv_myaddr, ni->ni_macaddr, m);
293832176cfdSRui Paulo 	} else {
293932176cfdSRui Paulo 		vap->iv_stats.is_tx_nobuf++;
294032176cfdSRui Paulo 		ieee80211_free_node(ni);
294132176cfdSRui Paulo 		return ENOMEM;
294232176cfdSRui Paulo 	}
294332176cfdSRui Paulo }
294432176cfdSRui Paulo 
294532176cfdSRui Paulo static int
2946085ff963SMatthew Dillon mesh_send_action_meshlmetric(struct ieee80211_node *ni,
294732176cfdSRui Paulo 	int category, int action, void *arg0)
294832176cfdSRui Paulo {
294932176cfdSRui Paulo 	struct ieee80211vap *vap = ni->ni_vap;
295032176cfdSRui Paulo 	struct ieee80211com *ic = ni->ni_ic;
2951085ff963SMatthew Dillon 	struct ieee80211_meshlmetric_ie *ie = arg0;
295232176cfdSRui Paulo 	struct mbuf *m;
295332176cfdSRui Paulo 	uint8_t *frm;
295432176cfdSRui Paulo 
2955085ff963SMatthew Dillon 	if (ie->lm_flags & IEEE80211_MESH_LMETRIC_FLAGS_REQ) {
2956085ff963SMatthew Dillon 		IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH,
2957085ff963SMatthew Dillon 		    ni, "%s", "send LINK METRIC REQUEST action");
295832176cfdSRui Paulo 	} else {
2959085ff963SMatthew Dillon 		IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH,
2960085ff963SMatthew Dillon 		    ni, "send LINK METRIC REPLY action: metric 0x%x",
2961085ff963SMatthew Dillon 		    ie->lm_metric);
296232176cfdSRui Paulo 	}
296332176cfdSRui Paulo 	IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
29641e290df3SAntonio Huete Jimenez 	    "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__,
2965085ff963SMatthew Dillon 	    ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1);
296632176cfdSRui Paulo 	ieee80211_ref_node(ni);
296732176cfdSRui Paulo 
296832176cfdSRui Paulo 	m = ieee80211_getmgtframe(&frm,
296932176cfdSRui Paulo 	    ic->ic_headroom + sizeof(struct ieee80211_frame),
2970085ff963SMatthew Dillon 	    sizeof(uint16_t) +	/* action+category */
2971085ff963SMatthew Dillon 	    sizeof(struct ieee80211_meshlmetric_ie)
297232176cfdSRui Paulo 	);
297332176cfdSRui Paulo 	if (m != NULL) {
297432176cfdSRui Paulo 		/*
2975085ff963SMatthew Dillon 		 * mesh link metric
297632176cfdSRui Paulo 		 *   [1] category
297732176cfdSRui Paulo 		 *   [1] action
297832176cfdSRui Paulo 		 *   [tlv] mesh link metric
297932176cfdSRui Paulo 		 */
298032176cfdSRui Paulo 		*frm++ = category;
298132176cfdSRui Paulo 		*frm++ = action;
2982085ff963SMatthew Dillon 		frm = ieee80211_add_meshlmetric(frm,
2983085ff963SMatthew Dillon 		    ie->lm_flags, ie->lm_metric);
298432176cfdSRui Paulo 		m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
2985085ff963SMatthew Dillon 		return mesh_send_action(ni, vap->iv_myaddr, ni->ni_macaddr, m);
2986085ff963SMatthew Dillon 	} else {
2987085ff963SMatthew Dillon 		vap->iv_stats.is_tx_nobuf++;
2988085ff963SMatthew Dillon 		ieee80211_free_node(ni);
2989085ff963SMatthew Dillon 		return ENOMEM;
2990085ff963SMatthew Dillon 	}
2991085ff963SMatthew Dillon }
2992085ff963SMatthew Dillon 
2993085ff963SMatthew Dillon static int
2994085ff963SMatthew Dillon mesh_send_action_meshgate(struct ieee80211_node *ni,
2995085ff963SMatthew Dillon 	int category, int action, void *arg0)
2996085ff963SMatthew Dillon {
2997085ff963SMatthew Dillon 	struct ieee80211vap *vap = ni->ni_vap;
2998085ff963SMatthew Dillon 	struct ieee80211com *ic = ni->ni_ic;
2999085ff963SMatthew Dillon 	struct ieee80211_meshgann_ie *ie = arg0;
3000085ff963SMatthew Dillon 	struct mbuf *m;
3001085ff963SMatthew Dillon 	uint8_t *frm;
3002085ff963SMatthew Dillon 
3003085ff963SMatthew Dillon 	IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
3004085ff963SMatthew Dillon 	    "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__,
3005085ff963SMatthew Dillon 	    ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1);
3006085ff963SMatthew Dillon 	ieee80211_ref_node(ni);
3007085ff963SMatthew Dillon 
3008085ff963SMatthew Dillon 	m = ieee80211_getmgtframe(&frm,
3009085ff963SMatthew Dillon 	    ic->ic_headroom + sizeof(struct ieee80211_frame),
3010085ff963SMatthew Dillon 	    sizeof(uint16_t) +	/* action+category */
3011085ff963SMatthew Dillon 	    IEEE80211_MESHGANN_BASE_SZ
3012085ff963SMatthew Dillon 	);
3013085ff963SMatthew Dillon 	if (m != NULL) {
3014085ff963SMatthew Dillon 		/*
3015085ff963SMatthew Dillon 		 * mesh link metric
3016085ff963SMatthew Dillon 		 *   [1] category
3017085ff963SMatthew Dillon 		 *   [1] action
3018085ff963SMatthew Dillon 		 *   [tlv] mesh gate annoucement
3019085ff963SMatthew Dillon 		 */
3020085ff963SMatthew Dillon 		*frm++ = category;
3021085ff963SMatthew Dillon 		*frm++ = action;
3022085ff963SMatthew Dillon 		frm = ieee80211_add_meshgate(frm, ie);
3023085ff963SMatthew Dillon 		m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
3024085ff963SMatthew Dillon 		return mesh_send_action(ni, vap->iv_myaddr, broadcastaddr, m);
302532176cfdSRui Paulo 	} else {
302632176cfdSRui Paulo 		vap->iv_stats.is_tx_nobuf++;
302732176cfdSRui Paulo 		ieee80211_free_node(ni);
302832176cfdSRui Paulo 		return ENOMEM;
302932176cfdSRui Paulo 	}
303032176cfdSRui Paulo }
303132176cfdSRui Paulo 
303232176cfdSRui Paulo static void
303332176cfdSRui Paulo mesh_peer_timeout_setup(struct ieee80211_node *ni)
303432176cfdSRui Paulo {
303532176cfdSRui Paulo 	switch (ni->ni_mlstate) {
303632176cfdSRui Paulo 	case IEEE80211_NODE_MESH_HOLDING:
303732176cfdSRui Paulo 		ni->ni_mltval = ieee80211_mesh_holdingtimeout;
303832176cfdSRui Paulo 		break;
303932176cfdSRui Paulo 	case IEEE80211_NODE_MESH_CONFIRMRCV:
304032176cfdSRui Paulo 		ni->ni_mltval = ieee80211_mesh_confirmtimeout;
304132176cfdSRui Paulo 		break;
304232176cfdSRui Paulo 	case IEEE80211_NODE_MESH_IDLE:
304332176cfdSRui Paulo 		ni->ni_mltval = 0;
304432176cfdSRui Paulo 		break;
304532176cfdSRui Paulo 	default:
304632176cfdSRui Paulo 		ni->ni_mltval = ieee80211_mesh_retrytimeout;
304732176cfdSRui Paulo 		break;
304832176cfdSRui Paulo 	}
3049085ff963SMatthew Dillon 	if (ni->ni_mltval)
305032176cfdSRui Paulo 		callout_reset(&ni->ni_mltimer, ni->ni_mltval,
3051085ff963SMatthew Dillon 		    mesh_peer_timeout_cb, ni);
305232176cfdSRui Paulo }
305332176cfdSRui Paulo 
305432176cfdSRui Paulo /*
305532176cfdSRui Paulo  * Same as above but backoffs timer statisically 50%.
305632176cfdSRui Paulo  */
305732176cfdSRui Paulo static void
305832176cfdSRui Paulo mesh_peer_timeout_backoff(struct ieee80211_node *ni)
305932176cfdSRui Paulo {
306032176cfdSRui Paulo 	uint32_t r;
306132176cfdSRui Paulo 
3062085ff963SMatthew Dillon 	r = arc4random();
306332176cfdSRui Paulo 	ni->ni_mltval += r % ni->ni_mltval;
3064085ff963SMatthew Dillon 	callout_reset(&ni->ni_mltimer, ni->ni_mltval, mesh_peer_timeout_cb,
3065085ff963SMatthew Dillon 	    ni);
306632176cfdSRui Paulo }
306732176cfdSRui Paulo 
306832176cfdSRui Paulo static __inline void
306932176cfdSRui Paulo mesh_peer_timeout_stop(struct ieee80211_node *ni)
307032176cfdSRui Paulo {
3071085ff963SMatthew Dillon 	callout_drain(&ni->ni_mltimer);
3072085ff963SMatthew Dillon }
3073085ff963SMatthew Dillon 
3074085ff963SMatthew Dillon static void
3075085ff963SMatthew Dillon mesh_peer_backoff_cb(void *arg)
3076085ff963SMatthew Dillon {
3077085ff963SMatthew Dillon 	struct ieee80211_node *ni = (struct ieee80211_node *)arg;
3078085ff963SMatthew Dillon 
3079085ff963SMatthew Dillon 	/* After backoff timeout, try to peer automatically again. */
3080085ff963SMatthew Dillon 	ni->ni_mlhcnt = 0;
308132176cfdSRui Paulo }
308232176cfdSRui Paulo 
308332176cfdSRui Paulo /*
308432176cfdSRui Paulo  * Mesh Peer Link Management FSM timeout handling.
308532176cfdSRui Paulo  */
308632176cfdSRui Paulo static void
3087085ff963SMatthew Dillon mesh_peer_timeout_cb(void *arg)
308832176cfdSRui Paulo {
308932176cfdSRui Paulo 	struct ieee80211_node *ni = (struct ieee80211_node *)arg;
309032176cfdSRui Paulo 	uint16_t args[3];
309132176cfdSRui Paulo 
309232176cfdSRui Paulo 	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_MESH,
309332176cfdSRui Paulo 	    ni, "mesh link timeout, state %d, retry counter %d",
309432176cfdSRui Paulo 	    ni->ni_mlstate, ni->ni_mlrcnt);
309532176cfdSRui Paulo 
309632176cfdSRui Paulo 	switch (ni->ni_mlstate) {
309732176cfdSRui Paulo 	case IEEE80211_NODE_MESH_IDLE:
309832176cfdSRui Paulo 	case IEEE80211_NODE_MESH_ESTABLISHED:
309932176cfdSRui Paulo 		break;
310032176cfdSRui Paulo 	case IEEE80211_NODE_MESH_OPENSNT:
310132176cfdSRui Paulo 	case IEEE80211_NODE_MESH_OPENRCV:
310232176cfdSRui Paulo 		if (ni->ni_mlrcnt == ieee80211_mesh_maxretries) {
310332176cfdSRui Paulo 			args[0] = ni->ni_mlpid;
310432176cfdSRui Paulo 			args[2] = IEEE80211_REASON_MESH_MAX_RETRIES;
310532176cfdSRui Paulo 			ieee80211_send_action(ni,
3106085ff963SMatthew Dillon 			    IEEE80211_ACTION_CAT_SELF_PROT,
310732176cfdSRui Paulo 			    IEEE80211_ACTION_MESHPEERING_CLOSE, args);
310832176cfdSRui Paulo 			ni->ni_mlrcnt = 0;
310932176cfdSRui Paulo 			mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING);
311032176cfdSRui Paulo 			mesh_peer_timeout_setup(ni);
311132176cfdSRui Paulo 		} else {
311232176cfdSRui Paulo 			args[0] = ni->ni_mlpid;
311332176cfdSRui Paulo 			ieee80211_send_action(ni,
3114085ff963SMatthew Dillon 			    IEEE80211_ACTION_CAT_SELF_PROT,
311532176cfdSRui Paulo 			    IEEE80211_ACTION_MESHPEERING_OPEN, args);
311632176cfdSRui Paulo 			ni->ni_mlrcnt++;
311732176cfdSRui Paulo 			mesh_peer_timeout_backoff(ni);
311832176cfdSRui Paulo 		}
311932176cfdSRui Paulo 		break;
312032176cfdSRui Paulo 	case IEEE80211_NODE_MESH_CONFIRMRCV:
312132176cfdSRui Paulo 		args[0] = ni->ni_mlpid;
312232176cfdSRui Paulo 		args[2] = IEEE80211_REASON_MESH_CONFIRM_TIMEOUT;
312332176cfdSRui Paulo 		ieee80211_send_action(ni,
3124085ff963SMatthew Dillon 		    IEEE80211_ACTION_CAT_SELF_PROT,
312532176cfdSRui Paulo 		    IEEE80211_ACTION_MESHPEERING_CLOSE, args);
312632176cfdSRui Paulo 		mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING);
312732176cfdSRui Paulo 		mesh_peer_timeout_setup(ni);
312832176cfdSRui Paulo 		break;
312932176cfdSRui Paulo 	case IEEE80211_NODE_MESH_HOLDING:
3130085ff963SMatthew Dillon 		ni->ni_mlhcnt++;
3131085ff963SMatthew Dillon 		if (ni->ni_mlhcnt >= ieee80211_mesh_maxholding)
3132085ff963SMatthew Dillon 			callout_reset(&ni->ni_mlhtimer,
3133085ff963SMatthew Dillon 			    ieee80211_mesh_backofftimeout,
3134085ff963SMatthew Dillon 			    mesh_peer_backoff_cb, ni);
313532176cfdSRui Paulo 		mesh_linkchange(ni, IEEE80211_NODE_MESH_IDLE);
313632176cfdSRui Paulo 		break;
313732176cfdSRui Paulo 	}
313832176cfdSRui Paulo }
313932176cfdSRui Paulo 
314032176cfdSRui Paulo static int
314132176cfdSRui Paulo mesh_verify_meshid(struct ieee80211vap *vap, const uint8_t *ie)
314232176cfdSRui Paulo {
314332176cfdSRui Paulo 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
314432176cfdSRui Paulo 
314532176cfdSRui Paulo 	if (ie == NULL || ie[1] != ms->ms_idlen)
314632176cfdSRui Paulo 		return 1;
314732176cfdSRui Paulo 	return memcmp(ms->ms_id, ie + 2, ms->ms_idlen);
314832176cfdSRui Paulo }
314932176cfdSRui Paulo 
315032176cfdSRui Paulo /*
315132176cfdSRui Paulo  * Check if we are using the same algorithms for this mesh.
315232176cfdSRui Paulo  */
315332176cfdSRui Paulo static int
315432176cfdSRui Paulo mesh_verify_meshconf(struct ieee80211vap *vap, const uint8_t *ie)
315532176cfdSRui Paulo {
315632176cfdSRui Paulo 	const struct ieee80211_meshconf_ie *meshconf =
315732176cfdSRui Paulo 	    (const struct ieee80211_meshconf_ie *) ie;
315832176cfdSRui Paulo 	const struct ieee80211_mesh_state *ms = vap->iv_mesh;
315932176cfdSRui Paulo 
316032176cfdSRui Paulo 	if (meshconf == NULL)
316132176cfdSRui Paulo 		return 1;
316232176cfdSRui Paulo 	if (meshconf->conf_pselid != ms->ms_ppath->mpp_ie) {
316332176cfdSRui Paulo 		IEEE80211_DPRINTF(vap, IEEE80211_MSG_MESH,
316432176cfdSRui Paulo 		    "unknown path selection algorithm: 0x%x\n",
316532176cfdSRui Paulo 		    meshconf->conf_pselid);
316632176cfdSRui Paulo 		return 1;
316732176cfdSRui Paulo 	}
316832176cfdSRui Paulo 	if (meshconf->conf_pmetid != ms->ms_pmetric->mpm_ie) {
316932176cfdSRui Paulo 		IEEE80211_DPRINTF(vap, IEEE80211_MSG_MESH,
317032176cfdSRui Paulo 		    "unknown path metric algorithm: 0x%x\n",
317132176cfdSRui Paulo 		    meshconf->conf_pmetid);
317232176cfdSRui Paulo 		return 1;
317332176cfdSRui Paulo 	}
317432176cfdSRui Paulo 	if (meshconf->conf_ccid != 0) {
317532176cfdSRui Paulo 		IEEE80211_DPRINTF(vap, IEEE80211_MSG_MESH,
317632176cfdSRui Paulo 		    "unknown congestion control algorithm: 0x%x\n",
317732176cfdSRui Paulo 		    meshconf->conf_ccid);
317832176cfdSRui Paulo 		return 1;
317932176cfdSRui Paulo 	}
318032176cfdSRui Paulo 	if (meshconf->conf_syncid != IEEE80211_MESHCONF_SYNC_NEIGHOFF) {
318132176cfdSRui Paulo 		IEEE80211_DPRINTF(vap, IEEE80211_MSG_MESH,
318232176cfdSRui Paulo 		    "unknown sync algorithm: 0x%x\n",
318332176cfdSRui Paulo 		    meshconf->conf_syncid);
318432176cfdSRui Paulo 		return 1;
318532176cfdSRui Paulo 	}
318632176cfdSRui Paulo 	if (meshconf->conf_authid != 0) {
318732176cfdSRui Paulo 		IEEE80211_DPRINTF(vap, IEEE80211_MSG_MESH,
3188085ff963SMatthew Dillon 		    "unknown auth auth algorithm: 0x%x\n",
318932176cfdSRui Paulo 		    meshconf->conf_pselid);
319032176cfdSRui Paulo 		return 1;
319132176cfdSRui Paulo 	}
319232176cfdSRui Paulo 	/* Not accepting peers */
3193085ff963SMatthew Dillon 	if (!(meshconf->conf_cap & IEEE80211_MESHCONF_CAP_AP)) {
319432176cfdSRui Paulo 		IEEE80211_DPRINTF(vap, IEEE80211_MSG_MESH,
319532176cfdSRui Paulo 		    "not accepting peers: 0x%x\n", meshconf->conf_cap);
319632176cfdSRui Paulo 		return 1;
319732176cfdSRui Paulo 	}
319832176cfdSRui Paulo 	return 0;
319932176cfdSRui Paulo }
320032176cfdSRui Paulo 
320132176cfdSRui Paulo static int
320232176cfdSRui Paulo mesh_verify_meshpeer(struct ieee80211vap *vap, uint8_t subtype,
320332176cfdSRui Paulo     const uint8_t *ie)
320432176cfdSRui Paulo {
320532176cfdSRui Paulo 	const struct ieee80211_meshpeer_ie *meshpeer =
320632176cfdSRui Paulo 	    (const struct ieee80211_meshpeer_ie *) ie;
320732176cfdSRui Paulo 
3208085ff963SMatthew Dillon 	if (meshpeer == NULL ||
3209085ff963SMatthew Dillon 	    meshpeer->peer_len < IEEE80211_MPM_BASE_SZ ||
3210085ff963SMatthew Dillon 	    meshpeer->peer_len > IEEE80211_MPM_MAX_SZ)
321132176cfdSRui Paulo 		return 1;
3212085ff963SMatthew Dillon 	if (meshpeer->peer_proto != IEEE80211_MPPID_MPM) {
3213085ff963SMatthew Dillon 		IEEE80211_DPRINTF(vap,
3214085ff963SMatthew Dillon 		    IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH,
3215085ff963SMatthew Dillon 		    "Only MPM protocol is supported (proto: 0x%02X)",
3216085ff963SMatthew Dillon 		    meshpeer->peer_proto);
3217085ff963SMatthew Dillon 		return 1;
3218085ff963SMatthew Dillon 	}
321932176cfdSRui Paulo 	switch (subtype) {
3220085ff963SMatthew Dillon 	case IEEE80211_ACTION_MESHPEERING_OPEN:
3221085ff963SMatthew Dillon 		if (meshpeer->peer_len != IEEE80211_MPM_BASE_SZ)
322232176cfdSRui Paulo 			return 1;
322332176cfdSRui Paulo 		break;
3224085ff963SMatthew Dillon 	case IEEE80211_ACTION_MESHPEERING_CONFIRM:
3225085ff963SMatthew Dillon 		if (meshpeer->peer_len != IEEE80211_MPM_BASE_SZ + 2)
322632176cfdSRui Paulo 			return 1;
322732176cfdSRui Paulo 		break;
3228085ff963SMatthew Dillon 	case IEEE80211_ACTION_MESHPEERING_CLOSE:
3229085ff963SMatthew Dillon 		if (meshpeer->peer_len < IEEE80211_MPM_BASE_SZ + 2)
323032176cfdSRui Paulo 			return 1;
3231085ff963SMatthew Dillon 		if (meshpeer->peer_len == (IEEE80211_MPM_BASE_SZ + 2) &&
3232085ff963SMatthew Dillon 		    meshpeer->peer_linkid != 0)
323332176cfdSRui Paulo 			return 1;
323432176cfdSRui Paulo 		if (meshpeer->peer_rcode == 0)
323532176cfdSRui Paulo 			return 1;
323632176cfdSRui Paulo 		break;
323732176cfdSRui Paulo 	}
323832176cfdSRui Paulo 	return 0;
323932176cfdSRui Paulo }
324032176cfdSRui Paulo 
324132176cfdSRui Paulo /*
324232176cfdSRui Paulo  * Add a Mesh ID IE to a frame.
324332176cfdSRui Paulo  */
324432176cfdSRui Paulo uint8_t *
324532176cfdSRui Paulo ieee80211_add_meshid(uint8_t *frm, struct ieee80211vap *vap)
324632176cfdSRui Paulo {
324732176cfdSRui Paulo 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
324832176cfdSRui Paulo 
324932176cfdSRui Paulo 	KASSERT(vap->iv_opmode == IEEE80211_M_MBSS, ("not a mbss vap"));
325032176cfdSRui Paulo 
325132176cfdSRui Paulo 	*frm++ = IEEE80211_ELEMID_MESHID;
325232176cfdSRui Paulo 	*frm++ = ms->ms_idlen;
325332176cfdSRui Paulo 	memcpy(frm, ms->ms_id, ms->ms_idlen);
325432176cfdSRui Paulo 	return frm + ms->ms_idlen;
325532176cfdSRui Paulo }
325632176cfdSRui Paulo 
325732176cfdSRui Paulo /*
325832176cfdSRui Paulo  * Add a Mesh Configuration IE to a frame.
325932176cfdSRui Paulo  * For now just use HWMP routing, Airtime link metric, Null Congestion
326032176cfdSRui Paulo  * Signaling, Null Sync Protocol and Null Authentication.
326132176cfdSRui Paulo  */
326232176cfdSRui Paulo uint8_t *
326332176cfdSRui Paulo ieee80211_add_meshconf(uint8_t *frm, struct ieee80211vap *vap)
326432176cfdSRui Paulo {
326532176cfdSRui Paulo 	const struct ieee80211_mesh_state *ms = vap->iv_mesh;
326632176cfdSRui Paulo 	uint16_t caps;
326732176cfdSRui Paulo 
326832176cfdSRui Paulo 	KASSERT(vap->iv_opmode == IEEE80211_M_MBSS, ("not a MBSS vap"));
326932176cfdSRui Paulo 
327032176cfdSRui Paulo 	*frm++ = IEEE80211_ELEMID_MESHCONF;
3271085ff963SMatthew Dillon 	*frm++ = IEEE80211_MESH_CONF_SZ;
327232176cfdSRui Paulo 	*frm++ = ms->ms_ppath->mpp_ie;		/* path selection */
327332176cfdSRui Paulo 	*frm++ = ms->ms_pmetric->mpm_ie;	/* link metric */
327432176cfdSRui Paulo 	*frm++ = IEEE80211_MESHCONF_CC_DISABLED;
327532176cfdSRui Paulo 	*frm++ = IEEE80211_MESHCONF_SYNC_NEIGHOFF;
327632176cfdSRui Paulo 	*frm++ = IEEE80211_MESHCONF_AUTH_DISABLED;
327732176cfdSRui Paulo 	/* NB: set the number of neighbors before the rest */
3278085ff963SMatthew Dillon 	*frm = (ms->ms_neighbors > IEEE80211_MESH_MAX_NEIGHBORS ?
3279085ff963SMatthew Dillon 	    IEEE80211_MESH_MAX_NEIGHBORS : ms->ms_neighbors) << 1;
3280085ff963SMatthew Dillon 	if (ms->ms_flags & IEEE80211_MESHFLAGS_GATE)
3281085ff963SMatthew Dillon 		*frm |= IEEE80211_MESHCONF_FORM_GATE;
328232176cfdSRui Paulo 	frm += 1;
328332176cfdSRui Paulo 	caps = 0;
328432176cfdSRui Paulo 	if (ms->ms_flags & IEEE80211_MESHFLAGS_AP)
328532176cfdSRui Paulo 		caps |= IEEE80211_MESHCONF_CAP_AP;
328632176cfdSRui Paulo 	if (ms->ms_flags & IEEE80211_MESHFLAGS_FWD)
328732176cfdSRui Paulo 		caps |= IEEE80211_MESHCONF_CAP_FWRD;
3288085ff963SMatthew Dillon 	*frm++ = caps;
328932176cfdSRui Paulo 	return frm;
329032176cfdSRui Paulo }
329132176cfdSRui Paulo 
329232176cfdSRui Paulo /*
329332176cfdSRui Paulo  * Add a Mesh Peer Management IE to a frame.
329432176cfdSRui Paulo  */
329532176cfdSRui Paulo uint8_t *
329632176cfdSRui Paulo ieee80211_add_meshpeer(uint8_t *frm, uint8_t subtype, uint16_t localid,
329732176cfdSRui Paulo     uint16_t peerid, uint16_t reason)
329832176cfdSRui Paulo {
329932176cfdSRui Paulo 
330032176cfdSRui Paulo 	KASSERT(localid != 0, ("localid == 0"));
330132176cfdSRui Paulo 
330232176cfdSRui Paulo 	*frm++ = IEEE80211_ELEMID_MESHPEER;
330332176cfdSRui Paulo 	switch (subtype) {
3304085ff963SMatthew Dillon 	case IEEE80211_ACTION_MESHPEERING_OPEN:
3305085ff963SMatthew Dillon 		*frm++ = IEEE80211_MPM_BASE_SZ;		/* length */
3306085ff963SMatthew Dillon 		ADDSHORT(frm, IEEE80211_MPPID_MPM);	/* proto */
330732176cfdSRui Paulo 		ADDSHORT(frm, localid);			/* local ID */
330832176cfdSRui Paulo 		break;
3309085ff963SMatthew Dillon 	case IEEE80211_ACTION_MESHPEERING_CONFIRM:
331032176cfdSRui Paulo 		KASSERT(peerid != 0, ("sending peer confirm without peer id"));
3311085ff963SMatthew Dillon 		*frm++ = IEEE80211_MPM_BASE_SZ + 2;	/* length */
3312085ff963SMatthew Dillon 		ADDSHORT(frm, IEEE80211_MPPID_MPM);	/* proto */
331332176cfdSRui Paulo 		ADDSHORT(frm, localid);			/* local ID */
331432176cfdSRui Paulo 		ADDSHORT(frm, peerid);			/* peer ID */
331532176cfdSRui Paulo 		break;
3316085ff963SMatthew Dillon 	case IEEE80211_ACTION_MESHPEERING_CLOSE:
331732176cfdSRui Paulo 		if (peerid)
3318085ff963SMatthew Dillon 			*frm++ = IEEE80211_MPM_MAX_SZ;	/* length */
331932176cfdSRui Paulo 		else
3320085ff963SMatthew Dillon 			*frm++ = IEEE80211_MPM_BASE_SZ + 2; /* length */
3321085ff963SMatthew Dillon 		ADDSHORT(frm, IEEE80211_MPPID_MPM);	/* proto */
332232176cfdSRui Paulo 		ADDSHORT(frm, localid);	/* local ID */
332332176cfdSRui Paulo 		if (peerid)
332432176cfdSRui Paulo 			ADDSHORT(frm, peerid);	/* peer ID */
332532176cfdSRui Paulo 		ADDSHORT(frm, reason);
332632176cfdSRui Paulo 		break;
332732176cfdSRui Paulo 	}
332832176cfdSRui Paulo 	return frm;
332932176cfdSRui Paulo }
333032176cfdSRui Paulo 
333132176cfdSRui Paulo /*
333232176cfdSRui Paulo  * Compute an Airtime Link Metric for the link with this node.
333332176cfdSRui Paulo  *
333432176cfdSRui Paulo  * Based on Draft 3.0 spec (11B.10, p.149).
333532176cfdSRui Paulo  */
333632176cfdSRui Paulo /*
333732176cfdSRui Paulo  * Max 802.11s overhead.
333832176cfdSRui Paulo  */
333932176cfdSRui Paulo #define IEEE80211_MESH_MAXOVERHEAD \
334032176cfdSRui Paulo 	(sizeof(struct ieee80211_qosframe_addr4) \
3341085ff963SMatthew Dillon 	 + sizeof(struct ieee80211_meshcntl_ae10) \
334232176cfdSRui Paulo 	+ sizeof(struct llc) \
334332176cfdSRui Paulo 	+ IEEE80211_ADDR_LEN \
334432176cfdSRui Paulo 	+ IEEE80211_WEP_IVLEN \
334532176cfdSRui Paulo 	+ IEEE80211_WEP_KIDLEN \
334632176cfdSRui Paulo 	+ IEEE80211_WEP_CRCLEN \
334732176cfdSRui Paulo 	+ IEEE80211_WEP_MICLEN \
334832176cfdSRui Paulo 	+ IEEE80211_CRC_LEN)
334932176cfdSRui Paulo uint32_t
335032176cfdSRui Paulo mesh_airtime_calc(struct ieee80211_node *ni)
335132176cfdSRui Paulo {
335232176cfdSRui Paulo #define M_BITS 8
335332176cfdSRui Paulo #define S_FACTOR (2 * M_BITS)
335432176cfdSRui Paulo 	struct ieee80211com *ic = ni->ni_ic;
335532176cfdSRui Paulo 	struct ifnet *ifp = ni->ni_vap->iv_ifp;
3356db11cb20SSascha Wildner 	static const int nbits = 8192 << M_BITS;
335732176cfdSRui Paulo 	uint32_t overhead, rate, errrate;
335832176cfdSRui Paulo 	uint64_t res;
335932176cfdSRui Paulo 
336032176cfdSRui Paulo 	/* Time to transmit a frame */
336132176cfdSRui Paulo 	rate = ni->ni_txrate;
336232176cfdSRui Paulo 	overhead = ieee80211_compute_duration(ic->ic_rt,
336332176cfdSRui Paulo 	    ifp->if_mtu + IEEE80211_MESH_MAXOVERHEAD, rate, 0) << M_BITS;
336432176cfdSRui Paulo 	/* Error rate in percentage */
336532176cfdSRui Paulo 	/* XXX assuming small failures are ok */
3366085ff963SMatthew Dillon #if defined(__DragonFly__)
3367085ff963SMatthew Dillon 	u_long	icount;
3368085ff963SMatthew Dillon 	u_long	ocount;
3369085ff963SMatthew Dillon 	IFNET_STAT_GET(ifp, ierrors, icount);
3370085ff963SMatthew Dillon 	IFNET_STAT_GET(ifp, oerrors, ocount);
3371085ff963SMatthew Dillon 	errrate = (((ocount + icount) / 100) << M_BITS)
3372085ff963SMatthew Dillon 	    / 100;
3373085ff963SMatthew Dillon #else
3374085ff963SMatthew Dillon 	errrate = (((ifp->if_get_counter(ifp, IFCOUNTER_OERRORS) +
3375085ff963SMatthew Dillon 	    ifp->if_get_counter(ifp, IFCOUNTER_IERRORS)) / 100) << M_BITS)
3376085ff963SMatthew Dillon 	    / 100;
3377085ff963SMatthew Dillon #endif
337832176cfdSRui Paulo 	res = (overhead + (nbits / rate)) *
337932176cfdSRui Paulo 	    ((1 << S_FACTOR) / ((1 << M_BITS) - errrate));
338032176cfdSRui Paulo 
338132176cfdSRui Paulo 	return (uint32_t)(res >> S_FACTOR);
338232176cfdSRui Paulo #undef M_BITS
338332176cfdSRui Paulo #undef S_FACTOR
338432176cfdSRui Paulo }
338532176cfdSRui Paulo 
338632176cfdSRui Paulo /*
338732176cfdSRui Paulo  * Add a Mesh Link Metric report IE to a frame.
338832176cfdSRui Paulo  */
338932176cfdSRui Paulo uint8_t *
3390085ff963SMatthew Dillon ieee80211_add_meshlmetric(uint8_t *frm, uint8_t flags, uint32_t metric)
339132176cfdSRui Paulo {
339232176cfdSRui Paulo 	*frm++ = IEEE80211_ELEMID_MESHLINK;
3393085ff963SMatthew Dillon 	*frm++ = 5;
3394085ff963SMatthew Dillon 	*frm++ = flags;
339532176cfdSRui Paulo 	ADDWORD(frm, metric);
339632176cfdSRui Paulo 	return frm;
339732176cfdSRui Paulo }
3398085ff963SMatthew Dillon 
3399085ff963SMatthew Dillon /*
3400085ff963SMatthew Dillon  * Add a Mesh Gate Announcement IE to a frame.
3401085ff963SMatthew Dillon  */
3402085ff963SMatthew Dillon uint8_t *
3403085ff963SMatthew Dillon ieee80211_add_meshgate(uint8_t *frm, struct ieee80211_meshgann_ie *ie)
3404085ff963SMatthew Dillon {
3405085ff963SMatthew Dillon 	*frm++ = IEEE80211_ELEMID_MESHGANN; /* ie */
3406085ff963SMatthew Dillon 	*frm++ = IEEE80211_MESHGANN_BASE_SZ; /* len */
3407085ff963SMatthew Dillon 	*frm++ = ie->gann_flags;
3408085ff963SMatthew Dillon 	*frm++ = ie->gann_hopcount;
3409085ff963SMatthew Dillon 	*frm++ = ie->gann_ttl;
3410085ff963SMatthew Dillon 	IEEE80211_ADDR_COPY(frm, ie->gann_addr);
3411085ff963SMatthew Dillon 	frm += 6;
3412085ff963SMatthew Dillon 	ADDWORD(frm, ie->gann_seq);
3413085ff963SMatthew Dillon 	ADDSHORT(frm, ie->gann_interval);
3414085ff963SMatthew Dillon 	return frm;
3415085ff963SMatthew Dillon }
341632176cfdSRui Paulo #undef ADDSHORT
341732176cfdSRui Paulo #undef ADDWORD
341832176cfdSRui Paulo 
341932176cfdSRui Paulo /*
342032176cfdSRui Paulo  * Initialize any mesh-specific node state.
342132176cfdSRui Paulo  */
342232176cfdSRui Paulo void
342332176cfdSRui Paulo ieee80211_mesh_node_init(struct ieee80211vap *vap, struct ieee80211_node *ni)
342432176cfdSRui Paulo {
342532176cfdSRui Paulo 	ni->ni_flags |= IEEE80211_NODE_QOS;
342634a60cf6SRui Paulo 	callout_init_mp(&ni->ni_mltimer);
3427085ff963SMatthew Dillon 	callout_init_mp(&ni->ni_mlhtimer);
342832176cfdSRui Paulo }
342932176cfdSRui Paulo 
343032176cfdSRui Paulo /*
343132176cfdSRui Paulo  * Cleanup any mesh-specific node state.
343232176cfdSRui Paulo  */
343332176cfdSRui Paulo void
343432176cfdSRui Paulo ieee80211_mesh_node_cleanup(struct ieee80211_node *ni)
343532176cfdSRui Paulo {
343632176cfdSRui Paulo 	struct ieee80211vap *vap = ni->ni_vap;
343732176cfdSRui Paulo 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
343832176cfdSRui Paulo 
3439085ff963SMatthew Dillon 	callout_drain(&ni->ni_mltimer);
3440085ff963SMatthew Dillon 	callout_drain(&ni->ni_mlhtimer);
344132176cfdSRui Paulo 	/* NB: short-circuit callbacks after mesh_vdetach */
344232176cfdSRui Paulo 	if (vap->iv_mesh != NULL)
344332176cfdSRui Paulo 		ms->ms_ppath->mpp_peerdown(ni);
344432176cfdSRui Paulo }
344532176cfdSRui Paulo 
344632176cfdSRui Paulo void
344732176cfdSRui Paulo ieee80211_parse_meshid(struct ieee80211_node *ni, const uint8_t *ie)
344832176cfdSRui Paulo {
344932176cfdSRui Paulo 	ni->ni_meshidlen = ie[1];
345032176cfdSRui Paulo 	memcpy(ni->ni_meshid, ie + 2, ie[1]);
345132176cfdSRui Paulo }
345232176cfdSRui Paulo 
345332176cfdSRui Paulo /*
345432176cfdSRui Paulo  * Setup mesh-specific node state on neighbor discovery.
345532176cfdSRui Paulo  */
345632176cfdSRui Paulo void
345732176cfdSRui Paulo ieee80211_mesh_init_neighbor(struct ieee80211_node *ni,
345832176cfdSRui Paulo 	const struct ieee80211_frame *wh,
345932176cfdSRui Paulo 	const struct ieee80211_scanparams *sp)
346032176cfdSRui Paulo {
346132176cfdSRui Paulo 	ieee80211_parse_meshid(ni, sp->meshid);
346232176cfdSRui Paulo }
346332176cfdSRui Paulo 
346432176cfdSRui Paulo void
346532176cfdSRui Paulo ieee80211_mesh_update_beacon(struct ieee80211vap *vap,
346632176cfdSRui Paulo 	struct ieee80211_beacon_offsets *bo)
346732176cfdSRui Paulo {
346832176cfdSRui Paulo 	KASSERT(vap->iv_opmode == IEEE80211_M_MBSS, ("not a MBSS vap"));
346932176cfdSRui Paulo 
347032176cfdSRui Paulo 	if (isset(bo->bo_flags, IEEE80211_BEACON_MESHCONF)) {
347132176cfdSRui Paulo 		(void)ieee80211_add_meshconf(bo->bo_meshconf, vap);
347232176cfdSRui Paulo 		clrbit(bo->bo_flags, IEEE80211_BEACON_MESHCONF);
347332176cfdSRui Paulo 	}
347432176cfdSRui Paulo }
347532176cfdSRui Paulo 
347632176cfdSRui Paulo static int
347732176cfdSRui Paulo mesh_ioctl_get80211(struct ieee80211vap *vap, struct ieee80211req *ireq)
347832176cfdSRui Paulo {
347932176cfdSRui Paulo 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
348032176cfdSRui Paulo 	uint8_t tmpmeshid[IEEE80211_NWID_LEN];
348132176cfdSRui Paulo 	struct ieee80211_mesh_route *rt;
348232176cfdSRui Paulo 	struct ieee80211req_mesh_route *imr;
348332176cfdSRui Paulo 	size_t len, off;
348432176cfdSRui Paulo 	uint8_t *p;
348532176cfdSRui Paulo 	int error;
348632176cfdSRui Paulo 
348732176cfdSRui Paulo 	if (vap->iv_opmode != IEEE80211_M_MBSS)
348832176cfdSRui Paulo 		return ENOSYS;
348932176cfdSRui Paulo 
349032176cfdSRui Paulo 	error = 0;
349132176cfdSRui Paulo 	switch (ireq->i_type) {
349232176cfdSRui Paulo 	case IEEE80211_IOC_MESH_ID:
349332176cfdSRui Paulo 		ireq->i_len = ms->ms_idlen;
349432176cfdSRui Paulo 		memcpy(tmpmeshid, ms->ms_id, ireq->i_len);
349532176cfdSRui Paulo 		error = copyout(tmpmeshid, ireq->i_data, ireq->i_len);
349632176cfdSRui Paulo 		break;
349732176cfdSRui Paulo 	case IEEE80211_IOC_MESH_AP:
349832176cfdSRui Paulo 		ireq->i_val = (ms->ms_flags & IEEE80211_MESHFLAGS_AP) != 0;
349932176cfdSRui Paulo 		break;
350032176cfdSRui Paulo 	case IEEE80211_IOC_MESH_FWRD:
350132176cfdSRui Paulo 		ireq->i_val = (ms->ms_flags & IEEE80211_MESHFLAGS_FWD) != 0;
350232176cfdSRui Paulo 		break;
3503085ff963SMatthew Dillon 	case IEEE80211_IOC_MESH_GATE:
3504085ff963SMatthew Dillon 		ireq->i_val = (ms->ms_flags & IEEE80211_MESHFLAGS_GATE) != 0;
3505085ff963SMatthew Dillon 		break;
350632176cfdSRui Paulo 	case IEEE80211_IOC_MESH_TTL:
350732176cfdSRui Paulo 		ireq->i_val = ms->ms_ttl;
350832176cfdSRui Paulo 		break;
350932176cfdSRui Paulo 	case IEEE80211_IOC_MESH_RTCMD:
351032176cfdSRui Paulo 		switch (ireq->i_val) {
351132176cfdSRui Paulo 		case IEEE80211_MESH_RTCMD_LIST:
351232176cfdSRui Paulo 			len = 0;
3513085ff963SMatthew Dillon 			MESH_RT_LOCK(ms);
351432176cfdSRui Paulo 			TAILQ_FOREACH(rt, &ms->ms_routes, rt_next) {
351532176cfdSRui Paulo 				len += sizeof(*imr);
351632176cfdSRui Paulo 			}
3517085ff963SMatthew Dillon 			MESH_RT_UNLOCK(ms);
351832176cfdSRui Paulo 			if (len > ireq->i_len || ireq->i_len < sizeof(*imr)) {
351932176cfdSRui Paulo 				ireq->i_len = len;
352032176cfdSRui Paulo 				return ENOMEM;
352132176cfdSRui Paulo 			}
352232176cfdSRui Paulo 			ireq->i_len = len;
352332176cfdSRui Paulo 			/* XXX M_WAIT? */
3524fcaa651dSRui Paulo 			p = kmalloc(len, M_TEMP, M_INTWAIT | M_ZERO);
3525085ff963SMatthew Dillon 			if (p == NULL)
3526085ff963SMatthew Dillon 				return ENOMEM;
352732176cfdSRui Paulo 			off = 0;
3528085ff963SMatthew Dillon 			MESH_RT_LOCK(ms);
352932176cfdSRui Paulo 			TAILQ_FOREACH(rt, &ms->ms_routes, rt_next) {
353032176cfdSRui Paulo 				if (off >= len)
353132176cfdSRui Paulo 					break;
353232176cfdSRui Paulo 				imr = (struct ieee80211req_mesh_route *)
353332176cfdSRui Paulo 				    (p + off);
353432176cfdSRui Paulo 				IEEE80211_ADDR_COPY(imr->imr_dest,
353532176cfdSRui Paulo 				    rt->rt_dest);
353632176cfdSRui Paulo 				IEEE80211_ADDR_COPY(imr->imr_nexthop,
353732176cfdSRui Paulo 				    rt->rt_nexthop);
353832176cfdSRui Paulo 				imr->imr_metric = rt->rt_metric;
353932176cfdSRui Paulo 				imr->imr_nhops = rt->rt_nhops;
3540085ff963SMatthew Dillon 				imr->imr_lifetime =
3541085ff963SMatthew Dillon 				    ieee80211_mesh_rt_update(rt, 0);
354232176cfdSRui Paulo 				imr->imr_lastmseq = rt->rt_lastmseq;
3543085ff963SMatthew Dillon 				imr->imr_flags = rt->rt_flags; /* last */
354432176cfdSRui Paulo 				off += sizeof(*imr);
354532176cfdSRui Paulo 			}
3546085ff963SMatthew Dillon 			MESH_RT_UNLOCK(ms);
354732176cfdSRui Paulo 			error = copyout(p, (uint8_t *)ireq->i_data,
354832176cfdSRui Paulo 			    ireq->i_len);
354932176cfdSRui Paulo 			kfree(p, M_TEMP);
355032176cfdSRui Paulo 			break;
355132176cfdSRui Paulo 		case IEEE80211_MESH_RTCMD_FLUSH:
355232176cfdSRui Paulo 		case IEEE80211_MESH_RTCMD_ADD:
355332176cfdSRui Paulo 		case IEEE80211_MESH_RTCMD_DELETE:
355432176cfdSRui Paulo 			return EINVAL;
355532176cfdSRui Paulo 		default:
355632176cfdSRui Paulo 			return ENOSYS;
355732176cfdSRui Paulo 		}
355832176cfdSRui Paulo 		break;
355932176cfdSRui Paulo 	case IEEE80211_IOC_MESH_PR_METRIC:
356032176cfdSRui Paulo 		len = strlen(ms->ms_pmetric->mpm_descr);
356132176cfdSRui Paulo 		if (ireq->i_len < len)
356232176cfdSRui Paulo 			return EINVAL;
356332176cfdSRui Paulo 		ireq->i_len = len;
356432176cfdSRui Paulo 		error = copyout(ms->ms_pmetric->mpm_descr,
356532176cfdSRui Paulo 		    (uint8_t *)ireq->i_data, len);
356632176cfdSRui Paulo 		break;
356732176cfdSRui Paulo 	case IEEE80211_IOC_MESH_PR_PATH:
356832176cfdSRui Paulo 		len = strlen(ms->ms_ppath->mpp_descr);
356932176cfdSRui Paulo 		if (ireq->i_len < len)
357032176cfdSRui Paulo 			return EINVAL;
357132176cfdSRui Paulo 		ireq->i_len = len;
357232176cfdSRui Paulo 		error = copyout(ms->ms_ppath->mpp_descr,
357332176cfdSRui Paulo 		    (uint8_t *)ireq->i_data, len);
357432176cfdSRui Paulo 		break;
357532176cfdSRui Paulo 	default:
357632176cfdSRui Paulo 		return ENOSYS;
357732176cfdSRui Paulo 	}
357832176cfdSRui Paulo 
357932176cfdSRui Paulo 	return error;
358032176cfdSRui Paulo }
358132176cfdSRui Paulo IEEE80211_IOCTL_GET(mesh, mesh_ioctl_get80211);
358232176cfdSRui Paulo 
358332176cfdSRui Paulo static int
358432176cfdSRui Paulo mesh_ioctl_set80211(struct ieee80211vap *vap, struct ieee80211req *ireq)
358532176cfdSRui Paulo {
358632176cfdSRui Paulo 	struct ieee80211_mesh_state *ms = vap->iv_mesh;
358732176cfdSRui Paulo 	uint8_t tmpmeshid[IEEE80211_NWID_LEN];
358832176cfdSRui Paulo 	uint8_t tmpaddr[IEEE80211_ADDR_LEN];
358932176cfdSRui Paulo 	char tmpproto[IEEE80211_MESH_PROTO_DSZ];
359032176cfdSRui Paulo 	int error;
359132176cfdSRui Paulo 
359232176cfdSRui Paulo 	if (vap->iv_opmode != IEEE80211_M_MBSS)
359332176cfdSRui Paulo 		return ENOSYS;
359432176cfdSRui Paulo 
359532176cfdSRui Paulo 	error = 0;
359632176cfdSRui Paulo 	switch (ireq->i_type) {
359732176cfdSRui Paulo 	case IEEE80211_IOC_MESH_ID:
359832176cfdSRui Paulo 		if (ireq->i_val != 0 || ireq->i_len > IEEE80211_MESHID_LEN)
359932176cfdSRui Paulo 			return EINVAL;
360032176cfdSRui Paulo 		error = copyin(ireq->i_data, tmpmeshid, ireq->i_len);
360132176cfdSRui Paulo 		if (error != 0)
360232176cfdSRui Paulo 			break;
360332176cfdSRui Paulo 		memset(ms->ms_id, 0, IEEE80211_NWID_LEN);
360432176cfdSRui Paulo 		ms->ms_idlen = ireq->i_len;
360532176cfdSRui Paulo 		memcpy(ms->ms_id, tmpmeshid, ireq->i_len);
360632176cfdSRui Paulo 		error = ENETRESET;
360732176cfdSRui Paulo 		break;
360832176cfdSRui Paulo 	case IEEE80211_IOC_MESH_AP:
360932176cfdSRui Paulo 		if (ireq->i_val)
361032176cfdSRui Paulo 			ms->ms_flags |= IEEE80211_MESHFLAGS_AP;
361132176cfdSRui Paulo 		else
361232176cfdSRui Paulo 			ms->ms_flags &= ~IEEE80211_MESHFLAGS_AP;
361332176cfdSRui Paulo 		error = ENETRESET;
361432176cfdSRui Paulo 		break;
361532176cfdSRui Paulo 	case IEEE80211_IOC_MESH_FWRD:
361632176cfdSRui Paulo 		if (ireq->i_val)
361732176cfdSRui Paulo 			ms->ms_flags |= IEEE80211_MESHFLAGS_FWD;
361832176cfdSRui Paulo 		else
361932176cfdSRui Paulo 			ms->ms_flags &= ~IEEE80211_MESHFLAGS_FWD;
3620085ff963SMatthew Dillon 		mesh_gatemode_setup(vap);
3621085ff963SMatthew Dillon 		break;
3622085ff963SMatthew Dillon 	case IEEE80211_IOC_MESH_GATE:
3623085ff963SMatthew Dillon 		if (ireq->i_val)
3624085ff963SMatthew Dillon 			ms->ms_flags |= IEEE80211_MESHFLAGS_GATE;
3625085ff963SMatthew Dillon 		else
3626085ff963SMatthew Dillon 			ms->ms_flags &= ~IEEE80211_MESHFLAGS_GATE;
362732176cfdSRui Paulo 		break;
362832176cfdSRui Paulo 	case IEEE80211_IOC_MESH_TTL:
362932176cfdSRui Paulo 		ms->ms_ttl = (uint8_t) ireq->i_val;
363032176cfdSRui Paulo 		break;
363132176cfdSRui Paulo 	case IEEE80211_IOC_MESH_RTCMD:
363232176cfdSRui Paulo 		switch (ireq->i_val) {
363332176cfdSRui Paulo 		case IEEE80211_MESH_RTCMD_LIST:
363432176cfdSRui Paulo 			return EINVAL;
363532176cfdSRui Paulo 		case IEEE80211_MESH_RTCMD_FLUSH:
363632176cfdSRui Paulo 			ieee80211_mesh_rt_flush(vap);
363732176cfdSRui Paulo 			break;
363832176cfdSRui Paulo 		case IEEE80211_MESH_RTCMD_ADD:
363932176cfdSRui Paulo 			if (IEEE80211_ADDR_EQ(vap->iv_myaddr, ireq->i_data) ||
364032176cfdSRui Paulo 			    IEEE80211_ADDR_EQ(broadcastaddr, ireq->i_data))
364132176cfdSRui Paulo 				return EINVAL;
364232176cfdSRui Paulo 			error = copyin(ireq->i_data, &tmpaddr,
364332176cfdSRui Paulo 			    IEEE80211_ADDR_LEN);
364432176cfdSRui Paulo 			if (error == 0)
364532176cfdSRui Paulo 				ieee80211_mesh_discover(vap, tmpaddr, NULL);
364632176cfdSRui Paulo 			break;
364732176cfdSRui Paulo 		case IEEE80211_MESH_RTCMD_DELETE:
364832176cfdSRui Paulo 			ieee80211_mesh_rt_del(vap, ireq->i_data);
364932176cfdSRui Paulo 			break;
365032176cfdSRui Paulo 		default:
365132176cfdSRui Paulo 			return ENOSYS;
365232176cfdSRui Paulo 		}
365332176cfdSRui Paulo 		break;
365432176cfdSRui Paulo 	case IEEE80211_IOC_MESH_PR_METRIC:
365532176cfdSRui Paulo 		error = copyin(ireq->i_data, tmpproto, sizeof(tmpproto));
365632176cfdSRui Paulo 		if (error == 0) {
365732176cfdSRui Paulo 			error = mesh_select_proto_metric(vap, tmpproto);
365832176cfdSRui Paulo 			if (error == 0)
365932176cfdSRui Paulo 				error = ENETRESET;
366032176cfdSRui Paulo 		}
366132176cfdSRui Paulo 		break;
366232176cfdSRui Paulo 	case IEEE80211_IOC_MESH_PR_PATH:
366332176cfdSRui Paulo 		error = copyin(ireq->i_data, tmpproto, sizeof(tmpproto));
366432176cfdSRui Paulo 		if (error == 0) {
366532176cfdSRui Paulo 			error = mesh_select_proto_path(vap, tmpproto);
366632176cfdSRui Paulo 			if (error == 0)
366732176cfdSRui Paulo 				error = ENETRESET;
366832176cfdSRui Paulo 		}
366932176cfdSRui Paulo 		break;
367032176cfdSRui Paulo 	default:
367132176cfdSRui Paulo 		return ENOSYS;
367232176cfdSRui Paulo 	}
367332176cfdSRui Paulo 	return error;
367432176cfdSRui Paulo }
367532176cfdSRui Paulo IEEE80211_IOCTL_SET(mesh, mesh_ioctl_set80211);
3676