1195618Srpaulo/*-
2195618Srpaulo * Copyright (c) 2009 The FreeBSD Foundation
3195618Srpaulo * All rights reserved.
4195618Srpaulo *
5195618Srpaulo * This software was developed by Rui Paulo under sponsorship from the
6195618Srpaulo * FreeBSD Foundation.
7195618Srpaulo *
8195618Srpaulo * Redistribution and use in source and binary forms, with or without
9195618Srpaulo * modification, are permitted provided that the following conditions
10195618Srpaulo * are met:
11195618Srpaulo * 1. Redistributions of source code must retain the above copyright
12195618Srpaulo *    notice, this list of conditions and the following disclaimer.
13195618Srpaulo * 2. Redistributions in binary form must reproduce the above copyright
14195618Srpaulo *    notice, this list of conditions and the following disclaimer in the
15195618Srpaulo *    documentation and/or other materials provided with the distribution.
16195618Srpaulo *
17195618Srpaulo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18195618Srpaulo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19195618Srpaulo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20195618Srpaulo * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21195618Srpaulo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22195618Srpaulo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23195618Srpaulo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24195618Srpaulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25195618Srpaulo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26195618Srpaulo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27195618Srpaulo * SUCH DAMAGE.
28195618Srpaulo */
29195618Srpaulo#include <sys/cdefs.h>
30195618Srpaulo#ifdef __FreeBSD__
31195618Srpaulo__FBSDID("$FreeBSD: stable/11/sys/net80211/ieee80211_mesh.c 361038 2020-05-14 17:56:43Z jhb $");
32195618Srpaulo#endif
33195618Srpaulo
34195618Srpaulo/*
35195618Srpaulo * IEEE 802.11s Mesh Point (MBSS) support.
36195618Srpaulo *
37195618Srpaulo * Based on March 2009, D3.0 802.11s draft spec.
38195618Srpaulo */
39195618Srpaulo#include "opt_inet.h"
40195618Srpaulo#include "opt_wlan.h"
41195618Srpaulo
42195618Srpaulo#include <sys/param.h>
43195618Srpaulo#include <sys/systm.h>
44195618Srpaulo#include <sys/mbuf.h>
45195618Srpaulo#include <sys/malloc.h>
46195618Srpaulo#include <sys/kernel.h>
47195618Srpaulo
48195618Srpaulo#include <sys/socket.h>
49195618Srpaulo#include <sys/sockio.h>
50195618Srpaulo#include <sys/endian.h>
51195618Srpaulo#include <sys/errno.h>
52195618Srpaulo#include <sys/proc.h>
53195618Srpaulo#include <sys/sysctl.h>
54195618Srpaulo
55246511Smonthadar#include <net/bpf.h>
56195618Srpaulo#include <net/if.h>
57257176Sglebius#include <net/if_var.h>
58195618Srpaulo#include <net/if_media.h>
59195618Srpaulo#include <net/if_llc.h>
60195618Srpaulo#include <net/ethernet.h>
61195618Srpaulo
62195618Srpaulo#include <net80211/ieee80211_var.h>
63195618Srpaulo#include <net80211/ieee80211_action.h>
64246537Sadrian#ifdef IEEE80211_SUPPORT_SUPERG
65246537Sadrian#include <net80211/ieee80211_superg.h>
66246537Sadrian#endif
67195618Srpaulo#include <net80211/ieee80211_input.h>
68195618Srpaulo#include <net80211/ieee80211_mesh.h>
69195618Srpaulo
70195784Srpaulostatic void	mesh_rt_flush_invalid(struct ieee80211vap *);
71195618Srpaulostatic int	mesh_select_proto_path(struct ieee80211vap *, const char *);
72195618Srpaulostatic int	mesh_select_proto_metric(struct ieee80211vap *, const char *);
73195618Srpaulostatic void	mesh_vattach(struct ieee80211vap *);
74195618Srpaulostatic int	mesh_newstate(struct ieee80211vap *, enum ieee80211_state, int);
75195784Srpaulostatic void	mesh_rt_cleanup_cb(void *);
76246506Smonthadarstatic void	mesh_gatemode_setup(struct ieee80211vap *);
77246506Smonthadarstatic void	mesh_gatemode_cb(void *);
78195661Srpaulostatic void	mesh_linkchange(struct ieee80211_node *,
79195661Srpaulo		    enum ieee80211_mesh_mlstate);
80195618Srpaulostatic void	mesh_checkid(void *, struct ieee80211_node *);
81195618Srpaulostatic uint32_t	mesh_generateid(struct ieee80211vap *);
82195618Srpaulostatic int	mesh_checkpseq(struct ieee80211vap *,
83195618Srpaulo		    const uint8_t [IEEE80211_ADDR_LEN], uint32_t);
84246511Smonthadarstatic void	mesh_transmit_to_gate(struct ieee80211vap *, struct mbuf *,
85246511Smonthadar		    struct ieee80211_mesh_route *);
86195618Srpaulostatic void	mesh_forward(struct ieee80211vap *, struct mbuf *,
87195618Srpaulo		    const struct ieee80211_meshcntl *);
88283535Sadrianstatic int	mesh_input(struct ieee80211_node *, struct mbuf *,
89283535Sadrian		    const struct ieee80211_rx_stats *rxs, int, int);
90195618Srpaulostatic void	mesh_recv_mgmt(struct ieee80211_node *, struct mbuf *, int,
91283535Sadrian		    const struct ieee80211_rx_stats *rxs, int, int);
92205277Srpaulostatic void	mesh_recv_ctl(struct ieee80211_node *, struct mbuf *, int);
93195618Srpaulostatic void	mesh_peer_timeout_setup(struct ieee80211_node *);
94195618Srpaulostatic void	mesh_peer_timeout_backoff(struct ieee80211_node *);
95195618Srpaulostatic void	mesh_peer_timeout_cb(void *);
96195618Srpaulostatic __inline void
97195618Srpaulo		mesh_peer_timeout_stop(struct ieee80211_node *);
98195618Srpaulostatic int	mesh_verify_meshid(struct ieee80211vap *, const uint8_t *);
99195618Srpaulostatic int	mesh_verify_meshconf(struct ieee80211vap *, const uint8_t *);
100197413Srpaulostatic int	mesh_verify_meshpeer(struct ieee80211vap *, uint8_t,
101197413Srpaulo    		    const uint8_t *);
102195618Srpaulouint32_t	mesh_airtime_calc(struct ieee80211_node *);
103195618Srpaulo
104195618Srpaulo/*
105195618Srpaulo * Timeout values come from the specification and are in milliseconds.
106195618Srpaulo */
107227309Sedstatic SYSCTL_NODE(_net_wlan, OID_AUTO, mesh, CTLFLAG_RD, 0,
108195618Srpaulo    "IEEE 802.11s parameters");
109246506Smonthadarstatic int	ieee80211_mesh_gateint = -1;
110246506SmonthadarSYSCTL_PROC(_net_wlan_mesh, OID_AUTO, gateint, CTLTYPE_INT | CTLFLAG_RW,
111246506Smonthadar    &ieee80211_mesh_gateint, 0, ieee80211_sysctl_msecs_ticks, "I",
112246506Smonthadar    "mesh gate interval (ms)");
113195618Srpaulostatic int ieee80211_mesh_retrytimeout = -1;
114195618SrpauloSYSCTL_PROC(_net_wlan_mesh, OID_AUTO, retrytimeout, CTLTYPE_INT | CTLFLAG_RW,
115195618Srpaulo    &ieee80211_mesh_retrytimeout, 0, ieee80211_sysctl_msecs_ticks, "I",
116195618Srpaulo    "Retry timeout (msec)");
117195618Srpaulostatic int ieee80211_mesh_holdingtimeout = -1;
118246537Sadrian
119195618SrpauloSYSCTL_PROC(_net_wlan_mesh, OID_AUTO, holdingtimeout, CTLTYPE_INT | CTLFLAG_RW,
120195618Srpaulo    &ieee80211_mesh_holdingtimeout, 0, ieee80211_sysctl_msecs_ticks, "I",
121195618Srpaulo    "Holding state timeout (msec)");
122195618Srpaulostatic int ieee80211_mesh_confirmtimeout = -1;
123195618SrpauloSYSCTL_PROC(_net_wlan_mesh, OID_AUTO, confirmtimeout, CTLTYPE_INT | CTLFLAG_RW,
124195618Srpaulo    &ieee80211_mesh_confirmtimeout, 0, ieee80211_sysctl_msecs_ticks, "I",
125195618Srpaulo    "Confirm state timeout (msec)");
126246497Smonthadarstatic int ieee80211_mesh_backofftimeout = -1;
127246497SmonthadarSYSCTL_PROC(_net_wlan_mesh, OID_AUTO, backofftimeout, CTLTYPE_INT | CTLFLAG_RW,
128246497Smonthadar    &ieee80211_mesh_backofftimeout, 0, ieee80211_sysctl_msecs_ticks, "I",
129246497Smonthadar    "Backoff timeout (msec). This is to throutles peering forever when "
130281383Seadler    "not receiving answer or is rejected by a neighbor");
131195618Srpaulostatic int ieee80211_mesh_maxretries = 2;
132273377ShselaskySYSCTL_INT(_net_wlan_mesh, OID_AUTO, maxretries, CTLFLAG_RW,
133195618Srpaulo    &ieee80211_mesh_maxretries, 0,
134195618Srpaulo    "Maximum retries during peer link establishment");
135246497Smonthadarstatic int ieee80211_mesh_maxholding = 2;
136273377ShselaskySYSCTL_INT(_net_wlan_mesh, OID_AUTO, maxholding, CTLFLAG_RW,
137246497Smonthadar    &ieee80211_mesh_maxholding, 0,
138246497Smonthadar    "Maximum times we are allowed to transition to HOLDING state before "
139246497Smonthadar    "backinoff during peer link establishment");
140195618Srpaulo
141195618Srpaulostatic const uint8_t broadcastaddr[IEEE80211_ADDR_LEN] =
142195618Srpaulo	{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
143195618Srpaulo
144195618Srpaulostatic	ieee80211_recv_action_func mesh_recv_action_meshpeering_open;
145195618Srpaulostatic	ieee80211_recv_action_func mesh_recv_action_meshpeering_confirm;
146195618Srpaulostatic	ieee80211_recv_action_func mesh_recv_action_meshpeering_close;
147232479Sadrianstatic	ieee80211_recv_action_func mesh_recv_action_meshlmetric;
148246506Smonthadarstatic	ieee80211_recv_action_func mesh_recv_action_meshgate;
149195618Srpaulo
150195618Srpaulostatic	ieee80211_send_action_func mesh_send_action_meshpeering_open;
151195618Srpaulostatic	ieee80211_send_action_func mesh_send_action_meshpeering_confirm;
152195618Srpaulostatic	ieee80211_send_action_func mesh_send_action_meshpeering_close;
153232479Sadrianstatic	ieee80211_send_action_func mesh_send_action_meshlmetric;
154246506Smonthadarstatic	ieee80211_send_action_func mesh_send_action_meshgate;
155195618Srpaulo
156195618Srpaulostatic const struct ieee80211_mesh_proto_metric mesh_metric_airtime = {
157195618Srpaulo	.mpm_descr	= "AIRTIME",
158197975Srpaulo	.mpm_ie		= IEEE80211_MESHCONF_METRIC_AIRTIME,
159195618Srpaulo	.mpm_metric	= mesh_airtime_calc,
160195618Srpaulo};
161195618Srpaulo
162195618Srpaulostatic struct ieee80211_mesh_proto_path		mesh_proto_paths[4];
163195618Srpaulostatic struct ieee80211_mesh_proto_metric	mesh_proto_metrics[4];
164195618Srpaulo
165232625SadrianMALLOC_DEFINE(M_80211_MESH_PREQ, "80211preq", "802.11 MESH Path Request frame");
166232625SadrianMALLOC_DEFINE(M_80211_MESH_PREP, "80211prep", "802.11 MESH Path Reply frame");
167232625SadrianMALLOC_DEFINE(M_80211_MESH_PERR, "80211perr", "802.11 MESH Path Error frame");
168232625Sadrian
169234877Smonthadar/* The longer one of the lifetime should be stored as new lifetime */
170234877Smonthadar#define MESH_ROUTE_LIFETIME_MAX(a, b)	(a > b ? a : b)
171234877Smonthadar
172246508SmonthadarMALLOC_DEFINE(M_80211_MESH_RT, "80211mesh_rt", "802.11s routing table");
173246508SmonthadarMALLOC_DEFINE(M_80211_MESH_GT_RT, "80211mesh_gt", "802.11s known gates table");
174195618Srpaulo
175195618Srpaulo/*
176195618Srpaulo * Helper functions to manipulate the Mesh routing table.
177195618Srpaulo */
178195784Srpaulo
179195784Srpaulostatic struct ieee80211_mesh_route *
180195784Srpaulomesh_rt_find_locked(struct ieee80211_mesh_state *ms,
181195784Srpaulo    const uint8_t dest[IEEE80211_ADDR_LEN])
182195784Srpaulo{
183195784Srpaulo	struct ieee80211_mesh_route *rt;
184195784Srpaulo
185195784Srpaulo	MESH_RT_LOCK_ASSERT(ms);
186195784Srpaulo
187195784Srpaulo	TAILQ_FOREACH(rt, &ms->ms_routes, rt_next) {
188195784Srpaulo		if (IEEE80211_ADDR_EQ(dest, rt->rt_dest))
189195784Srpaulo			return rt;
190195784Srpaulo	}
191195784Srpaulo	return NULL;
192195784Srpaulo}
193195784Srpaulo
194195784Srpaulostatic struct ieee80211_mesh_route *
195234894Smonthadarmesh_rt_add_locked(struct ieee80211vap *vap,
196195784Srpaulo    const uint8_t dest[IEEE80211_ADDR_LEN])
197195784Srpaulo{
198234894Smonthadar	struct ieee80211_mesh_state *ms = vap->iv_mesh;
199195784Srpaulo	struct ieee80211_mesh_route *rt;
200195784Srpaulo
201195784Srpaulo	KASSERT(!IEEE80211_ADDR_EQ(broadcastaddr, dest),
202195784Srpaulo	    ("%s: adding broadcast to the routing table", __func__));
203195784Srpaulo
204195784Srpaulo	MESH_RT_LOCK_ASSERT(ms);
205195784Srpaulo
206283538Sadrian	rt = IEEE80211_MALLOC(ALIGN(sizeof(struct ieee80211_mesh_route)) +
207283538Sadrian	    ms->ms_ppath->mpp_privlen, M_80211_MESH_RT,
208283538Sadrian	    IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
209195784Srpaulo	if (rt != NULL) {
210234894Smonthadar		rt->rt_vap = vap;
211195784Srpaulo		IEEE80211_ADDR_COPY(rt->rt_dest, dest);
212195784Srpaulo		rt->rt_priv = (void *)ALIGN(&rt[1]);
213283555Sadrian		MESH_RT_ENTRY_LOCK_INIT(rt, "MBSS_RT");
214283291Sjkim		callout_init(&rt->rt_discovery, 1);
215234877Smonthadar		rt->rt_updtime = ticks;	/* create time */
216195784Srpaulo		TAILQ_INSERT_TAIL(&ms->ms_routes, rt, rt_next);
217195784Srpaulo	}
218195784Srpaulo	return rt;
219195784Srpaulo}
220195784Srpaulo
221195618Srpaulostruct ieee80211_mesh_route *
222195618Srpauloieee80211_mesh_rt_find(struct ieee80211vap *vap,
223195618Srpaulo    const uint8_t dest[IEEE80211_ADDR_LEN])
224195618Srpaulo{
225195618Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
226195618Srpaulo	struct ieee80211_mesh_route *rt;
227195618Srpaulo
228195618Srpaulo	MESH_RT_LOCK(ms);
229195784Srpaulo	rt = mesh_rt_find_locked(ms, dest);
230195618Srpaulo	MESH_RT_UNLOCK(ms);
231195784Srpaulo	return rt;
232195618Srpaulo}
233195618Srpaulo
234195618Srpaulostruct ieee80211_mesh_route *
235195618Srpauloieee80211_mesh_rt_add(struct ieee80211vap *vap,
236195618Srpaulo    const uint8_t dest[IEEE80211_ADDR_LEN])
237195618Srpaulo{
238195618Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
239195618Srpaulo	struct ieee80211_mesh_route *rt;
240195618Srpaulo
241195618Srpaulo	KASSERT(ieee80211_mesh_rt_find(vap, dest) == NULL,
242195618Srpaulo	    ("%s: duplicate entry in the routing table", __func__));
243195618Srpaulo	KASSERT(!IEEE80211_ADDR_EQ(vap->iv_myaddr, dest),
244195618Srpaulo	    ("%s: adding self to the routing table", __func__));
245195618Srpaulo
246195618Srpaulo	MESH_RT_LOCK(ms);
247234894Smonthadar	rt = mesh_rt_add_locked(vap, dest);
248195618Srpaulo	MESH_RT_UNLOCK(ms);
249195618Srpaulo	return rt;
250195618Srpaulo}
251195618Srpaulo
252195784Srpaulo/*
253234877Smonthadar * Update the route lifetime and returns the updated lifetime.
254234877Smonthadar * If new_lifetime is zero and route is timedout it will be invalidated.
255234877Smonthadar * new_lifetime is in msec
256234877Smonthadar */
257234877Smonthadarint
258234877Smonthadarieee80211_mesh_rt_update(struct ieee80211_mesh_route *rt, int new_lifetime)
259234877Smonthadar{
260234877Smonthadar	int timesince, now;
261234877Smonthadar	uint32_t lifetime = 0;
262234877Smonthadar
263234881Smonthadar	KASSERT(rt != NULL, ("route is NULL"));
264234881Smonthadar
265234877Smonthadar	now = ticks;
266283555Sadrian	MESH_RT_ENTRY_LOCK(rt);
267234879Smonthadar
268234879Smonthadar	/* dont clobber a proxy entry gated by us */
269234879Smonthadar	if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY && rt->rt_nhops == 0) {
270283555Sadrian		MESH_RT_ENTRY_UNLOCK(rt);
271234879Smonthadar		return rt->rt_lifetime;
272234879Smonthadar	}
273234879Smonthadar
274234877Smonthadar	timesince = ticks_to_msecs(now - rt->rt_updtime);
275234877Smonthadar	rt->rt_updtime = now;
276234877Smonthadar	if (timesince >= rt->rt_lifetime) {
277234877Smonthadar		if (new_lifetime != 0) {
278234877Smonthadar			rt->rt_lifetime = new_lifetime;
279234877Smonthadar		}
280234877Smonthadar		else {
281234877Smonthadar			rt->rt_flags &= ~IEEE80211_MESHRT_FLAGS_VALID;
282234877Smonthadar			rt->rt_lifetime = 0;
283234877Smonthadar		}
284234877Smonthadar	} else {
285234877Smonthadar		/* update what is left of lifetime */
286234877Smonthadar		rt->rt_lifetime = rt->rt_lifetime - timesince;
287234877Smonthadar		rt->rt_lifetime  = MESH_ROUTE_LIFETIME_MAX(
288234877Smonthadar			new_lifetime, rt->rt_lifetime);
289234877Smonthadar	}
290234877Smonthadar	lifetime = rt->rt_lifetime;
291283555Sadrian	MESH_RT_ENTRY_UNLOCK(rt);
292234877Smonthadar
293234877Smonthadar	return lifetime;
294234877Smonthadar}
295234877Smonthadar
296234877Smonthadar/*
297195784Srpaulo * Add a proxy route (as needed) for the specified destination.
298195784Srpaulo */
299195618Srpaulovoid
300195784Srpauloieee80211_mesh_proxy_check(struct ieee80211vap *vap,
301195784Srpaulo    const uint8_t dest[IEEE80211_ADDR_LEN])
302195784Srpaulo{
303195784Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
304195784Srpaulo	struct ieee80211_mesh_route *rt;
305195784Srpaulo
306195784Srpaulo	MESH_RT_LOCK(ms);
307195784Srpaulo	rt = mesh_rt_find_locked(ms, dest);
308195784Srpaulo	if (rt == NULL) {
309234894Smonthadar		rt = mesh_rt_add_locked(vap, dest);
310195784Srpaulo		if (rt == NULL) {
311195784Srpaulo			IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, dest,
312195784Srpaulo			    "%s", "unable to add proxy entry");
313195784Srpaulo			vap->iv_stats.is_mesh_rtaddfailed++;
314195784Srpaulo		} else {
315195908Srpaulo			IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, dest,
316195908Srpaulo			    "%s", "add proxy entry");
317234878Smonthadar			IEEE80211_ADDR_COPY(rt->rt_mesh_gate, vap->iv_myaddr);
318195784Srpaulo			IEEE80211_ADDR_COPY(rt->rt_nexthop, vap->iv_myaddr);
319195784Srpaulo			rt->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID
320195784Srpaulo				     |  IEEE80211_MESHRT_FLAGS_PROXY;
321195784Srpaulo		}
322195784Srpaulo	} else if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0) {
323234878Smonthadar		KASSERT(rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY,
324234878Smonthadar		    ("no proxy flag for poxy entry"));
325195784Srpaulo		struct ieee80211com *ic = vap->iv_ic;
326195784Srpaulo		/*
327195784Srpaulo		 * Fix existing entry created by received frames from
328195784Srpaulo		 * stations that have some memory of dest.  We also
329195784Srpaulo		 * flush any frames held on the staging queue; delivering
330195784Srpaulo		 * them is too much trouble right now.
331195784Srpaulo		 */
332195784Srpaulo		IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, dest,
333195784Srpaulo		    "%s", "fix proxy entry");
334195784Srpaulo		IEEE80211_ADDR_COPY(rt->rt_nexthop, vap->iv_myaddr);
335195784Srpaulo		rt->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID
336195784Srpaulo			     |  IEEE80211_MESHRT_FLAGS_PROXY;
337195784Srpaulo		/* XXX belongs in hwmp */
338195784Srpaulo		ieee80211_ageq_drain_node(&ic->ic_stageq,
339195784Srpaulo		   (void *)(uintptr_t) ieee80211_mac_hash(ic, dest));
340195784Srpaulo		/* XXX stat? */
341195784Srpaulo	}
342195784Srpaulo	MESH_RT_UNLOCK(ms);
343195784Srpaulo}
344195784Srpaulo
345195784Srpaulostatic __inline void
346195784Srpaulomesh_rt_del(struct ieee80211_mesh_state *ms, struct ieee80211_mesh_route *rt)
347195784Srpaulo{
348195784Srpaulo	TAILQ_REMOVE(&ms->ms_routes, rt, rt_next);
349234877Smonthadar	/*
350234877Smonthadar	 * Grab the lock before destroying it, to be sure no one else
351234877Smonthadar	 * is holding the route.
352234877Smonthadar	 */
353283555Sadrian	MESH_RT_ENTRY_LOCK(rt);
354234894Smonthadar	callout_drain(&rt->rt_discovery);
355283555Sadrian	MESH_RT_ENTRY_LOCK_DESTROY(rt);
356283538Sadrian	IEEE80211_FREE(rt, M_80211_MESH_RT);
357195784Srpaulo}
358195784Srpaulo
359195784Srpaulovoid
360195618Srpauloieee80211_mesh_rt_del(struct ieee80211vap *vap,
361195618Srpaulo    const uint8_t dest[IEEE80211_ADDR_LEN])
362195618Srpaulo{
363195618Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
364195618Srpaulo	struct ieee80211_mesh_route *rt, *next;
365195618Srpaulo
366195618Srpaulo	MESH_RT_LOCK(ms);
367195618Srpaulo	TAILQ_FOREACH_SAFE(rt, &ms->ms_routes, rt_next, next) {
368195618Srpaulo		if (IEEE80211_ADDR_EQ(rt->rt_dest, dest)) {
369234890Smonthadar			if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY) {
370234890Smonthadar				ms->ms_ppath->mpp_senderror(vap, dest, rt,
371234890Smonthadar				    IEEE80211_REASON_MESH_PERR_NO_PROXY);
372234890Smonthadar			} else {
373234890Smonthadar				ms->ms_ppath->mpp_senderror(vap, dest, rt,
374234890Smonthadar				    IEEE80211_REASON_MESH_PERR_DEST_UNREACH);
375234890Smonthadar			}
376195784Srpaulo			mesh_rt_del(ms, rt);
377195618Srpaulo			MESH_RT_UNLOCK(ms);
378195618Srpaulo			return;
379195618Srpaulo		}
380195618Srpaulo	}
381195618Srpaulo	MESH_RT_UNLOCK(ms);
382195618Srpaulo}
383195618Srpaulo
384195618Srpaulovoid
385195618Srpauloieee80211_mesh_rt_flush(struct ieee80211vap *vap)
386195618Srpaulo{
387195618Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
388195618Srpaulo	struct ieee80211_mesh_route *rt, *next;
389195618Srpaulo
390195618Srpaulo	if (ms == NULL)
391195618Srpaulo		return;
392195618Srpaulo	MESH_RT_LOCK(ms);
393195784Srpaulo	TAILQ_FOREACH_SAFE(rt, &ms->ms_routes, rt_next, next)
394195784Srpaulo		mesh_rt_del(ms, rt);
395195784Srpaulo	MESH_RT_UNLOCK(ms);
396195784Srpaulo}
397195784Srpaulo
398195908Srpaulovoid
399195908Srpauloieee80211_mesh_rt_flush_peer(struct ieee80211vap *vap,
400195908Srpaulo    const uint8_t peer[IEEE80211_ADDR_LEN])
401195908Srpaulo{
402195908Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
403195908Srpaulo	struct ieee80211_mesh_route *rt, *next;
404195908Srpaulo
405195908Srpaulo	MESH_RT_LOCK(ms);
406195908Srpaulo	TAILQ_FOREACH_SAFE(rt, &ms->ms_routes, rt_next, next) {
407195908Srpaulo		if (IEEE80211_ADDR_EQ(rt->rt_nexthop, peer))
408195908Srpaulo			mesh_rt_del(ms, rt);
409195908Srpaulo	}
410195908Srpaulo	MESH_RT_UNLOCK(ms);
411195908Srpaulo}
412195908Srpaulo
413195784Srpaulo/*
414195784Srpaulo * Flush expired routing entries, i.e. those in invalid state for
415195784Srpaulo * some time.
416195784Srpaulo */
417195784Srpaulostatic void
418195784Srpaulomesh_rt_flush_invalid(struct ieee80211vap *vap)
419195784Srpaulo{
420195784Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
421195784Srpaulo	struct ieee80211_mesh_route *rt, *next;
422195784Srpaulo
423195784Srpaulo	if (ms == NULL)
424195784Srpaulo		return;
425195784Srpaulo	MESH_RT_LOCK(ms);
426195618Srpaulo	TAILQ_FOREACH_SAFE(rt, &ms->ms_routes, rt_next, next) {
427234894Smonthadar		/* Discover paths will be deleted by their own callout */
428234894Smonthadar		if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_DISCOVER)
429234894Smonthadar			continue;
430234877Smonthadar		ieee80211_mesh_rt_update(rt, 0);
431234894Smonthadar		if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0)
432195784Srpaulo			mesh_rt_del(ms, rt);
433195618Srpaulo	}
434195618Srpaulo	MESH_RT_UNLOCK(ms);
435195618Srpaulo}
436195618Srpaulo
437195618Srpauloint
438195618Srpauloieee80211_mesh_register_proto_path(const struct ieee80211_mesh_proto_path *mpp)
439195618Srpaulo{
440195618Srpaulo	int i, firstempty = -1;
441195618Srpaulo
442288086Sadrian	for (i = 0; i < nitems(mesh_proto_paths); i++) {
443197975Srpaulo		if (strncmp(mpp->mpp_descr, mesh_proto_paths[i].mpp_descr,
444197975Srpaulo		    IEEE80211_MESH_PROTO_DSZ) == 0)
445195618Srpaulo			return EEXIST;
446197975Srpaulo		if (!mesh_proto_paths[i].mpp_active && firstempty == -1)
447195618Srpaulo			firstempty = i;
448195618Srpaulo	}
449195618Srpaulo	if (firstempty < 0)
450195618Srpaulo		return ENOSPC;
451195618Srpaulo	memcpy(&mesh_proto_paths[firstempty], mpp, sizeof(*mpp));
452197975Srpaulo	mesh_proto_paths[firstempty].mpp_active = 1;
453195618Srpaulo	return 0;
454195618Srpaulo}
455195618Srpaulo
456195618Srpauloint
457195618Srpauloieee80211_mesh_register_proto_metric(const struct
458195618Srpaulo    ieee80211_mesh_proto_metric *mpm)
459195618Srpaulo{
460195618Srpaulo	int i, firstempty = -1;
461195618Srpaulo
462288086Sadrian	for (i = 0; i < nitems(mesh_proto_metrics); i++) {
463197975Srpaulo		if (strncmp(mpm->mpm_descr, mesh_proto_metrics[i].mpm_descr,
464197975Srpaulo		    IEEE80211_MESH_PROTO_DSZ) == 0)
465195618Srpaulo			return EEXIST;
466197975Srpaulo		if (!mesh_proto_metrics[i].mpm_active && firstempty == -1)
467195618Srpaulo			firstempty = i;
468195618Srpaulo	}
469195618Srpaulo	if (firstempty < 0)
470195618Srpaulo		return ENOSPC;
471195618Srpaulo	memcpy(&mesh_proto_metrics[firstempty], mpm, sizeof(*mpm));
472197975Srpaulo	mesh_proto_metrics[firstempty].mpm_active = 1;
473195618Srpaulo	return 0;
474195618Srpaulo}
475195618Srpaulo
476195618Srpaulostatic int
477195618Srpaulomesh_select_proto_path(struct ieee80211vap *vap, const char *name)
478195618Srpaulo{
479195618Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
480195618Srpaulo	int i;
481195618Srpaulo
482288086Sadrian	for (i = 0; i < nitems(mesh_proto_paths); i++) {
483195618Srpaulo		if (strcasecmp(mesh_proto_paths[i].mpp_descr, name) == 0) {
484195618Srpaulo			ms->ms_ppath = &mesh_proto_paths[i];
485195618Srpaulo			return 0;
486195618Srpaulo		}
487195618Srpaulo	}
488195618Srpaulo	return ENOENT;
489195618Srpaulo}
490195618Srpaulo
491195618Srpaulostatic int
492195618Srpaulomesh_select_proto_metric(struct ieee80211vap *vap, const char *name)
493195618Srpaulo{
494195618Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
495195618Srpaulo	int i;
496195618Srpaulo
497288086Sadrian	for (i = 0; i < nitems(mesh_proto_metrics); i++) {
498195618Srpaulo		if (strcasecmp(mesh_proto_metrics[i].mpm_descr, name) == 0) {
499195618Srpaulo			ms->ms_pmetric = &mesh_proto_metrics[i];
500195618Srpaulo			return 0;
501195618Srpaulo		}
502195618Srpaulo	}
503195618Srpaulo	return ENOENT;
504195618Srpaulo}
505195618Srpaulo
506195618Srpaulostatic void
507246506Smonthadarmesh_gatemode_setup(struct ieee80211vap *vap)
508246506Smonthadar{
509246506Smonthadar	struct ieee80211_mesh_state *ms = vap->iv_mesh;
510246506Smonthadar
511246506Smonthadar	/*
512246506Smonthadar	 * NB: When a mesh gate is running as a ROOT it shall
513246506Smonthadar	 * not send out periodic GANNs but instead mark the
514246506Smonthadar	 * mesh gate flag for the corresponding proactive PREQ
515246506Smonthadar	 * and RANN frames.
516246506Smonthadar	 */
517246506Smonthadar	if (ms->ms_flags & IEEE80211_MESHFLAGS_ROOT ||
518246506Smonthadar	    (ms->ms_flags & IEEE80211_MESHFLAGS_GATE) == 0) {
519246506Smonthadar		callout_drain(&ms->ms_gatetimer);
520246506Smonthadar		return ;
521246506Smonthadar	}
522246506Smonthadar	callout_reset(&ms->ms_gatetimer, ieee80211_mesh_gateint,
523246506Smonthadar	    mesh_gatemode_cb, vap);
524246506Smonthadar}
525246506Smonthadar
526246506Smonthadarstatic void
527246506Smonthadarmesh_gatemode_cb(void *arg)
528246506Smonthadar{
529246506Smonthadar	struct ieee80211vap *vap = (struct ieee80211vap *)arg;
530246506Smonthadar	struct ieee80211_mesh_state *ms = vap->iv_mesh;
531246506Smonthadar	struct ieee80211_meshgann_ie gann;
532246506Smonthadar
533246506Smonthadar	gann.gann_flags = 0; /* Reserved */
534246506Smonthadar	gann.gann_hopcount = 0;
535246506Smonthadar	gann.gann_ttl = ms->ms_ttl;
536246506Smonthadar	IEEE80211_ADDR_COPY(gann.gann_addr, vap->iv_myaddr);
537246506Smonthadar	gann.gann_seq = ms->ms_gateseq++;
538246506Smonthadar	gann.gann_interval = ieee80211_mesh_gateint;
539246506Smonthadar
540246520Smonthadar	IEEE80211_NOTE(vap, IEEE80211_MSG_MESH, vap->iv_bss,
541246520Smonthadar	    "send broadcast GANN (seq %u)", gann.gann_seq);
542246520Smonthadar
543246506Smonthadar	ieee80211_send_action(vap->iv_bss, IEEE80211_ACTION_CAT_MESH,
544246506Smonthadar	    IEEE80211_ACTION_MESH_GANN, &gann);
545246506Smonthadar	mesh_gatemode_setup(vap);
546246506Smonthadar}
547246506Smonthadar
548246506Smonthadarstatic void
549195618Srpauloieee80211_mesh_init(void)
550195618Srpaulo{
551195618Srpaulo
552195618Srpaulo	memset(mesh_proto_paths, 0, sizeof(mesh_proto_paths));
553195618Srpaulo	memset(mesh_proto_metrics, 0, sizeof(mesh_proto_metrics));
554195618Srpaulo
555195618Srpaulo	/*
556195618Srpaulo	 * Setup mesh parameters that depends on the clock frequency.
557195618Srpaulo	 */
558246506Smonthadar	ieee80211_mesh_gateint = msecs_to_ticks(10000);
559195618Srpaulo	ieee80211_mesh_retrytimeout = msecs_to_ticks(40);
560195618Srpaulo	ieee80211_mesh_holdingtimeout = msecs_to_ticks(40);
561195618Srpaulo	ieee80211_mesh_confirmtimeout = msecs_to_ticks(40);
562246497Smonthadar	ieee80211_mesh_backofftimeout = msecs_to_ticks(5000);
563195618Srpaulo
564195618Srpaulo	/*
565195618Srpaulo	 * Register action frame handlers.
566195618Srpaulo	 */
567234874Smonthadar	ieee80211_recv_action_register(IEEE80211_ACTION_CAT_SELF_PROT,
568195618Srpaulo	    IEEE80211_ACTION_MESHPEERING_OPEN,
569195618Srpaulo	    mesh_recv_action_meshpeering_open);
570234874Smonthadar	ieee80211_recv_action_register(IEEE80211_ACTION_CAT_SELF_PROT,
571195618Srpaulo	    IEEE80211_ACTION_MESHPEERING_CONFIRM,
572195618Srpaulo	    mesh_recv_action_meshpeering_confirm);
573234874Smonthadar	ieee80211_recv_action_register(IEEE80211_ACTION_CAT_SELF_PROT,
574195618Srpaulo	    IEEE80211_ACTION_MESHPEERING_CLOSE,
575195618Srpaulo	    mesh_recv_action_meshpeering_close);
576232479Sadrian	ieee80211_recv_action_register(IEEE80211_ACTION_CAT_MESH,
577232479Sadrian	    IEEE80211_ACTION_MESH_LMETRIC, mesh_recv_action_meshlmetric);
578246506Smonthadar	ieee80211_recv_action_register(IEEE80211_ACTION_CAT_MESH,
579246506Smonthadar	    IEEE80211_ACTION_MESH_GANN, mesh_recv_action_meshgate);
580195618Srpaulo
581234874Smonthadar	ieee80211_send_action_register(IEEE80211_ACTION_CAT_SELF_PROT,
582195618Srpaulo	    IEEE80211_ACTION_MESHPEERING_OPEN,
583195618Srpaulo	    mesh_send_action_meshpeering_open);
584234874Smonthadar	ieee80211_send_action_register(IEEE80211_ACTION_CAT_SELF_PROT,
585195618Srpaulo	    IEEE80211_ACTION_MESHPEERING_CONFIRM,
586195618Srpaulo	    mesh_send_action_meshpeering_confirm);
587234874Smonthadar	ieee80211_send_action_register(IEEE80211_ACTION_CAT_SELF_PROT,
588195618Srpaulo	    IEEE80211_ACTION_MESHPEERING_CLOSE,
589195618Srpaulo	    mesh_send_action_meshpeering_close);
590232479Sadrian	ieee80211_send_action_register(IEEE80211_ACTION_CAT_MESH,
591232479Sadrian	    IEEE80211_ACTION_MESH_LMETRIC,
592232479Sadrian	    mesh_send_action_meshlmetric);
593246506Smonthadar	ieee80211_send_action_register(IEEE80211_ACTION_CAT_MESH,
594246506Smonthadar	    IEEE80211_ACTION_MESH_GANN,
595246506Smonthadar	    mesh_send_action_meshgate);
596195618Srpaulo
597195618Srpaulo	/*
598195618Srpaulo	 * Register Airtime Link Metric.
599195618Srpaulo	 */
600195618Srpaulo	ieee80211_mesh_register_proto_metric(&mesh_metric_airtime);
601195618Srpaulo
602195618Srpaulo}
603195618SrpauloSYSINIT(wlan_mesh, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_mesh_init, NULL);
604195618Srpaulo
605195618Srpaulovoid
606195618Srpauloieee80211_mesh_attach(struct ieee80211com *ic)
607195618Srpaulo{
608195618Srpaulo	ic->ic_vattach[IEEE80211_M_MBSS] = mesh_vattach;
609195618Srpaulo}
610195618Srpaulo
611195618Srpaulovoid
612195618Srpauloieee80211_mesh_detach(struct ieee80211com *ic)
613195618Srpaulo{
614195618Srpaulo}
615195618Srpaulo
616195618Srpaulostatic void
617195618Srpaulomesh_vdetach_peers(void *arg, struct ieee80211_node *ni)
618195618Srpaulo{
619195618Srpaulo	struct ieee80211com *ic = ni->ni_ic;
620195618Srpaulo	uint16_t args[3];
621195618Srpaulo
622195618Srpaulo	if (ni->ni_mlstate == IEEE80211_NODE_MESH_ESTABLISHED) {
623195618Srpaulo		args[0] = ni->ni_mlpid;
624195618Srpaulo		args[1] = ni->ni_mllid;
625195618Srpaulo		args[2] = IEEE80211_REASON_PEER_LINK_CANCELED;
626195618Srpaulo		ieee80211_send_action(ni,
627234874Smonthadar		    IEEE80211_ACTION_CAT_SELF_PROT,
628195618Srpaulo		    IEEE80211_ACTION_MESHPEERING_CLOSE,
629195618Srpaulo		    args);
630195618Srpaulo	}
631195784Srpaulo	callout_drain(&ni->ni_mltimer);
632195618Srpaulo	/* XXX belongs in hwmp */
633195618Srpaulo	ieee80211_ageq_drain_node(&ic->ic_stageq,
634195618Srpaulo	   (void *)(uintptr_t) ieee80211_mac_hash(ic, ni->ni_macaddr));
635195618Srpaulo}
636195618Srpaulo
637195618Srpaulostatic void
638195618Srpaulomesh_vdetach(struct ieee80211vap *vap)
639195618Srpaulo{
640195618Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
641195618Srpaulo
642195784Srpaulo	callout_drain(&ms->ms_cleantimer);
643195618Srpaulo	ieee80211_iterate_nodes(&vap->iv_ic->ic_sta, mesh_vdetach_peers,
644195618Srpaulo	    NULL);
645195618Srpaulo	ieee80211_mesh_rt_flush(vap);
646283555Sadrian	MESH_RT_LOCK_DESTROY(ms);
647195618Srpaulo	ms->ms_ppath->mpp_vdetach(vap);
648283538Sadrian	IEEE80211_FREE(vap->iv_mesh, M_80211_VAP);
649195618Srpaulo	vap->iv_mesh = NULL;
650195618Srpaulo}
651195618Srpaulo
652195618Srpaulostatic void
653195618Srpaulomesh_vattach(struct ieee80211vap *vap)
654195618Srpaulo{
655195618Srpaulo	struct ieee80211_mesh_state *ms;
656195618Srpaulo	vap->iv_newstate = mesh_newstate;
657195618Srpaulo	vap->iv_input = mesh_input;
658195618Srpaulo	vap->iv_opdetach = mesh_vdetach;
659195618Srpaulo	vap->iv_recv_mgmt = mesh_recv_mgmt;
660205277Srpaulo	vap->iv_recv_ctl = mesh_recv_ctl;
661283538Sadrian	ms = IEEE80211_MALLOC(sizeof(struct ieee80211_mesh_state), M_80211_VAP,
662283538Sadrian	    IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
663195618Srpaulo	if (ms == NULL) {
664195618Srpaulo		printf("%s: couldn't alloc MBSS state\n", __func__);
665195784Srpaulo		return;
666195618Srpaulo	}
667195618Srpaulo	vap->iv_mesh = ms;
668195618Srpaulo	ms->ms_seq = 0;
669195618Srpaulo	ms->ms_flags = (IEEE80211_MESHFLAGS_AP | IEEE80211_MESHFLAGS_FWD);
670195618Srpaulo	ms->ms_ttl = IEEE80211_MESH_DEFAULT_TTL;
671246508Smonthadar	TAILQ_INIT(&ms->ms_known_gates);
672195618Srpaulo	TAILQ_INIT(&ms->ms_routes);
673283555Sadrian	MESH_RT_LOCK_INIT(ms, "MBSS");
674283291Sjkim	callout_init(&ms->ms_cleantimer, 1);
675283291Sjkim	callout_init(&ms->ms_gatetimer, 1);
676246506Smonthadar	ms->ms_gateseq = 0;
677195618Srpaulo	mesh_select_proto_metric(vap, "AIRTIME");
678195618Srpaulo	KASSERT(ms->ms_pmetric, ("ms_pmetric == NULL"));
679195618Srpaulo	mesh_select_proto_path(vap, "HWMP");
680195618Srpaulo	KASSERT(ms->ms_ppath, ("ms_ppath == NULL"));
681195618Srpaulo	ms->ms_ppath->mpp_vattach(vap);
682195618Srpaulo}
683195618Srpaulo
684195618Srpaulo/*
685195618Srpaulo * IEEE80211_M_MBSS vap state machine handler.
686195618Srpaulo */
687195618Srpaulostatic int
688195618Srpaulomesh_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
689195618Srpaulo{
690195618Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
691195618Srpaulo	struct ieee80211com *ic = vap->iv_ic;
692195618Srpaulo	struct ieee80211_node *ni;
693195618Srpaulo	enum ieee80211_state ostate;
694195618Srpaulo
695195618Srpaulo	IEEE80211_LOCK_ASSERT(ic);
696195618Srpaulo
697195618Srpaulo	ostate = vap->iv_state;
698195618Srpaulo	IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n",
699195618Srpaulo	    __func__, ieee80211_state_name[ostate],
700195618Srpaulo	    ieee80211_state_name[nstate], arg);
701195618Srpaulo	vap->iv_state = nstate;		/* state transition */
702195618Srpaulo	if (ostate != IEEE80211_S_SCAN)
703195618Srpaulo		ieee80211_cancel_scan(vap);	/* background scan */
704195618Srpaulo	ni = vap->iv_bss;			/* NB: no reference held */
705246506Smonthadar	if (nstate != IEEE80211_S_RUN && ostate == IEEE80211_S_RUN) {
706195784Srpaulo		callout_drain(&ms->ms_cleantimer);
707246506Smonthadar		callout_drain(&ms->ms_gatetimer);
708246506Smonthadar	}
709195618Srpaulo	switch (nstate) {
710195618Srpaulo	case IEEE80211_S_INIT:
711195618Srpaulo		switch (ostate) {
712195618Srpaulo		case IEEE80211_S_SCAN:
713195618Srpaulo			ieee80211_cancel_scan(vap);
714195618Srpaulo			break;
715195618Srpaulo		case IEEE80211_S_CAC:
716195618Srpaulo			ieee80211_dfs_cac_stop(vap);
717195618Srpaulo			break;
718195618Srpaulo		case IEEE80211_S_RUN:
719195618Srpaulo			ieee80211_iterate_nodes(&ic->ic_sta,
720195618Srpaulo			    mesh_vdetach_peers, NULL);
721195618Srpaulo			break;
722195618Srpaulo		default:
723195618Srpaulo			break;
724195618Srpaulo		}
725195618Srpaulo		if (ostate != IEEE80211_S_INIT) {
726195618Srpaulo			/* NB: optimize INIT -> INIT case */
727195618Srpaulo			ieee80211_reset_bss(vap);
728195784Srpaulo			ieee80211_mesh_rt_flush(vap);
729195618Srpaulo		}
730195618Srpaulo		break;
731195618Srpaulo	case IEEE80211_S_SCAN:
732195618Srpaulo		switch (ostate) {
733195618Srpaulo		case IEEE80211_S_INIT:
734195618Srpaulo			if (vap->iv_des_chan != IEEE80211_CHAN_ANYC &&
735195618Srpaulo			    !IEEE80211_IS_CHAN_RADAR(vap->iv_des_chan) &&
736195618Srpaulo			    ms->ms_idlen != 0) {
737195618Srpaulo				/*
738195618Srpaulo				 * Already have a channel and a mesh ID; bypass
739195618Srpaulo				 * the scan and startup immediately.
740195618Srpaulo				 */
741195618Srpaulo				ieee80211_create_ibss(vap, vap->iv_des_chan);
742195618Srpaulo				break;
743195618Srpaulo			}
744195618Srpaulo			/*
745195618Srpaulo			 * Initiate a scan.  We can come here as a result
746195618Srpaulo			 * of an IEEE80211_IOC_SCAN_REQ too in which case
747195618Srpaulo			 * the vap will be marked with IEEE80211_FEXT_SCANREQ
748195618Srpaulo			 * and the scan request parameters will be present
749195618Srpaulo			 * in iv_scanreq.  Otherwise we do the default.
750195618Srpaulo			*/
751195618Srpaulo			if (vap->iv_flags_ext & IEEE80211_FEXT_SCANREQ) {
752195618Srpaulo				ieee80211_check_scan(vap,
753195618Srpaulo				    vap->iv_scanreq_flags,
754195618Srpaulo				    vap->iv_scanreq_duration,
755195618Srpaulo				    vap->iv_scanreq_mindwell,
756195618Srpaulo				    vap->iv_scanreq_maxdwell,
757195618Srpaulo				    vap->iv_scanreq_nssid, vap->iv_scanreq_ssid);
758195618Srpaulo				vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANREQ;
759195618Srpaulo			} else
760195618Srpaulo				ieee80211_check_scan_current(vap);
761195618Srpaulo			break;
762195618Srpaulo		default:
763195618Srpaulo			break;
764195618Srpaulo		}
765195618Srpaulo		break;
766195618Srpaulo	case IEEE80211_S_CAC:
767195618Srpaulo		/*
768195618Srpaulo		 * Start CAC on a DFS channel.  We come here when starting
769195618Srpaulo		 * a bss on a DFS channel (see ieee80211_create_ibss).
770195618Srpaulo		 */
771195618Srpaulo		ieee80211_dfs_cac_start(vap);
772195618Srpaulo		break;
773195618Srpaulo	case IEEE80211_S_RUN:
774195618Srpaulo		switch (ostate) {
775195618Srpaulo		case IEEE80211_S_INIT:
776195618Srpaulo			/*
777195618Srpaulo			 * Already have a channel; bypass the
778195618Srpaulo			 * scan and startup immediately.
779195618Srpaulo			 * Note that ieee80211_create_ibss will call
780195618Srpaulo			 * back to do a RUN->RUN state change.
781195618Srpaulo			 */
782195618Srpaulo			ieee80211_create_ibss(vap,
783195618Srpaulo			    ieee80211_ht_adjust_channel(ic,
784195618Srpaulo				ic->ic_curchan, vap->iv_flags_ht));
785195618Srpaulo			/* NB: iv_bss is changed on return */
786195618Srpaulo			break;
787195618Srpaulo		case IEEE80211_S_CAC:
788195618Srpaulo			/*
789195618Srpaulo			 * NB: This is the normal state change when CAC
790195618Srpaulo			 * expires and no radar was detected; no need to
791195618Srpaulo			 * clear the CAC timer as it's already expired.
792195618Srpaulo			 */
793195618Srpaulo			/* fall thru... */
794195618Srpaulo		case IEEE80211_S_CSA:
795195618Srpaulo#if 0
796195618Srpaulo			/*
797195618Srpaulo			 * Shorten inactivity timer of associated stations
798195618Srpaulo			 * to weed out sta's that don't follow a CSA.
799195618Srpaulo			 */
800195618Srpaulo			ieee80211_iterate_nodes(&ic->ic_sta, sta_csa, vap);
801195618Srpaulo#endif
802195618Srpaulo			/*
803195618Srpaulo			 * Update bss node channel to reflect where
804195618Srpaulo			 * we landed after CSA.
805195618Srpaulo			 */
806300232Savos			ieee80211_node_set_chan(ni,
807195618Srpaulo			    ieee80211_ht_adjust_channel(ic, ic->ic_curchan,
808300232Savos				ieee80211_htchanflags(ni->ni_chan)));
809195618Srpaulo			/* XXX bypass debug msgs */
810195618Srpaulo			break;
811195618Srpaulo		case IEEE80211_S_SCAN:
812195618Srpaulo		case IEEE80211_S_RUN:
813195618Srpaulo#ifdef IEEE80211_DEBUG
814195618Srpaulo			if (ieee80211_msg_debug(vap)) {
815195618Srpaulo				ieee80211_note(vap,
816195618Srpaulo				    "synchronized with %s meshid ",
817195618Srpaulo				    ether_sprintf(ni->ni_meshid));
818195618Srpaulo				ieee80211_print_essid(ni->ni_meshid,
819195618Srpaulo				    ni->ni_meshidlen);
820195618Srpaulo				/* XXX MCS/HT */
821195618Srpaulo				printf(" channel %d\n",
822195618Srpaulo				    ieee80211_chan2ieee(ic, ic->ic_curchan));
823195618Srpaulo			}
824195618Srpaulo#endif
825195618Srpaulo			break;
826195618Srpaulo		default:
827195618Srpaulo			break;
828195618Srpaulo		}
829300232Savos		ieee80211_node_authorize(ni);
830195813Ssam		callout_reset(&ms->ms_cleantimer, ms->ms_ppath->mpp_inact,
831195784Srpaulo                    mesh_rt_cleanup_cb, vap);
832246506Smonthadar		mesh_gatemode_setup(vap);
833195618Srpaulo		break;
834195618Srpaulo	default:
835195618Srpaulo		break;
836195618Srpaulo	}
837195618Srpaulo	/* NB: ostate not nstate */
838195618Srpaulo	ms->ms_ppath->mpp_newstate(vap, ostate, arg);
839195618Srpaulo	return 0;
840195618Srpaulo}
841195618Srpaulo
842195784Srpaulostatic void
843195784Srpaulomesh_rt_cleanup_cb(void *arg)
844195784Srpaulo{
845195784Srpaulo	struct ieee80211vap *vap = arg;
846195784Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
847195784Srpaulo
848195784Srpaulo	mesh_rt_flush_invalid(vap);
849195813Ssam	callout_reset(&ms->ms_cleantimer, ms->ms_ppath->mpp_inact,
850195784Srpaulo	    mesh_rt_cleanup_cb, vap);
851195784Srpaulo}
852195784Srpaulo
853246509Smonthadar/*
854246509Smonthadar * Mark a mesh STA as gate and return a pointer to it.
855246509Smonthadar * If this is first time, we create a new gate route.
856246509Smonthadar * Always update the path route to this mesh gate.
857246509Smonthadar */
858246509Smonthadarstruct ieee80211_mesh_gate_route *
859246509Smonthadarieee80211_mesh_mark_gate(struct ieee80211vap *vap, const uint8_t *addr,
860246509Smonthadar    struct ieee80211_mesh_route *rt)
861246509Smonthadar{
862246509Smonthadar	struct ieee80211_mesh_state *ms = vap->iv_mesh;
863246509Smonthadar	struct ieee80211_mesh_gate_route *gr = NULL, *next;
864246509Smonthadar	int found = 0;
865195784Srpaulo
866246509Smonthadar	MESH_RT_LOCK(ms);
867246509Smonthadar	TAILQ_FOREACH_SAFE(gr, &ms->ms_known_gates, gr_next, next) {
868246509Smonthadar		if (IEEE80211_ADDR_EQ(gr->gr_addr, addr)) {
869246509Smonthadar			found = 1;
870246509Smonthadar			break;
871246509Smonthadar		}
872246509Smonthadar	}
873246509Smonthadar
874246509Smonthadar	if (!found) {
875246509Smonthadar		/* New mesh gate add it to known table. */
876246509Smonthadar		IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, addr,
877246509Smonthadar		    "%s", "stored new gate information from pro-PREQ.");
878283538Sadrian		gr = IEEE80211_MALLOC(ALIGN(sizeof(struct ieee80211_mesh_gate_route)),
879283538Sadrian		    M_80211_MESH_GT_RT,
880283538Sadrian		    IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
881246509Smonthadar		IEEE80211_ADDR_COPY(gr->gr_addr, addr);
882246509Smonthadar		TAILQ_INSERT_TAIL(&ms->ms_known_gates, gr, gr_next);
883246509Smonthadar	}
884246509Smonthadar	gr->gr_route = rt;
885246509Smonthadar	/* TODO: link from path route to gate route */
886246509Smonthadar	MESH_RT_UNLOCK(ms);
887246509Smonthadar
888246509Smonthadar	return gr;
889246509Smonthadar}
890246509Smonthadar
891246509Smonthadar
892195618Srpaulo/*
893195618Srpaulo * Helper function to note the Mesh Peer Link FSM change.
894195618Srpaulo */
895195618Srpaulostatic void
896195618Srpaulomesh_linkchange(struct ieee80211_node *ni, enum ieee80211_mesh_mlstate state)
897195618Srpaulo{
898195618Srpaulo	struct ieee80211vap *vap = ni->ni_vap;
899195618Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
900195618Srpaulo#ifdef IEEE80211_DEBUG
901195618Srpaulo	static const char *meshlinkstates[] = {
902195618Srpaulo		[IEEE80211_NODE_MESH_IDLE]		= "IDLE",
903195618Srpaulo		[IEEE80211_NODE_MESH_OPENSNT]		= "OPEN SENT",
904195618Srpaulo		[IEEE80211_NODE_MESH_OPENRCV]		= "OPEN RECEIVED",
905195618Srpaulo		[IEEE80211_NODE_MESH_CONFIRMRCV]	= "CONFIRM RECEIVED",
906195618Srpaulo		[IEEE80211_NODE_MESH_ESTABLISHED]	= "ESTABLISHED",
907195618Srpaulo		[IEEE80211_NODE_MESH_HOLDING]		= "HOLDING"
908195618Srpaulo	};
909195618Srpaulo#endif
910195618Srpaulo	IEEE80211_NOTE(vap, IEEE80211_MSG_MESH,
911195618Srpaulo	    ni, "peer link: %s -> %s",
912195618Srpaulo	    meshlinkstates[ni->ni_mlstate], meshlinkstates[state]);
913195618Srpaulo
914195618Srpaulo	/* track neighbor count */
915195618Srpaulo	if (state == IEEE80211_NODE_MESH_ESTABLISHED &&
916195618Srpaulo	    ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED) {
917195618Srpaulo		KASSERT(ms->ms_neighbors < 65535, ("neighbor count overflow"));
918195618Srpaulo		ms->ms_neighbors++;
919198242Srpaulo		ieee80211_beacon_notify(vap, IEEE80211_BEACON_MESHCONF);
920195618Srpaulo	} else if (ni->ni_mlstate == IEEE80211_NODE_MESH_ESTABLISHED &&
921195618Srpaulo	    state != IEEE80211_NODE_MESH_ESTABLISHED) {
922195618Srpaulo		KASSERT(ms->ms_neighbors > 0, ("neighbor count 0"));
923195618Srpaulo		ms->ms_neighbors--;
924198242Srpaulo		ieee80211_beacon_notify(vap, IEEE80211_BEACON_MESHCONF);
925195618Srpaulo	}
926195618Srpaulo	ni->ni_mlstate = state;
927195784Srpaulo	switch (state) {
928195784Srpaulo	case IEEE80211_NODE_MESH_HOLDING:
929195618Srpaulo		ms->ms_ppath->mpp_peerdown(ni);
930195784Srpaulo		break;
931195784Srpaulo	case IEEE80211_NODE_MESH_ESTABLISHED:
932195784Srpaulo		ieee80211_mesh_discover(vap, ni->ni_macaddr, NULL);
933195784Srpaulo		break;
934195784Srpaulo	default:
935195784Srpaulo		break;
936195784Srpaulo	}
937195618Srpaulo}
938195618Srpaulo
939195618Srpaulo/*
940195618Srpaulo * Helper function to generate a unique local ID required for mesh
941195618Srpaulo * peer establishment.
942195618Srpaulo */
943195618Srpaulostatic void
944195618Srpaulomesh_checkid(void *arg, struct ieee80211_node *ni)
945195618Srpaulo{
946195618Srpaulo	uint16_t *r = arg;
947195618Srpaulo
948195618Srpaulo	if (*r == ni->ni_mllid)
949195618Srpaulo		*(uint16_t *)arg = 0;
950195618Srpaulo}
951195618Srpaulo
952195618Srpaulostatic uint32_t
953195618Srpaulomesh_generateid(struct ieee80211vap *vap)
954195618Srpaulo{
955195618Srpaulo	int maxiter = 4;
956195618Srpaulo	uint16_t r;
957195618Srpaulo
958195618Srpaulo	do {
959195618Srpaulo		get_random_bytes(&r, 2);
960195618Srpaulo		ieee80211_iterate_nodes(&vap->iv_ic->ic_sta, mesh_checkid, &r);
961195618Srpaulo		maxiter--;
962195618Srpaulo	} while (r == 0 && maxiter > 0);
963195618Srpaulo	return r;
964195618Srpaulo}
965195618Srpaulo
966195618Srpaulo/*
967195618Srpaulo * Verifies if we already received this packet by checking its
968195618Srpaulo * sequence number.
969195908Srpaulo * Returns 0 if the frame is to be accepted, 1 otherwise.
970195618Srpaulo */
971195618Srpaulostatic int
972195618Srpaulomesh_checkpseq(struct ieee80211vap *vap,
973195618Srpaulo    const uint8_t source[IEEE80211_ADDR_LEN], uint32_t seq)
974195618Srpaulo{
975195618Srpaulo	struct ieee80211_mesh_route *rt;
976195618Srpaulo
977195618Srpaulo	rt = ieee80211_mesh_rt_find(vap, source);
978195618Srpaulo	if (rt == NULL) {
979195908Srpaulo		rt = ieee80211_mesh_rt_add(vap, source);
980195908Srpaulo		if (rt == NULL) {
981195908Srpaulo			IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, source,
982195908Srpaulo			    "%s", "add mcast route failed");
983195908Srpaulo			vap->iv_stats.is_mesh_rtaddfailed++;
984195908Srpaulo			return 1;
985195908Srpaulo		}
986195784Srpaulo		IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, source,
987195784Srpaulo		    "add mcast route, mesh seqno %d", seq);
988195908Srpaulo		rt->rt_lastmseq = seq;
989195618Srpaulo		return 0;
990195618Srpaulo	}
991195618Srpaulo	if (IEEE80211_MESH_SEQ_GEQ(rt->rt_lastmseq, seq)) {
992195618Srpaulo		return 1;
993195618Srpaulo	} else {
994195618Srpaulo		rt->rt_lastmseq = seq;
995195618Srpaulo		return 0;
996195618Srpaulo	}
997195618Srpaulo}
998195618Srpaulo
999195618Srpaulo/*
1000195618Srpaulo * Iterate the routing table and locate the next hop.
1001195618Srpaulo */
1002246512Smonthadarstruct ieee80211_node *
1003246512Smonthadarieee80211_mesh_find_txnode(struct ieee80211vap *vap,
1004195618Srpaulo    const uint8_t dest[IEEE80211_ADDR_LEN])
1005195618Srpaulo{
1006195618Srpaulo	struct ieee80211_mesh_route *rt;
1007195618Srpaulo
1008195618Srpaulo	rt = ieee80211_mesh_rt_find(vap, dest);
1009195618Srpaulo	if (rt == NULL)
1010195618Srpaulo		return NULL;
1011246511Smonthadar	if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0) {
1012195784Srpaulo		IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, dest,
1013246511Smonthadar		    "%s: !valid, flags 0x%x", __func__, rt->rt_flags);
1014195784Srpaulo		/* XXX stat */
1015195784Srpaulo		return NULL;
1016195784Srpaulo	}
1017246511Smonthadar	if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY) {
1018246511Smonthadar		rt = ieee80211_mesh_rt_find(vap, rt->rt_mesh_gate);
1019246511Smonthadar		if (rt == NULL) return NULL;
1020246511Smonthadar		if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0) {
1021246511Smonthadar			IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, dest,
1022246511Smonthadar			    "%s: meshgate !valid, flags 0x%x", __func__,
1023246511Smonthadar			    rt->rt_flags);
1024246511Smonthadar			/* XXX stat */
1025246511Smonthadar			return NULL;
1026246511Smonthadar		}
1027246511Smonthadar	}
1028195618Srpaulo	return ieee80211_find_txnode(vap, rt->rt_nexthop);
1029195618Srpaulo}
1030195618Srpaulo
1031246511Smonthadarstatic void
1032246511Smonthadarmesh_transmit_to_gate(struct ieee80211vap *vap, struct mbuf *m,
1033246511Smonthadar    struct ieee80211_mesh_route *rt_gate)
1034246511Smonthadar{
1035246511Smonthadar	struct ifnet *ifp = vap->iv_ifp;
1036246511Smonthadar	struct ieee80211_node *ni;
1037246511Smonthadar
1038253745Sadrian	IEEE80211_TX_UNLOCK_ASSERT(vap->iv_ic);
1039248069Sadrian
1040246512Smonthadar	ni = ieee80211_mesh_find_txnode(vap, rt_gate->rt_dest);
1041246511Smonthadar	if (ni == NULL) {
1042271861Sglebius		if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
1043246511Smonthadar		m_freem(m);
1044246511Smonthadar		return;
1045246511Smonthadar	}
1046246511Smonthadar
1047246511Smonthadar	/*
1048253727Sadrian	 * Send through the VAP packet transmit path.
1049253727Sadrian	 * This consumes the node ref grabbed above and
1050253727Sadrian	 * the mbuf, regardless of whether there's a problem
1051253727Sadrian	 * or not.
1052246511Smonthadar	 */
1053253727Sadrian	(void) ieee80211_vap_pkt_send_dest(vap, m, ni);
1054246511Smonthadar}
1055246511Smonthadar
1056195618Srpaulo/*
1057246510Smonthadar * Forward the queued frames to known valid mesh gates.
1058246510Smonthadar * Assume destination to be outside the MBSS (i.e. proxy entry),
1059246510Smonthadar * If no valid mesh gates are known silently discard queued frames.
1060246511Smonthadar * After transmitting frames to all known valid mesh gates, this route
1061246511Smonthadar * will be marked invalid, and a new path discovery will happen in the hopes
1062246511Smonthadar * that (at least) one of the mesh gates have a new proxy entry for us to use.
1063246510Smonthadar */
1064246510Smonthadarvoid
1065246510Smonthadarieee80211_mesh_forward_to_gates(struct ieee80211vap *vap,
1066246510Smonthadar    struct ieee80211_mesh_route *rt_dest)
1067246510Smonthadar{
1068246510Smonthadar	struct ieee80211com *ic = vap->iv_ic;
1069246510Smonthadar	struct ieee80211_mesh_state *ms = vap->iv_mesh;
1070246510Smonthadar	struct ieee80211_mesh_route *rt_gate;
1071246510Smonthadar	struct ieee80211_mesh_gate_route *gr = NULL, *gr_next;
1072246511Smonthadar	struct mbuf *m, *mcopy, *next;
1073246510Smonthadar
1074248069Sadrian	IEEE80211_TX_UNLOCK_ASSERT(ic);
1075248069Sadrian
1076246510Smonthadar	KASSERT( rt_dest->rt_flags == IEEE80211_MESHRT_FLAGS_DISCOVER,
1077246510Smonthadar	    ("Route is not marked with IEEE80211_MESHRT_FLAGS_DISCOVER"));
1078246510Smonthadar
1079246510Smonthadar	/* XXX: send to more than one valid mash gate */
1080246510Smonthadar	MESH_RT_LOCK(ms);
1081246511Smonthadar
1082246511Smonthadar	m = ieee80211_ageq_remove(&ic->ic_stageq,
1083246511Smonthadar	    (struct ieee80211_node *)(uintptr_t)
1084246511Smonthadar	    ieee80211_mac_hash(ic, rt_dest->rt_dest));
1085246511Smonthadar
1086246510Smonthadar	TAILQ_FOREACH_SAFE(gr, &ms->ms_known_gates, gr_next, gr_next) {
1087246510Smonthadar		rt_gate = gr->gr_route;
1088246510Smonthadar		if (rt_gate == NULL) {
1089246510Smonthadar			IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP,
1090246510Smonthadar				rt_dest->rt_dest,
1091246510Smonthadar				"mesh gate with no path %6D",
1092246510Smonthadar				gr->gr_addr, ":");
1093246510Smonthadar			continue;
1094246510Smonthadar		}
1095246511Smonthadar		if ((rt_gate->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0)
1096246511Smonthadar			continue;
1097246511Smonthadar		KASSERT(rt_gate->rt_flags & IEEE80211_MESHRT_FLAGS_GATE,
1098246511Smonthadar		    ("route not marked as a mesh gate"));
1099246511Smonthadar		KASSERT((rt_gate->rt_flags &
1100246511Smonthadar			IEEE80211_MESHRT_FLAGS_PROXY) == 0,
1101246511Smonthadar			("found mesh gate that is also marked porxy"));
1102246511Smonthadar		/*
1103246511Smonthadar		 * convert route to a proxy route gated by the current
1104246511Smonthadar		 * mesh gate, this is needed so encap can built data
1105246511Smonthadar		 * frame with correct address.
1106246511Smonthadar		 */
1107246510Smonthadar		rt_dest->rt_flags = IEEE80211_MESHRT_FLAGS_PROXY |
1108246510Smonthadar			IEEE80211_MESHRT_FLAGS_VALID;
1109246510Smonthadar		rt_dest->rt_ext_seq = 1; /* random value */
1110246510Smonthadar		IEEE80211_ADDR_COPY(rt_dest->rt_mesh_gate, rt_gate->rt_dest);
1111246510Smonthadar		IEEE80211_ADDR_COPY(rt_dest->rt_nexthop, rt_gate->rt_nexthop);
1112246510Smonthadar		rt_dest->rt_metric = rt_gate->rt_metric;
1113246510Smonthadar		rt_dest->rt_nhops = rt_gate->rt_nhops;
1114246510Smonthadar		ieee80211_mesh_rt_update(rt_dest, ms->ms_ppath->mpp_inact);
1115246510Smonthadar		MESH_RT_UNLOCK(ms);
1116246511Smonthadar		/* XXX: lock?? */
1117246511Smonthadar		mcopy = m_dup(m, M_NOWAIT);
1118246511Smonthadar		for (; mcopy != NULL; mcopy = next) {
1119246511Smonthadar			next = mcopy->m_nextpkt;
1120246511Smonthadar			mcopy->m_nextpkt = NULL;
1121246511Smonthadar			IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP,
1122246511Smonthadar			    rt_dest->rt_dest,
1123246511Smonthadar			    "flush queued frame %p len %d", mcopy,
1124246511Smonthadar			    mcopy->m_pkthdr.len);
1125246511Smonthadar			mesh_transmit_to_gate(vap, mcopy, rt_gate);
1126246510Smonthadar		}
1127246510Smonthadar		MESH_RT_LOCK(ms);
1128246510Smonthadar	}
1129246511Smonthadar	rt_dest->rt_flags = 0; /* Mark invalid */
1130246511Smonthadar	m_freem(m);
1131246510Smonthadar	MESH_RT_UNLOCK(ms);
1132246510Smonthadar}
1133246510Smonthadar
1134246510Smonthadar/*
1135195618Srpaulo * Forward the specified frame.
1136195618Srpaulo * Decrement the TTL and set TA to our MAC address.
1137195618Srpaulo */
1138195618Srpaulostatic void
1139195618Srpaulomesh_forward(struct ieee80211vap *vap, struct mbuf *m,
1140195618Srpaulo    const struct ieee80211_meshcntl *mc)
1141195618Srpaulo{
1142195618Srpaulo	struct ieee80211com *ic = vap->iv_ic;
1143195618Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
1144195618Srpaulo	struct ifnet *ifp = vap->iv_ifp;
1145195618Srpaulo	const struct ieee80211_frame *wh =
1146195618Srpaulo	    mtod(m, const struct ieee80211_frame *);
1147195618Srpaulo	struct mbuf *mcopy;
1148195618Srpaulo	struct ieee80211_meshcntl *mccopy;
1149195618Srpaulo	struct ieee80211_frame *whcopy;
1150195618Srpaulo	struct ieee80211_node *ni;
1151195618Srpaulo	int err;
1152195618Srpaulo
1153248069Sadrian	/* This is called from the RX path - don't hold this lock */
1154248069Sadrian	IEEE80211_TX_UNLOCK_ASSERT(ic);
1155248069Sadrian
1156234878Smonthadar	/*
1157298995Spfg	 * mesh ttl of 1 means we are the last one receiving it,
1158234878Smonthadar	 * according to amendment we decrement and then check if
1159234878Smonthadar	 * 0, if so we dont forward.
1160234878Smonthadar	 */
1161234878Smonthadar	if (mc->mc_ttl < 1) {
1162195618Srpaulo		IEEE80211_NOTE_FRAME(vap, IEEE80211_MSG_MESH, wh,
1163234878Smonthadar		    "%s", "frame not fwd'd, ttl 1");
1164195618Srpaulo		vap->iv_stats.is_mesh_fwd_ttl++;
1165195618Srpaulo		return;
1166195618Srpaulo	}
1167195618Srpaulo	if (!(ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) {
1168195618Srpaulo		IEEE80211_NOTE_FRAME(vap, IEEE80211_MSG_MESH, wh,
1169195618Srpaulo		    "%s", "frame not fwd'd, fwding disabled");
1170195618Srpaulo		vap->iv_stats.is_mesh_fwd_disabled++;
1171195618Srpaulo		return;
1172195618Srpaulo	}
1173243882Sglebius	mcopy = m_dup(m, M_NOWAIT);
1174195618Srpaulo	if (mcopy == NULL) {
1175195618Srpaulo		IEEE80211_NOTE_FRAME(vap, IEEE80211_MSG_MESH, wh,
1176195618Srpaulo		    "%s", "frame not fwd'd, cannot dup");
1177195618Srpaulo		vap->iv_stats.is_mesh_fwd_nobuf++;
1178271861Sglebius		if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
1179195618Srpaulo		return;
1180195618Srpaulo	}
1181195618Srpaulo	mcopy = m_pullup(mcopy, ieee80211_hdrspace(ic, wh) +
1182195618Srpaulo	    sizeof(struct ieee80211_meshcntl));
1183195618Srpaulo	if (mcopy == NULL) {
1184195618Srpaulo		IEEE80211_NOTE_FRAME(vap, IEEE80211_MSG_MESH, wh,
1185195618Srpaulo		    "%s", "frame not fwd'd, too short");
1186195618Srpaulo		vap->iv_stats.is_mesh_fwd_tooshort++;
1187271861Sglebius		if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
1188195618Srpaulo		m_freem(mcopy);
1189195618Srpaulo		return;
1190195618Srpaulo	}
1191195618Srpaulo	whcopy = mtod(mcopy, struct ieee80211_frame *);
1192195618Srpaulo	mccopy = (struct ieee80211_meshcntl *)
1193195618Srpaulo	    (mtod(mcopy, uint8_t *) + ieee80211_hdrspace(ic, wh));
1194195618Srpaulo	/* XXX clear other bits? */
1195195618Srpaulo	whcopy->i_fc[1] &= ~IEEE80211_FC1_RETRY;
1196195618Srpaulo	IEEE80211_ADDR_COPY(whcopy->i_addr2, vap->iv_myaddr);
1197195618Srpaulo	if (IEEE80211_IS_MULTICAST(wh->i_addr1)) {
1198195618Srpaulo		ni = ieee80211_ref_node(vap->iv_bss);
1199195618Srpaulo		mcopy->m_flags |= M_MCAST;
1200195618Srpaulo	} else {
1201246512Smonthadar		ni = ieee80211_mesh_find_txnode(vap, whcopy->i_addr3);
1202195618Srpaulo		if (ni == NULL) {
1203234878Smonthadar			/*
1204234878Smonthadar			 * [Optional] any of the following three actions:
1205234878Smonthadar			 * o silently discard
1206234878Smonthadar			 * o trigger a path discovery
1207234890Smonthadar			 * o inform TA that meshDA is unknown.
1208234878Smonthadar			 */
1209195618Srpaulo			IEEE80211_NOTE_FRAME(vap, IEEE80211_MSG_MESH, wh,
1210195618Srpaulo			    "%s", "frame not fwd'd, no path");
1211234890Smonthadar			ms->ms_ppath->mpp_senderror(vap, whcopy->i_addr3, NULL,
1212234890Smonthadar			    IEEE80211_REASON_MESH_PERR_NO_FI);
1213195618Srpaulo			vap->iv_stats.is_mesh_fwd_nopath++;
1214195618Srpaulo			m_freem(mcopy);
1215195618Srpaulo			return;
1216195618Srpaulo		}
1217195618Srpaulo		IEEE80211_ADDR_COPY(whcopy->i_addr1, ni->ni_macaddr);
1218195618Srpaulo	}
1219195618Srpaulo	KASSERT(mccopy->mc_ttl > 0, ("%s called with wrong ttl", __func__));
1220195618Srpaulo	mccopy->mc_ttl--;
1221195618Srpaulo
1222195618Srpaulo	/* XXX calculate priority so drivers can find the tx queue */
1223195618Srpaulo	M_WME_SETAC(mcopy, WME_AC_BE);
1224195618Srpaulo
1225195618Srpaulo	/* XXX do we know m_nextpkt is NULL? */
1226195618Srpaulo	mcopy->m_pkthdr.rcvif = (void *) ni;
1227248069Sadrian
1228248069Sadrian	/*
1229248069Sadrian	 * XXX this bypasses all of the VAP TX handling; it passes frames
1230248069Sadrian	 * directly to the parent interface.
1231248069Sadrian	 *
1232248069Sadrian	 * Because of this, there's no TX lock being held as there's no
1233248069Sadrian	 * encaps state being used.
1234248069Sadrian	 *
1235248069Sadrian	 * Doing a direct parent transmit may not be the correct thing
1236248069Sadrian	 * to do here; we'll have to re-think this soon.
1237248069Sadrian	 */
1238248069Sadrian	IEEE80211_TX_LOCK(ic);
1239254082Sadrian	err = ieee80211_parent_xmitpkt(ic, mcopy);
1240248069Sadrian	IEEE80211_TX_UNLOCK(ic);
1241289164Sadrian	if (!err)
1242271861Sglebius		if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
1243195618Srpaulo}
1244195618Srpaulo
1245195784Srpaulostatic struct mbuf *
1246195784Srpaulomesh_decap(struct ieee80211vap *vap, struct mbuf *m, int hdrlen, int meshdrlen)
1247195784Srpaulo{
1248234878Smonthadar#define	WHDIR(wh)	((wh)->i_fc[1] & IEEE80211_FC1_DIR_MASK)
1249234878Smonthadar#define	MC01(mc)	((const struct ieee80211_meshcntl_ae01 *)mc)
1250195784Srpaulo	uint8_t b[sizeof(struct ieee80211_qosframe_addr4) +
1251234878Smonthadar		  sizeof(struct ieee80211_meshcntl_ae10)];
1252195784Srpaulo	const struct ieee80211_qosframe_addr4 *wh;
1253195784Srpaulo	const struct ieee80211_meshcntl_ae10 *mc;
1254195784Srpaulo	struct ether_header *eh;
1255195784Srpaulo	struct llc *llc;
1256195784Srpaulo	int ae;
1257195784Srpaulo
1258195784Srpaulo	if (m->m_len < hdrlen + sizeof(*llc) &&
1259195784Srpaulo	    (m = m_pullup(m, hdrlen + sizeof(*llc))) == NULL) {
1260195784Srpaulo		IEEE80211_DPRINTF(vap, IEEE80211_MSG_ANY,
1261195784Srpaulo		    "discard data frame: %s", "m_pullup failed");
1262195784Srpaulo		vap->iv_stats.is_rx_tooshort++;
1263195784Srpaulo		return NULL;
1264195784Srpaulo	}
1265195784Srpaulo	memcpy(b, mtod(m, caddr_t), hdrlen);
1266195784Srpaulo	wh = (const struct ieee80211_qosframe_addr4 *)&b[0];
1267195784Srpaulo	mc = (const struct ieee80211_meshcntl_ae10 *)&b[hdrlen - meshdrlen];
1268195784Srpaulo	KASSERT(WHDIR(wh) == IEEE80211_FC1_DIR_FROMDS ||
1269195784Srpaulo		WHDIR(wh) == IEEE80211_FC1_DIR_DSTODS,
1270195784Srpaulo	    ("bogus dir, fc 0x%x:0x%x", wh->i_fc[0], wh->i_fc[1]));
1271195784Srpaulo
1272195784Srpaulo	llc = (struct llc *)(mtod(m, caddr_t) + hdrlen);
1273195784Srpaulo	if (llc->llc_dsap == LLC_SNAP_LSAP && llc->llc_ssap == LLC_SNAP_LSAP &&
1274195784Srpaulo	    llc->llc_control == LLC_UI && llc->llc_snap.org_code[0] == 0 &&
1275195784Srpaulo	    llc->llc_snap.org_code[1] == 0 && llc->llc_snap.org_code[2] == 0 &&
1276195784Srpaulo	    /* NB: preserve AppleTalk frames that have a native SNAP hdr */
1277195784Srpaulo	    !(llc->llc_snap.ether_type == htons(ETHERTYPE_AARP) ||
1278195784Srpaulo	      llc->llc_snap.ether_type == htons(ETHERTYPE_IPX))) {
1279195784Srpaulo		m_adj(m, hdrlen + sizeof(struct llc) - sizeof(*eh));
1280195784Srpaulo		llc = NULL;
1281195784Srpaulo	} else {
1282195784Srpaulo		m_adj(m, hdrlen - sizeof(*eh));
1283195784Srpaulo	}
1284195784Srpaulo	eh = mtod(m, struct ether_header *);
1285234878Smonthadar	ae = mc->mc_flags & IEEE80211_MESH_AE_MASK;
1286195784Srpaulo	if (WHDIR(wh) == IEEE80211_FC1_DIR_FROMDS) {
1287195784Srpaulo		IEEE80211_ADDR_COPY(eh->ether_dhost, wh->i_addr1);
1288234878Smonthadar		if (ae == IEEE80211_MESH_AE_00) {
1289195784Srpaulo			IEEE80211_ADDR_COPY(eh->ether_shost, wh->i_addr3);
1290234878Smonthadar		} else if (ae == IEEE80211_MESH_AE_01) {
1291234878Smonthadar			IEEE80211_ADDR_COPY(eh->ether_shost,
1292234878Smonthadar			    MC01(mc)->mc_addr4);
1293195784Srpaulo		} else {
1294195784Srpaulo			IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
1295195784Srpaulo			    (const struct ieee80211_frame *)wh, NULL,
1296195784Srpaulo			    "bad AE %d", ae);
1297195784Srpaulo			vap->iv_stats.is_mesh_badae++;
1298195784Srpaulo			m_freem(m);
1299195784Srpaulo			return NULL;
1300195784Srpaulo		}
1301195784Srpaulo	} else {
1302234878Smonthadar		if (ae == IEEE80211_MESH_AE_00) {
1303195784Srpaulo			IEEE80211_ADDR_COPY(eh->ether_dhost, wh->i_addr3);
1304195784Srpaulo			IEEE80211_ADDR_COPY(eh->ether_shost, wh->i_addr4);
1305234878Smonthadar		} else if (ae == IEEE80211_MESH_AE_10) {
1306234878Smonthadar			IEEE80211_ADDR_COPY(eh->ether_dhost, mc->mc_addr5);
1307234878Smonthadar			IEEE80211_ADDR_COPY(eh->ether_shost, mc->mc_addr6);
1308195784Srpaulo		} else {
1309195784Srpaulo			IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
1310195784Srpaulo			    (const struct ieee80211_frame *)wh, NULL,
1311195784Srpaulo			    "bad AE %d", ae);
1312195784Srpaulo			vap->iv_stats.is_mesh_badae++;
1313195784Srpaulo			m_freem(m);
1314195784Srpaulo			return NULL;
1315195784Srpaulo		}
1316195784Srpaulo	}
1317246710Sglebius#ifndef __NO_STRICT_ALIGNMENT
1318195784Srpaulo	if (!ALIGNED_POINTER(mtod(m, caddr_t) + sizeof(*eh), uint32_t)) {
1319195784Srpaulo		m = ieee80211_realign(vap, m, sizeof(*eh));
1320195784Srpaulo		if (m == NULL)
1321195784Srpaulo			return NULL;
1322195784Srpaulo	}
1323246710Sglebius#endif /* !__NO_STRICT_ALIGNMENT */
1324195784Srpaulo	if (llc != NULL) {
1325195784Srpaulo		eh = mtod(m, struct ether_header *);
1326195784Srpaulo		eh->ether_type = htons(m->m_pkthdr.len - sizeof(*eh));
1327195784Srpaulo	}
1328195784Srpaulo	return m;
1329234878Smonthadar#undef	WDIR
1330234878Smonthadar#undef	MC01
1331195784Srpaulo}
1332195784Srpaulo
1333195784Srpaulo/*
1334195784Srpaulo * Return non-zero if the unicast mesh data frame should be processed
1335195784Srpaulo * locally.  Frames that are not proxy'd have our address, otherwise
1336195784Srpaulo * we need to consult the routing table to look for a proxy entry.
1337195784Srpaulo */
1338195784Srpaulostatic __inline int
1339195784Srpaulomesh_isucastforme(struct ieee80211vap *vap, const struct ieee80211_frame *wh,
1340195784Srpaulo    const struct ieee80211_meshcntl *mc)
1341195784Srpaulo{
1342195784Srpaulo	int ae = mc->mc_flags & 3;
1343195784Srpaulo
1344195784Srpaulo	KASSERT((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS,
1345195784Srpaulo	    ("bad dir 0x%x:0x%x", wh->i_fc[0], wh->i_fc[1]));
1346234878Smonthadar	KASSERT(ae == IEEE80211_MESH_AE_00 || ae == IEEE80211_MESH_AE_10,
1347234878Smonthadar	    ("bad AE %d", ae));
1348234878Smonthadar	if (ae == IEEE80211_MESH_AE_10) {	/* ucast w/ proxy */
1349195784Srpaulo		const struct ieee80211_meshcntl_ae10 *mc10 =
1350195784Srpaulo		    (const struct ieee80211_meshcntl_ae10 *) mc;
1351195784Srpaulo		struct ieee80211_mesh_route *rt =
1352234878Smonthadar		    ieee80211_mesh_rt_find(vap, mc10->mc_addr5);
1353195784Srpaulo		/* check for proxy route to ourself */
1354195784Srpaulo		return (rt != NULL &&
1355195784Srpaulo		    (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY));
1356195784Srpaulo	} else					/* ucast w/o proxy */
1357195784Srpaulo		return IEEE80211_ADDR_EQ(wh->i_addr3, vap->iv_myaddr);
1358195784Srpaulo}
1359195784Srpaulo
1360234878Smonthadar/*
1361234878Smonthadar * Verifies transmitter, updates lifetime, precursor list and forwards data.
1362234878Smonthadar * > 0 means we have forwarded data and no need to process locally
1363234878Smonthadar * == 0 means we want to process locally (and we may have forwarded data
1364234878Smonthadar * < 0 means there was an error and data should be discarded
1365234878Smonthadar */
1366195618Srpaulostatic int
1367234878Smonthadarmesh_recv_indiv_data_to_fwrd(struct ieee80211vap *vap, struct mbuf *m,
1368234878Smonthadar    struct ieee80211_frame *wh, const struct ieee80211_meshcntl *mc)
1369234878Smonthadar{
1370234879Smonthadar	struct ieee80211_qosframe_addr4 *qwh;
1371234879Smonthadar	struct ieee80211_mesh_state *ms = vap->iv_mesh;
1372234879Smonthadar	struct ieee80211_mesh_route *rt_meshda, *rt_meshsa;
1373234878Smonthadar
1374248069Sadrian	/* This is called from the RX path - don't hold this lock */
1375248089Sadrian	IEEE80211_TX_UNLOCK_ASSERT(vap->iv_ic);
1376248069Sadrian
1377234879Smonthadar	qwh = (struct ieee80211_qosframe_addr4 *)wh;
1378234879Smonthadar
1379234878Smonthadar	/*
1380234878Smonthadar	 * TODO:
1381234878Smonthadar	 * o verify addr2 is  a legitimate transmitter
1382234878Smonthadar	 * o lifetime of precursor of addr3 (addr2) is max(init, curr)
1383234878Smonthadar	 * o lifetime of precursor of addr4 (nexthop) is max(init, curr)
1384234878Smonthadar	 */
1385234878Smonthadar
1386234879Smonthadar	/* set lifetime of addr3 (meshDA) to initial value */
1387234879Smonthadar	rt_meshda = ieee80211_mesh_rt_find(vap, qwh->i_addr3);
1388234880Smonthadar	if (rt_meshda == NULL) {
1389234880Smonthadar		IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, qwh->i_addr2,
1390234880Smonthadar		    "no route to meshDA(%6D)", qwh->i_addr3, ":");
1391234880Smonthadar		/*
1392234880Smonthadar		 * [Optional] any of the following three actions:
1393234880Smonthadar		 * o silently discard 				[X]
1394234880Smonthadar		 * o trigger a path discovery			[ ]
1395234880Smonthadar		 * o inform TA that meshDA is unknown.		[ ]
1396234880Smonthadar		 */
1397234880Smonthadar		/* XXX: stats */
1398234880Smonthadar		return (-1);
1399234880Smonthadar	}
1400234880Smonthadar
1401234879Smonthadar	ieee80211_mesh_rt_update(rt_meshda, ticks_to_msecs(
1402234879Smonthadar	    ms->ms_ppath->mpp_inact));
1403234879Smonthadar
1404234879Smonthadar	/* set lifetime of addr4 (meshSA) to initial value */
1405234879Smonthadar	rt_meshsa = ieee80211_mesh_rt_find(vap, qwh->i_addr4);
1406234879Smonthadar	KASSERT(rt_meshsa != NULL, ("no route"));
1407234879Smonthadar	ieee80211_mesh_rt_update(rt_meshsa, ticks_to_msecs(
1408234879Smonthadar	    ms->ms_ppath->mpp_inact));
1409234879Smonthadar
1410234878Smonthadar	mesh_forward(vap, m, mc);
1411234878Smonthadar	return (1); /* dont process locally */
1412234878Smonthadar}
1413234878Smonthadar
1414234878Smonthadar/*
1415234878Smonthadar * Verifies transmitter, updates lifetime, precursor list and process data
1416240521Seadler * locally, if data is proxy with AE = 10 it could mean data should go
1417234878Smonthadar * on another mesh path or data should be forwarded to the DS.
1418234878Smonthadar *
1419234878Smonthadar * > 0 means we have forwarded data and no need to process locally
1420234878Smonthadar * == 0 means we want to process locally (and we may have forwarded data
1421234878Smonthadar * < 0 means there was an error and data should be discarded
1422234878Smonthadar */
1423234878Smonthadarstatic int
1424234878Smonthadarmesh_recv_indiv_data_to_me(struct ieee80211vap *vap, struct mbuf *m,
1425234878Smonthadar    struct ieee80211_frame *wh, const struct ieee80211_meshcntl *mc)
1426234878Smonthadar{
1427234878Smonthadar	struct ieee80211_qosframe_addr4 *qwh;
1428234878Smonthadar	const struct ieee80211_meshcntl_ae10 *mc10;
1429234879Smonthadar	struct ieee80211_mesh_state *ms = vap->iv_mesh;
1430234878Smonthadar	struct ieee80211_mesh_route *rt;
1431234878Smonthadar	int ae;
1432234878Smonthadar
1433248069Sadrian	/* This is called from the RX path - don't hold this lock */
1434248089Sadrian	IEEE80211_TX_UNLOCK_ASSERT(vap->iv_ic);
1435248069Sadrian
1436234878Smonthadar	qwh = (struct ieee80211_qosframe_addr4 *)wh;
1437234878Smonthadar	mc10 = (const struct ieee80211_meshcntl_ae10 *)mc;
1438234878Smonthadar
1439234878Smonthadar	/*
1440234878Smonthadar	 * TODO:
1441234878Smonthadar	 * o verify addr2 is  a legitimate transmitter
1442234878Smonthadar	 * o lifetime of precursor entry is max(init, curr)
1443234878Smonthadar	 */
1444234878Smonthadar
1445234879Smonthadar	/* set lifetime of addr4 (meshSA) to initial value */
1446234879Smonthadar	rt = ieee80211_mesh_rt_find(vap, qwh->i_addr4);
1447234879Smonthadar	KASSERT(rt != NULL, ("no route"));
1448234879Smonthadar	ieee80211_mesh_rt_update(rt, ticks_to_msecs(ms->ms_ppath->mpp_inact));
1449234879Smonthadar	rt = NULL;
1450234879Smonthadar
1451234878Smonthadar	ae = mc10->mc_flags & IEEE80211_MESH_AE_MASK;
1452234878Smonthadar	KASSERT(ae == IEEE80211_MESH_AE_00 ||
1453234878Smonthadar	    ae == IEEE80211_MESH_AE_10, ("bad AE %d", ae));
1454234878Smonthadar	if (ae == IEEE80211_MESH_AE_10) {
1455234878Smonthadar		if (IEEE80211_ADDR_EQ(mc10->mc_addr5, qwh->i_addr3)) {
1456234878Smonthadar			return (0); /* process locally */
1457234878Smonthadar		}
1458234878Smonthadar
1459234878Smonthadar		rt =  ieee80211_mesh_rt_find(vap, mc10->mc_addr5);
1460234878Smonthadar		if (rt != NULL &&
1461234878Smonthadar		    (rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) &&
1462234878Smonthadar		    (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY) == 0) {
1463234878Smonthadar			/*
1464234878Smonthadar			 * Forward on another mesh-path, according to
1465234878Smonthadar			 * amendment as specified in 9.32.4.1
1466234878Smonthadar			 */
1467234878Smonthadar			IEEE80211_ADDR_COPY(qwh->i_addr3, mc10->mc_addr5);
1468234878Smonthadar			mesh_forward(vap, m,
1469234878Smonthadar			    (const struct ieee80211_meshcntl *)mc10);
1470234878Smonthadar			return (1); /* dont process locally */
1471234878Smonthadar		}
1472234878Smonthadar		/*
1473234878Smonthadar		 * All other cases: forward of MSDUs from the MBSS to DS indiv.
1474234878Smonthadar		 * addressed according to 13.11.3.2.
1475234878Smonthadar		 */
1476246499Smonthadar		IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_OUTPUT, qwh->i_addr2,
1477246499Smonthadar		    "forward frame to DS, SA(%6D) DA(%6D)",
1478246499Smonthadar		    mc10->mc_addr6, ":", mc10->mc_addr5, ":");
1479234878Smonthadar	}
1480234878Smonthadar	return (0); /* process locally */
1481234878Smonthadar}
1482234878Smonthadar
1483234878Smonthadar/*
1484234878Smonthadar * Try to forward the group addressed data on to other mesh STAs, and
1485234878Smonthadar * also to the DS.
1486234878Smonthadar *
1487234878Smonthadar * > 0 means we have forwarded data and no need to process locally
1488234878Smonthadar * == 0 means we want to process locally (and we may have forwarded data
1489234878Smonthadar * < 0 means there was an error and data should be discarded
1490234878Smonthadar */
1491234878Smonthadarstatic int
1492234878Smonthadarmesh_recv_group_data(struct ieee80211vap *vap, struct mbuf *m,
1493234878Smonthadar    struct ieee80211_frame *wh, const struct ieee80211_meshcntl *mc)
1494234878Smonthadar{
1495234878Smonthadar#define	MC01(mc)	((const struct ieee80211_meshcntl_ae01 *)mc)
1496234878Smonthadar	struct ieee80211_mesh_state *ms = vap->iv_mesh;
1497234878Smonthadar
1498248069Sadrian	/* This is called from the RX path - don't hold this lock */
1499248089Sadrian	IEEE80211_TX_UNLOCK_ASSERT(vap->iv_ic);
1500248069Sadrian
1501234878Smonthadar	mesh_forward(vap, m, mc);
1502234878Smonthadar
1503234878Smonthadar	if(mc->mc_ttl > 0) {
1504234878Smonthadar		if (mc->mc_flags & IEEE80211_MESH_AE_01) {
1505234878Smonthadar			/*
1506234878Smonthadar			 * Forward of MSDUs from the MBSS to DS group addressed
1507234878Smonthadar			 * (according to 13.11.3.2)
1508234878Smonthadar			 * This happens by delivering the packet, and a bridge
1509234878Smonthadar			 * will sent it on another port member.
1510234878Smonthadar			 */
1511234892Smonthadar			if (ms->ms_flags & IEEE80211_MESHFLAGS_GATE &&
1512300232Savos			    ms->ms_flags & IEEE80211_MESHFLAGS_FWD) {
1513234878Smonthadar				IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH,
1514234878Smonthadar				    MC01(mc)->mc_addr4, "%s",
1515234878Smonthadar				    "forward from MBSS to the DS");
1516300232Savos			}
1517234878Smonthadar		}
1518234878Smonthadar	}
1519234878Smonthadar	return (0); /* process locally */
1520234878Smonthadar#undef	MC01
1521234878Smonthadar}
1522234878Smonthadar
1523234878Smonthadarstatic int
1524283535Sadrianmesh_input(struct ieee80211_node *ni, struct mbuf *m,
1525283535Sadrian    const struct ieee80211_rx_stats *rxs, int rssi, int nf)
1526195618Srpaulo{
1527195618Srpaulo#define	HAS_SEQ(type)	((type & 0x4) == 0)
1528234878Smonthadar#define	MC01(mc)	((const struct ieee80211_meshcntl_ae01 *)mc)
1529195618Srpaulo	struct ieee80211vap *vap = ni->ni_vap;
1530195618Srpaulo	struct ieee80211com *ic = ni->ni_ic;
1531195618Srpaulo	struct ifnet *ifp = vap->iv_ifp;
1532195618Srpaulo	struct ieee80211_frame *wh;
1533195618Srpaulo	const struct ieee80211_meshcntl *mc;
1534234878Smonthadar	int hdrspace, meshdrlen, need_tap, error;
1535234878Smonthadar	uint8_t dir, type, subtype, ae;
1536195618Srpaulo	uint32_t seq;
1537234878Smonthadar	const uint8_t *addr;
1538234878Smonthadar	uint8_t qos[2];
1539195618Srpaulo
1540195618Srpaulo	KASSERT(ni != NULL, ("null node"));
1541195618Srpaulo	ni->ni_inact = ni->ni_inact_reload;
1542195618Srpaulo
1543195618Srpaulo	need_tap = 1;			/* mbuf need to be tapped. */
1544195618Srpaulo	type = -1;			/* undefined */
1545195618Srpaulo
1546248069Sadrian	/* This is called from the RX path - don't hold this lock */
1547248069Sadrian	IEEE80211_TX_UNLOCK_ASSERT(ic);
1548248069Sadrian
1549195618Srpaulo	if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) {
1550195618Srpaulo		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
1551195618Srpaulo		    ni->ni_macaddr, NULL,
1552195618Srpaulo		    "too short (1): len %u", m->m_pkthdr.len);
1553195618Srpaulo		vap->iv_stats.is_rx_tooshort++;
1554195618Srpaulo		goto out;
1555195618Srpaulo	}
1556195618Srpaulo	/*
1557195618Srpaulo	 * Bit of a cheat here, we use a pointer for a 3-address
1558195618Srpaulo	 * frame format but don't reference fields past outside
1559195618Srpaulo	 * ieee80211_frame_min w/o first validating the data is
1560195618Srpaulo	 * present.
1561195618Srpaulo	*/
1562195618Srpaulo	wh = mtod(m, struct ieee80211_frame *);
1563195618Srpaulo
1564195618Srpaulo	if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
1565195618Srpaulo	    IEEE80211_FC0_VERSION_0) {
1566195618Srpaulo		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
1567195618Srpaulo		    ni->ni_macaddr, NULL, "wrong version %x", wh->i_fc[0]);
1568195618Srpaulo		vap->iv_stats.is_rx_badversion++;
1569195618Srpaulo		goto err;
1570195618Srpaulo	}
1571195618Srpaulo	dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
1572195618Srpaulo	type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
1573195618Srpaulo	subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
1574195618Srpaulo	if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
1575195618Srpaulo		IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi);
1576195618Srpaulo		ni->ni_noise = nf;
1577195618Srpaulo		if (HAS_SEQ(type)) {
1578195618Srpaulo			uint8_t tid = ieee80211_gettid(wh);
1579195618Srpaulo
1580195618Srpaulo			if (IEEE80211_QOS_HAS_SEQ(wh) &&
1581195618Srpaulo			    TID_TO_WME_AC(tid) >= WME_AC_VI)
1582195618Srpaulo				ic->ic_wme.wme_hipri_traffic++;
1583296254Savos			if (! ieee80211_check_rxseq(ni, wh, wh->i_addr1))
1584195618Srpaulo				goto out;
1585195618Srpaulo		}
1586195618Srpaulo	}
1587195618Srpaulo#ifdef IEEE80211_DEBUG
1588195618Srpaulo	/*
1589195618Srpaulo	 * It's easier, but too expensive, to simulate different mesh
1590195618Srpaulo	 * topologies by consulting the ACL policy very early, so do this
1591195618Srpaulo	 * only under DEBUG.
1592195618Srpaulo	 *
1593195618Srpaulo	 * NB: this check is also done upon peering link initiation.
1594195618Srpaulo	 */
1595228622Sbschmidt	if (vap->iv_acl != NULL && !vap->iv_acl->iac_check(vap, wh)) {
1596195618Srpaulo		IEEE80211_DISCARD(vap, IEEE80211_MSG_ACL,
1597195618Srpaulo		    wh, NULL, "%s", "disallowed by ACL");
1598195618Srpaulo		vap->iv_stats.is_rx_acl++;
1599195618Srpaulo		goto out;
1600195618Srpaulo	}
1601195618Srpaulo#endif
1602195618Srpaulo	switch (type) {
1603195618Srpaulo	case IEEE80211_FC0_TYPE_DATA:
1604195618Srpaulo		if (ni == vap->iv_bss)
1605195618Srpaulo			goto out;
1606195618Srpaulo		if (ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED) {
1607195618Srpaulo			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_MESH,
1608195618Srpaulo			    ni->ni_macaddr, NULL,
1609195618Srpaulo			    "peer link not yet established (%d)",
1610195618Srpaulo			    ni->ni_mlstate);
1611195618Srpaulo			vap->iv_stats.is_mesh_nolink++;
1612195618Srpaulo			goto out;
1613234878Smonthadar		}
1614195618Srpaulo		if (dir != IEEE80211_FC1_DIR_FROMDS &&
1615195618Srpaulo		    dir != IEEE80211_FC1_DIR_DSTODS) {
1616195618Srpaulo			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
1617195618Srpaulo			    wh, "data", "incorrect dir 0x%x", dir);
1618195618Srpaulo			vap->iv_stats.is_rx_wrongdir++;
1619195618Srpaulo			goto err;
1620195618Srpaulo		}
1621232480Sadrian
1622232480Sadrian		/* All Mesh data frames are QoS subtype */
1623232480Sadrian		if (!HAS_SEQ(type)) {
1624232480Sadrian			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
1625232480Sadrian			    wh, "data", "incorrect subtype 0x%x", subtype);
1626232480Sadrian			vap->iv_stats.is_rx_badsubtype++;
1627232480Sadrian			goto err;
1628232480Sadrian		}
1629232480Sadrian
1630232480Sadrian		/*
1631232480Sadrian		 * Next up, any fragmentation.
1632232480Sadrian		 * XXX: we defrag before we even try to forward,
1633232480Sadrian		 * Mesh Control field is not present in sub-sequent
1634232480Sadrian		 * fragmented frames. This is in contrast to Draft 4.0.
1635232480Sadrian		 */
1636232480Sadrian		hdrspace = ieee80211_hdrspace(ic, wh);
1637232480Sadrian		if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
1638232480Sadrian			m = ieee80211_defrag(ni, m, hdrspace);
1639232480Sadrian			if (m == NULL) {
1640232480Sadrian				/* Fragment dropped or frame not complete yet */
1641232480Sadrian				goto out;
1642232480Sadrian			}
1643232480Sadrian		}
1644232480Sadrian		wh = mtod(m, struct ieee80211_frame *); /* NB: after defrag */
1645232480Sadrian
1646232480Sadrian		/*
1647232480Sadrian		 * Now we have a complete Mesh Data frame.
1648232480Sadrian		 */
1649232480Sadrian
1650232480Sadrian		/*
1651232480Sadrian		 * Only fromDStoDS data frames use 4 address qos frames
1652232480Sadrian		 * as specified in amendment. Otherwise addr4 is located
1653232480Sadrian		 * in the Mesh Control field and a 3 address qos frame
1654232480Sadrian		 * is used.
1655232480Sadrian		 */
1656344969Savos		*(uint16_t *)qos = *(uint16_t *)ieee80211_getqos(wh);
1657232480Sadrian
1658232480Sadrian		/*
1659232480Sadrian		 * NB: The mesh STA sets the Mesh Control Present
1660232480Sadrian		 * subfield to 1 in the Mesh Data frame containing
1661232480Sadrian		 * an unfragmented MSDU, an A-MSDU, or the first
1662232480Sadrian		 * fragment of an MSDU.
1663232480Sadrian		 * After defrag it should always be present.
1664232480Sadrian		 */
1665232480Sadrian		if (!(qos[1] & IEEE80211_QOS_MC)) {
1666232480Sadrian			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_MESH,
1667232480Sadrian			    ni->ni_macaddr, NULL,
1668232480Sadrian			    "%s", "Mesh control field not present");
1669232480Sadrian			vap->iv_stats.is_rx_elem_missing++; /* XXX: kinda */
1670232480Sadrian			goto err;
1671232480Sadrian		}
1672232480Sadrian
1673195618Srpaulo		/* pull up enough to get to the mesh control */
1674195618Srpaulo		if (m->m_len < hdrspace + sizeof(struct ieee80211_meshcntl) &&
1675195618Srpaulo		    (m = m_pullup(m, hdrspace +
1676195618Srpaulo		        sizeof(struct ieee80211_meshcntl))) == NULL) {
1677195618Srpaulo			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
1678195618Srpaulo			    ni->ni_macaddr, NULL,
1679195618Srpaulo			    "data too short: expecting %u", hdrspace);
1680195618Srpaulo			vap->iv_stats.is_rx_tooshort++;
1681195618Srpaulo			goto out;		/* XXX */
1682195618Srpaulo		}
1683195618Srpaulo		/*
1684195618Srpaulo		 * Now calculate the full extent of the headers. Note
1685195784Srpaulo		 * mesh_decap will pull up anything we didn't get
1686195618Srpaulo		 * above when it strips the 802.11 headers.
1687195618Srpaulo		 */
1688195618Srpaulo		mc = (const struct ieee80211_meshcntl *)
1689195618Srpaulo		    (mtod(m, const uint8_t *) + hdrspace);
1690234878Smonthadar		ae = mc->mc_flags & IEEE80211_MESH_AE_MASK;
1691195784Srpaulo		meshdrlen = sizeof(struct ieee80211_meshcntl) +
1692234878Smonthadar		    ae * IEEE80211_ADDR_LEN;
1693195784Srpaulo		hdrspace += meshdrlen;
1694234878Smonthadar
1695234878Smonthadar		/* pull complete hdrspace = ieee80211_hdrspace + meshcontrol */
1696234878Smonthadar		if ((meshdrlen > sizeof(struct ieee80211_meshcntl)) &&
1697234878Smonthadar		    (m->m_len < hdrspace) &&
1698234878Smonthadar		    ((m = m_pullup(m, hdrspace)) == NULL)) {
1699234878Smonthadar			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
1700234878Smonthadar			    ni->ni_macaddr, NULL,
1701234878Smonthadar			    "data too short: expecting %u", hdrspace);
1702234878Smonthadar			vap->iv_stats.is_rx_tooshort++;
1703234878Smonthadar			goto out;		/* XXX */
1704234878Smonthadar		}
1705234878Smonthadar		/* XXX: are we sure there is no reallocating after m_pullup? */
1706234878Smonthadar
1707298359Savos		seq = le32dec(mc->mc_seq);
1708195618Srpaulo		if (IEEE80211_IS_MULTICAST(wh->i_addr1))
1709195618Srpaulo			addr = wh->i_addr3;
1710234878Smonthadar		else if (ae == IEEE80211_MESH_AE_01)
1711234878Smonthadar			addr = MC01(mc)->mc_addr4;
1712195618Srpaulo		else
1713195618Srpaulo			addr = ((struct ieee80211_qosframe_addr4 *)wh)->i_addr4;
1714195618Srpaulo		if (IEEE80211_ADDR_EQ(vap->iv_myaddr, addr)) {
1715195618Srpaulo			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
1716195618Srpaulo			    addr, "data", "%s", "not to me");
1717195618Srpaulo			vap->iv_stats.is_rx_wrongbss++;	/* XXX kinda */
1718195618Srpaulo			goto out;
1719195618Srpaulo		}
1720195618Srpaulo		if (mesh_checkpseq(vap, addr, seq) != 0) {
1721195618Srpaulo			vap->iv_stats.is_rx_dup++;
1722195618Srpaulo			goto out;
1723195618Srpaulo		}
1724195618Srpaulo
1725234878Smonthadar		/* This code "routes" the frame to the right control path */
1726234878Smonthadar		if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
1727234878Smonthadar			if (IEEE80211_ADDR_EQ(vap->iv_myaddr, wh->i_addr3))
1728234878Smonthadar				error =
1729234878Smonthadar				    mesh_recv_indiv_data_to_me(vap, m, wh, mc);
1730234878Smonthadar			else if (IEEE80211_IS_MULTICAST(wh->i_addr3))
1731234878Smonthadar				error = mesh_recv_group_data(vap, m, wh, mc);
1732234878Smonthadar			else
1733234878Smonthadar				error = mesh_recv_indiv_data_to_fwrd(vap, m,
1734234878Smonthadar				    wh, mc);
1735234878Smonthadar		} else
1736234878Smonthadar			error = mesh_recv_group_data(vap, m, wh, mc);
1737234878Smonthadar		if (error < 0)
1738234878Smonthadar			goto err;
1739234878Smonthadar		else if (error > 0)
1740234878Smonthadar			goto out;
1741195618Srpaulo
1742195618Srpaulo		if (ieee80211_radiotap_active_vap(vap))
1743195618Srpaulo			ieee80211_radiotap_rx(vap, m);
1744195618Srpaulo		need_tap = 0;
1745195618Srpaulo
1746195618Srpaulo		/*
1747195618Srpaulo		 * Finally, strip the 802.11 header.
1748195618Srpaulo		 */
1749195784Srpaulo		m = mesh_decap(vap, m, hdrspace, meshdrlen);
1750195618Srpaulo		if (m == NULL) {
1751195618Srpaulo			/* XXX mask bit to check for both */
1752195618Srpaulo			/* don't count Null data frames as errors */
1753195618Srpaulo			if (subtype == IEEE80211_FC0_SUBTYPE_NODATA ||
1754195618Srpaulo			    subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL)
1755195618Srpaulo				goto out;
1756195618Srpaulo			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
1757195618Srpaulo			    ni->ni_macaddr, "data", "%s", "decap error");
1758195618Srpaulo			vap->iv_stats.is_rx_decap++;
1759195618Srpaulo			IEEE80211_NODE_STAT(ni, rx_decap);
1760195618Srpaulo			goto err;
1761195618Srpaulo		}
1762232480Sadrian		if (qos[0] & IEEE80211_QOS_AMSDU) {
1763195618Srpaulo			m = ieee80211_decap_amsdu(ni, m);
1764195618Srpaulo			if (m == NULL)
1765195618Srpaulo				return IEEE80211_FC0_TYPE_DATA;
1766195784Srpaulo		}
1767195618Srpaulo		ieee80211_deliver_data(vap, ni, m);
1768195618Srpaulo		return type;
1769195618Srpaulo	case IEEE80211_FC0_TYPE_MGT:
1770195618Srpaulo		vap->iv_stats.is_rx_mgmt++;
1771195618Srpaulo		IEEE80211_NODE_STAT(ni, rx_mgmt);
1772195618Srpaulo		if (dir != IEEE80211_FC1_DIR_NODS) {
1773195618Srpaulo			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
1774195618Srpaulo			    wh, "mgt", "incorrect dir 0x%x", dir);
1775195618Srpaulo			vap->iv_stats.is_rx_wrongdir++;
1776195618Srpaulo			goto err;
1777195618Srpaulo		}
1778195618Srpaulo		if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) {
1779195618Srpaulo			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
1780195618Srpaulo			    ni->ni_macaddr, "mgt", "too short: len %u",
1781195618Srpaulo			    m->m_pkthdr.len);
1782195618Srpaulo			vap->iv_stats.is_rx_tooshort++;
1783195618Srpaulo			goto out;
1784195618Srpaulo		}
1785195618Srpaulo#ifdef IEEE80211_DEBUG
1786195618Srpaulo		if ((ieee80211_msg_debug(vap) &&
1787195618Srpaulo		    (vap->iv_ic->ic_flags & IEEE80211_F_SCAN)) ||
1788195618Srpaulo		    ieee80211_msg_dumppkts(vap)) {
1789195618Srpaulo			if_printf(ifp, "received %s from %s rssi %d\n",
1790298376Savos			    ieee80211_mgt_subtype_name(subtype),
1791195618Srpaulo			    ether_sprintf(wh->i_addr2), rssi);
1792195618Srpaulo		}
1793195618Srpaulo#endif
1794260444Skevlo		if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
1795195618Srpaulo			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
1796195618Srpaulo			    wh, NULL, "%s", "WEP set but not permitted");
1797195618Srpaulo			vap->iv_stats.is_rx_mgtdiscard++; /* XXX */
1798195618Srpaulo			goto out;
1799195618Srpaulo		}
1800283535Sadrian		vap->iv_recv_mgmt(ni, m, subtype, rxs, rssi, nf);
1801195618Srpaulo		goto out;
1802195618Srpaulo	case IEEE80211_FC0_TYPE_CTL:
1803195618Srpaulo		vap->iv_stats.is_rx_ctl++;
1804195618Srpaulo		IEEE80211_NODE_STAT(ni, rx_ctrl);
1805195618Srpaulo		goto out;
1806195618Srpaulo	default:
1807195618Srpaulo		IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
1808195618Srpaulo		    wh, "bad", "frame type 0x%x", type);
1809195618Srpaulo		/* should not come here */
1810195618Srpaulo		break;
1811195618Srpaulo	}
1812195618Srpauloerr:
1813271861Sglebius	if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
1814195618Srpauloout:
1815195618Srpaulo	if (m != NULL) {
1816195618Srpaulo		if (need_tap && ieee80211_radiotap_active_vap(vap))
1817195618Srpaulo			ieee80211_radiotap_rx(vap, m);
1818195618Srpaulo		m_freem(m);
1819195618Srpaulo	}
1820195618Srpaulo	return type;
1821234878Smonthadar#undef	HAS_SEQ
1822234878Smonthadar#undef	MC01
1823195618Srpaulo}
1824195618Srpaulo
1825195618Srpaulostatic void
1826195618Srpaulomesh_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, int subtype,
1827283535Sadrian    const struct ieee80211_rx_stats *rxs, int rssi, int nf)
1828195618Srpaulo{
1829195618Srpaulo	struct ieee80211vap *vap = ni->ni_vap;
1830195618Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
1831195618Srpaulo	struct ieee80211com *ic = ni->ni_ic;
1832283535Sadrian	struct ieee80211_channel *rxchan = ic->ic_curchan;
1833195618Srpaulo	struct ieee80211_frame *wh;
1834234877Smonthadar	struct ieee80211_mesh_route *rt;
1835195618Srpaulo	uint8_t *frm, *efrm;
1836195618Srpaulo
1837195618Srpaulo	wh = mtod(m0, struct ieee80211_frame *);
1838195618Srpaulo	frm = (uint8_t *)&wh[1];
1839195618Srpaulo	efrm = mtod(m0, uint8_t *) + m0->m_len;
1840195618Srpaulo	switch (subtype) {
1841195618Srpaulo	case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
1842195618Srpaulo	case IEEE80211_FC0_SUBTYPE_BEACON:
1843195618Srpaulo	{
1844195618Srpaulo		struct ieee80211_scanparams scan;
1845283535Sadrian		struct ieee80211_channel *c;
1846195618Srpaulo		/*
1847195618Srpaulo		 * We process beacon/probe response
1848195618Srpaulo		 * frames to discover neighbors.
1849195618Srpaulo		 */
1850283535Sadrian		if (rxs != NULL) {
1851283535Sadrian			c = ieee80211_lookup_channel_rxstatus(vap, rxs);
1852283535Sadrian			if (c != NULL)
1853283535Sadrian				rxchan = c;
1854283535Sadrian		}
1855283535Sadrian		if (ieee80211_parse_beacon(ni, m0, rxchan, &scan) != 0)
1856195618Srpaulo			return;
1857195618Srpaulo		/*
1858195618Srpaulo		 * Count frame now that we know it's to be processed.
1859195618Srpaulo		 */
1860195618Srpaulo		if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) {
1861195618Srpaulo			vap->iv_stats.is_rx_beacon++;	/* XXX remove */
1862195618Srpaulo			IEEE80211_NODE_STAT(ni, rx_beacons);
1863195618Srpaulo		} else
1864195618Srpaulo			IEEE80211_NODE_STAT(ni, rx_proberesp);
1865195618Srpaulo		/*
1866195618Srpaulo		 * If scanning, just pass information to the scan module.
1867195618Srpaulo		 */
1868195618Srpaulo		if (ic->ic_flags & IEEE80211_F_SCAN) {
1869195618Srpaulo			if (ic->ic_flags_ext & IEEE80211_FEXT_PROBECHAN) {
1870195618Srpaulo				/*
1871195618Srpaulo				 * Actively scanning a channel marked passive;
1872195618Srpaulo				 * send a probe request now that we know there
1873195618Srpaulo				 * is 802.11 traffic present.
1874195618Srpaulo				 *
1875195618Srpaulo				 * XXX check if the beacon we recv'd gives
1876195618Srpaulo				 * us what we need and suppress the probe req
1877195618Srpaulo				 */
1878195618Srpaulo				ieee80211_probe_curchan(vap, 1);
1879195618Srpaulo				ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN;
1880195618Srpaulo			}
1881283535Sadrian			ieee80211_add_scan(vap, rxchan, &scan, wh,
1882195618Srpaulo			    subtype, rssi, nf);
1883195618Srpaulo			return;
1884195618Srpaulo		}
1885195618Srpaulo
1886195618Srpaulo		/* The rest of this code assumes we are running */
1887195618Srpaulo		if (vap->iv_state != IEEE80211_S_RUN)
1888195618Srpaulo			return;
1889195618Srpaulo		/*
1890195618Srpaulo		 * Ignore non-mesh STAs.
1891195618Srpaulo		 */
1892195618Srpaulo		if ((scan.capinfo &
1893195618Srpaulo		     (IEEE80211_CAPINFO_ESS|IEEE80211_CAPINFO_IBSS)) ||
1894195618Srpaulo		    scan.meshid == NULL || scan.meshconf == NULL) {
1895195618Srpaulo			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
1896195618Srpaulo			    wh, "beacon", "%s", "not a mesh sta");
1897195618Srpaulo			vap->iv_stats.is_mesh_wrongmesh++;
1898195618Srpaulo			return;
1899195618Srpaulo		}
1900195618Srpaulo		/*
1901195618Srpaulo		 * Ignore STAs for other mesh networks.
1902195618Srpaulo		 */
1903195618Srpaulo		if (memcmp(scan.meshid+2, ms->ms_id, ms->ms_idlen) != 0 ||
1904195618Srpaulo		    mesh_verify_meshconf(vap, scan.meshconf)) {
1905195618Srpaulo			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
1906195618Srpaulo			    wh, "beacon", "%s", "not for our mesh");
1907195618Srpaulo			vap->iv_stats.is_mesh_wrongmesh++;
1908195618Srpaulo			return;
1909195618Srpaulo		}
1910195618Srpaulo		/*
1911195618Srpaulo		 * Peer only based on the current ACL policy.
1912195618Srpaulo		 */
1913228622Sbschmidt		if (vap->iv_acl != NULL && !vap->iv_acl->iac_check(vap, wh)) {
1914195618Srpaulo			IEEE80211_DISCARD(vap, IEEE80211_MSG_ACL,
1915195618Srpaulo			    wh, NULL, "%s", "disallowed by ACL");
1916195618Srpaulo			vap->iv_stats.is_rx_acl++;
1917195618Srpaulo			return;
1918195618Srpaulo		}
1919195618Srpaulo		/*
1920195618Srpaulo		 * Do neighbor discovery.
1921195618Srpaulo		 */
1922195618Srpaulo		if (!IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) {
1923195618Srpaulo			/*
1924195618Srpaulo			 * Create a new entry in the neighbor table.
1925195618Srpaulo			 */
1926195618Srpaulo			ni = ieee80211_add_neighbor(vap, wh, &scan);
1927195618Srpaulo		}
1928195618Srpaulo		/*
1929195618Srpaulo		 * Automatically peer with discovered nodes if possible.
1930195618Srpaulo		 */
1931195618Srpaulo		if (ni != vap->iv_bss &&
1932234877Smonthadar		    (ms->ms_flags & IEEE80211_MESHFLAGS_AP)) {
1933234877Smonthadar			switch (ni->ni_mlstate) {
1934234877Smonthadar			case IEEE80211_NODE_MESH_IDLE:
1935234877Smonthadar			{
1936234877Smonthadar				uint16_t args[1];
1937195618Srpaulo
1938246497Smonthadar				/* Wait for backoff callout to reset counter */
1939246497Smonthadar				if (ni->ni_mlhcnt >= ieee80211_mesh_maxholding)
1940246497Smonthadar					return;
1941246497Smonthadar
1942234877Smonthadar				ni->ni_mlpid = mesh_generateid(vap);
1943234877Smonthadar				if (ni->ni_mlpid == 0)
1944234877Smonthadar					return;
1945234877Smonthadar				mesh_linkchange(ni, IEEE80211_NODE_MESH_OPENSNT);
1946234877Smonthadar				args[0] = ni->ni_mlpid;
1947234877Smonthadar				ieee80211_send_action(ni,
1948234877Smonthadar				IEEE80211_ACTION_CAT_SELF_PROT,
1949234877Smonthadar				IEEE80211_ACTION_MESHPEERING_OPEN, args);
1950234877Smonthadar				ni->ni_mlrcnt = 0;
1951234877Smonthadar				mesh_peer_timeout_setup(ni);
1952234877Smonthadar				break;
1953234877Smonthadar			}
1954234877Smonthadar			case IEEE80211_NODE_MESH_ESTABLISHED:
1955234877Smonthadar			{
1956234877Smonthadar				/*
1957234877Smonthadar				 * Valid beacon from a peer mesh STA
1958234877Smonthadar				 * bump TA lifetime
1959234877Smonthadar				 */
1960234877Smonthadar				rt = ieee80211_mesh_rt_find(vap, wh->i_addr2);
1961234877Smonthadar				if(rt != NULL) {
1962234877Smonthadar					ieee80211_mesh_rt_update(rt,
1963234879Smonthadar					    ticks_to_msecs(
1964234879Smonthadar					    ms->ms_ppath->mpp_inact));
1965234877Smonthadar				}
1966234877Smonthadar				break;
1967234877Smonthadar			}
1968234877Smonthadar			default:
1969234877Smonthadar				break; /* ignore */
1970234877Smonthadar			}
1971195618Srpaulo		}
1972195618Srpaulo		break;
1973195618Srpaulo	}
1974195618Srpaulo	case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
1975195618Srpaulo	{
1976195618Srpaulo		uint8_t *ssid, *meshid, *rates, *xrates;
1977195618Srpaulo
1978195618Srpaulo		if (vap->iv_state != IEEE80211_S_RUN) {
1979195618Srpaulo			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
1980195618Srpaulo			    wh, NULL, "wrong state %s",
1981195618Srpaulo			    ieee80211_state_name[vap->iv_state]);
1982195618Srpaulo			vap->iv_stats.is_rx_mgtdiscard++;
1983195618Srpaulo			return;
1984195618Srpaulo		}
1985195618Srpaulo		if (IEEE80211_IS_MULTICAST(wh->i_addr2)) {
1986195618Srpaulo			/* frame must be directed */
1987195618Srpaulo			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
1988195618Srpaulo			    wh, NULL, "%s", "not unicast");
1989195618Srpaulo			vap->iv_stats.is_rx_mgtdiscard++;	/* XXX stat */
1990195618Srpaulo			return;
1991195618Srpaulo		}
1992195618Srpaulo		/*
1993195618Srpaulo		 * prreq frame format
1994195618Srpaulo		 *      [tlv] ssid
1995195618Srpaulo		 *      [tlv] supported rates
1996195618Srpaulo		 *      [tlv] extended supported rates
1997195618Srpaulo		 *	[tlv] mesh id
1998195618Srpaulo		 */
1999195618Srpaulo		ssid = meshid = rates = xrates = NULL;
2000195618Srpaulo		while (efrm - frm > 1) {
2001195618Srpaulo			IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return);
2002195618Srpaulo			switch (*frm) {
2003195618Srpaulo			case IEEE80211_ELEMID_SSID:
2004195618Srpaulo				ssid = frm;
2005195618Srpaulo				break;
2006195618Srpaulo			case IEEE80211_ELEMID_RATES:
2007195618Srpaulo				rates = frm;
2008195618Srpaulo				break;
2009195618Srpaulo			case IEEE80211_ELEMID_XRATES:
2010195618Srpaulo				xrates = frm;
2011195618Srpaulo				break;
2012195618Srpaulo			case IEEE80211_ELEMID_MESHID:
2013195618Srpaulo				meshid = frm;
2014195618Srpaulo				break;
2015195618Srpaulo			}
2016217590Sbschmidt			frm += frm[1] + 2;
2017195618Srpaulo		}
2018195618Srpaulo		IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN, return);
2019195618Srpaulo		IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE, return);
2020195618Srpaulo		if (xrates != NULL)
2021195618Srpaulo			IEEE80211_VERIFY_ELEMENT(xrates,
2022195618Srpaulo			    IEEE80211_RATE_MAXSIZE - rates[1], return);
2023203423Srpaulo		if (meshid != NULL) {
2024195618Srpaulo			IEEE80211_VERIFY_ELEMENT(meshid,
2025195618Srpaulo			    IEEE80211_MESHID_LEN, return);
2026203423Srpaulo			/* NB: meshid, not ssid */
2027203423Srpaulo			IEEE80211_VERIFY_SSID(vap->iv_bss, meshid, return);
2028203423Srpaulo		}
2029195618Srpaulo
2030195618Srpaulo		/* XXX find a better class or define it's own */
2031195618Srpaulo		IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_INPUT, wh->i_addr2,
2032195618Srpaulo		    "%s", "recv probe req");
2033195618Srpaulo		/*
2034195618Srpaulo		 * Some legacy 11b clients cannot hack a complete
2035195618Srpaulo		 * probe response frame.  When the request includes
2036195618Srpaulo		 * only a bare-bones rate set, communicate this to
2037195618Srpaulo		 * the transmit side.
2038195618Srpaulo		 */
2039195618Srpaulo		ieee80211_send_proberesp(vap, wh->i_addr2, 0);
2040195618Srpaulo		break;
2041195618Srpaulo	}
2042218927Sbschmidt
2043195618Srpaulo	case IEEE80211_FC0_SUBTYPE_ACTION:
2044218927Sbschmidt	case IEEE80211_FC0_SUBTYPE_ACTION_NOACK:
2045195618Srpaulo		if (ni == vap->iv_bss) {
2046218958Sbschmidt			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
2047195618Srpaulo			    wh, NULL, "%s", "unknown node");
2048195618Srpaulo			vap->iv_stats.is_rx_mgtdiscard++;
2049218958Sbschmidt		} else if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, wh->i_addr1) &&
2050195618Srpaulo		    !IEEE80211_IS_MULTICAST(wh->i_addr1)) {
2051218958Sbschmidt			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
2052218958Sbschmidt			    wh, NULL, "%s", "not for us");
2053195618Srpaulo			vap->iv_stats.is_rx_mgtdiscard++;
2054218958Sbschmidt		} else if (vap->iv_state != IEEE80211_S_RUN) {
2055218927Sbschmidt			IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
2056218927Sbschmidt			    wh, NULL, "wrong state %s",
2057218927Sbschmidt			    ieee80211_state_name[vap->iv_state]);
2058218927Sbschmidt			vap->iv_stats.is_rx_mgtdiscard++;
2059218958Sbschmidt		} else {
2060218958Sbschmidt			if (ieee80211_parse_action(ni, m0) == 0)
2061218958Sbschmidt				(void)ic->ic_recv_action(ni, wh, frm, efrm);
2062218927Sbschmidt		}
2063195618Srpaulo		break;
2064218927Sbschmidt
2065195618Srpaulo	case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
2066218927Sbschmidt	case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
2067195618Srpaulo	case IEEE80211_FC0_SUBTYPE_REASSOC_REQ:
2068195618Srpaulo	case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
2069295795Savos	case IEEE80211_FC0_SUBTYPE_TIMING_ADV:
2070218927Sbschmidt	case IEEE80211_FC0_SUBTYPE_ATIM:
2071218927Sbschmidt	case IEEE80211_FC0_SUBTYPE_DISASSOC:
2072218927Sbschmidt	case IEEE80211_FC0_SUBTYPE_AUTH:
2073195618Srpaulo	case IEEE80211_FC0_SUBTYPE_DEAUTH:
2074195618Srpaulo		IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
2075195618Srpaulo		    wh, NULL, "%s", "not handled");
2076195618Srpaulo		vap->iv_stats.is_rx_mgtdiscard++;
2077218927Sbschmidt		break;
2078218927Sbschmidt
2079195618Srpaulo	default:
2080195618Srpaulo		IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
2081195618Srpaulo		    wh, "mgt", "subtype 0x%x not handled", subtype);
2082195618Srpaulo		vap->iv_stats.is_rx_badsubtype++;
2083195618Srpaulo		break;
2084195618Srpaulo	}
2085195618Srpaulo}
2086195618Srpaulo
2087205277Srpaulostatic void
2088205277Srpaulomesh_recv_ctl(struct ieee80211_node *ni, struct mbuf *m, int subtype)
2089205277Srpaulo{
2090205277Srpaulo
2091205277Srpaulo	switch (subtype) {
2092205277Srpaulo	case IEEE80211_FC0_SUBTYPE_BAR:
2093205277Srpaulo		ieee80211_recv_bar(ni, m);
2094205277Srpaulo		break;
2095205277Srpaulo	}
2096205277Srpaulo}
2097205277Srpaulo
2098195618Srpaulo/*
2099234876Smonthadar * Parse meshpeering action ie's for MPM frames
2100195618Srpaulo */
2101195618Srpaulostatic const struct ieee80211_meshpeer_ie *
2102195618Srpaulomesh_parse_meshpeering_action(struct ieee80211_node *ni,
2103195618Srpaulo	const struct ieee80211_frame *wh,	/* XXX for VERIFY_LENGTH */
2104195618Srpaulo	const uint8_t *frm, const uint8_t *efrm,
2105197413Srpaulo	struct ieee80211_meshpeer_ie *mp, uint8_t subtype)
2106195618Srpaulo{
2107195618Srpaulo	struct ieee80211vap *vap = ni->ni_vap;
2108195618Srpaulo	const struct ieee80211_meshpeer_ie *mpie;
2109234876Smonthadar	uint16_t args[3];
2110299575Savos	const uint8_t *meshid, *meshconf;
2111234876Smonthadar	uint8_t sendclose = 0; /* 1 = MPM frame rejected, close will be sent */
2112195618Srpaulo
2113299575Savos	meshid = meshconf = NULL;
2114195618Srpaulo	while (efrm - frm > 1) {
2115195618Srpaulo		IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return NULL);
2116195618Srpaulo		switch (*frm) {
2117195618Srpaulo		case IEEE80211_ELEMID_MESHID:
2118195618Srpaulo			meshid = frm;
2119195618Srpaulo			break;
2120195618Srpaulo		case IEEE80211_ELEMID_MESHCONF:
2121195618Srpaulo			meshconf = frm;
2122195618Srpaulo			break;
2123195618Srpaulo		case IEEE80211_ELEMID_MESHPEER:
2124195618Srpaulo			mpie = (const struct ieee80211_meshpeer_ie *) frm;
2125195618Srpaulo			memset(mp, 0, sizeof(*mp));
2126234876Smonthadar			mp->peer_len = mpie->peer_len;
2127298359Savos			mp->peer_proto = le16dec(&mpie->peer_proto);
2128298359Savos			mp->peer_llinkid = le16dec(&mpie->peer_llinkid);
2129234875Smonthadar			switch (subtype) {
2130234875Smonthadar			case IEEE80211_ACTION_MESHPEERING_CONFIRM:
2131234875Smonthadar				mp->peer_linkid =
2132298359Savos				    le16dec(&mpie->peer_linkid);
2133234875Smonthadar				break;
2134234875Smonthadar			case IEEE80211_ACTION_MESHPEERING_CLOSE:
2135234875Smonthadar				/* NB: peer link ID is optional */
2136234875Smonthadar				if (mpie->peer_len ==
2137234875Smonthadar				    (IEEE80211_MPM_BASE_SZ + 2)) {
2138234875Smonthadar					mp->peer_linkid = 0;
2139234875Smonthadar					mp->peer_rcode =
2140298359Savos					    le16dec(&mpie->peer_linkid);
2141234875Smonthadar				} else {
2142234875Smonthadar					mp->peer_linkid =
2143298359Savos					    le16dec(&mpie->peer_linkid);
2144234875Smonthadar					mp->peer_rcode =
2145298359Savos					    le16dec(&mpie->peer_rcode);
2146234875Smonthadar				}
2147234875Smonthadar				break;
2148195618Srpaulo			}
2149195618Srpaulo			break;
2150195618Srpaulo		}
2151195618Srpaulo		frm += frm[1] + 2;
2152195618Srpaulo	}
2153195618Srpaulo
2154195618Srpaulo	/*
2155234876Smonthadar	 * Verify the contents of the frame.
2156234876Smonthadar	 * If it fails validation, close the peer link.
2157195618Srpaulo	 */
2158234876Smonthadar	if (mesh_verify_meshpeer(vap, subtype, (const uint8_t *)mp)) {
2159234876Smonthadar		sendclose = 1;
2160234876Smonthadar		IEEE80211_DISCARD(vap,
2161234876Smonthadar		    IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH,
2162234876Smonthadar		    wh, NULL, "%s", "MPM validation failed");
2163234876Smonthadar	}
2164195618Srpaulo
2165234876Smonthadar	/* If meshid is not the same reject any frames type. */
2166234876Smonthadar	if (sendclose == 0 && mesh_verify_meshid(vap, meshid)) {
2167234876Smonthadar		sendclose = 1;
2168234876Smonthadar		IEEE80211_DISCARD(vap,
2169234876Smonthadar		    IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH,
2170234876Smonthadar		    wh, NULL, "%s", "not for our mesh");
2171234876Smonthadar		if (subtype == IEEE80211_ACTION_MESHPEERING_CLOSE) {
2172234876Smonthadar			/*
2173234876Smonthadar			 * Standard not clear about this, if we dont ignore
2174234876Smonthadar			 * there will be an endless loop between nodes sending
2175234876Smonthadar			 * CLOSE frames between each other with wrong meshid.
2176234876Smonthadar			 * Discard and timers will bring FSM to IDLE state.
2177234876Smonthadar			 */
2178234876Smonthadar			return NULL;
2179234876Smonthadar		}
2180234876Smonthadar	}
2181234876Smonthadar
2182234876Smonthadar	/*
2183234876Smonthadar	 * Close frames are accepted if meshid is the same.
2184234876Smonthadar	 * Verify the other two types.
2185234876Smonthadar	 */
2186234876Smonthadar	if (sendclose == 0 && subtype != IEEE80211_ACTION_MESHPEERING_CLOSE &&
2187195618Srpaulo	    mesh_verify_meshconf(vap, meshconf)) {
2188234876Smonthadar		sendclose = 1;
2189195618Srpaulo		IEEE80211_DISCARD(vap,
2190195618Srpaulo		    IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH,
2191234876Smonthadar		    wh, NULL, "%s", "configuration missmatch");
2192234876Smonthadar	}
2193234876Smonthadar
2194234876Smonthadar	if (sendclose) {
2195195618Srpaulo		vap->iv_stats.is_rx_mgtdiscard++;
2196195618Srpaulo		switch (ni->ni_mlstate) {
2197195618Srpaulo		case IEEE80211_NODE_MESH_IDLE:
2198195618Srpaulo		case IEEE80211_NODE_MESH_ESTABLISHED:
2199195618Srpaulo		case IEEE80211_NODE_MESH_HOLDING:
2200195618Srpaulo			/* ignore */
2201195618Srpaulo			break;
2202195618Srpaulo		case IEEE80211_NODE_MESH_OPENSNT:
2203195618Srpaulo		case IEEE80211_NODE_MESH_OPENRCV:
2204195618Srpaulo		case IEEE80211_NODE_MESH_CONFIRMRCV:
2205195618Srpaulo			args[0] = ni->ni_mlpid;
2206195618Srpaulo			args[1] = ni->ni_mllid;
2207234876Smonthadar			/* Reason codes for rejection */
2208234876Smonthadar			switch (subtype) {
2209234876Smonthadar			case IEEE80211_ACTION_MESHPEERING_OPEN:
2210234876Smonthadar				args[2] = IEEE80211_REASON_MESH_CPVIOLATION;
2211234876Smonthadar				break;
2212234876Smonthadar			case IEEE80211_ACTION_MESHPEERING_CONFIRM:
2213234876Smonthadar				args[2] = IEEE80211_REASON_MESH_INCONS_PARAMS;
2214234876Smonthadar				break;
2215234876Smonthadar			}
2216195618Srpaulo			ieee80211_send_action(ni,
2217234874Smonthadar			    IEEE80211_ACTION_CAT_SELF_PROT,
2218195618Srpaulo			    IEEE80211_ACTION_MESHPEERING_CLOSE,
2219195618Srpaulo			    args);
2220195618Srpaulo			mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING);
2221195618Srpaulo			mesh_peer_timeout_setup(ni);
2222195618Srpaulo			break;
2223195618Srpaulo		}
2224195618Srpaulo		return NULL;
2225195618Srpaulo	}
2226234876Smonthadar
2227195618Srpaulo	return (const struct ieee80211_meshpeer_ie *) mp;
2228195618Srpaulo}
2229195618Srpaulo
2230195618Srpaulostatic int
2231195618Srpaulomesh_recv_action_meshpeering_open(struct ieee80211_node *ni,
2232195618Srpaulo	const struct ieee80211_frame *wh,
2233195618Srpaulo	const uint8_t *frm, const uint8_t *efrm)
2234195618Srpaulo{
2235195618Srpaulo	struct ieee80211vap *vap = ni->ni_vap;
2236234876Smonthadar	struct ieee80211_mesh_state *ms = vap->iv_mesh;
2237195618Srpaulo	struct ieee80211_meshpeer_ie ie;
2238195618Srpaulo	const struct ieee80211_meshpeer_ie *meshpeer;
2239195618Srpaulo	uint16_t args[3];
2240195618Srpaulo
2241195618Srpaulo	/* +2+2 for action + code + capabilites */
2242197413Srpaulo	meshpeer = mesh_parse_meshpeering_action(ni, wh, frm+2+2, efrm, &ie,
2243197413Srpaulo	    IEEE80211_ACTION_MESHPEERING_OPEN);
2244195618Srpaulo	if (meshpeer == NULL) {
2245195618Srpaulo		return 0;
2246195618Srpaulo	}
2247195618Srpaulo
2248195618Srpaulo	/* XXX move up */
2249195618Srpaulo	IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, ni,
2250195618Srpaulo	    "recv PEER OPEN, lid 0x%x", meshpeer->peer_llinkid);
2251195618Srpaulo
2252195618Srpaulo	switch (ni->ni_mlstate) {
2253195618Srpaulo	case IEEE80211_NODE_MESH_IDLE:
2254234876Smonthadar		/* Reject open request if reached our maximum neighbor count */
2255234876Smonthadar		if (ms->ms_neighbors >= IEEE80211_MESH_MAX_NEIGHBORS) {
2256234876Smonthadar			args[0] = meshpeer->peer_llinkid;
2257234876Smonthadar			args[1] = 0;
2258234876Smonthadar			args[2] = IEEE80211_REASON_MESH_MAX_PEERS;
2259234876Smonthadar			ieee80211_send_action(ni,
2260234876Smonthadar			    IEEE80211_ACTION_CAT_SELF_PROT,
2261234876Smonthadar			    IEEE80211_ACTION_MESHPEERING_CLOSE,
2262234876Smonthadar			    args);
2263234876Smonthadar			/* stay in IDLE state */
2264234876Smonthadar			return (0);
2265234876Smonthadar		}
2266234876Smonthadar		/* Open frame accepted */
2267195618Srpaulo		mesh_linkchange(ni, IEEE80211_NODE_MESH_OPENRCV);
2268195618Srpaulo		ni->ni_mllid = meshpeer->peer_llinkid;
2269195618Srpaulo		ni->ni_mlpid = mesh_generateid(vap);
2270195618Srpaulo		if (ni->ni_mlpid == 0)
2271195618Srpaulo			return 0;		/* XXX */
2272195618Srpaulo		args[0] = ni->ni_mlpid;
2273195618Srpaulo		/* Announce we're open too... */
2274195618Srpaulo		ieee80211_send_action(ni,
2275234874Smonthadar		    IEEE80211_ACTION_CAT_SELF_PROT,
2276195618Srpaulo		    IEEE80211_ACTION_MESHPEERING_OPEN, args);
2277195618Srpaulo		/* ...and confirm the link. */
2278195618Srpaulo		args[0] = ni->ni_mlpid;
2279195618Srpaulo		args[1] = ni->ni_mllid;
2280195618Srpaulo		ieee80211_send_action(ni,
2281234874Smonthadar		    IEEE80211_ACTION_CAT_SELF_PROT,
2282195618Srpaulo		    IEEE80211_ACTION_MESHPEERING_CONFIRM,
2283195618Srpaulo		    args);
2284195618Srpaulo		mesh_peer_timeout_setup(ni);
2285195618Srpaulo		break;
2286195618Srpaulo	case IEEE80211_NODE_MESH_OPENRCV:
2287195618Srpaulo		/* Wrong Link ID */
2288195618Srpaulo		if (ni->ni_mllid != meshpeer->peer_llinkid) {
2289195618Srpaulo			args[0] = ni->ni_mllid;
2290195618Srpaulo			args[1] = ni->ni_mlpid;
2291195618Srpaulo			args[2] = IEEE80211_REASON_PEER_LINK_CANCELED;
2292195618Srpaulo			ieee80211_send_action(ni,
2293234874Smonthadar			    IEEE80211_ACTION_CAT_SELF_PROT,
2294195618Srpaulo			    IEEE80211_ACTION_MESHPEERING_CLOSE,
2295195618Srpaulo			    args);
2296195618Srpaulo			mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING);
2297195618Srpaulo			mesh_peer_timeout_setup(ni);
2298195618Srpaulo			break;
2299195618Srpaulo		}
2300195618Srpaulo		/* Duplicate open, confirm again. */
2301195618Srpaulo		args[0] = ni->ni_mlpid;
2302195618Srpaulo		args[1] = ni->ni_mllid;
2303195618Srpaulo		ieee80211_send_action(ni,
2304234874Smonthadar		    IEEE80211_ACTION_CAT_SELF_PROT,
2305195618Srpaulo		    IEEE80211_ACTION_MESHPEERING_CONFIRM,
2306195618Srpaulo		    args);
2307195618Srpaulo		break;
2308195618Srpaulo	case IEEE80211_NODE_MESH_OPENSNT:
2309195618Srpaulo		ni->ni_mllid = meshpeer->peer_llinkid;
2310195618Srpaulo		mesh_linkchange(ni, IEEE80211_NODE_MESH_OPENRCV);
2311195618Srpaulo		args[0] = ni->ni_mlpid;
2312195618Srpaulo		args[1] = ni->ni_mllid;
2313195618Srpaulo		ieee80211_send_action(ni,
2314234874Smonthadar		    IEEE80211_ACTION_CAT_SELF_PROT,
2315195618Srpaulo		    IEEE80211_ACTION_MESHPEERING_CONFIRM,
2316195618Srpaulo		    args);
2317195618Srpaulo		/* NB: don't setup/clear any timeout */
2318195618Srpaulo		break;
2319195618Srpaulo	case IEEE80211_NODE_MESH_CONFIRMRCV:
2320195618Srpaulo		if (ni->ni_mlpid != meshpeer->peer_linkid ||
2321195618Srpaulo		    ni->ni_mllid != meshpeer->peer_llinkid) {
2322195618Srpaulo			args[0] = ni->ni_mlpid;
2323195618Srpaulo			args[1] = ni->ni_mllid;
2324195618Srpaulo			args[2] = IEEE80211_REASON_PEER_LINK_CANCELED;
2325195618Srpaulo			ieee80211_send_action(ni,
2326234874Smonthadar			    IEEE80211_ACTION_CAT_SELF_PROT,
2327195618Srpaulo			    IEEE80211_ACTION_MESHPEERING_CLOSE,
2328195618Srpaulo			    args);
2329195618Srpaulo			mesh_linkchange(ni,
2330195618Srpaulo			    IEEE80211_NODE_MESH_HOLDING);
2331195618Srpaulo			mesh_peer_timeout_setup(ni);
2332195618Srpaulo			break;
2333195618Srpaulo		}
2334195618Srpaulo		mesh_linkchange(ni, IEEE80211_NODE_MESH_ESTABLISHED);
2335195618Srpaulo		ni->ni_mllid = meshpeer->peer_llinkid;
2336195618Srpaulo		args[0] = ni->ni_mlpid;
2337195618Srpaulo		args[1] = ni->ni_mllid;
2338195618Srpaulo		ieee80211_send_action(ni,
2339234874Smonthadar		    IEEE80211_ACTION_CAT_SELF_PROT,
2340195618Srpaulo		    IEEE80211_ACTION_MESHPEERING_CONFIRM,
2341195618Srpaulo		    args);
2342195618Srpaulo		mesh_peer_timeout_stop(ni);
2343195618Srpaulo		break;
2344195618Srpaulo	case IEEE80211_NODE_MESH_ESTABLISHED:
2345195618Srpaulo		if (ni->ni_mllid != meshpeer->peer_llinkid) {
2346195618Srpaulo			args[0] = ni->ni_mllid;
2347195618Srpaulo			args[1] = ni->ni_mlpid;
2348195618Srpaulo			args[2] = IEEE80211_REASON_PEER_LINK_CANCELED;
2349195618Srpaulo			ieee80211_send_action(ni,
2350234874Smonthadar			    IEEE80211_ACTION_CAT_SELF_PROT,
2351195618Srpaulo			    IEEE80211_ACTION_MESHPEERING_CLOSE,
2352195618Srpaulo			    args);
2353195618Srpaulo			mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING);
2354195618Srpaulo			mesh_peer_timeout_setup(ni);
2355195618Srpaulo			break;
2356195618Srpaulo		}
2357195618Srpaulo		args[0] = ni->ni_mlpid;
2358195618Srpaulo		args[1] = ni->ni_mllid;
2359195618Srpaulo		ieee80211_send_action(ni,
2360234874Smonthadar		    IEEE80211_ACTION_CAT_SELF_PROT,
2361195618Srpaulo		    IEEE80211_ACTION_MESHPEERING_CONFIRM,
2362195618Srpaulo		    args);
2363195618Srpaulo		break;
2364195618Srpaulo	case IEEE80211_NODE_MESH_HOLDING:
2365195618Srpaulo		args[0] = ni->ni_mlpid;
2366195618Srpaulo		args[1] = meshpeer->peer_llinkid;
2367234876Smonthadar		/* Standard not clear about what the reaason code should be */
2368234876Smonthadar		args[2] = IEEE80211_REASON_PEER_LINK_CANCELED;
2369195618Srpaulo		ieee80211_send_action(ni,
2370234874Smonthadar		    IEEE80211_ACTION_CAT_SELF_PROT,
2371195618Srpaulo		    IEEE80211_ACTION_MESHPEERING_CLOSE,
2372195618Srpaulo		    args);
2373195618Srpaulo		break;
2374195618Srpaulo	}
2375195618Srpaulo	return 0;
2376195618Srpaulo}
2377195618Srpaulo
2378195618Srpaulostatic int
2379195618Srpaulomesh_recv_action_meshpeering_confirm(struct ieee80211_node *ni,
2380195618Srpaulo	const struct ieee80211_frame *wh,
2381195618Srpaulo	const uint8_t *frm, const uint8_t *efrm)
2382195618Srpaulo{
2383195618Srpaulo	struct ieee80211vap *vap = ni->ni_vap;
2384195618Srpaulo	struct ieee80211_meshpeer_ie ie;
2385195618Srpaulo	const struct ieee80211_meshpeer_ie *meshpeer;
2386195618Srpaulo	uint16_t args[3];
2387195618Srpaulo
2388195618Srpaulo	/* +2+2+2+2 for action + code + capabilites + status code + AID */
2389197413Srpaulo	meshpeer = mesh_parse_meshpeering_action(ni, wh, frm+2+2+2+2, efrm, &ie,
2390197413Srpaulo	    IEEE80211_ACTION_MESHPEERING_CONFIRM);
2391195618Srpaulo	if (meshpeer == NULL) {
2392195618Srpaulo		return 0;
2393195618Srpaulo	}
2394195618Srpaulo
2395195618Srpaulo	IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, ni,
2396195618Srpaulo	    "recv PEER CONFIRM, local id 0x%x, peer id 0x%x",
2397195618Srpaulo	    meshpeer->peer_llinkid, meshpeer->peer_linkid);
2398195618Srpaulo
2399195618Srpaulo	switch (ni->ni_mlstate) {
2400195618Srpaulo	case IEEE80211_NODE_MESH_OPENRCV:
2401195618Srpaulo		mesh_linkchange(ni, IEEE80211_NODE_MESH_ESTABLISHED);
2402195618Srpaulo		mesh_peer_timeout_stop(ni);
2403195618Srpaulo		break;
2404195618Srpaulo	case IEEE80211_NODE_MESH_OPENSNT:
2405195618Srpaulo		mesh_linkchange(ni, IEEE80211_NODE_MESH_CONFIRMRCV);
2406234876Smonthadar		mesh_peer_timeout_setup(ni);
2407195618Srpaulo		break;
2408195618Srpaulo	case IEEE80211_NODE_MESH_HOLDING:
2409195618Srpaulo		args[0] = ni->ni_mlpid;
2410195618Srpaulo		args[1] = meshpeer->peer_llinkid;
2411234876Smonthadar		/* Standard not clear about what the reaason code should be */
2412234876Smonthadar		args[2] = IEEE80211_REASON_PEER_LINK_CANCELED;
2413195618Srpaulo		ieee80211_send_action(ni,
2414234874Smonthadar		    IEEE80211_ACTION_CAT_SELF_PROT,
2415195618Srpaulo		    IEEE80211_ACTION_MESHPEERING_CLOSE,
2416195618Srpaulo		    args);
2417195618Srpaulo		break;
2418195618Srpaulo	case IEEE80211_NODE_MESH_CONFIRMRCV:
2419195618Srpaulo		if (ni->ni_mllid != meshpeer->peer_llinkid) {
2420195618Srpaulo			args[0] = ni->ni_mlpid;
2421195618Srpaulo			args[1] = ni->ni_mllid;
2422195618Srpaulo			args[2] = IEEE80211_REASON_PEER_LINK_CANCELED;
2423195618Srpaulo			ieee80211_send_action(ni,
2424234874Smonthadar			    IEEE80211_ACTION_CAT_SELF_PROT,
2425195618Srpaulo			    IEEE80211_ACTION_MESHPEERING_CLOSE,
2426195618Srpaulo			    args);
2427195618Srpaulo			mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING);
2428195618Srpaulo			mesh_peer_timeout_setup(ni);
2429195618Srpaulo		}
2430195618Srpaulo		break;
2431195618Srpaulo	default:
2432195618Srpaulo		IEEE80211_DISCARD(vap,
2433195618Srpaulo		    IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH,
2434195618Srpaulo		    wh, NULL, "received confirm in invalid state %d",
2435195618Srpaulo		    ni->ni_mlstate);
2436195618Srpaulo		vap->iv_stats.is_rx_mgtdiscard++;
2437195618Srpaulo		break;
2438195618Srpaulo	}
2439195618Srpaulo	return 0;
2440195618Srpaulo}
2441195618Srpaulo
2442195618Srpaulostatic int
2443195618Srpaulomesh_recv_action_meshpeering_close(struct ieee80211_node *ni,
2444195618Srpaulo	const struct ieee80211_frame *wh,
2445195618Srpaulo	const uint8_t *frm, const uint8_t *efrm)
2446195618Srpaulo{
2447234876Smonthadar	struct ieee80211_meshpeer_ie ie;
2448234876Smonthadar	const struct ieee80211_meshpeer_ie *meshpeer;
2449195618Srpaulo	uint16_t args[3];
2450195618Srpaulo
2451234876Smonthadar	/* +2 for action + code */
2452234876Smonthadar	meshpeer = mesh_parse_meshpeering_action(ni, wh, frm+2, efrm, &ie,
2453234876Smonthadar	    IEEE80211_ACTION_MESHPEERING_CLOSE);
2454234876Smonthadar	if (meshpeer == NULL) {
2455234876Smonthadar		return 0;
2456234876Smonthadar	}
2457234876Smonthadar
2458234876Smonthadar	/*
2459234876Smonthadar	 * XXX: check reason code, for example we could receive
2460234876Smonthadar	 * IEEE80211_REASON_MESH_MAX_PEERS then we should not attempt
2461234876Smonthadar	 * to peer again.
2462234876Smonthadar	 */
2463234876Smonthadar
2464195618Srpaulo	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH,
2465195618Srpaulo	    ni, "%s", "recv PEER CLOSE");
2466195618Srpaulo
2467195618Srpaulo	switch (ni->ni_mlstate) {
2468195618Srpaulo	case IEEE80211_NODE_MESH_IDLE:
2469195618Srpaulo		/* ignore */
2470195618Srpaulo		break;
2471195618Srpaulo	case IEEE80211_NODE_MESH_OPENRCV:
2472195618Srpaulo	case IEEE80211_NODE_MESH_OPENSNT:
2473195618Srpaulo	case IEEE80211_NODE_MESH_CONFIRMRCV:
2474195618Srpaulo	case IEEE80211_NODE_MESH_ESTABLISHED:
2475195618Srpaulo		args[0] = ni->ni_mlpid;
2476195618Srpaulo		args[1] = ni->ni_mllid;
2477195618Srpaulo		args[2] = IEEE80211_REASON_MESH_CLOSE_RCVD;
2478195618Srpaulo		ieee80211_send_action(ni,
2479234874Smonthadar		    IEEE80211_ACTION_CAT_SELF_PROT,
2480195618Srpaulo		    IEEE80211_ACTION_MESHPEERING_CLOSE,
2481195618Srpaulo		    args);
2482195618Srpaulo		mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING);
2483195618Srpaulo		mesh_peer_timeout_setup(ni);
2484195618Srpaulo		break;
2485195618Srpaulo	case IEEE80211_NODE_MESH_HOLDING:
2486195618Srpaulo		mesh_linkchange(ni, IEEE80211_NODE_MESH_IDLE);
2487234876Smonthadar		mesh_peer_timeout_stop(ni);
2488195618Srpaulo		break;
2489195618Srpaulo	}
2490195618Srpaulo	return 0;
2491195618Srpaulo}
2492195618Srpaulo
2493195618Srpaulo/*
2494195618Srpaulo * Link Metric handling.
2495195618Srpaulo */
2496195618Srpaulostatic int
2497232479Sadrianmesh_recv_action_meshlmetric(struct ieee80211_node *ni,
2498195618Srpaulo	const struct ieee80211_frame *wh,
2499195618Srpaulo	const uint8_t *frm, const uint8_t *efrm)
2500195618Srpaulo{
2501232479Sadrian	const struct ieee80211_meshlmetric_ie *ie =
2502232479Sadrian	    (const struct ieee80211_meshlmetric_ie *)
2503232479Sadrian	    (frm+2); /* action + code */
2504232479Sadrian	struct ieee80211_meshlmetric_ie lm_rep;
2505232479Sadrian
2506232479Sadrian	if (ie->lm_flags & IEEE80211_MESH_LMETRIC_FLAGS_REQ) {
2507232479Sadrian		lm_rep.lm_flags = 0;
2508232479Sadrian		lm_rep.lm_metric = mesh_airtime_calc(ni);
2509232479Sadrian		ieee80211_send_action(ni,
2510232479Sadrian		    IEEE80211_ACTION_CAT_MESH,
2511232479Sadrian		    IEEE80211_ACTION_MESH_LMETRIC,
2512232479Sadrian		    &lm_rep);
2513232479Sadrian	}
2514232479Sadrian	/* XXX: else do nothing for now */
2515195618Srpaulo	return 0;
2516195618Srpaulo}
2517195618Srpaulo
2518246506Smonthadar/*
2519246520Smonthadar * Parse meshgate action ie's for GANN frames.
2520246520Smonthadar * Returns -1 if parsing fails, otherwise 0.
2521246520Smonthadar */
2522246520Smonthadarstatic int
2523246520Smonthadarmesh_parse_meshgate_action(struct ieee80211_node *ni,
2524246520Smonthadar    const struct ieee80211_frame *wh,	/* XXX for VERIFY_LENGTH */
2525246520Smonthadar    struct ieee80211_meshgann_ie *ie, const uint8_t *frm, const uint8_t *efrm)
2526246520Smonthadar{
2527246520Smonthadar	struct ieee80211vap *vap = ni->ni_vap;
2528246520Smonthadar	const struct ieee80211_meshgann_ie *gannie;
2529246520Smonthadar
2530246520Smonthadar	while (efrm - frm > 1) {
2531246520Smonthadar		IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return -1);
2532246520Smonthadar		switch (*frm) {
2533246520Smonthadar		case IEEE80211_ELEMID_MESHGANN:
2534246520Smonthadar			gannie = (const struct ieee80211_meshgann_ie *) frm;
2535246861Sadrian			memset(ie, 0, sizeof(*ie));
2536246520Smonthadar			ie->gann_ie = gannie->gann_ie;
2537246520Smonthadar			ie->gann_len = gannie->gann_len;
2538246520Smonthadar			ie->gann_flags = gannie->gann_flags;
2539246520Smonthadar			ie->gann_hopcount = gannie->gann_hopcount;
2540246520Smonthadar			ie->gann_ttl = gannie->gann_ttl;
2541246520Smonthadar			IEEE80211_ADDR_COPY(ie->gann_addr, gannie->gann_addr);
2542298359Savos			ie->gann_seq = le32dec(&gannie->gann_seq);
2543298359Savos			ie->gann_interval = le16dec(&gannie->gann_interval);
2544246520Smonthadar			break;
2545246520Smonthadar		}
2546246520Smonthadar		frm += frm[1] + 2;
2547246520Smonthadar	}
2548246520Smonthadar
2549246520Smonthadar	return 0;
2550246520Smonthadar}
2551246520Smonthadar
2552246520Smonthadar/*
2553246506Smonthadar * Mesh Gate Announcement handling.
2554246506Smonthadar */
2555195618Srpaulostatic int
2556246506Smonthadarmesh_recv_action_meshgate(struct ieee80211_node *ni,
2557246506Smonthadar	const struct ieee80211_frame *wh,
2558246506Smonthadar	const uint8_t *frm, const uint8_t *efrm)
2559195618Srpaulo{
2560246506Smonthadar	struct ieee80211vap *vap = ni->ni_vap;
2561246508Smonthadar	struct ieee80211_mesh_state *ms = vap->iv_mesh;
2562246508Smonthadar	struct ieee80211_mesh_gate_route *gr, *next;
2563246506Smonthadar	struct ieee80211_mesh_route *rt_gate;
2564246508Smonthadar	struct ieee80211_meshgann_ie pgann;
2565246520Smonthadar	struct ieee80211_meshgann_ie ie;
2566246508Smonthadar	int found = 0;
2567246506Smonthadar
2568246520Smonthadar	/* +2 for action + code */
2569246520Smonthadar	if (mesh_parse_meshgate_action(ni, wh, &ie, frm+2, efrm) != 0) {
2570246520Smonthadar		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_MESH,
2571246520Smonthadar		    ni->ni_macaddr, NULL, "%s",
2572246520Smonthadar		    "GANN parsing failed");
2573246520Smonthadar		vap->iv_stats.is_rx_mgtdiscard++;
2574246520Smonthadar		return (0);
2575246520Smonthadar	}
2576246520Smonthadar
2577246520Smonthadar	if (IEEE80211_ADDR_EQ(vap->iv_myaddr, ie.gann_addr))
2578246508Smonthadar		return 0;
2579246506Smonthadar
2580246508Smonthadar	IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, ni->ni_macaddr,
2581246520Smonthadar	    "received GANN, meshgate: %6D (seq %u)", ie.gann_addr, ":",
2582246520Smonthadar	    ie.gann_seq);
2583246508Smonthadar
2584246508Smonthadar	if (ms == NULL)
2585246508Smonthadar		return (0);
2586246508Smonthadar	MESH_RT_LOCK(ms);
2587246508Smonthadar	TAILQ_FOREACH_SAFE(gr, &ms->ms_known_gates, gr_next, next) {
2588246520Smonthadar		if (!IEEE80211_ADDR_EQ(gr->gr_addr, ie.gann_addr))
2589246508Smonthadar			continue;
2590246520Smonthadar		if (ie.gann_seq <= gr->gr_lastseq) {
2591246508Smonthadar			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_MESH,
2592246508Smonthadar			    ni->ni_macaddr, NULL,
2593246508Smonthadar			    "GANN old seqno %u <= %u",
2594246520Smonthadar			    ie.gann_seq, gr->gr_lastseq);
2595246508Smonthadar			MESH_RT_UNLOCK(ms);
2596246508Smonthadar			return (0);
2597246508Smonthadar		}
2598246508Smonthadar		/* corresponding mesh gate found & GANN accepted */
2599246508Smonthadar		found = 1;
2600246508Smonthadar		break;
2601246508Smonthadar
2602246508Smonthadar	}
2603246508Smonthadar	if (found == 0) {
2604246508Smonthadar		/* this GANN is from a new mesh Gate add it to known table. */
2605246520Smonthadar		IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, ie.gann_addr,
2606246520Smonthadar		    "stored new GANN information, seq %u.", ie.gann_seq);
2607283538Sadrian		gr = IEEE80211_MALLOC(ALIGN(sizeof(struct ieee80211_mesh_gate_route)),
2608283538Sadrian		    M_80211_MESH_GT_RT,
2609283538Sadrian		    IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
2610246520Smonthadar		IEEE80211_ADDR_COPY(gr->gr_addr, ie.gann_addr);
2611246508Smonthadar		TAILQ_INSERT_TAIL(&ms->ms_known_gates, gr, gr_next);
2612246508Smonthadar	}
2613246520Smonthadar	gr->gr_lastseq = ie.gann_seq;
2614246508Smonthadar
2615246508Smonthadar	/* check if we have a path to this gate */
2616246508Smonthadar	rt_gate = mesh_rt_find_locked(ms, gr->gr_addr);
2617246506Smonthadar	if (rt_gate != NULL &&
2618246508Smonthadar	    rt_gate->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) {
2619246508Smonthadar		gr->gr_route = rt_gate;
2620246506Smonthadar		rt_gate->rt_flags |= IEEE80211_MESHRT_FLAGS_GATE;
2621246508Smonthadar	}
2622246506Smonthadar
2623246508Smonthadar	MESH_RT_UNLOCK(ms);
2624246508Smonthadar
2625246508Smonthadar	/* popagate only if decremented ttl >= 1 && forwarding is enabled */
2626246520Smonthadar	if ((ie.gann_ttl - 1) < 1 && !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD))
2627246508Smonthadar		return 0;
2628322060Spfg	pgann.gann_flags = ie.gann_flags; /* Reserved */
2629246520Smonthadar	pgann.gann_hopcount = ie.gann_hopcount + 1;
2630246520Smonthadar	pgann.gann_ttl = ie.gann_ttl - 1;
2631246520Smonthadar	IEEE80211_ADDR_COPY(pgann.gann_addr, ie.gann_addr);
2632246520Smonthadar	pgann.gann_seq = ie.gann_seq;
2633246520Smonthadar	pgann.gann_interval = ie.gann_interval;
2634246508Smonthadar
2635246520Smonthadar	IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, ie.gann_addr,
2636246508Smonthadar	    "%s", "propagate GANN");
2637246508Smonthadar
2638246508Smonthadar	ieee80211_send_action(vap->iv_bss, IEEE80211_ACTION_CAT_MESH,
2639246508Smonthadar	    IEEE80211_ACTION_MESH_GANN, &pgann);
2640246508Smonthadar
2641246506Smonthadar	return 0;
2642246506Smonthadar}
2643246506Smonthadar
2644246506Smonthadarstatic int
2645246506Smonthadarmesh_send_action(struct ieee80211_node *ni,
2646246506Smonthadar    const uint8_t sa[IEEE80211_ADDR_LEN],
2647246506Smonthadar    const uint8_t da[IEEE80211_ADDR_LEN],
2648246506Smonthadar    struct mbuf *m)
2649246506Smonthadar{
2650246506Smonthadar	struct ieee80211vap *vap = ni->ni_vap;
2651246506Smonthadar	struct ieee80211com *ic = ni->ni_ic;
2652195618Srpaulo	struct ieee80211_bpf_params params;
2653248069Sadrian	int ret;
2654195618Srpaulo
2655246506Smonthadar	KASSERT(ni != NULL, ("null node"));
2656246506Smonthadar
2657246506Smonthadar	if (vap->iv_state == IEEE80211_S_CAC) {
2658246506Smonthadar		IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, ni,
2659246506Smonthadar		    "block %s frame in CAC state", "Mesh action");
2660246506Smonthadar		vap->iv_stats.is_tx_badstate++;
2661246506Smonthadar		ieee80211_free_node(ni);
2662246506Smonthadar		m_freem(m);
2663246506Smonthadar		return EIO;		/* XXX */
2664246506Smonthadar	}
2665246506Smonthadar
2666260718Sglebius	M_PREPEND(m, sizeof(struct ieee80211_frame), M_NOWAIT);
2667246506Smonthadar	if (m == NULL) {
2668246506Smonthadar		ieee80211_free_node(ni);
2669246506Smonthadar		return ENOMEM;
2670246506Smonthadar	}
2671246506Smonthadar
2672248069Sadrian	IEEE80211_TX_LOCK(ic);
2673246506Smonthadar	ieee80211_send_setup(ni, m,
2674246506Smonthadar	     IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_ACTION,
2675246506Smonthadar	     IEEE80211_NONQOS_TID, sa, da, sa);
2676246506Smonthadar	m->m_flags |= M_ENCAP;		/* mark encapsulated */
2677246506Smonthadar
2678195618Srpaulo	memset(&params, 0, sizeof(params));
2679195618Srpaulo	params.ibp_pri = WME_AC_VO;
2680195618Srpaulo	params.ibp_rate0 = ni->ni_txparms->mgmtrate;
2681246506Smonthadar	if (IEEE80211_IS_MULTICAST(da))
2682246506Smonthadar		params.ibp_try0 = 1;
2683246506Smonthadar	else
2684246506Smonthadar		params.ibp_try0 = ni->ni_txparms->maxretry;
2685195618Srpaulo	params.ibp_power = ni->ni_txpower;
2686246506Smonthadar
2687246506Smonthadar	IEEE80211_NODE_STAT(ni, tx_mgmt);
2688246506Smonthadar
2689248069Sadrian	ret = ieee80211_raw_output(vap, ni, m, &params);
2690248069Sadrian	IEEE80211_TX_UNLOCK(ic);
2691248069Sadrian	return (ret);
2692195618Srpaulo}
2693195618Srpaulo
2694195618Srpaulo#define	ADDSHORT(frm, v) do {			\
2695195618Srpaulo	frm[0] = (v) & 0xff;			\
2696195618Srpaulo	frm[1] = (v) >> 8;			\
2697195618Srpaulo	frm += 2;				\
2698195618Srpaulo} while (0)
2699195618Srpaulo#define	ADDWORD(frm, v) do {			\
2700195618Srpaulo	frm[0] = (v) & 0xff;			\
2701195618Srpaulo	frm[1] = ((v) >> 8) & 0xff;		\
2702195618Srpaulo	frm[2] = ((v) >> 16) & 0xff;		\
2703195618Srpaulo	frm[3] = ((v) >> 24) & 0xff;		\
2704195618Srpaulo	frm += 4;				\
2705195618Srpaulo} while (0)
2706195618Srpaulo
2707195618Srpaulostatic int
2708195618Srpaulomesh_send_action_meshpeering_open(struct ieee80211_node *ni,
2709195618Srpaulo	int category, int action, void *args0)
2710195618Srpaulo{
2711195618Srpaulo	struct ieee80211vap *vap = ni->ni_vap;
2712195618Srpaulo	struct ieee80211com *ic = ni->ni_ic;
2713195618Srpaulo	uint16_t *args = args0;
2714195618Srpaulo	const struct ieee80211_rateset *rs;
2715195618Srpaulo	struct mbuf *m;
2716195618Srpaulo	uint8_t *frm;
2717195618Srpaulo
2718195618Srpaulo	IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, ni,
2719195618Srpaulo	    "send PEER OPEN action: localid 0x%x", args[0]);
2720195618Srpaulo
2721195618Srpaulo	IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
2722195618Srpaulo	    "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__,
2723195618Srpaulo	    ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1);
2724195618Srpaulo	ieee80211_ref_node(ni);
2725195618Srpaulo
2726195618Srpaulo	m = ieee80211_getmgtframe(&frm,
2727195618Srpaulo	    ic->ic_headroom + sizeof(struct ieee80211_frame),
2728195618Srpaulo	    sizeof(uint16_t)	/* action+category */
2729195618Srpaulo	    + sizeof(uint16_t)	/* capabilites */
2730230926Srpaulo	    + 2 + IEEE80211_RATE_SIZE
2731230926Srpaulo	    + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
2732195618Srpaulo	    + 2 + IEEE80211_MESHID_LEN
2733230926Srpaulo	    + sizeof(struct ieee80211_meshconf_ie)
2734195618Srpaulo	    + sizeof(struct ieee80211_meshpeer_ie)
2735195618Srpaulo	);
2736195618Srpaulo	if (m != NULL) {
2737195618Srpaulo		/*
2738195618Srpaulo		 * mesh peer open action frame format:
2739195618Srpaulo		 *   [1] category
2740195618Srpaulo		 *   [1] action
2741195618Srpaulo		 *   [2] capabilities
2742195618Srpaulo		 *   [tlv] rates
2743195618Srpaulo		 *   [tlv] xrates
2744195618Srpaulo		 *   [tlv] mesh id
2745195618Srpaulo		 *   [tlv] mesh conf
2746195618Srpaulo		 *   [tlv] mesh peer link mgmt
2747195618Srpaulo		 */
2748195618Srpaulo		*frm++ = category;
2749195618Srpaulo		*frm++ = action;
2750195618Srpaulo		ADDSHORT(frm, ieee80211_getcapinfo(vap, ni->ni_chan));
2751195618Srpaulo		rs = ieee80211_get_suprates(ic, ic->ic_curchan);
2752195618Srpaulo		frm = ieee80211_add_rates(frm, rs);
2753195618Srpaulo		frm = ieee80211_add_xrates(frm, rs);
2754195618Srpaulo		frm = ieee80211_add_meshid(frm, vap);
2755195618Srpaulo		frm = ieee80211_add_meshconf(frm, vap);
2756234874Smonthadar		frm = ieee80211_add_meshpeer(frm, IEEE80211_ACTION_MESHPEERING_OPEN,
2757195618Srpaulo		    args[0], 0, 0);
2758195618Srpaulo		m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
2759246506Smonthadar		return mesh_send_action(ni, vap->iv_myaddr, ni->ni_macaddr, m);
2760195618Srpaulo	} else {
2761195618Srpaulo		vap->iv_stats.is_tx_nobuf++;
2762195618Srpaulo		ieee80211_free_node(ni);
2763195618Srpaulo		return ENOMEM;
2764195618Srpaulo	}
2765195618Srpaulo}
2766195618Srpaulo
2767195618Srpaulostatic int
2768195618Srpaulomesh_send_action_meshpeering_confirm(struct ieee80211_node *ni,
2769195618Srpaulo	int category, int action, void *args0)
2770195618Srpaulo{
2771195618Srpaulo	struct ieee80211vap *vap = ni->ni_vap;
2772195618Srpaulo	struct ieee80211com *ic = ni->ni_ic;
2773195618Srpaulo	uint16_t *args = args0;
2774195618Srpaulo	const struct ieee80211_rateset *rs;
2775195618Srpaulo	struct mbuf *m;
2776195618Srpaulo	uint8_t *frm;
2777195618Srpaulo
2778195618Srpaulo	IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, ni,
2779195618Srpaulo	    "send PEER CONFIRM action: localid 0x%x, peerid 0x%x",
2780195618Srpaulo	    args[0], args[1]);
2781195618Srpaulo
2782195618Srpaulo	IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
2783195618Srpaulo	    "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__,
2784195618Srpaulo	    ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1);
2785195618Srpaulo	ieee80211_ref_node(ni);
2786195618Srpaulo
2787195618Srpaulo	m = ieee80211_getmgtframe(&frm,
2788195618Srpaulo	    ic->ic_headroom + sizeof(struct ieee80211_frame),
2789195618Srpaulo	    sizeof(uint16_t)	/* action+category */
2790195618Srpaulo	    + sizeof(uint16_t)	/* capabilites */
2791195618Srpaulo	    + sizeof(uint16_t)	/* status code */
2792195618Srpaulo	    + sizeof(uint16_t)	/* AID */
2793230926Srpaulo	    + 2 + IEEE80211_RATE_SIZE
2794230926Srpaulo	    + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
2795195618Srpaulo	    + 2 + IEEE80211_MESHID_LEN
2796230926Srpaulo	    + sizeof(struct ieee80211_meshconf_ie)
2797195618Srpaulo	    + sizeof(struct ieee80211_meshpeer_ie)
2798195618Srpaulo	);
2799195618Srpaulo	if (m != NULL) {
2800195618Srpaulo		/*
2801195618Srpaulo		 * mesh peer confirm action frame format:
2802195618Srpaulo		 *   [1] category
2803195618Srpaulo		 *   [1] action
2804195618Srpaulo		 *   [2] capabilities
2805195618Srpaulo		 *   [2] status code
2806195618Srpaulo		 *   [2] association id (peer ID)
2807195618Srpaulo		 *   [tlv] rates
2808195618Srpaulo		 *   [tlv] xrates
2809195618Srpaulo		 *   [tlv] mesh id
2810195618Srpaulo		 *   [tlv] mesh conf
2811195618Srpaulo		 *   [tlv] mesh peer link mgmt
2812195618Srpaulo		 */
2813195618Srpaulo		*frm++ = category;
2814195618Srpaulo		*frm++ = action;
2815195618Srpaulo		ADDSHORT(frm, ieee80211_getcapinfo(vap, ni->ni_chan));
2816195618Srpaulo		ADDSHORT(frm, 0);		/* status code */
2817195618Srpaulo		ADDSHORT(frm, args[1]);		/* AID */
2818195618Srpaulo		rs = ieee80211_get_suprates(ic, ic->ic_curchan);
2819195618Srpaulo		frm = ieee80211_add_rates(frm, rs);
2820195618Srpaulo		frm = ieee80211_add_xrates(frm, rs);
2821195618Srpaulo		frm = ieee80211_add_meshid(frm, vap);
2822195618Srpaulo		frm = ieee80211_add_meshconf(frm, vap);
2823195618Srpaulo		frm = ieee80211_add_meshpeer(frm,
2824234874Smonthadar		    IEEE80211_ACTION_MESHPEERING_CONFIRM,
2825195618Srpaulo		    args[0], args[1], 0);
2826195618Srpaulo		m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
2827246506Smonthadar		return mesh_send_action(ni, vap->iv_myaddr, ni->ni_macaddr, m);
2828195618Srpaulo	} else {
2829195618Srpaulo		vap->iv_stats.is_tx_nobuf++;
2830195618Srpaulo		ieee80211_free_node(ni);
2831195618Srpaulo		return ENOMEM;
2832195618Srpaulo	}
2833195618Srpaulo}
2834195618Srpaulo
2835195618Srpaulostatic int
2836195618Srpaulomesh_send_action_meshpeering_close(struct ieee80211_node *ni,
2837195618Srpaulo	int category, int action, void *args0)
2838195618Srpaulo{
2839195618Srpaulo	struct ieee80211vap *vap = ni->ni_vap;
2840195618Srpaulo	struct ieee80211com *ic = ni->ni_ic;
2841195618Srpaulo	uint16_t *args = args0;
2842195618Srpaulo	struct mbuf *m;
2843195618Srpaulo	uint8_t *frm;
2844195618Srpaulo
2845195618Srpaulo	IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, ni,
2846298364Savos	    "send PEER CLOSE action: localid 0x%x, peerid 0x%x reason %d (%s)",
2847298364Savos	    args[0], args[1], args[2], ieee80211_reason_to_string(args[2]));
2848195618Srpaulo
2849195618Srpaulo	IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
2850195618Srpaulo	    "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__,
2851195618Srpaulo	    ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1);
2852195618Srpaulo	ieee80211_ref_node(ni);
2853195618Srpaulo
2854195618Srpaulo	m = ieee80211_getmgtframe(&frm,
2855195618Srpaulo	    ic->ic_headroom + sizeof(struct ieee80211_frame),
2856195618Srpaulo	    sizeof(uint16_t)	/* action+category */
2857195618Srpaulo	    + sizeof(uint16_t)	/* reason code */
2858195618Srpaulo	    + 2 + IEEE80211_MESHID_LEN
2859230926Srpaulo	    + sizeof(struct ieee80211_meshpeer_ie)
2860195618Srpaulo	);
2861195618Srpaulo	if (m != NULL) {
2862195618Srpaulo		/*
2863195618Srpaulo		 * mesh peer close action frame format:
2864195618Srpaulo		 *   [1] category
2865195618Srpaulo		 *   [1] action
2866195618Srpaulo		 *   [tlv] mesh id
2867195618Srpaulo		 *   [tlv] mesh peer link mgmt
2868195618Srpaulo		 */
2869195618Srpaulo		*frm++ = category;
2870195618Srpaulo		*frm++ = action;
2871195618Srpaulo		frm = ieee80211_add_meshid(frm, vap);
2872195618Srpaulo		frm = ieee80211_add_meshpeer(frm,
2873234874Smonthadar		    IEEE80211_ACTION_MESHPEERING_CLOSE,
2874195618Srpaulo		    args[0], args[1], args[2]);
2875195618Srpaulo		m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
2876246506Smonthadar		return mesh_send_action(ni, vap->iv_myaddr, ni->ni_macaddr, m);
2877195618Srpaulo	} else {
2878195618Srpaulo		vap->iv_stats.is_tx_nobuf++;
2879195618Srpaulo		ieee80211_free_node(ni);
2880195618Srpaulo		return ENOMEM;
2881195618Srpaulo	}
2882195618Srpaulo}
2883195618Srpaulo
2884195618Srpaulostatic int
2885232479Sadrianmesh_send_action_meshlmetric(struct ieee80211_node *ni,
2886195618Srpaulo	int category, int action, void *arg0)
2887195618Srpaulo{
2888195618Srpaulo	struct ieee80211vap *vap = ni->ni_vap;
2889195618Srpaulo	struct ieee80211com *ic = ni->ni_ic;
2890232479Sadrian	struct ieee80211_meshlmetric_ie *ie = arg0;
2891195618Srpaulo	struct mbuf *m;
2892195618Srpaulo	uint8_t *frm;
2893195618Srpaulo
2894232479Sadrian	if (ie->lm_flags & IEEE80211_MESH_LMETRIC_FLAGS_REQ) {
2895232479Sadrian		IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH,
2896232479Sadrian		    ni, "%s", "send LINK METRIC REQUEST action");
2897195618Srpaulo	} else {
2898232479Sadrian		IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH,
2899232479Sadrian		    ni, "send LINK METRIC REPLY action: metric 0x%x",
2900232479Sadrian		    ie->lm_metric);
2901195618Srpaulo	}
2902195618Srpaulo	IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
2903195618Srpaulo	    "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__,
2904195618Srpaulo	    ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1);
2905195618Srpaulo	ieee80211_ref_node(ni);
2906195618Srpaulo
2907195618Srpaulo	m = ieee80211_getmgtframe(&frm,
2908195618Srpaulo	    ic->ic_headroom + sizeof(struct ieee80211_frame),
2909232479Sadrian	    sizeof(uint16_t) +	/* action+category */
2910232479Sadrian	    sizeof(struct ieee80211_meshlmetric_ie)
2911195618Srpaulo	);
2912195618Srpaulo	if (m != NULL) {
2913195618Srpaulo		/*
2914232479Sadrian		 * mesh link metric
2915195618Srpaulo		 *   [1] category
2916195618Srpaulo		 *   [1] action
2917195618Srpaulo		 *   [tlv] mesh link metric
2918195618Srpaulo		 */
2919195618Srpaulo		*frm++ = category;
2920195618Srpaulo		*frm++ = action;
2921232479Sadrian		frm = ieee80211_add_meshlmetric(frm,
2922232479Sadrian		    ie->lm_flags, ie->lm_metric);
2923195618Srpaulo		m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
2924246506Smonthadar		return mesh_send_action(ni, vap->iv_myaddr, ni->ni_macaddr, m);
2925195618Srpaulo	} else {
2926195618Srpaulo		vap->iv_stats.is_tx_nobuf++;
2927195618Srpaulo		ieee80211_free_node(ni);
2928195618Srpaulo		return ENOMEM;
2929195618Srpaulo	}
2930195618Srpaulo}
2931195618Srpaulo
2932246506Smonthadarstatic int
2933246506Smonthadarmesh_send_action_meshgate(struct ieee80211_node *ni,
2934246506Smonthadar	int category, int action, void *arg0)
2935246506Smonthadar{
2936246506Smonthadar	struct ieee80211vap *vap = ni->ni_vap;
2937246506Smonthadar	struct ieee80211com *ic = ni->ni_ic;
2938246506Smonthadar	struct ieee80211_meshgann_ie *ie = arg0;
2939246506Smonthadar	struct mbuf *m;
2940246506Smonthadar	uint8_t *frm;
2941246506Smonthadar
2942246506Smonthadar	IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
2943246506Smonthadar	    "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__,
2944246506Smonthadar	    ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1);
2945246506Smonthadar	ieee80211_ref_node(ni);
2946246506Smonthadar
2947246506Smonthadar	m = ieee80211_getmgtframe(&frm,
2948246506Smonthadar	    ic->ic_headroom + sizeof(struct ieee80211_frame),
2949246506Smonthadar	    sizeof(uint16_t) +	/* action+category */
2950246506Smonthadar	    IEEE80211_MESHGANN_BASE_SZ
2951246506Smonthadar	);
2952246506Smonthadar	if (m != NULL) {
2953246506Smonthadar		/*
2954246506Smonthadar		 * mesh link metric
2955246506Smonthadar		 *   [1] category
2956246506Smonthadar		 *   [1] action
2957246506Smonthadar		 *   [tlv] mesh gate annoucement
2958246506Smonthadar		 */
2959246506Smonthadar		*frm++ = category;
2960246506Smonthadar		*frm++ = action;
2961246506Smonthadar		frm = ieee80211_add_meshgate(frm, ie);
2962246506Smonthadar		m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
2963246506Smonthadar		return mesh_send_action(ni, vap->iv_myaddr, broadcastaddr, m);
2964246506Smonthadar	} else {
2965246506Smonthadar		vap->iv_stats.is_tx_nobuf++;
2966246506Smonthadar		ieee80211_free_node(ni);
2967246506Smonthadar		return ENOMEM;
2968246506Smonthadar	}
2969246506Smonthadar}
2970246506Smonthadar
2971195618Srpaulostatic void
2972195618Srpaulomesh_peer_timeout_setup(struct ieee80211_node *ni)
2973195618Srpaulo{
2974195618Srpaulo	switch (ni->ni_mlstate) {
2975195618Srpaulo	case IEEE80211_NODE_MESH_HOLDING:
2976195618Srpaulo		ni->ni_mltval = ieee80211_mesh_holdingtimeout;
2977195618Srpaulo		break;
2978195618Srpaulo	case IEEE80211_NODE_MESH_CONFIRMRCV:
2979195618Srpaulo		ni->ni_mltval = ieee80211_mesh_confirmtimeout;
2980195618Srpaulo		break;
2981195618Srpaulo	case IEEE80211_NODE_MESH_IDLE:
2982195618Srpaulo		ni->ni_mltval = 0;
2983195618Srpaulo		break;
2984195618Srpaulo	default:
2985195618Srpaulo		ni->ni_mltval = ieee80211_mesh_retrytimeout;
2986195618Srpaulo		break;
2987195618Srpaulo	}
2988195618Srpaulo	if (ni->ni_mltval)
2989195618Srpaulo		callout_reset(&ni->ni_mltimer, ni->ni_mltval,
2990195618Srpaulo		    mesh_peer_timeout_cb, ni);
2991195618Srpaulo}
2992195618Srpaulo
2993195618Srpaulo/*
2994195618Srpaulo * Same as above but backoffs timer statisically 50%.
2995195618Srpaulo */
2996195618Srpaulostatic void
2997195618Srpaulomesh_peer_timeout_backoff(struct ieee80211_node *ni)
2998195618Srpaulo{
2999195618Srpaulo	uint32_t r;
3000195618Srpaulo
3001195618Srpaulo	r = arc4random();
3002195618Srpaulo	ni->ni_mltval += r % ni->ni_mltval;
3003195618Srpaulo	callout_reset(&ni->ni_mltimer, ni->ni_mltval, mesh_peer_timeout_cb,
3004195618Srpaulo	    ni);
3005195618Srpaulo}
3006195618Srpaulo
3007195618Srpaulostatic __inline void
3008195618Srpaulomesh_peer_timeout_stop(struct ieee80211_node *ni)
3009195618Srpaulo{
3010195784Srpaulo	callout_drain(&ni->ni_mltimer);
3011195618Srpaulo}
3012195618Srpaulo
3013246497Smonthadarstatic void
3014246497Smonthadarmesh_peer_backoff_cb(void *arg)
3015246497Smonthadar{
3016246497Smonthadar	struct ieee80211_node *ni = (struct ieee80211_node *)arg;
3017246497Smonthadar
3018246497Smonthadar	/* After backoff timeout, try to peer automatically again. */
3019246497Smonthadar	ni->ni_mlhcnt = 0;
3020246497Smonthadar}
3021246497Smonthadar
3022195618Srpaulo/*
3023195618Srpaulo * Mesh Peer Link Management FSM timeout handling.
3024195618Srpaulo */
3025195618Srpaulostatic void
3026195618Srpaulomesh_peer_timeout_cb(void *arg)
3027195618Srpaulo{
3028195618Srpaulo	struct ieee80211_node *ni = (struct ieee80211_node *)arg;
3029195618Srpaulo	uint16_t args[3];
3030195618Srpaulo
3031195618Srpaulo	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_MESH,
3032195618Srpaulo	    ni, "mesh link timeout, state %d, retry counter %d",
3033195618Srpaulo	    ni->ni_mlstate, ni->ni_mlrcnt);
3034195618Srpaulo
3035195618Srpaulo	switch (ni->ni_mlstate) {
3036195618Srpaulo	case IEEE80211_NODE_MESH_IDLE:
3037195618Srpaulo	case IEEE80211_NODE_MESH_ESTABLISHED:
3038195618Srpaulo		break;
3039195618Srpaulo	case IEEE80211_NODE_MESH_OPENSNT:
3040195618Srpaulo	case IEEE80211_NODE_MESH_OPENRCV:
3041195618Srpaulo		if (ni->ni_mlrcnt == ieee80211_mesh_maxretries) {
3042195618Srpaulo			args[0] = ni->ni_mlpid;
3043195618Srpaulo			args[2] = IEEE80211_REASON_MESH_MAX_RETRIES;
3044195618Srpaulo			ieee80211_send_action(ni,
3045234874Smonthadar			    IEEE80211_ACTION_CAT_SELF_PROT,
3046195618Srpaulo			    IEEE80211_ACTION_MESHPEERING_CLOSE, args);
3047195618Srpaulo			ni->ni_mlrcnt = 0;
3048195618Srpaulo			mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING);
3049195618Srpaulo			mesh_peer_timeout_setup(ni);
3050195618Srpaulo		} else {
3051195618Srpaulo			args[0] = ni->ni_mlpid;
3052195618Srpaulo			ieee80211_send_action(ni,
3053234874Smonthadar			    IEEE80211_ACTION_CAT_SELF_PROT,
3054195618Srpaulo			    IEEE80211_ACTION_MESHPEERING_OPEN, args);
3055195618Srpaulo			ni->ni_mlrcnt++;
3056195618Srpaulo			mesh_peer_timeout_backoff(ni);
3057195618Srpaulo		}
3058195618Srpaulo		break;
3059195618Srpaulo	case IEEE80211_NODE_MESH_CONFIRMRCV:
3060234876Smonthadar		args[0] = ni->ni_mlpid;
3061234876Smonthadar		args[2] = IEEE80211_REASON_MESH_CONFIRM_TIMEOUT;
3062234876Smonthadar		ieee80211_send_action(ni,
3063234876Smonthadar		    IEEE80211_ACTION_CAT_SELF_PROT,
3064234876Smonthadar		    IEEE80211_ACTION_MESHPEERING_CLOSE, args);
3065234876Smonthadar		mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING);
3066234876Smonthadar		mesh_peer_timeout_setup(ni);
3067195618Srpaulo		break;
3068195618Srpaulo	case IEEE80211_NODE_MESH_HOLDING:
3069246497Smonthadar		ni->ni_mlhcnt++;
3070246497Smonthadar		if (ni->ni_mlhcnt >= ieee80211_mesh_maxholding)
3071246497Smonthadar			callout_reset(&ni->ni_mlhtimer,
3072246497Smonthadar			    ieee80211_mesh_backofftimeout,
3073246497Smonthadar			    mesh_peer_backoff_cb, ni);
3074195618Srpaulo		mesh_linkchange(ni, IEEE80211_NODE_MESH_IDLE);
3075195618Srpaulo		break;
3076195618Srpaulo	}
3077195618Srpaulo}
3078195618Srpaulo
3079195661Srpaulostatic int
3080195618Srpaulomesh_verify_meshid(struct ieee80211vap *vap, const uint8_t *ie)
3081195618Srpaulo{
3082195618Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
3083195618Srpaulo
3084195618Srpaulo	if (ie == NULL || ie[1] != ms->ms_idlen)
3085195618Srpaulo		return 1;
3086195618Srpaulo	return memcmp(ms->ms_id, ie + 2, ms->ms_idlen);
3087195618Srpaulo}
3088195618Srpaulo
3089195618Srpaulo/*
3090195618Srpaulo * Check if we are using the same algorithms for this mesh.
3091195618Srpaulo */
3092195618Srpaulostatic int
3093195618Srpaulomesh_verify_meshconf(struct ieee80211vap *vap, const uint8_t *ie)
3094195618Srpaulo{
3095195618Srpaulo	const struct ieee80211_meshconf_ie *meshconf =
3096195618Srpaulo	    (const struct ieee80211_meshconf_ie *) ie;
3097195618Srpaulo	const struct ieee80211_mesh_state *ms = vap->iv_mesh;
3098195618Srpaulo
3099195618Srpaulo	if (meshconf == NULL)
3100195618Srpaulo		return 1;
3101197975Srpaulo	if (meshconf->conf_pselid != ms->ms_ppath->mpp_ie) {
3102195618Srpaulo		IEEE80211_DPRINTF(vap, IEEE80211_MSG_MESH,
3103197975Srpaulo		    "unknown path selection algorithm: 0x%x\n",
3104197975Srpaulo		    meshconf->conf_pselid);
3105195618Srpaulo		return 1;
3106195618Srpaulo	}
3107197975Srpaulo	if (meshconf->conf_pmetid != ms->ms_pmetric->mpm_ie) {
3108195618Srpaulo		IEEE80211_DPRINTF(vap, IEEE80211_MSG_MESH,
3109197975Srpaulo		    "unknown path metric algorithm: 0x%x\n",
3110197975Srpaulo		    meshconf->conf_pmetid);
3111195618Srpaulo		return 1;
3112195618Srpaulo	}
3113197975Srpaulo	if (meshconf->conf_ccid != 0) {
3114195618Srpaulo		IEEE80211_DPRINTF(vap, IEEE80211_MSG_MESH,
3115197975Srpaulo		    "unknown congestion control algorithm: 0x%x\n",
3116197975Srpaulo		    meshconf->conf_ccid);
3117195618Srpaulo		return 1;
3118195618Srpaulo	}
3119197975Srpaulo	if (meshconf->conf_syncid != IEEE80211_MESHCONF_SYNC_NEIGHOFF) {
3120195618Srpaulo		IEEE80211_DPRINTF(vap, IEEE80211_MSG_MESH,
3121197975Srpaulo		    "unknown sync algorithm: 0x%x\n",
3122197975Srpaulo		    meshconf->conf_syncid);
3123195618Srpaulo		return 1;
3124195618Srpaulo	}
3125197975Srpaulo	if (meshconf->conf_authid != 0) {
3126195618Srpaulo		IEEE80211_DPRINTF(vap, IEEE80211_MSG_MESH,
3127197975Srpaulo		    "unknown auth auth algorithm: 0x%x\n",
3128197975Srpaulo		    meshconf->conf_pselid);
3129195618Srpaulo		return 1;
3130195618Srpaulo	}
3131195618Srpaulo	/* Not accepting peers */
3132231576Sadrian	if (!(meshconf->conf_cap & IEEE80211_MESHCONF_CAP_AP)) {
3133195618Srpaulo		IEEE80211_DPRINTF(vap, IEEE80211_MSG_MESH,
3134195618Srpaulo		    "not accepting peers: 0x%x\n", meshconf->conf_cap);
3135195618Srpaulo		return 1;
3136195618Srpaulo	}
3137195618Srpaulo	return 0;
3138195618Srpaulo}
3139195618Srpaulo
3140195618Srpaulostatic int
3141197413Srpaulomesh_verify_meshpeer(struct ieee80211vap *vap, uint8_t subtype,
3142197413Srpaulo    const uint8_t *ie)
3143195618Srpaulo{
3144195618Srpaulo	const struct ieee80211_meshpeer_ie *meshpeer =
3145195618Srpaulo	    (const struct ieee80211_meshpeer_ie *) ie;
3146195618Srpaulo
3147234875Smonthadar	if (meshpeer == NULL ||
3148234875Smonthadar	    meshpeer->peer_len < IEEE80211_MPM_BASE_SZ ||
3149234875Smonthadar	    meshpeer->peer_len > IEEE80211_MPM_MAX_SZ)
3150195618Srpaulo		return 1;
3151234875Smonthadar	if (meshpeer->peer_proto != IEEE80211_MPPID_MPM) {
3152234875Smonthadar		IEEE80211_DPRINTF(vap,
3153234875Smonthadar		    IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH,
3154234875Smonthadar		    "Only MPM protocol is supported (proto: 0x%02X)",
3155234875Smonthadar		    meshpeer->peer_proto);
3156234875Smonthadar		return 1;
3157234875Smonthadar	}
3158197413Srpaulo	switch (subtype) {
3159234874Smonthadar	case IEEE80211_ACTION_MESHPEERING_OPEN:
3160234875Smonthadar		if (meshpeer->peer_len != IEEE80211_MPM_BASE_SZ)
3161195618Srpaulo			return 1;
3162195618Srpaulo		break;
3163234874Smonthadar	case IEEE80211_ACTION_MESHPEERING_CONFIRM:
3164234875Smonthadar		if (meshpeer->peer_len != IEEE80211_MPM_BASE_SZ + 2)
3165195618Srpaulo			return 1;
3166195618Srpaulo		break;
3167234874Smonthadar	case IEEE80211_ACTION_MESHPEERING_CLOSE:
3168234875Smonthadar		if (meshpeer->peer_len < IEEE80211_MPM_BASE_SZ + 2)
3169195618Srpaulo			return 1;
3170234875Smonthadar		if (meshpeer->peer_len == (IEEE80211_MPM_BASE_SZ + 2) &&
3171234875Smonthadar		    meshpeer->peer_linkid != 0)
3172195618Srpaulo			return 1;
3173195618Srpaulo		if (meshpeer->peer_rcode == 0)
3174195618Srpaulo			return 1;
3175195618Srpaulo		break;
3176195618Srpaulo	}
3177195618Srpaulo	return 0;
3178195618Srpaulo}
3179195618Srpaulo
3180195618Srpaulo/*
3181195618Srpaulo * Add a Mesh ID IE to a frame.
3182195618Srpaulo */
3183195618Srpaulouint8_t *
3184195618Srpauloieee80211_add_meshid(uint8_t *frm, struct ieee80211vap *vap)
3185195618Srpaulo{
3186195618Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
3187195618Srpaulo
3188195618Srpaulo	KASSERT(vap->iv_opmode == IEEE80211_M_MBSS, ("not a mbss vap"));
3189195618Srpaulo
3190195618Srpaulo	*frm++ = IEEE80211_ELEMID_MESHID;
3191195618Srpaulo	*frm++ = ms->ms_idlen;
3192195618Srpaulo	memcpy(frm, ms->ms_id, ms->ms_idlen);
3193195618Srpaulo	return frm + ms->ms_idlen;
3194195618Srpaulo}
3195195618Srpaulo
3196195618Srpaulo/*
3197195618Srpaulo * Add a Mesh Configuration IE to a frame.
3198195618Srpaulo * For now just use HWMP routing, Airtime link metric, Null Congestion
3199195618Srpaulo * Signaling, Null Sync Protocol and Null Authentication.
3200195618Srpaulo */
3201195618Srpaulouint8_t *
3202195618Srpauloieee80211_add_meshconf(uint8_t *frm, struct ieee80211vap *vap)
3203195618Srpaulo{
3204195618Srpaulo	const struct ieee80211_mesh_state *ms = vap->iv_mesh;
3205202178Srpaulo	uint16_t caps;
3206195618Srpaulo
3207195618Srpaulo	KASSERT(vap->iv_opmode == IEEE80211_M_MBSS, ("not a MBSS vap"));
3208195618Srpaulo
3209195618Srpaulo	*frm++ = IEEE80211_ELEMID_MESHCONF;
3210231576Sadrian	*frm++ = IEEE80211_MESH_CONF_SZ;
3211197975Srpaulo	*frm++ = ms->ms_ppath->mpp_ie;		/* path selection */
3212197975Srpaulo	*frm++ = ms->ms_pmetric->mpm_ie;	/* link metric */
3213197975Srpaulo	*frm++ = IEEE80211_MESHCONF_CC_DISABLED;
3214197975Srpaulo	*frm++ = IEEE80211_MESHCONF_SYNC_NEIGHOFF;
3215197975Srpaulo	*frm++ = IEEE80211_MESHCONF_AUTH_DISABLED;
3216195618Srpaulo	/* NB: set the number of neighbors before the rest */
3217234876Smonthadar	*frm = (ms->ms_neighbors > IEEE80211_MESH_MAX_NEIGHBORS ?
3218234876Smonthadar	    IEEE80211_MESH_MAX_NEIGHBORS : ms->ms_neighbors) << 1;
3219234892Smonthadar	if (ms->ms_flags & IEEE80211_MESHFLAGS_GATE)
3220234892Smonthadar		*frm |= IEEE80211_MESHCONF_FORM_GATE;
3221195618Srpaulo	frm += 1;
3222202178Srpaulo	caps = 0;
3223195618Srpaulo	if (ms->ms_flags & IEEE80211_MESHFLAGS_AP)
3224202178Srpaulo		caps |= IEEE80211_MESHCONF_CAP_AP;
3225195618Srpaulo	if (ms->ms_flags & IEEE80211_MESHFLAGS_FWD)
3226202178Srpaulo		caps |= IEEE80211_MESHCONF_CAP_FWRD;
3227231576Sadrian	*frm++ = caps;
3228195618Srpaulo	return frm;
3229195618Srpaulo}
3230195618Srpaulo
3231195618Srpaulo/*
3232195618Srpaulo * Add a Mesh Peer Management IE to a frame.
3233195618Srpaulo */
3234195618Srpaulouint8_t *
3235195618Srpauloieee80211_add_meshpeer(uint8_t *frm, uint8_t subtype, uint16_t localid,
3236195618Srpaulo    uint16_t peerid, uint16_t reason)
3237195618Srpaulo{
3238197413Srpaulo
3239195618Srpaulo	KASSERT(localid != 0, ("localid == 0"));
3240195618Srpaulo
3241195618Srpaulo	*frm++ = IEEE80211_ELEMID_MESHPEER;
3242195618Srpaulo	switch (subtype) {
3243234874Smonthadar	case IEEE80211_ACTION_MESHPEERING_OPEN:
3244234875Smonthadar		*frm++ = IEEE80211_MPM_BASE_SZ;		/* length */
3245234875Smonthadar		ADDSHORT(frm, IEEE80211_MPPID_MPM);	/* proto */
3246234875Smonthadar		ADDSHORT(frm, localid);			/* local ID */
3247195618Srpaulo		break;
3248234874Smonthadar	case IEEE80211_ACTION_MESHPEERING_CONFIRM:
3249195618Srpaulo		KASSERT(peerid != 0, ("sending peer confirm without peer id"));
3250234875Smonthadar		*frm++ = IEEE80211_MPM_BASE_SZ + 2;	/* length */
3251234875Smonthadar		ADDSHORT(frm, IEEE80211_MPPID_MPM);	/* proto */
3252234875Smonthadar		ADDSHORT(frm, localid);			/* local ID */
3253234875Smonthadar		ADDSHORT(frm, peerid);			/* peer ID */
3254195618Srpaulo		break;
3255234874Smonthadar	case IEEE80211_ACTION_MESHPEERING_CLOSE:
3256195618Srpaulo		if (peerid)
3257234875Smonthadar			*frm++ = IEEE80211_MPM_MAX_SZ;	/* length */
3258195618Srpaulo		else
3259234875Smonthadar			*frm++ = IEEE80211_MPM_BASE_SZ + 2; /* length */
3260234875Smonthadar		ADDSHORT(frm, IEEE80211_MPPID_MPM);	/* proto */
3261195618Srpaulo		ADDSHORT(frm, localid);	/* local ID */
3262195618Srpaulo		if (peerid)
3263195618Srpaulo			ADDSHORT(frm, peerid);	/* peer ID */
3264195618Srpaulo		ADDSHORT(frm, reason);
3265195618Srpaulo		break;
3266195618Srpaulo	}
3267195618Srpaulo	return frm;
3268195618Srpaulo}
3269195618Srpaulo
3270195618Srpaulo/*
3271195618Srpaulo * Compute an Airtime Link Metric for the link with this node.
3272195618Srpaulo *
3273195618Srpaulo * Based on Draft 3.0 spec (11B.10, p.149).
3274195618Srpaulo */
3275195618Srpaulo/*
3276195618Srpaulo * Max 802.11s overhead.
3277195618Srpaulo */
3278195618Srpaulo#define IEEE80211_MESH_MAXOVERHEAD \
3279195618Srpaulo	(sizeof(struct ieee80211_qosframe_addr4) \
3280234878Smonthadar	 + sizeof(struct ieee80211_meshcntl_ae10) \
3281195618Srpaulo	+ sizeof(struct llc) \
3282195618Srpaulo	+ IEEE80211_ADDR_LEN \
3283195618Srpaulo	+ IEEE80211_WEP_IVLEN \
3284195618Srpaulo	+ IEEE80211_WEP_KIDLEN \
3285195618Srpaulo	+ IEEE80211_WEP_CRCLEN \
3286195618Srpaulo	+ IEEE80211_WEP_MICLEN \
3287195618Srpaulo	+ IEEE80211_CRC_LEN)
3288195618Srpaulouint32_t
3289195618Srpaulomesh_airtime_calc(struct ieee80211_node *ni)
3290195618Srpaulo{
3291195618Srpaulo#define M_BITS 8
3292195618Srpaulo#define S_FACTOR (2 * M_BITS)
3293195618Srpaulo	struct ieee80211com *ic = ni->ni_ic;
3294195618Srpaulo	struct ifnet *ifp = ni->ni_vap->iv_ifp;
3295195618Srpaulo	const static int nbits = 8192 << M_BITS;
3296195618Srpaulo	uint32_t overhead, rate, errrate;
3297195618Srpaulo	uint64_t res;
3298195618Srpaulo
3299195618Srpaulo	/* Time to transmit a frame */
3300195618Srpaulo	rate = ni->ni_txrate;
3301195618Srpaulo	overhead = ieee80211_compute_duration(ic->ic_rt,
3302195618Srpaulo	    ifp->if_mtu + IEEE80211_MESH_MAXOVERHEAD, rate, 0) << M_BITS;
3303195618Srpaulo	/* Error rate in percentage */
3304195618Srpaulo	/* XXX assuming small failures are ok */
3305271861Sglebius	errrate = (((ifp->if_get_counter(ifp, IFCOUNTER_OERRORS) +
3306271861Sglebius	    ifp->if_get_counter(ifp, IFCOUNTER_IERRORS)) / 100) << M_BITS)
3307271861Sglebius	    / 100;
3308195618Srpaulo	res = (overhead + (nbits / rate)) *
3309195618Srpaulo	    ((1 << S_FACTOR) / ((1 << M_BITS) - errrate));
3310195618Srpaulo
3311195618Srpaulo	return (uint32_t)(res >> S_FACTOR);
3312195618Srpaulo#undef M_BITS
3313195618Srpaulo#undef S_FACTOR
3314195618Srpaulo}
3315195618Srpaulo
3316195618Srpaulo/*
3317195618Srpaulo * Add a Mesh Link Metric report IE to a frame.
3318195618Srpaulo */
3319195618Srpaulouint8_t *
3320232479Sadrianieee80211_add_meshlmetric(uint8_t *frm, uint8_t flags, uint32_t metric)
3321195618Srpaulo{
3322195618Srpaulo	*frm++ = IEEE80211_ELEMID_MESHLINK;
3323232479Sadrian	*frm++ = 5;
3324232479Sadrian	*frm++ = flags;
3325195618Srpaulo	ADDWORD(frm, metric);
3326195618Srpaulo	return frm;
3327195618Srpaulo}
3328246506Smonthadar
3329246506Smonthadar/*
3330246506Smonthadar * Add a Mesh Gate Announcement IE to a frame.
3331246506Smonthadar */
3332246506Smonthadaruint8_t *
3333246506Smonthadarieee80211_add_meshgate(uint8_t *frm, struct ieee80211_meshgann_ie *ie)
3334246506Smonthadar{
3335246506Smonthadar	*frm++ = IEEE80211_ELEMID_MESHGANN; /* ie */
3336246506Smonthadar	*frm++ = IEEE80211_MESHGANN_BASE_SZ; /* len */
3337246506Smonthadar	*frm++ = ie->gann_flags;
3338246506Smonthadar	*frm++ = ie->gann_hopcount;
3339246506Smonthadar	*frm++ = ie->gann_ttl;
3340246506Smonthadar	IEEE80211_ADDR_COPY(frm, ie->gann_addr);
3341246506Smonthadar	frm += 6;
3342246506Smonthadar	ADDWORD(frm, ie->gann_seq);
3343246506Smonthadar	ADDSHORT(frm, ie->gann_interval);
3344246506Smonthadar	return frm;
3345246506Smonthadar}
3346195618Srpaulo#undef ADDSHORT
3347195618Srpaulo#undef ADDWORD
3348195618Srpaulo
3349195618Srpaulo/*
3350195618Srpaulo * Initialize any mesh-specific node state.
3351195618Srpaulo */
3352195618Srpaulovoid
3353195618Srpauloieee80211_mesh_node_init(struct ieee80211vap *vap, struct ieee80211_node *ni)
3354195618Srpaulo{
3355195618Srpaulo	ni->ni_flags |= IEEE80211_NODE_QOS;
3356283291Sjkim	callout_init(&ni->ni_mltimer, 1);
3357283291Sjkim	callout_init(&ni->ni_mlhtimer, 1);
3358195618Srpaulo}
3359195618Srpaulo
3360195618Srpaulo/*
3361195618Srpaulo * Cleanup any mesh-specific node state.
3362195618Srpaulo */
3363195618Srpaulovoid
3364195618Srpauloieee80211_mesh_node_cleanup(struct ieee80211_node *ni)
3365195618Srpaulo{
3366195618Srpaulo	struct ieee80211vap *vap = ni->ni_vap;
3367195618Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
3368195618Srpaulo
3369195618Srpaulo	callout_drain(&ni->ni_mltimer);
3370246497Smonthadar	callout_drain(&ni->ni_mlhtimer);
3371195618Srpaulo	/* NB: short-circuit callbacks after mesh_vdetach */
3372195618Srpaulo	if (vap->iv_mesh != NULL)
3373195618Srpaulo		ms->ms_ppath->mpp_peerdown(ni);
3374195618Srpaulo}
3375195618Srpaulo
3376195618Srpaulovoid
3377195618Srpauloieee80211_parse_meshid(struct ieee80211_node *ni, const uint8_t *ie)
3378195618Srpaulo{
3379195618Srpaulo	ni->ni_meshidlen = ie[1];
3380195618Srpaulo	memcpy(ni->ni_meshid, ie + 2, ie[1]);
3381195618Srpaulo}
3382195618Srpaulo
3383195618Srpaulo/*
3384195618Srpaulo * Setup mesh-specific node state on neighbor discovery.
3385195618Srpaulo */
3386195618Srpaulovoid
3387195618Srpauloieee80211_mesh_init_neighbor(struct ieee80211_node *ni,
3388195618Srpaulo	const struct ieee80211_frame *wh,
3389195618Srpaulo	const struct ieee80211_scanparams *sp)
3390195618Srpaulo{
3391195618Srpaulo	ieee80211_parse_meshid(ni, sp->meshid);
3392195618Srpaulo}
3393195618Srpaulo
3394198242Srpaulovoid
3395198242Srpauloieee80211_mesh_update_beacon(struct ieee80211vap *vap,
3396198242Srpaulo	struct ieee80211_beacon_offsets *bo)
3397198242Srpaulo{
3398198242Srpaulo	KASSERT(vap->iv_opmode == IEEE80211_M_MBSS, ("not a MBSS vap"));
3399198242Srpaulo
3400198242Srpaulo	if (isset(bo->bo_flags, IEEE80211_BEACON_MESHCONF)) {
3401198242Srpaulo		(void)ieee80211_add_meshconf(bo->bo_meshconf, vap);
3402198242Srpaulo		clrbit(bo->bo_flags, IEEE80211_BEACON_MESHCONF);
3403198242Srpaulo	}
3404198242Srpaulo}
3405198242Srpaulo
3406195618Srpaulostatic int
3407195618Srpaulomesh_ioctl_get80211(struct ieee80211vap *vap, struct ieee80211req *ireq)
3408195618Srpaulo{
3409195618Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
3410195618Srpaulo	uint8_t tmpmeshid[IEEE80211_NWID_LEN];
3411195618Srpaulo	struct ieee80211_mesh_route *rt;
3412195618Srpaulo	struct ieee80211req_mesh_route *imr;
3413195618Srpaulo	size_t len, off;
3414195618Srpaulo	uint8_t *p;
3415195618Srpaulo	int error;
3416195618Srpaulo
3417195618Srpaulo	if (vap->iv_opmode != IEEE80211_M_MBSS)
3418195618Srpaulo		return ENOSYS;
3419195618Srpaulo
3420195618Srpaulo	error = 0;
3421195618Srpaulo	switch (ireq->i_type) {
3422195618Srpaulo	case IEEE80211_IOC_MESH_ID:
3423195618Srpaulo		ireq->i_len = ms->ms_idlen;
3424195618Srpaulo		memcpy(tmpmeshid, ms->ms_id, ireq->i_len);
3425195618Srpaulo		error = copyout(tmpmeshid, ireq->i_data, ireq->i_len);
3426195618Srpaulo		break;
3427195618Srpaulo	case IEEE80211_IOC_MESH_AP:
3428195618Srpaulo		ireq->i_val = (ms->ms_flags & IEEE80211_MESHFLAGS_AP) != 0;
3429195618Srpaulo		break;
3430195618Srpaulo	case IEEE80211_IOC_MESH_FWRD:
3431195618Srpaulo		ireq->i_val = (ms->ms_flags & IEEE80211_MESHFLAGS_FWD) != 0;
3432195618Srpaulo		break;
3433234892Smonthadar	case IEEE80211_IOC_MESH_GATE:
3434234892Smonthadar		ireq->i_val = (ms->ms_flags & IEEE80211_MESHFLAGS_GATE) != 0;
3435234892Smonthadar		break;
3436195618Srpaulo	case IEEE80211_IOC_MESH_TTL:
3437195618Srpaulo		ireq->i_val = ms->ms_ttl;
3438195618Srpaulo		break;
3439195618Srpaulo	case IEEE80211_IOC_MESH_RTCMD:
3440195618Srpaulo		switch (ireq->i_val) {
3441195618Srpaulo		case IEEE80211_MESH_RTCMD_LIST:
3442195618Srpaulo			len = 0;
3443195618Srpaulo			MESH_RT_LOCK(ms);
3444195618Srpaulo			TAILQ_FOREACH(rt, &ms->ms_routes, rt_next) {
3445195618Srpaulo				len += sizeof(*imr);
3446195618Srpaulo			}
3447195618Srpaulo			MESH_RT_UNLOCK(ms);
3448195618Srpaulo			if (len > ireq->i_len || ireq->i_len < sizeof(*imr)) {
3449195618Srpaulo				ireq->i_len = len;
3450195618Srpaulo				return ENOMEM;
3451195618Srpaulo			}
3452195618Srpaulo			ireq->i_len = len;
3453195784Srpaulo			/* XXX M_WAIT? */
3454283538Sadrian			p = IEEE80211_MALLOC(len, M_TEMP,
3455283538Sadrian			    IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
3456195618Srpaulo			if (p == NULL)
3457195618Srpaulo				return ENOMEM;
3458195618Srpaulo			off = 0;
3459195618Srpaulo			MESH_RT_LOCK(ms);
3460195618Srpaulo			TAILQ_FOREACH(rt, &ms->ms_routes, rt_next) {
3461195618Srpaulo				if (off >= len)
3462195618Srpaulo					break;
3463195618Srpaulo				imr = (struct ieee80211req_mesh_route *)
3464195618Srpaulo				    (p + off);
3465195618Srpaulo				IEEE80211_ADDR_COPY(imr->imr_dest,
3466195618Srpaulo				    rt->rt_dest);
3467195618Srpaulo				IEEE80211_ADDR_COPY(imr->imr_nexthop,
3468195618Srpaulo				    rt->rt_nexthop);
3469195618Srpaulo				imr->imr_metric = rt->rt_metric;
3470195618Srpaulo				imr->imr_nhops = rt->rt_nhops;
3471234877Smonthadar				imr->imr_lifetime =
3472234877Smonthadar				    ieee80211_mesh_rt_update(rt, 0);
3473195784Srpaulo				imr->imr_lastmseq = rt->rt_lastmseq;
3474234877Smonthadar				imr->imr_flags = rt->rt_flags; /* last */
3475195618Srpaulo				off += sizeof(*imr);
3476195618Srpaulo			}
3477195618Srpaulo			MESH_RT_UNLOCK(ms);
3478195618Srpaulo			error = copyout(p, (uint8_t *)ireq->i_data,
3479195618Srpaulo			    ireq->i_len);
3480283538Sadrian			IEEE80211_FREE(p, M_TEMP);
3481195618Srpaulo			break;
3482195618Srpaulo		case IEEE80211_MESH_RTCMD_FLUSH:
3483195618Srpaulo		case IEEE80211_MESH_RTCMD_ADD:
3484195618Srpaulo		case IEEE80211_MESH_RTCMD_DELETE:
3485195618Srpaulo			return EINVAL;
3486195618Srpaulo		default:
3487195618Srpaulo			return ENOSYS;
3488195618Srpaulo		}
3489195618Srpaulo		break;
3490195618Srpaulo	case IEEE80211_IOC_MESH_PR_METRIC:
3491195618Srpaulo		len = strlen(ms->ms_pmetric->mpm_descr);
3492195618Srpaulo		if (ireq->i_len < len)
3493195618Srpaulo			return EINVAL;
3494195618Srpaulo		ireq->i_len = len;
3495195618Srpaulo		error = copyout(ms->ms_pmetric->mpm_descr,
3496195618Srpaulo		    (uint8_t *)ireq->i_data, len);
3497195618Srpaulo		break;
3498195618Srpaulo	case IEEE80211_IOC_MESH_PR_PATH:
3499195618Srpaulo		len = strlen(ms->ms_ppath->mpp_descr);
3500195618Srpaulo		if (ireq->i_len < len)
3501195618Srpaulo			return EINVAL;
3502195618Srpaulo		ireq->i_len = len;
3503195618Srpaulo		error = copyout(ms->ms_ppath->mpp_descr,
3504195618Srpaulo		    (uint8_t *)ireq->i_data, len);
3505195618Srpaulo		break;
3506195618Srpaulo	default:
3507195618Srpaulo		return ENOSYS;
3508195618Srpaulo	}
3509195618Srpaulo
3510195618Srpaulo	return error;
3511195618Srpaulo}
3512195618SrpauloIEEE80211_IOCTL_GET(mesh, mesh_ioctl_get80211);
3513195618Srpaulo
3514195618Srpaulostatic int
3515195618Srpaulomesh_ioctl_set80211(struct ieee80211vap *vap, struct ieee80211req *ireq)
3516195618Srpaulo{
3517195618Srpaulo	struct ieee80211_mesh_state *ms = vap->iv_mesh;
3518195618Srpaulo	uint8_t tmpmeshid[IEEE80211_NWID_LEN];
3519195618Srpaulo	uint8_t tmpaddr[IEEE80211_ADDR_LEN];
3520195618Srpaulo	char tmpproto[IEEE80211_MESH_PROTO_DSZ];
3521195618Srpaulo	int error;
3522195618Srpaulo
3523195618Srpaulo	if (vap->iv_opmode != IEEE80211_M_MBSS)
3524195618Srpaulo		return ENOSYS;
3525195618Srpaulo
3526195618Srpaulo	error = 0;
3527195618Srpaulo	switch (ireq->i_type) {
3528195618Srpaulo	case IEEE80211_IOC_MESH_ID:
3529195618Srpaulo		if (ireq->i_val != 0 || ireq->i_len > IEEE80211_MESHID_LEN)
3530195618Srpaulo			return EINVAL;
3531195618Srpaulo		error = copyin(ireq->i_data, tmpmeshid, ireq->i_len);
3532195784Srpaulo		if (error != 0)
3533195618Srpaulo			break;
3534195618Srpaulo		memset(ms->ms_id, 0, IEEE80211_NWID_LEN);
3535195618Srpaulo		ms->ms_idlen = ireq->i_len;
3536195618Srpaulo		memcpy(ms->ms_id, tmpmeshid, ireq->i_len);
3537195784Srpaulo		error = ENETRESET;
3538195618Srpaulo		break;
3539195618Srpaulo	case IEEE80211_IOC_MESH_AP:
3540195618Srpaulo		if (ireq->i_val)
3541195618Srpaulo			ms->ms_flags |= IEEE80211_MESHFLAGS_AP;
3542195618Srpaulo		else
3543195618Srpaulo			ms->ms_flags &= ~IEEE80211_MESHFLAGS_AP;
3544195784Srpaulo		error = ENETRESET;
3545195618Srpaulo		break;
3546195618Srpaulo	case IEEE80211_IOC_MESH_FWRD:
3547195618Srpaulo		if (ireq->i_val)
3548195618Srpaulo			ms->ms_flags |= IEEE80211_MESHFLAGS_FWD;
3549195618Srpaulo		else
3550195618Srpaulo			ms->ms_flags &= ~IEEE80211_MESHFLAGS_FWD;
3551246506Smonthadar		mesh_gatemode_setup(vap);
3552195618Srpaulo		break;
3553234892Smonthadar	case IEEE80211_IOC_MESH_GATE:
3554234892Smonthadar		if (ireq->i_val)
3555234892Smonthadar			ms->ms_flags |= IEEE80211_MESHFLAGS_GATE;
3556234892Smonthadar		else
3557234892Smonthadar			ms->ms_flags &= ~IEEE80211_MESHFLAGS_GATE;
3558234892Smonthadar		break;
3559195618Srpaulo	case IEEE80211_IOC_MESH_TTL:
3560195618Srpaulo		ms->ms_ttl = (uint8_t) ireq->i_val;
3561195618Srpaulo		break;
3562195618Srpaulo	case IEEE80211_IOC_MESH_RTCMD:
3563195618Srpaulo		switch (ireq->i_val) {
3564195618Srpaulo		case IEEE80211_MESH_RTCMD_LIST:
3565195618Srpaulo			return EINVAL;
3566195618Srpaulo		case IEEE80211_MESH_RTCMD_FLUSH:
3567195618Srpaulo			ieee80211_mesh_rt_flush(vap);
3568195618Srpaulo			break;
3569195618Srpaulo		case IEEE80211_MESH_RTCMD_ADD:
3570361038Sjhb			error = copyin(ireq->i_data, tmpaddr,
3571361038Sjhb			    IEEE80211_ADDR_LEN);
3572361038Sjhb			if (error != 0)
3573361038Sjhb				break;
3574361038Sjhb			if (IEEE80211_ADDR_EQ(vap->iv_myaddr, tmpaddr) ||
3575361038Sjhb			    IEEE80211_ADDR_EQ(broadcastaddr, tmpaddr))
3576195618Srpaulo				return EINVAL;
3577361038Sjhb			ieee80211_mesh_discover(vap, tmpaddr, NULL);
3578195618Srpaulo			break;
3579195618Srpaulo		case IEEE80211_MESH_RTCMD_DELETE:
3580361038Sjhb			error = copyin(ireq->i_data, tmpaddr,
3581361038Sjhb			    IEEE80211_ADDR_LEN);
3582361038Sjhb			if (error != 0)
3583361038Sjhb				break;
3584361038Sjhb			ieee80211_mesh_rt_del(vap, tmpaddr);
3585195618Srpaulo			break;
3586195618Srpaulo		default:
3587195618Srpaulo			return ENOSYS;
3588195618Srpaulo		}
3589195618Srpaulo		break;
3590195618Srpaulo	case IEEE80211_IOC_MESH_PR_METRIC:
3591195618Srpaulo		error = copyin(ireq->i_data, tmpproto, sizeof(tmpproto));
3592195784Srpaulo		if (error == 0) {
3593195784Srpaulo			error = mesh_select_proto_metric(vap, tmpproto);
3594195784Srpaulo			if (error == 0)
3595195784Srpaulo				error = ENETRESET;
3596195784Srpaulo		}
3597195618Srpaulo		break;
3598195618Srpaulo	case IEEE80211_IOC_MESH_PR_PATH:
3599195618Srpaulo		error = copyin(ireq->i_data, tmpproto, sizeof(tmpproto));
3600195784Srpaulo		if (error == 0) {
3601195784Srpaulo			error = mesh_select_proto_path(vap, tmpproto);
3602195784Srpaulo			if (error == 0)
3603195784Srpaulo				error = ENETRESET;
3604195784Srpaulo		}
3605195618Srpaulo		break;
3606195618Srpaulo	default:
3607195618Srpaulo		return ENOSYS;
3608195618Srpaulo	}
3609195618Srpaulo	return error;
3610195618Srpaulo}
3611195618SrpauloIEEE80211_IOCTL_SET(mesh, mesh_ioctl_set80211);
3612