132176cfdSRui Paulo /*-
232176cfdSRui Paulo * Copyright (c) 2009 The FreeBSD Foundation
332176cfdSRui Paulo * All rights reserved.
432176cfdSRui Paulo *
532176cfdSRui Paulo * This software was developed by Rui Paulo under sponsorship from the
632176cfdSRui Paulo * FreeBSD Foundation.
732176cfdSRui Paulo *
832176cfdSRui Paulo * Redistribution and use in source and binary forms, with or without
932176cfdSRui Paulo * modification, are permitted provided that the following conditions
1032176cfdSRui Paulo * are met:
1132176cfdSRui Paulo * 1. Redistributions of source code must retain the above copyright
1232176cfdSRui Paulo * notice, this list of conditions and the following disclaimer.
1332176cfdSRui Paulo * 2. Redistributions in binary form must reproduce the above copyright
1432176cfdSRui Paulo * notice, this list of conditions and the following disclaimer in the
1532176cfdSRui Paulo * documentation and/or other materials provided with the distribution.
1632176cfdSRui Paulo *
1732176cfdSRui Paulo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1832176cfdSRui Paulo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1932176cfdSRui Paulo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2032176cfdSRui Paulo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2132176cfdSRui Paulo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2232176cfdSRui Paulo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2332176cfdSRui Paulo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2432176cfdSRui Paulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2532176cfdSRui Paulo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2632176cfdSRui Paulo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2732176cfdSRui Paulo * SUCH DAMAGE.
2832176cfdSRui Paulo */
29085ff963SMatthew Dillon #include <sys/cdefs.h>
30085ff963SMatthew Dillon #ifdef __FreeBSD__
31085ff963SMatthew Dillon __FBSDID("$FreeBSD$");
32085ff963SMatthew Dillon #endif
3332176cfdSRui Paulo
3432176cfdSRui Paulo /*
3532176cfdSRui Paulo * IEEE 802.11s Mesh Point (MBSS) support.
3632176cfdSRui Paulo *
3732176cfdSRui Paulo * Based on March 2009, D3.0 802.11s draft spec.
3832176cfdSRui Paulo */
3932176cfdSRui Paulo #include "opt_inet.h"
4032176cfdSRui Paulo #include "opt_wlan.h"
4132176cfdSRui Paulo
4232176cfdSRui Paulo #include <sys/param.h>
4332176cfdSRui Paulo #include <sys/systm.h>
4432176cfdSRui Paulo #include <sys/mbuf.h>
4532176cfdSRui Paulo #include <sys/malloc.h>
4632176cfdSRui Paulo #include <sys/kernel.h>
4732176cfdSRui Paulo
4832176cfdSRui Paulo #include <sys/socket.h>
4932176cfdSRui Paulo #include <sys/sockio.h>
5032176cfdSRui Paulo #include <sys/endian.h>
5132176cfdSRui Paulo #include <sys/errno.h>
5232176cfdSRui Paulo #include <sys/proc.h>
5332176cfdSRui Paulo #include <sys/sysctl.h>
5432176cfdSRui Paulo
55085ff963SMatthew Dillon #include <net/bpf.h>
5632176cfdSRui Paulo #include <net/if.h>
57085ff963SMatthew Dillon #include <net/if_var.h>
5832176cfdSRui Paulo #include <net/if_media.h>
5932176cfdSRui Paulo #include <net/if_llc.h>
6032176cfdSRui Paulo #include <net/ethernet.h>
6132176cfdSRui Paulo
6232176cfdSRui Paulo #include <netproto/802_11/ieee80211_var.h>
6332176cfdSRui Paulo #include <netproto/802_11/ieee80211_action.h>
64085ff963SMatthew Dillon #ifdef IEEE80211_SUPPORT_SUPERG
65085ff963SMatthew Dillon #include <netproto/802_11/ieee80211_superg.h>
66085ff963SMatthew Dillon #endif
6732176cfdSRui Paulo #include <netproto/802_11/ieee80211_input.h>
6832176cfdSRui Paulo #include <netproto/802_11/ieee80211_mesh.h>
6932176cfdSRui Paulo
7032176cfdSRui Paulo static void mesh_rt_flush_invalid(struct ieee80211vap *);
7132176cfdSRui Paulo static int mesh_select_proto_path(struct ieee80211vap *, const char *);
7232176cfdSRui Paulo static int mesh_select_proto_metric(struct ieee80211vap *, const char *);
7332176cfdSRui Paulo static void mesh_vattach(struct ieee80211vap *);
7432176cfdSRui Paulo static int mesh_newstate(struct ieee80211vap *, enum ieee80211_state, int);
75085ff963SMatthew Dillon static void mesh_rt_cleanup_cb(void *);
76085ff963SMatthew Dillon static void mesh_gatemode_setup(struct ieee80211vap *);
77085ff963SMatthew Dillon static void mesh_gatemode_cb(void *);
7832176cfdSRui Paulo static void mesh_linkchange(struct ieee80211_node *,
7932176cfdSRui Paulo enum ieee80211_mesh_mlstate);
8032176cfdSRui Paulo static void mesh_checkid(void *, struct ieee80211_node *);
8132176cfdSRui Paulo static uint32_t mesh_generateid(struct ieee80211vap *);
8232176cfdSRui Paulo static int mesh_checkpseq(struct ieee80211vap *,
8332176cfdSRui Paulo const uint8_t [IEEE80211_ADDR_LEN], uint32_t);
84085ff963SMatthew Dillon static void mesh_transmit_to_gate(struct ieee80211vap *, struct mbuf *,
85085ff963SMatthew Dillon struct ieee80211_mesh_route *);
8632176cfdSRui Paulo static void mesh_forward(struct ieee80211vap *, struct mbuf *,
8732176cfdSRui Paulo const struct ieee80211_meshcntl *);
884f898719SImre Vadász static int mesh_input(struct ieee80211_node *, struct mbuf *,
894f898719SImre Vadász const struct ieee80211_rx_stats *rxs, int, int);
9032176cfdSRui Paulo static void mesh_recv_mgmt(struct ieee80211_node *, struct mbuf *, int,
914f898719SImre Vadász const struct ieee80211_rx_stats *rxs, int, int);
92085ff963SMatthew Dillon static void mesh_recv_ctl(struct ieee80211_node *, struct mbuf *, int);
9332176cfdSRui Paulo static void mesh_peer_timeout_setup(struct ieee80211_node *);
9432176cfdSRui Paulo static void mesh_peer_timeout_backoff(struct ieee80211_node *);
95085ff963SMatthew Dillon static void mesh_peer_timeout_cb(void *);
9632176cfdSRui Paulo static __inline void
9732176cfdSRui Paulo mesh_peer_timeout_stop(struct ieee80211_node *);
9832176cfdSRui Paulo static int mesh_verify_meshid(struct ieee80211vap *, const uint8_t *);
9932176cfdSRui Paulo static int mesh_verify_meshconf(struct ieee80211vap *, const uint8_t *);
10032176cfdSRui Paulo static int mesh_verify_meshpeer(struct ieee80211vap *, uint8_t,
10132176cfdSRui Paulo const uint8_t *);
10232176cfdSRui Paulo uint32_t mesh_airtime_calc(struct ieee80211_node *);
10332176cfdSRui Paulo
10432176cfdSRui Paulo /*
10532176cfdSRui Paulo * Timeout values come from the specification and are in milliseconds.
10632176cfdSRui Paulo */
107085ff963SMatthew Dillon static SYSCTL_NODE(_net_wlan, OID_AUTO, mesh, CTLFLAG_RD, 0,
10832176cfdSRui Paulo "IEEE 802.11s parameters");
109085ff963SMatthew Dillon static int ieee80211_mesh_gateint = -1;
110085ff963SMatthew Dillon SYSCTL_PROC(_net_wlan_mesh, OID_AUTO, gateint, CTLTYPE_INT | CTLFLAG_RW,
111085ff963SMatthew Dillon &ieee80211_mesh_gateint, 0, ieee80211_sysctl_msecs_ticks, "I",
112085ff963SMatthew Dillon "mesh gate interval (ms)");
11332176cfdSRui Paulo static int ieee80211_mesh_retrytimeout = -1;
11432176cfdSRui Paulo SYSCTL_PROC(_net_wlan_mesh, OID_AUTO, retrytimeout, CTLTYPE_INT | CTLFLAG_RW,
11532176cfdSRui Paulo &ieee80211_mesh_retrytimeout, 0, ieee80211_sysctl_msecs_ticks, "I",
11632176cfdSRui Paulo "Retry timeout (msec)");
11732176cfdSRui Paulo static int ieee80211_mesh_holdingtimeout = -1;
118085ff963SMatthew Dillon
11932176cfdSRui Paulo SYSCTL_PROC(_net_wlan_mesh, OID_AUTO, holdingtimeout, CTLTYPE_INT | CTLFLAG_RW,
12032176cfdSRui Paulo &ieee80211_mesh_holdingtimeout, 0, ieee80211_sysctl_msecs_ticks, "I",
12132176cfdSRui Paulo "Holding state timeout (msec)");
12232176cfdSRui Paulo static int ieee80211_mesh_confirmtimeout = -1;
12332176cfdSRui Paulo SYSCTL_PROC(_net_wlan_mesh, OID_AUTO, confirmtimeout, CTLTYPE_INT | CTLFLAG_RW,
12432176cfdSRui Paulo &ieee80211_mesh_confirmtimeout, 0, ieee80211_sysctl_msecs_ticks, "I",
12532176cfdSRui Paulo "Confirm state timeout (msec)");
126085ff963SMatthew Dillon static int ieee80211_mesh_backofftimeout = -1;
127085ff963SMatthew Dillon SYSCTL_PROC(_net_wlan_mesh, OID_AUTO, backofftimeout, CTLTYPE_INT | CTLFLAG_RW,
128085ff963SMatthew Dillon &ieee80211_mesh_backofftimeout, 0, ieee80211_sysctl_msecs_ticks, "I",
129085ff963SMatthew Dillon "Backoff timeout (msec). This is to throutles peering forever when "
130d3e40d4dSSascha Wildner "not receiving answer or is rejected by a neighbor");
13132176cfdSRui Paulo static int ieee80211_mesh_maxretries = 2;
132085ff963SMatthew Dillon SYSCTL_INT(_net_wlan_mesh, OID_AUTO, maxretries, CTLFLAG_RW,
13332176cfdSRui Paulo &ieee80211_mesh_maxretries, 0,
13432176cfdSRui Paulo "Maximum retries during peer link establishment");
135085ff963SMatthew Dillon static int ieee80211_mesh_maxholding = 2;
136085ff963SMatthew Dillon SYSCTL_INT(_net_wlan_mesh, OID_AUTO, maxholding, CTLFLAG_RW,
137085ff963SMatthew Dillon &ieee80211_mesh_maxholding, 0,
138085ff963SMatthew Dillon "Maximum times we are allowed to transition to HOLDING state before "
139085ff963SMatthew Dillon "backinoff during peer link establishment");
14032176cfdSRui Paulo
14132176cfdSRui Paulo static const uint8_t broadcastaddr[IEEE80211_ADDR_LEN] =
14232176cfdSRui Paulo { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
14332176cfdSRui Paulo
14432176cfdSRui Paulo static ieee80211_recv_action_func mesh_recv_action_meshpeering_open;
14532176cfdSRui Paulo static ieee80211_recv_action_func mesh_recv_action_meshpeering_confirm;
14632176cfdSRui Paulo static ieee80211_recv_action_func mesh_recv_action_meshpeering_close;
147085ff963SMatthew Dillon static ieee80211_recv_action_func mesh_recv_action_meshlmetric;
148085ff963SMatthew Dillon static ieee80211_recv_action_func mesh_recv_action_meshgate;
14932176cfdSRui Paulo
15032176cfdSRui Paulo static ieee80211_send_action_func mesh_send_action_meshpeering_open;
15132176cfdSRui Paulo static ieee80211_send_action_func mesh_send_action_meshpeering_confirm;
15232176cfdSRui Paulo static ieee80211_send_action_func mesh_send_action_meshpeering_close;
153085ff963SMatthew Dillon static ieee80211_send_action_func mesh_send_action_meshlmetric;
154085ff963SMatthew Dillon static ieee80211_send_action_func mesh_send_action_meshgate;
15532176cfdSRui Paulo
15632176cfdSRui Paulo static const struct ieee80211_mesh_proto_metric mesh_metric_airtime = {
15732176cfdSRui Paulo .mpm_descr = "AIRTIME",
15832176cfdSRui Paulo .mpm_ie = IEEE80211_MESHCONF_METRIC_AIRTIME,
15932176cfdSRui Paulo .mpm_metric = mesh_airtime_calc,
16032176cfdSRui Paulo };
16132176cfdSRui Paulo
16232176cfdSRui Paulo static struct ieee80211_mesh_proto_path mesh_proto_paths[4];
16332176cfdSRui Paulo static struct ieee80211_mesh_proto_metric mesh_proto_metrics[4];
16432176cfdSRui Paulo
165085ff963SMatthew Dillon MALLOC_DEFINE(M_80211_MESH_PREQ, "80211preq", "802.11 MESH Path Request frame");
166085ff963SMatthew Dillon MALLOC_DEFINE(M_80211_MESH_PREP, "80211prep", "802.11 MESH Path Reply frame");
167085ff963SMatthew Dillon MALLOC_DEFINE(M_80211_MESH_PERR, "80211perr", "802.11 MESH Path Error frame");
168085ff963SMatthew Dillon
169085ff963SMatthew Dillon /* The longer one of the lifetime should be stored as new lifetime */
170085ff963SMatthew Dillon #define MESH_ROUTE_LIFETIME_MAX(a, b) (a > b ? a : b)
171085ff963SMatthew Dillon
172085ff963SMatthew Dillon MALLOC_DEFINE(M_80211_MESH_RT, "80211mesh_rt", "802.11s routing table");
173085ff963SMatthew Dillon MALLOC_DEFINE(M_80211_MESH_GT_RT, "80211mesh_gt", "802.11s known gates table");
17432176cfdSRui Paulo
17532176cfdSRui Paulo /*
17632176cfdSRui Paulo * Helper functions to manipulate the Mesh routing table.
17732176cfdSRui Paulo */
17832176cfdSRui Paulo
17932176cfdSRui Paulo static struct ieee80211_mesh_route *
mesh_rt_find_locked(struct ieee80211_mesh_state * ms,const uint8_t dest[IEEE80211_ADDR_LEN])18032176cfdSRui Paulo mesh_rt_find_locked(struct ieee80211_mesh_state *ms,
18132176cfdSRui Paulo const uint8_t dest[IEEE80211_ADDR_LEN])
18232176cfdSRui Paulo {
18332176cfdSRui Paulo struct ieee80211_mesh_route *rt;
18432176cfdSRui Paulo
185085ff963SMatthew Dillon MESH_RT_LOCK_ASSERT(ms);
186085ff963SMatthew Dillon
18732176cfdSRui Paulo TAILQ_FOREACH(rt, &ms->ms_routes, rt_next) {
18832176cfdSRui Paulo if (IEEE80211_ADDR_EQ(dest, rt->rt_dest))
18932176cfdSRui Paulo return rt;
19032176cfdSRui Paulo }
19132176cfdSRui Paulo return NULL;
19232176cfdSRui Paulo }
19332176cfdSRui Paulo
19432176cfdSRui Paulo static struct ieee80211_mesh_route *
mesh_rt_add_locked(struct ieee80211vap * vap,const uint8_t dest[IEEE80211_ADDR_LEN])195085ff963SMatthew Dillon mesh_rt_add_locked(struct ieee80211vap *vap,
19632176cfdSRui Paulo const uint8_t dest[IEEE80211_ADDR_LEN])
19732176cfdSRui Paulo {
198085ff963SMatthew Dillon struct ieee80211_mesh_state *ms = vap->iv_mesh;
19932176cfdSRui Paulo struct ieee80211_mesh_route *rt;
20032176cfdSRui Paulo
20132176cfdSRui Paulo KASSERT(!IEEE80211_ADDR_EQ(broadcastaddr, dest),
20232176cfdSRui Paulo ("%s: adding broadcast to the routing table", __func__));
20332176cfdSRui Paulo
204085ff963SMatthew Dillon MESH_RT_LOCK_ASSERT(ms);
205085ff963SMatthew Dillon
206*4f655ef5SMatthew Dillon #if defined(__DragonFly__)
20732176cfdSRui Paulo rt = kmalloc(ALIGN(sizeof(struct ieee80211_mesh_route)) +
208fcaa651dSRui Paulo ms->ms_ppath->mpp_privlen, M_80211_MESH_RT, M_INTWAIT | M_ZERO);
209*4f655ef5SMatthew Dillon #else
210*4f655ef5SMatthew Dillon rt = IEEE80211_MALLOC(ALIGN(sizeof(struct ieee80211_mesh_route)) +
211*4f655ef5SMatthew Dillon ms->ms_ppath->mpp_privlen, M_80211_MESH_RT,
212*4f655ef5SMatthew Dillon IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
213*4f655ef5SMatthew Dillon #endif
21432176cfdSRui Paulo if (rt != NULL) {
215085ff963SMatthew Dillon rt->rt_vap = vap;
21632176cfdSRui Paulo IEEE80211_ADDR_COPY(rt->rt_dest, dest);
21732176cfdSRui Paulo rt->rt_priv = (void *)ALIGN(&rt[1]);
218c3bc1bd4SImre Vadász MESH_RT_ENTRY_LOCK_INIT(rt, "MBSS_RT");
219*4f655ef5SMatthew Dillon #if defined(__DragonFly__)
220085ff963SMatthew Dillon callout_init_mp(&rt->rt_discovery);
221*4f655ef5SMatthew Dillon #else
222*4f655ef5SMatthew Dillon callout_init(&rt->rt_discovery, 1);
223*4f655ef5SMatthew Dillon #endif
224085ff963SMatthew Dillon rt->rt_updtime = ticks; /* create time */
22532176cfdSRui Paulo TAILQ_INSERT_TAIL(&ms->ms_routes, rt, rt_next);
22632176cfdSRui Paulo }
22732176cfdSRui Paulo return rt;
22832176cfdSRui Paulo }
22932176cfdSRui Paulo
23032176cfdSRui Paulo struct ieee80211_mesh_route *
ieee80211_mesh_rt_find(struct ieee80211vap * vap,const uint8_t dest[IEEE80211_ADDR_LEN])23132176cfdSRui Paulo ieee80211_mesh_rt_find(struct ieee80211vap *vap,
23232176cfdSRui Paulo const uint8_t dest[IEEE80211_ADDR_LEN])
23332176cfdSRui Paulo {
23432176cfdSRui Paulo struct ieee80211_mesh_state *ms = vap->iv_mesh;
23532176cfdSRui Paulo struct ieee80211_mesh_route *rt;
23632176cfdSRui Paulo
237085ff963SMatthew Dillon MESH_RT_LOCK(ms);
23832176cfdSRui Paulo rt = mesh_rt_find_locked(ms, dest);
239085ff963SMatthew Dillon MESH_RT_UNLOCK(ms);
24032176cfdSRui Paulo return rt;
24132176cfdSRui Paulo }
24232176cfdSRui Paulo
24332176cfdSRui Paulo struct ieee80211_mesh_route *
ieee80211_mesh_rt_add(struct ieee80211vap * vap,const uint8_t dest[IEEE80211_ADDR_LEN])24432176cfdSRui Paulo ieee80211_mesh_rt_add(struct ieee80211vap *vap,
24532176cfdSRui Paulo const uint8_t dest[IEEE80211_ADDR_LEN])
24632176cfdSRui Paulo {
24732176cfdSRui Paulo struct ieee80211_mesh_state *ms = vap->iv_mesh;
24832176cfdSRui Paulo struct ieee80211_mesh_route *rt;
24932176cfdSRui Paulo
25032176cfdSRui Paulo KASSERT(ieee80211_mesh_rt_find(vap, dest) == NULL,
25132176cfdSRui Paulo ("%s: duplicate entry in the routing table", __func__));
25232176cfdSRui Paulo KASSERT(!IEEE80211_ADDR_EQ(vap->iv_myaddr, dest),
25332176cfdSRui Paulo ("%s: adding self to the routing table", __func__));
25432176cfdSRui Paulo
255085ff963SMatthew Dillon MESH_RT_LOCK(ms);
256085ff963SMatthew Dillon rt = mesh_rt_add_locked(vap, dest);
257085ff963SMatthew Dillon MESH_RT_UNLOCK(ms);
25832176cfdSRui Paulo return rt;
25932176cfdSRui Paulo }
26032176cfdSRui Paulo
26132176cfdSRui Paulo /*
262085ff963SMatthew Dillon * Update the route lifetime and returns the updated lifetime.
263085ff963SMatthew Dillon * If new_lifetime is zero and route is timedout it will be invalidated.
264085ff963SMatthew Dillon * new_lifetime is in msec
265085ff963SMatthew Dillon */
266085ff963SMatthew Dillon int
ieee80211_mesh_rt_update(struct ieee80211_mesh_route * rt,int new_lifetime)267085ff963SMatthew Dillon ieee80211_mesh_rt_update(struct ieee80211_mesh_route *rt, int new_lifetime)
268085ff963SMatthew Dillon {
269085ff963SMatthew Dillon int timesince, now;
270085ff963SMatthew Dillon uint32_t lifetime = 0;
271085ff963SMatthew Dillon
272085ff963SMatthew Dillon KASSERT(rt != NULL, ("route is NULL"));
273085ff963SMatthew Dillon
274085ff963SMatthew Dillon now = ticks;
275c3bc1bd4SImre Vadász MESH_RT_ENTRY_LOCK(rt);
276085ff963SMatthew Dillon
277085ff963SMatthew Dillon /* dont clobber a proxy entry gated by us */
278085ff963SMatthew Dillon if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY && rt->rt_nhops == 0) {
279c3bc1bd4SImre Vadász MESH_RT_ENTRY_UNLOCK(rt);
280085ff963SMatthew Dillon return rt->rt_lifetime;
281085ff963SMatthew Dillon }
282085ff963SMatthew Dillon
283085ff963SMatthew Dillon timesince = ticks_to_msecs(now - rt->rt_updtime);
284085ff963SMatthew Dillon rt->rt_updtime = now;
285085ff963SMatthew Dillon if (timesince >= rt->rt_lifetime) {
286085ff963SMatthew Dillon if (new_lifetime != 0) {
287085ff963SMatthew Dillon rt->rt_lifetime = new_lifetime;
288085ff963SMatthew Dillon }
289085ff963SMatthew Dillon else {
290085ff963SMatthew Dillon rt->rt_flags &= ~IEEE80211_MESHRT_FLAGS_VALID;
291085ff963SMatthew Dillon rt->rt_lifetime = 0;
292085ff963SMatthew Dillon }
293085ff963SMatthew Dillon } else {
294085ff963SMatthew Dillon /* update what is left of lifetime */
295085ff963SMatthew Dillon rt->rt_lifetime = rt->rt_lifetime - timesince;
296085ff963SMatthew Dillon rt->rt_lifetime = MESH_ROUTE_LIFETIME_MAX(
297085ff963SMatthew Dillon new_lifetime, rt->rt_lifetime);
298085ff963SMatthew Dillon }
299085ff963SMatthew Dillon lifetime = rt->rt_lifetime;
300c3bc1bd4SImre Vadász MESH_RT_ENTRY_UNLOCK(rt);
301085ff963SMatthew Dillon
302085ff963SMatthew Dillon return lifetime;
303085ff963SMatthew Dillon }
304085ff963SMatthew Dillon
305085ff963SMatthew Dillon /*
30632176cfdSRui Paulo * Add a proxy route (as needed) for the specified destination.
30732176cfdSRui Paulo */
30832176cfdSRui Paulo void
ieee80211_mesh_proxy_check(struct ieee80211vap * vap,const uint8_t dest[IEEE80211_ADDR_LEN])30932176cfdSRui Paulo ieee80211_mesh_proxy_check(struct ieee80211vap *vap,
31032176cfdSRui Paulo const uint8_t dest[IEEE80211_ADDR_LEN])
31132176cfdSRui Paulo {
31232176cfdSRui Paulo struct ieee80211_mesh_state *ms = vap->iv_mesh;
31332176cfdSRui Paulo struct ieee80211_mesh_route *rt;
31432176cfdSRui Paulo
315085ff963SMatthew Dillon MESH_RT_LOCK(ms);
31632176cfdSRui Paulo rt = mesh_rt_find_locked(ms, dest);
31732176cfdSRui Paulo if (rt == NULL) {
318085ff963SMatthew Dillon rt = mesh_rt_add_locked(vap, dest);
31932176cfdSRui Paulo if (rt == NULL) {
32032176cfdSRui Paulo IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, dest,
32132176cfdSRui Paulo "%s", "unable to add proxy entry");
32232176cfdSRui Paulo vap->iv_stats.is_mesh_rtaddfailed++;
32332176cfdSRui Paulo } else {
32432176cfdSRui Paulo IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, dest,
32532176cfdSRui Paulo "%s", "add proxy entry");
326085ff963SMatthew Dillon IEEE80211_ADDR_COPY(rt->rt_mesh_gate, vap->iv_myaddr);
32732176cfdSRui Paulo IEEE80211_ADDR_COPY(rt->rt_nexthop, vap->iv_myaddr);
32832176cfdSRui Paulo rt->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID
32932176cfdSRui Paulo | IEEE80211_MESHRT_FLAGS_PROXY;
33032176cfdSRui Paulo }
33132176cfdSRui Paulo } else if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0) {
332085ff963SMatthew Dillon KASSERT(rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY,
333085ff963SMatthew Dillon ("no proxy flag for poxy entry"));
33432176cfdSRui Paulo struct ieee80211com *ic = vap->iv_ic;
33532176cfdSRui Paulo /*
33632176cfdSRui Paulo * Fix existing entry created by received frames from
33732176cfdSRui Paulo * stations that have some memory of dest. We also
33832176cfdSRui Paulo * flush any frames held on the staging queue; delivering
33932176cfdSRui Paulo * them is too much trouble right now.
34032176cfdSRui Paulo */
34132176cfdSRui Paulo IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, dest,
34232176cfdSRui Paulo "%s", "fix proxy entry");
34332176cfdSRui Paulo IEEE80211_ADDR_COPY(rt->rt_nexthop, vap->iv_myaddr);
34432176cfdSRui Paulo rt->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID
34532176cfdSRui Paulo | IEEE80211_MESHRT_FLAGS_PROXY;
34632176cfdSRui Paulo /* XXX belongs in hwmp */
34732176cfdSRui Paulo ieee80211_ageq_drain_node(&ic->ic_stageq,
34832176cfdSRui Paulo (void *)(uintptr_t) ieee80211_mac_hash(ic, dest));
34932176cfdSRui Paulo /* XXX stat? */
35032176cfdSRui Paulo }
351085ff963SMatthew Dillon MESH_RT_UNLOCK(ms);
35232176cfdSRui Paulo }
35332176cfdSRui Paulo
35432176cfdSRui Paulo static __inline void
mesh_rt_del(struct ieee80211_mesh_state * ms,struct ieee80211_mesh_route * rt)35532176cfdSRui Paulo mesh_rt_del(struct ieee80211_mesh_state *ms, struct ieee80211_mesh_route *rt)
35632176cfdSRui Paulo {
35732176cfdSRui Paulo TAILQ_REMOVE(&ms->ms_routes, rt, rt_next);
358085ff963SMatthew Dillon /*
359085ff963SMatthew Dillon * Grab the lock before destroying it, to be sure no one else
360085ff963SMatthew Dillon * is holding the route.
361085ff963SMatthew Dillon */
362c3bc1bd4SImre Vadász MESH_RT_ENTRY_LOCK(rt);
363085ff963SMatthew Dillon callout_drain(&rt->rt_discovery);
364c3bc1bd4SImre Vadász MESH_RT_ENTRY_LOCK_DESTROY(rt);
365*4f655ef5SMatthew Dillon IEEE80211_FREE(rt, M_80211_MESH_RT);
36632176cfdSRui Paulo }
36732176cfdSRui Paulo
36832176cfdSRui Paulo void
ieee80211_mesh_rt_del(struct ieee80211vap * vap,const uint8_t dest[IEEE80211_ADDR_LEN])36932176cfdSRui Paulo ieee80211_mesh_rt_del(struct ieee80211vap *vap,
37032176cfdSRui Paulo const uint8_t dest[IEEE80211_ADDR_LEN])
37132176cfdSRui Paulo {
37232176cfdSRui Paulo struct ieee80211_mesh_state *ms = vap->iv_mesh;
37332176cfdSRui Paulo struct ieee80211_mesh_route *rt, *next;
37432176cfdSRui Paulo
375085ff963SMatthew Dillon MESH_RT_LOCK(ms);
376085ff963SMatthew Dillon TAILQ_FOREACH_SAFE(rt, &ms->ms_routes, rt_next, next) {
37732176cfdSRui Paulo if (IEEE80211_ADDR_EQ(rt->rt_dest, dest)) {
378085ff963SMatthew Dillon if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY) {
379085ff963SMatthew Dillon ms->ms_ppath->mpp_senderror(vap, dest, rt,
380085ff963SMatthew Dillon IEEE80211_REASON_MESH_PERR_NO_PROXY);
381085ff963SMatthew Dillon } else {
382085ff963SMatthew Dillon ms->ms_ppath->mpp_senderror(vap, dest, rt,
383085ff963SMatthew Dillon IEEE80211_REASON_MESH_PERR_DEST_UNREACH);
384085ff963SMatthew Dillon }
38532176cfdSRui Paulo mesh_rt_del(ms, rt);
386085ff963SMatthew Dillon MESH_RT_UNLOCK(ms);
38732176cfdSRui Paulo return;
38832176cfdSRui Paulo }
38932176cfdSRui Paulo }
390085ff963SMatthew Dillon MESH_RT_UNLOCK(ms);
39132176cfdSRui Paulo }
39232176cfdSRui Paulo
39332176cfdSRui Paulo void
ieee80211_mesh_rt_flush(struct ieee80211vap * vap)39432176cfdSRui Paulo ieee80211_mesh_rt_flush(struct ieee80211vap *vap)
39532176cfdSRui Paulo {
39632176cfdSRui Paulo struct ieee80211_mesh_state *ms = vap->iv_mesh;
39732176cfdSRui Paulo struct ieee80211_mesh_route *rt, *next;
39832176cfdSRui Paulo
39932176cfdSRui Paulo if (ms == NULL)
40032176cfdSRui Paulo return;
401085ff963SMatthew Dillon MESH_RT_LOCK(ms);
402085ff963SMatthew Dillon TAILQ_FOREACH_SAFE(rt, &ms->ms_routes, rt_next, next)
40332176cfdSRui Paulo mesh_rt_del(ms, rt);
404085ff963SMatthew Dillon MESH_RT_UNLOCK(ms);
40532176cfdSRui Paulo }
40632176cfdSRui Paulo
40732176cfdSRui Paulo void
ieee80211_mesh_rt_flush_peer(struct ieee80211vap * vap,const uint8_t peer[IEEE80211_ADDR_LEN])40832176cfdSRui Paulo ieee80211_mesh_rt_flush_peer(struct ieee80211vap *vap,
40932176cfdSRui Paulo const uint8_t peer[IEEE80211_ADDR_LEN])
41032176cfdSRui Paulo {
41132176cfdSRui Paulo struct ieee80211_mesh_state *ms = vap->iv_mesh;
41232176cfdSRui Paulo struct ieee80211_mesh_route *rt, *next;
41332176cfdSRui Paulo
414085ff963SMatthew Dillon MESH_RT_LOCK(ms);
415085ff963SMatthew Dillon TAILQ_FOREACH_SAFE(rt, &ms->ms_routes, rt_next, next) {
41632176cfdSRui Paulo if (IEEE80211_ADDR_EQ(rt->rt_nexthop, peer))
41732176cfdSRui Paulo mesh_rt_del(ms, rt);
41832176cfdSRui Paulo }
419085ff963SMatthew Dillon MESH_RT_UNLOCK(ms);
42032176cfdSRui Paulo }
42132176cfdSRui Paulo
42232176cfdSRui Paulo /*
42332176cfdSRui Paulo * Flush expired routing entries, i.e. those in invalid state for
42432176cfdSRui Paulo * some time.
42532176cfdSRui Paulo */
42632176cfdSRui Paulo static void
mesh_rt_flush_invalid(struct ieee80211vap * vap)42732176cfdSRui Paulo mesh_rt_flush_invalid(struct ieee80211vap *vap)
42832176cfdSRui Paulo {
42932176cfdSRui Paulo struct ieee80211_mesh_state *ms = vap->iv_mesh;
43032176cfdSRui Paulo struct ieee80211_mesh_route *rt, *next;
43132176cfdSRui Paulo
43232176cfdSRui Paulo if (ms == NULL)
43332176cfdSRui Paulo return;
434085ff963SMatthew Dillon MESH_RT_LOCK(ms);
435085ff963SMatthew Dillon TAILQ_FOREACH_SAFE(rt, &ms->ms_routes, rt_next, next) {
436085ff963SMatthew Dillon /* Discover paths will be deleted by their own callout */
437085ff963SMatthew Dillon if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_DISCOVER)
438085ff963SMatthew Dillon continue;
439085ff963SMatthew Dillon ieee80211_mesh_rt_update(rt, 0);
440085ff963SMatthew Dillon if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0)
44132176cfdSRui Paulo mesh_rt_del(ms, rt);
44232176cfdSRui Paulo }
443085ff963SMatthew Dillon MESH_RT_UNLOCK(ms);
44432176cfdSRui Paulo }
44532176cfdSRui Paulo
44632176cfdSRui Paulo int
ieee80211_mesh_register_proto_path(const struct ieee80211_mesh_proto_path * mpp)44732176cfdSRui Paulo ieee80211_mesh_register_proto_path(const struct ieee80211_mesh_proto_path *mpp)
44832176cfdSRui Paulo {
44932176cfdSRui Paulo int i, firstempty = -1;
45032176cfdSRui Paulo
451*4f655ef5SMatthew Dillon for (i = 0; i < nitems(mesh_proto_paths); i++) {
45232176cfdSRui Paulo if (strncmp(mpp->mpp_descr, mesh_proto_paths[i].mpp_descr,
45332176cfdSRui Paulo IEEE80211_MESH_PROTO_DSZ) == 0)
45432176cfdSRui Paulo return EEXIST;
45532176cfdSRui Paulo if (!mesh_proto_paths[i].mpp_active && firstempty == -1)
45632176cfdSRui Paulo firstempty = i;
45732176cfdSRui Paulo }
45832176cfdSRui Paulo if (firstempty < 0)
45932176cfdSRui Paulo return ENOSPC;
46032176cfdSRui Paulo memcpy(&mesh_proto_paths[firstempty], mpp, sizeof(*mpp));
46132176cfdSRui Paulo mesh_proto_paths[firstempty].mpp_active = 1;
46232176cfdSRui Paulo return 0;
46332176cfdSRui Paulo }
46432176cfdSRui Paulo
46532176cfdSRui Paulo int
ieee80211_mesh_register_proto_metric(const struct ieee80211_mesh_proto_metric * mpm)46632176cfdSRui Paulo ieee80211_mesh_register_proto_metric(const struct
46732176cfdSRui Paulo ieee80211_mesh_proto_metric *mpm)
46832176cfdSRui Paulo {
46932176cfdSRui Paulo int i, firstempty = -1;
47032176cfdSRui Paulo
471*4f655ef5SMatthew Dillon for (i = 0; i < nitems(mesh_proto_metrics); i++) {
47232176cfdSRui Paulo if (strncmp(mpm->mpm_descr, mesh_proto_metrics[i].mpm_descr,
47332176cfdSRui Paulo IEEE80211_MESH_PROTO_DSZ) == 0)
47432176cfdSRui Paulo return EEXIST;
47532176cfdSRui Paulo if (!mesh_proto_metrics[i].mpm_active && firstempty == -1)
47632176cfdSRui Paulo firstempty = i;
47732176cfdSRui Paulo }
47832176cfdSRui Paulo if (firstempty < 0)
47932176cfdSRui Paulo return ENOSPC;
48032176cfdSRui Paulo memcpy(&mesh_proto_metrics[firstempty], mpm, sizeof(*mpm));
48132176cfdSRui Paulo mesh_proto_metrics[firstempty].mpm_active = 1;
48232176cfdSRui Paulo return 0;
48332176cfdSRui Paulo }
48432176cfdSRui Paulo
48532176cfdSRui Paulo static int
mesh_select_proto_path(struct ieee80211vap * vap,const char * name)48632176cfdSRui Paulo mesh_select_proto_path(struct ieee80211vap *vap, const char *name)
48732176cfdSRui Paulo {
48832176cfdSRui Paulo struct ieee80211_mesh_state *ms = vap->iv_mesh;
48932176cfdSRui Paulo int i;
49032176cfdSRui Paulo
491*4f655ef5SMatthew Dillon for (i = 0; i < nitems(mesh_proto_paths); i++) {
49232176cfdSRui Paulo if (strcasecmp(mesh_proto_paths[i].mpp_descr, name) == 0) {
49332176cfdSRui Paulo ms->ms_ppath = &mesh_proto_paths[i];
49432176cfdSRui Paulo return 0;
49532176cfdSRui Paulo }
49632176cfdSRui Paulo }
49732176cfdSRui Paulo return ENOENT;
49832176cfdSRui Paulo }
49932176cfdSRui Paulo
50032176cfdSRui Paulo static int
mesh_select_proto_metric(struct ieee80211vap * vap,const char * name)50132176cfdSRui Paulo mesh_select_proto_metric(struct ieee80211vap *vap, const char *name)
50232176cfdSRui Paulo {
50332176cfdSRui Paulo struct ieee80211_mesh_state *ms = vap->iv_mesh;
50432176cfdSRui Paulo int i;
50532176cfdSRui Paulo
506*4f655ef5SMatthew Dillon for (i = 0; i < nitems(mesh_proto_metrics); i++) {
50732176cfdSRui Paulo if (strcasecmp(mesh_proto_metrics[i].mpm_descr, name) == 0) {
50832176cfdSRui Paulo ms->ms_pmetric = &mesh_proto_metrics[i];
50932176cfdSRui Paulo return 0;
51032176cfdSRui Paulo }
51132176cfdSRui Paulo }
51232176cfdSRui Paulo return ENOENT;
51332176cfdSRui Paulo }
514085ff963SMatthew Dillon
515085ff963SMatthew Dillon static void
mesh_gatemode_setup(struct ieee80211vap * vap)516085ff963SMatthew Dillon mesh_gatemode_setup(struct ieee80211vap *vap)
517085ff963SMatthew Dillon {
518085ff963SMatthew Dillon struct ieee80211_mesh_state *ms = vap->iv_mesh;
519085ff963SMatthew Dillon
520085ff963SMatthew Dillon /*
521085ff963SMatthew Dillon * NB: When a mesh gate is running as a ROOT it shall
522085ff963SMatthew Dillon * not send out periodic GANNs but instead mark the
523085ff963SMatthew Dillon * mesh gate flag for the corresponding proactive PREQ
524085ff963SMatthew Dillon * and RANN frames.
525085ff963SMatthew Dillon */
526085ff963SMatthew Dillon if (ms->ms_flags & IEEE80211_MESHFLAGS_ROOT ||
527085ff963SMatthew Dillon (ms->ms_flags & IEEE80211_MESHFLAGS_GATE) == 0) {
528085ff963SMatthew Dillon callout_drain(&ms->ms_gatetimer);
529085ff963SMatthew Dillon return ;
530085ff963SMatthew Dillon }
531085ff963SMatthew Dillon callout_reset(&ms->ms_gatetimer, ieee80211_mesh_gateint,
532085ff963SMatthew Dillon mesh_gatemode_cb, vap);
533085ff963SMatthew Dillon }
534085ff963SMatthew Dillon
535085ff963SMatthew Dillon static void
mesh_gatemode_cb(void * arg)536085ff963SMatthew Dillon mesh_gatemode_cb(void *arg)
537085ff963SMatthew Dillon {
538085ff963SMatthew Dillon struct ieee80211vap *vap = (struct ieee80211vap *)arg;
539085ff963SMatthew Dillon struct ieee80211_mesh_state *ms = vap->iv_mesh;
540085ff963SMatthew Dillon struct ieee80211_meshgann_ie gann;
541085ff963SMatthew Dillon
542085ff963SMatthew Dillon gann.gann_flags = 0; /* Reserved */
543085ff963SMatthew Dillon gann.gann_hopcount = 0;
544085ff963SMatthew Dillon gann.gann_ttl = ms->ms_ttl;
545085ff963SMatthew Dillon IEEE80211_ADDR_COPY(gann.gann_addr, vap->iv_myaddr);
546085ff963SMatthew Dillon gann.gann_seq = ms->ms_gateseq++;
547085ff963SMatthew Dillon gann.gann_interval = ieee80211_mesh_gateint;
548085ff963SMatthew Dillon
549085ff963SMatthew Dillon IEEE80211_NOTE(vap, IEEE80211_MSG_MESH, vap->iv_bss,
550085ff963SMatthew Dillon "send broadcast GANN (seq %u)", gann.gann_seq);
551085ff963SMatthew Dillon
552085ff963SMatthew Dillon ieee80211_send_action(vap->iv_bss, IEEE80211_ACTION_CAT_MESH,
553085ff963SMatthew Dillon IEEE80211_ACTION_MESH_GANN, &gann);
554085ff963SMatthew Dillon mesh_gatemode_setup(vap);
555085ff963SMatthew Dillon }
55632176cfdSRui Paulo
55732176cfdSRui Paulo static void
ieee80211_mesh_init(void)55832176cfdSRui Paulo ieee80211_mesh_init(void)
55932176cfdSRui Paulo {
56032176cfdSRui Paulo
56132176cfdSRui Paulo memset(mesh_proto_paths, 0, sizeof(mesh_proto_paths));
56232176cfdSRui Paulo memset(mesh_proto_metrics, 0, sizeof(mesh_proto_metrics));
56332176cfdSRui Paulo
56432176cfdSRui Paulo /*
56532176cfdSRui Paulo * Setup mesh parameters that depends on the clock frequency.
56632176cfdSRui Paulo */
567085ff963SMatthew Dillon ieee80211_mesh_gateint = msecs_to_ticks(10000);
56832176cfdSRui Paulo ieee80211_mesh_retrytimeout = msecs_to_ticks(40);
56932176cfdSRui Paulo ieee80211_mesh_holdingtimeout = msecs_to_ticks(40);
57032176cfdSRui Paulo ieee80211_mesh_confirmtimeout = msecs_to_ticks(40);
571085ff963SMatthew Dillon ieee80211_mesh_backofftimeout = msecs_to_ticks(5000);
57232176cfdSRui Paulo
57332176cfdSRui Paulo /*
57432176cfdSRui Paulo * Register action frame handlers.
57532176cfdSRui Paulo */
576085ff963SMatthew Dillon ieee80211_recv_action_register(IEEE80211_ACTION_CAT_SELF_PROT,
57732176cfdSRui Paulo IEEE80211_ACTION_MESHPEERING_OPEN,
57832176cfdSRui Paulo mesh_recv_action_meshpeering_open);
579085ff963SMatthew Dillon ieee80211_recv_action_register(IEEE80211_ACTION_CAT_SELF_PROT,
58032176cfdSRui Paulo IEEE80211_ACTION_MESHPEERING_CONFIRM,
58132176cfdSRui Paulo mesh_recv_action_meshpeering_confirm);
582085ff963SMatthew Dillon ieee80211_recv_action_register(IEEE80211_ACTION_CAT_SELF_PROT,
58332176cfdSRui Paulo IEEE80211_ACTION_MESHPEERING_CLOSE,
58432176cfdSRui Paulo mesh_recv_action_meshpeering_close);
585085ff963SMatthew Dillon ieee80211_recv_action_register(IEEE80211_ACTION_CAT_MESH,
586085ff963SMatthew Dillon IEEE80211_ACTION_MESH_LMETRIC, mesh_recv_action_meshlmetric);
587085ff963SMatthew Dillon ieee80211_recv_action_register(IEEE80211_ACTION_CAT_MESH,
588085ff963SMatthew Dillon IEEE80211_ACTION_MESH_GANN, mesh_recv_action_meshgate);
58932176cfdSRui Paulo
590085ff963SMatthew Dillon ieee80211_send_action_register(IEEE80211_ACTION_CAT_SELF_PROT,
59132176cfdSRui Paulo IEEE80211_ACTION_MESHPEERING_OPEN,
59232176cfdSRui Paulo mesh_send_action_meshpeering_open);
593085ff963SMatthew Dillon ieee80211_send_action_register(IEEE80211_ACTION_CAT_SELF_PROT,
59432176cfdSRui Paulo IEEE80211_ACTION_MESHPEERING_CONFIRM,
59532176cfdSRui Paulo mesh_send_action_meshpeering_confirm);
596085ff963SMatthew Dillon ieee80211_send_action_register(IEEE80211_ACTION_CAT_SELF_PROT,
59732176cfdSRui Paulo IEEE80211_ACTION_MESHPEERING_CLOSE,
59832176cfdSRui Paulo mesh_send_action_meshpeering_close);
599085ff963SMatthew Dillon ieee80211_send_action_register(IEEE80211_ACTION_CAT_MESH,
600085ff963SMatthew Dillon IEEE80211_ACTION_MESH_LMETRIC,
601085ff963SMatthew Dillon mesh_send_action_meshlmetric);
602085ff963SMatthew Dillon ieee80211_send_action_register(IEEE80211_ACTION_CAT_MESH,
603085ff963SMatthew Dillon IEEE80211_ACTION_MESH_GANN,
604085ff963SMatthew Dillon mesh_send_action_meshgate);
60532176cfdSRui Paulo
60632176cfdSRui Paulo /*
60732176cfdSRui Paulo * Register Airtime Link Metric.
60832176cfdSRui Paulo */
60932176cfdSRui Paulo ieee80211_mesh_register_proto_metric(&mesh_metric_airtime);
61032176cfdSRui Paulo
61132176cfdSRui Paulo }
61232176cfdSRui Paulo SYSINIT(wlan_mesh, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_mesh_init, NULL);
61332176cfdSRui Paulo
61432176cfdSRui Paulo void
ieee80211_mesh_attach(struct ieee80211com * ic)61532176cfdSRui Paulo ieee80211_mesh_attach(struct ieee80211com *ic)
61632176cfdSRui Paulo {
61732176cfdSRui Paulo ic->ic_vattach[IEEE80211_M_MBSS] = mesh_vattach;
61832176cfdSRui Paulo }
61932176cfdSRui Paulo
62032176cfdSRui Paulo void
ieee80211_mesh_detach(struct ieee80211com * ic)62132176cfdSRui Paulo ieee80211_mesh_detach(struct ieee80211com *ic)
62232176cfdSRui Paulo {
62332176cfdSRui Paulo }
62432176cfdSRui Paulo
62532176cfdSRui Paulo static void
mesh_vdetach_peers(void * arg,struct ieee80211_node * ni)62632176cfdSRui Paulo mesh_vdetach_peers(void *arg, struct ieee80211_node *ni)
62732176cfdSRui Paulo {
62832176cfdSRui Paulo struct ieee80211com *ic = ni->ni_ic;
62932176cfdSRui Paulo uint16_t args[3];
63032176cfdSRui Paulo
63132176cfdSRui Paulo if (ni->ni_mlstate == IEEE80211_NODE_MESH_ESTABLISHED) {
63232176cfdSRui Paulo args[0] = ni->ni_mlpid;
63332176cfdSRui Paulo args[1] = ni->ni_mllid;
63432176cfdSRui Paulo args[2] = IEEE80211_REASON_PEER_LINK_CANCELED;
63532176cfdSRui Paulo ieee80211_send_action(ni,
636085ff963SMatthew Dillon IEEE80211_ACTION_CAT_SELF_PROT,
63732176cfdSRui Paulo IEEE80211_ACTION_MESHPEERING_CLOSE,
63832176cfdSRui Paulo args);
63932176cfdSRui Paulo }
640085ff963SMatthew Dillon callout_drain(&ni->ni_mltimer);
64132176cfdSRui Paulo /* XXX belongs in hwmp */
64232176cfdSRui Paulo ieee80211_ageq_drain_node(&ic->ic_stageq,
64332176cfdSRui Paulo (void *)(uintptr_t) ieee80211_mac_hash(ic, ni->ni_macaddr));
64432176cfdSRui Paulo }
64532176cfdSRui Paulo
64632176cfdSRui Paulo static void
mesh_vdetach(struct ieee80211vap * vap)64732176cfdSRui Paulo mesh_vdetach(struct ieee80211vap *vap)
64832176cfdSRui Paulo {
64932176cfdSRui Paulo struct ieee80211_mesh_state *ms = vap->iv_mesh;
65032176cfdSRui Paulo
651085ff963SMatthew Dillon callout_drain(&ms->ms_cleantimer);
65232176cfdSRui Paulo ieee80211_iterate_nodes(&vap->iv_ic->ic_sta, mesh_vdetach_peers,
65332176cfdSRui Paulo NULL);
65432176cfdSRui Paulo ieee80211_mesh_rt_flush(vap);
655c3bc1bd4SImre Vadász MESH_RT_LOCK_DESTROY(ms);
65632176cfdSRui Paulo ms->ms_ppath->mpp_vdetach(vap);
657*4f655ef5SMatthew Dillon IEEE80211_FREE(vap->iv_mesh, M_80211_VAP);
65832176cfdSRui Paulo vap->iv_mesh = NULL;
65932176cfdSRui Paulo }
66032176cfdSRui Paulo
66132176cfdSRui Paulo static void
mesh_vattach(struct ieee80211vap * vap)66232176cfdSRui Paulo mesh_vattach(struct ieee80211vap *vap)
66332176cfdSRui Paulo {
66432176cfdSRui Paulo struct ieee80211_mesh_state *ms;
66532176cfdSRui Paulo vap->iv_newstate = mesh_newstate;
66632176cfdSRui Paulo vap->iv_input = mesh_input;
66732176cfdSRui Paulo vap->iv_opdetach = mesh_vdetach;
66832176cfdSRui Paulo vap->iv_recv_mgmt = mesh_recv_mgmt;
669085ff963SMatthew Dillon vap->iv_recv_ctl = mesh_recv_ctl;
670*4f655ef5SMatthew Dillon #if defined(__DragonFly__)
67132176cfdSRui Paulo ms = kmalloc(sizeof(struct ieee80211_mesh_state), M_80211_VAP,
672fcaa651dSRui Paulo M_INTWAIT | M_ZERO);
673*4f655ef5SMatthew Dillon #else
674*4f655ef5SMatthew Dillon ms = IEEE80211_MALLOC(sizeof(struct ieee80211_mesh_state), M_80211_VAP,
675*4f655ef5SMatthew Dillon IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
676*4f655ef5SMatthew Dillon #endif
677085ff963SMatthew Dillon if (ms == NULL) {
678085ff963SMatthew Dillon kprintf("%s: couldn't alloc MBSS state\n", __func__);
679085ff963SMatthew Dillon return;
680085ff963SMatthew Dillon }
68132176cfdSRui Paulo vap->iv_mesh = ms;
68232176cfdSRui Paulo ms->ms_seq = 0;
68332176cfdSRui Paulo ms->ms_flags = (IEEE80211_MESHFLAGS_AP | IEEE80211_MESHFLAGS_FWD);
68432176cfdSRui Paulo ms->ms_ttl = IEEE80211_MESH_DEFAULT_TTL;
685085ff963SMatthew Dillon TAILQ_INIT(&ms->ms_known_gates);
68632176cfdSRui Paulo TAILQ_INIT(&ms->ms_routes);
687c3bc1bd4SImre Vadász MESH_RT_LOCK_INIT(ms, "MBSS");
688*4f655ef5SMatthew Dillon #if defined(__DragonFly__)
68934a60cf6SRui Paulo callout_init_mp(&ms->ms_cleantimer);
690085ff963SMatthew Dillon callout_init_mp(&ms->ms_gatetimer);
691*4f655ef5SMatthew Dillon #else
692*4f655ef5SMatthew Dillon callout_init(&ms->ms_cleantimer, 1);
693*4f655ef5SMatthew Dillon callout_init(&ms->ms_gatetimer, 1);
694*4f655ef5SMatthew Dillon #endif
695085ff963SMatthew Dillon ms->ms_gateseq = 0;
69632176cfdSRui Paulo mesh_select_proto_metric(vap, "AIRTIME");
69732176cfdSRui Paulo KASSERT(ms->ms_pmetric, ("ms_pmetric == NULL"));
69832176cfdSRui Paulo mesh_select_proto_path(vap, "HWMP");
69932176cfdSRui Paulo KASSERT(ms->ms_ppath, ("ms_ppath == NULL"));
70032176cfdSRui Paulo ms->ms_ppath->mpp_vattach(vap);
70132176cfdSRui Paulo }
70232176cfdSRui Paulo
70332176cfdSRui Paulo /*
70432176cfdSRui Paulo * IEEE80211_M_MBSS vap state machine handler.
70532176cfdSRui Paulo */
70632176cfdSRui Paulo static int
mesh_newstate(struct ieee80211vap * vap,enum ieee80211_state nstate,int arg)70732176cfdSRui Paulo mesh_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
70832176cfdSRui Paulo {
70932176cfdSRui Paulo struct ieee80211_mesh_state *ms = vap->iv_mesh;
71032176cfdSRui Paulo struct ieee80211com *ic = vap->iv_ic;
711085ff963SMatthew Dillon struct ieee80211_node *ni;
71232176cfdSRui Paulo enum ieee80211_state ostate;
713085ff963SMatthew Dillon
714085ff963SMatthew Dillon IEEE80211_LOCK_ASSERT(ic);
71532176cfdSRui Paulo
71632176cfdSRui Paulo ostate = vap->iv_state;
71732176cfdSRui Paulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n",
71832176cfdSRui Paulo __func__, ieee80211_state_name[ostate],
71932176cfdSRui Paulo ieee80211_state_name[nstate], arg);
72032176cfdSRui Paulo vap->iv_state = nstate; /* state transition */
72132176cfdSRui Paulo if (ostate != IEEE80211_S_SCAN)
72232176cfdSRui Paulo ieee80211_cancel_scan(vap); /* background scan */
723085ff963SMatthew Dillon ni = vap->iv_bss; /* NB: no reference held */
724085ff963SMatthew Dillon if (nstate != IEEE80211_S_RUN && ostate == IEEE80211_S_RUN) {
725085ff963SMatthew Dillon callout_drain(&ms->ms_cleantimer);
726085ff963SMatthew Dillon callout_drain(&ms->ms_gatetimer);
727085ff963SMatthew Dillon }
72832176cfdSRui Paulo switch (nstate) {
72932176cfdSRui Paulo case IEEE80211_S_INIT:
73032176cfdSRui Paulo switch (ostate) {
73132176cfdSRui Paulo case IEEE80211_S_SCAN:
73232176cfdSRui Paulo ieee80211_cancel_scan(vap);
73332176cfdSRui Paulo break;
73432176cfdSRui Paulo case IEEE80211_S_CAC:
73532176cfdSRui Paulo ieee80211_dfs_cac_stop(vap);
73632176cfdSRui Paulo break;
73732176cfdSRui Paulo case IEEE80211_S_RUN:
73832176cfdSRui Paulo ieee80211_iterate_nodes(&ic->ic_sta,
73932176cfdSRui Paulo mesh_vdetach_peers, NULL);
74032176cfdSRui Paulo break;
74132176cfdSRui Paulo default:
74232176cfdSRui Paulo break;
74332176cfdSRui Paulo }
74432176cfdSRui Paulo if (ostate != IEEE80211_S_INIT) {
74532176cfdSRui Paulo /* NB: optimize INIT -> INIT case */
74632176cfdSRui Paulo ieee80211_reset_bss(vap);
74732176cfdSRui Paulo ieee80211_mesh_rt_flush(vap);
74832176cfdSRui Paulo }
74932176cfdSRui Paulo break;
75032176cfdSRui Paulo case IEEE80211_S_SCAN:
75132176cfdSRui Paulo switch (ostate) {
75232176cfdSRui Paulo case IEEE80211_S_INIT:
75332176cfdSRui Paulo if (vap->iv_des_chan != IEEE80211_CHAN_ANYC &&
75432176cfdSRui Paulo !IEEE80211_IS_CHAN_RADAR(vap->iv_des_chan) &&
75532176cfdSRui Paulo ms->ms_idlen != 0) {
75632176cfdSRui Paulo /*
75732176cfdSRui Paulo * Already have a channel and a mesh ID; bypass
75832176cfdSRui Paulo * the scan and startup immediately.
75932176cfdSRui Paulo */
76032176cfdSRui Paulo ieee80211_create_ibss(vap, vap->iv_des_chan);
76132176cfdSRui Paulo break;
76232176cfdSRui Paulo }
76332176cfdSRui Paulo /*
76432176cfdSRui Paulo * Initiate a scan. We can come here as a result
76532176cfdSRui Paulo * of an IEEE80211_IOC_SCAN_REQ too in which case
76632176cfdSRui Paulo * the vap will be marked with IEEE80211_FEXT_SCANREQ
76732176cfdSRui Paulo * and the scan request parameters will be present
76832176cfdSRui Paulo * in iv_scanreq. Otherwise we do the default.
76932176cfdSRui Paulo */
77032176cfdSRui Paulo if (vap->iv_flags_ext & IEEE80211_FEXT_SCANREQ) {
77132176cfdSRui Paulo ieee80211_check_scan(vap,
77232176cfdSRui Paulo vap->iv_scanreq_flags,
77332176cfdSRui Paulo vap->iv_scanreq_duration,
77432176cfdSRui Paulo vap->iv_scanreq_mindwell,
77532176cfdSRui Paulo vap->iv_scanreq_maxdwell,
77632176cfdSRui Paulo vap->iv_scanreq_nssid, vap->iv_scanreq_ssid);
77732176cfdSRui Paulo vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANREQ;
77832176cfdSRui Paulo } else
77932176cfdSRui Paulo ieee80211_check_scan_current(vap);
78032176cfdSRui Paulo break;
78132176cfdSRui Paulo default:
78232176cfdSRui Paulo break;
78332176cfdSRui Paulo }
78432176cfdSRui Paulo break;
78532176cfdSRui Paulo case IEEE80211_S_CAC:
78632176cfdSRui Paulo /*
78732176cfdSRui Paulo * Start CAC on a DFS channel. We come here when starting
78832176cfdSRui Paulo * a bss on a DFS channel (see ieee80211_create_ibss).
78932176cfdSRui Paulo */
79032176cfdSRui Paulo ieee80211_dfs_cac_start(vap);
79132176cfdSRui Paulo break;
79232176cfdSRui Paulo case IEEE80211_S_RUN:
79332176cfdSRui Paulo switch (ostate) {
79432176cfdSRui Paulo case IEEE80211_S_INIT:
79532176cfdSRui Paulo /*
79632176cfdSRui Paulo * Already have a channel; bypass the
79732176cfdSRui Paulo * scan and startup immediately.
79832176cfdSRui Paulo * Note that ieee80211_create_ibss will call
79932176cfdSRui Paulo * back to do a RUN->RUN state change.
80032176cfdSRui Paulo */
80132176cfdSRui Paulo ieee80211_create_ibss(vap,
80232176cfdSRui Paulo ieee80211_ht_adjust_channel(ic,
80332176cfdSRui Paulo ic->ic_curchan, vap->iv_flags_ht));
80432176cfdSRui Paulo /* NB: iv_bss is changed on return */
80532176cfdSRui Paulo break;
80632176cfdSRui Paulo case IEEE80211_S_CAC:
80732176cfdSRui Paulo /*
80832176cfdSRui Paulo * NB: This is the normal state change when CAC
80932176cfdSRui Paulo * expires and no radar was detected; no need to
81032176cfdSRui Paulo * clear the CAC timer as it's already expired.
81132176cfdSRui Paulo */
81232176cfdSRui Paulo /* fall thru... */
81332176cfdSRui Paulo case IEEE80211_S_CSA:
81432176cfdSRui Paulo #if 0
81532176cfdSRui Paulo /*
81632176cfdSRui Paulo * Shorten inactivity timer of associated stations
81732176cfdSRui Paulo * to weed out sta's that don't follow a CSA.
81832176cfdSRui Paulo */
81932176cfdSRui Paulo ieee80211_iterate_nodes(&ic->ic_sta, sta_csa, vap);
82032176cfdSRui Paulo #endif
82132176cfdSRui Paulo /*
82232176cfdSRui Paulo * Update bss node channel to reflect where
82332176cfdSRui Paulo * we landed after CSA.
82432176cfdSRui Paulo */
82532176cfdSRui Paulo ieee80211_node_set_chan(vap->iv_bss,
82632176cfdSRui Paulo ieee80211_ht_adjust_channel(ic, ic->ic_curchan,
82732176cfdSRui Paulo ieee80211_htchanflags(vap->iv_bss->ni_chan)));
82832176cfdSRui Paulo /* XXX bypass debug msgs */
82932176cfdSRui Paulo break;
83032176cfdSRui Paulo case IEEE80211_S_SCAN:
83132176cfdSRui Paulo case IEEE80211_S_RUN:
83232176cfdSRui Paulo #ifdef IEEE80211_DEBUG
83332176cfdSRui Paulo if (ieee80211_msg_debug(vap)) {
83432176cfdSRui Paulo struct ieee80211_node *ni = vap->iv_bss;
83532176cfdSRui Paulo ieee80211_note(vap,
8361e290df3SAntonio Huete Jimenez "synchronized with %s meshid ",
837085ff963SMatthew Dillon ether_sprintf(ni->ni_meshid));
83832176cfdSRui Paulo ieee80211_print_essid(ni->ni_meshid,
83932176cfdSRui Paulo ni->ni_meshidlen);
84032176cfdSRui Paulo /* XXX MCS/HT */
8416168f72eSRui Paulo kprintf(" channel %d\n",
84232176cfdSRui Paulo ieee80211_chan2ieee(ic, ic->ic_curchan));
84332176cfdSRui Paulo }
84432176cfdSRui Paulo #endif
84532176cfdSRui Paulo break;
84632176cfdSRui Paulo default:
84732176cfdSRui Paulo break;
84832176cfdSRui Paulo }
84932176cfdSRui Paulo ieee80211_node_authorize(vap->iv_bss);
85032176cfdSRui Paulo callout_reset(&ms->ms_cleantimer, ms->ms_ppath->mpp_inact,
851085ff963SMatthew Dillon mesh_rt_cleanup_cb, vap);
852085ff963SMatthew Dillon mesh_gatemode_setup(vap);
85332176cfdSRui Paulo break;
85432176cfdSRui Paulo default:
85532176cfdSRui Paulo break;
85632176cfdSRui Paulo }
85732176cfdSRui Paulo /* NB: ostate not nstate */
85832176cfdSRui Paulo ms->ms_ppath->mpp_newstate(vap, ostate, arg);
85932176cfdSRui Paulo return 0;
86032176cfdSRui Paulo }
86132176cfdSRui Paulo
86232176cfdSRui Paulo static void
mesh_rt_cleanup_cb(void * arg)863085ff963SMatthew Dillon mesh_rt_cleanup_cb(void *arg)
86432176cfdSRui Paulo {
86532176cfdSRui Paulo struct ieee80211vap *vap = arg;
86632176cfdSRui Paulo struct ieee80211_mesh_state *ms = vap->iv_mesh;
86732176cfdSRui Paulo
86832176cfdSRui Paulo mesh_rt_flush_invalid(vap);
86932176cfdSRui Paulo callout_reset(&ms->ms_cleantimer, ms->ms_ppath->mpp_inact,
870085ff963SMatthew Dillon mesh_rt_cleanup_cb, vap);
871085ff963SMatthew Dillon }
872085ff963SMatthew Dillon
873085ff963SMatthew Dillon /*
874085ff963SMatthew Dillon * Mark a mesh STA as gate and return a pointer to it.
875085ff963SMatthew Dillon * If this is first time, we create a new gate route.
876085ff963SMatthew Dillon * Always update the path route to this mesh gate.
877085ff963SMatthew Dillon */
878085ff963SMatthew Dillon struct ieee80211_mesh_gate_route *
ieee80211_mesh_mark_gate(struct ieee80211vap * vap,const uint8_t * addr,struct ieee80211_mesh_route * rt)879085ff963SMatthew Dillon ieee80211_mesh_mark_gate(struct ieee80211vap *vap, const uint8_t *addr,
880085ff963SMatthew Dillon struct ieee80211_mesh_route *rt)
881085ff963SMatthew Dillon {
882085ff963SMatthew Dillon struct ieee80211_mesh_state *ms = vap->iv_mesh;
883085ff963SMatthew Dillon struct ieee80211_mesh_gate_route *gr = NULL, *next;
884085ff963SMatthew Dillon int found = 0;
885085ff963SMatthew Dillon
886085ff963SMatthew Dillon MESH_RT_LOCK(ms);
887085ff963SMatthew Dillon TAILQ_FOREACH_SAFE(gr, &ms->ms_known_gates, gr_next, next) {
888085ff963SMatthew Dillon if (IEEE80211_ADDR_EQ(gr->gr_addr, addr)) {
889085ff963SMatthew Dillon found = 1;
890085ff963SMatthew Dillon break;
891085ff963SMatthew Dillon }
892085ff963SMatthew Dillon }
893085ff963SMatthew Dillon
894085ff963SMatthew Dillon if (!found) {
895085ff963SMatthew Dillon /* New mesh gate add it to known table. */
896085ff963SMatthew Dillon IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, addr,
897085ff963SMatthew Dillon "%s", "stored new gate information from pro-PREQ.");
898*4f655ef5SMatthew Dillon #if defined(__DragonFly__)
899085ff963SMatthew Dillon gr = kmalloc(ALIGN(sizeof(struct ieee80211_mesh_gate_route)),
900085ff963SMatthew Dillon M_80211_MESH_GT_RT, M_INTWAIT | M_ZERO);
901*4f655ef5SMatthew Dillon #else
902*4f655ef5SMatthew Dillon gr = IEEE80211_MALLOC(ALIGN(sizeof(struct ieee80211_mesh_gate_route)),
903*4f655ef5SMatthew Dillon M_80211_MESH_GT_RT,
904*4f655ef5SMatthew Dillon IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
905*4f655ef5SMatthew Dillon #endif
906085ff963SMatthew Dillon IEEE80211_ADDR_COPY(gr->gr_addr, addr);
907085ff963SMatthew Dillon TAILQ_INSERT_TAIL(&ms->ms_known_gates, gr, gr_next);
908085ff963SMatthew Dillon }
909085ff963SMatthew Dillon gr->gr_route = rt;
910085ff963SMatthew Dillon /* TODO: link from path route to gate route */
911085ff963SMatthew Dillon MESH_RT_UNLOCK(ms);
912085ff963SMatthew Dillon
913085ff963SMatthew Dillon return gr;
91432176cfdSRui Paulo }
91532176cfdSRui Paulo
91632176cfdSRui Paulo
91732176cfdSRui Paulo /*
91832176cfdSRui Paulo * Helper function to note the Mesh Peer Link FSM change.
91932176cfdSRui Paulo */
92032176cfdSRui Paulo static void
mesh_linkchange(struct ieee80211_node * ni,enum ieee80211_mesh_mlstate state)92132176cfdSRui Paulo mesh_linkchange(struct ieee80211_node *ni, enum ieee80211_mesh_mlstate state)
92232176cfdSRui Paulo {
92332176cfdSRui Paulo struct ieee80211vap *vap = ni->ni_vap;
92432176cfdSRui Paulo struct ieee80211_mesh_state *ms = vap->iv_mesh;
92532176cfdSRui Paulo #ifdef IEEE80211_DEBUG
92632176cfdSRui Paulo static const char *meshlinkstates[] = {
92732176cfdSRui Paulo [IEEE80211_NODE_MESH_IDLE] = "IDLE",
92832176cfdSRui Paulo [IEEE80211_NODE_MESH_OPENSNT] = "OPEN SENT",
92932176cfdSRui Paulo [IEEE80211_NODE_MESH_OPENRCV] = "OPEN RECEIVED",
93032176cfdSRui Paulo [IEEE80211_NODE_MESH_CONFIRMRCV] = "CONFIRM RECEIVED",
93132176cfdSRui Paulo [IEEE80211_NODE_MESH_ESTABLISHED] = "ESTABLISHED",
93232176cfdSRui Paulo [IEEE80211_NODE_MESH_HOLDING] = "HOLDING"
93332176cfdSRui Paulo };
93432176cfdSRui Paulo #endif
93532176cfdSRui Paulo IEEE80211_NOTE(vap, IEEE80211_MSG_MESH,
93632176cfdSRui Paulo ni, "peer link: %s -> %s",
93732176cfdSRui Paulo meshlinkstates[ni->ni_mlstate], meshlinkstates[state]);
93832176cfdSRui Paulo
93932176cfdSRui Paulo /* track neighbor count */
94032176cfdSRui Paulo if (state == IEEE80211_NODE_MESH_ESTABLISHED &&
94132176cfdSRui Paulo ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED) {
94232176cfdSRui Paulo KASSERT(ms->ms_neighbors < 65535, ("neighbor count overflow"));
94332176cfdSRui Paulo ms->ms_neighbors++;
94432176cfdSRui Paulo ieee80211_beacon_notify(vap, IEEE80211_BEACON_MESHCONF);
94532176cfdSRui Paulo } else if (ni->ni_mlstate == IEEE80211_NODE_MESH_ESTABLISHED &&
94632176cfdSRui Paulo state != IEEE80211_NODE_MESH_ESTABLISHED) {
94732176cfdSRui Paulo KASSERT(ms->ms_neighbors > 0, ("neighbor count 0"));
94832176cfdSRui Paulo ms->ms_neighbors--;
94932176cfdSRui Paulo ieee80211_beacon_notify(vap, IEEE80211_BEACON_MESHCONF);
95032176cfdSRui Paulo }
95132176cfdSRui Paulo ni->ni_mlstate = state;
95232176cfdSRui Paulo switch (state) {
95332176cfdSRui Paulo case IEEE80211_NODE_MESH_HOLDING:
95432176cfdSRui Paulo ms->ms_ppath->mpp_peerdown(ni);
95532176cfdSRui Paulo break;
95632176cfdSRui Paulo case IEEE80211_NODE_MESH_ESTABLISHED:
95732176cfdSRui Paulo ieee80211_mesh_discover(vap, ni->ni_macaddr, NULL);
95832176cfdSRui Paulo break;
95932176cfdSRui Paulo default:
96032176cfdSRui Paulo break;
96132176cfdSRui Paulo }
96232176cfdSRui Paulo }
96332176cfdSRui Paulo
96432176cfdSRui Paulo /*
96532176cfdSRui Paulo * Helper function to generate a unique local ID required for mesh
96632176cfdSRui Paulo * peer establishment.
96732176cfdSRui Paulo */
96832176cfdSRui Paulo static void
mesh_checkid(void * arg,struct ieee80211_node * ni)96932176cfdSRui Paulo mesh_checkid(void *arg, struct ieee80211_node *ni)
97032176cfdSRui Paulo {
97132176cfdSRui Paulo uint16_t *r = arg;
97232176cfdSRui Paulo
97332176cfdSRui Paulo if (*r == ni->ni_mllid)
97432176cfdSRui Paulo *(uint16_t *)arg = 0;
97532176cfdSRui Paulo }
97632176cfdSRui Paulo
97732176cfdSRui Paulo static uint32_t
mesh_generateid(struct ieee80211vap * vap)97832176cfdSRui Paulo mesh_generateid(struct ieee80211vap *vap)
97932176cfdSRui Paulo {
98032176cfdSRui Paulo int maxiter = 4;
98132176cfdSRui Paulo uint16_t r;
98232176cfdSRui Paulo
98332176cfdSRui Paulo do {
98432176cfdSRui Paulo get_random_bytes(&r, 2);
98532176cfdSRui Paulo ieee80211_iterate_nodes(&vap->iv_ic->ic_sta, mesh_checkid, &r);
98632176cfdSRui Paulo maxiter--;
98732176cfdSRui Paulo } while (r == 0 && maxiter > 0);
98832176cfdSRui Paulo return r;
98932176cfdSRui Paulo }
99032176cfdSRui Paulo
99132176cfdSRui Paulo /*
99232176cfdSRui Paulo * Verifies if we already received this packet by checking its
99332176cfdSRui Paulo * sequence number.
99432176cfdSRui Paulo * Returns 0 if the frame is to be accepted, 1 otherwise.
99532176cfdSRui Paulo */
99632176cfdSRui Paulo static int
mesh_checkpseq(struct ieee80211vap * vap,const uint8_t source[IEEE80211_ADDR_LEN],uint32_t seq)99732176cfdSRui Paulo mesh_checkpseq(struct ieee80211vap *vap,
99832176cfdSRui Paulo const uint8_t source[IEEE80211_ADDR_LEN], uint32_t seq)
99932176cfdSRui Paulo {
100032176cfdSRui Paulo struct ieee80211_mesh_route *rt;
100132176cfdSRui Paulo
100232176cfdSRui Paulo rt = ieee80211_mesh_rt_find(vap, source);
100332176cfdSRui Paulo if (rt == NULL) {
100432176cfdSRui Paulo rt = ieee80211_mesh_rt_add(vap, source);
100532176cfdSRui Paulo if (rt == NULL) {
100632176cfdSRui Paulo IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, source,
100732176cfdSRui Paulo "%s", "add mcast route failed");
100832176cfdSRui Paulo vap->iv_stats.is_mesh_rtaddfailed++;
100932176cfdSRui Paulo return 1;
101032176cfdSRui Paulo }
101132176cfdSRui Paulo IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, source,
101232176cfdSRui Paulo "add mcast route, mesh seqno %d", seq);
101332176cfdSRui Paulo rt->rt_lastmseq = seq;
101432176cfdSRui Paulo return 0;
101532176cfdSRui Paulo }
101632176cfdSRui Paulo if (IEEE80211_MESH_SEQ_GEQ(rt->rt_lastmseq, seq)) {
101732176cfdSRui Paulo return 1;
101832176cfdSRui Paulo } else {
101932176cfdSRui Paulo rt->rt_lastmseq = seq;
102032176cfdSRui Paulo return 0;
102132176cfdSRui Paulo }
102232176cfdSRui Paulo }
102332176cfdSRui Paulo
102432176cfdSRui Paulo /*
102532176cfdSRui Paulo * Iterate the routing table and locate the next hop.
102632176cfdSRui Paulo */
1027085ff963SMatthew Dillon struct ieee80211_node *
ieee80211_mesh_find_txnode(struct ieee80211vap * vap,const uint8_t dest[IEEE80211_ADDR_LEN])1028085ff963SMatthew Dillon ieee80211_mesh_find_txnode(struct ieee80211vap *vap,
102932176cfdSRui Paulo const uint8_t dest[IEEE80211_ADDR_LEN])
103032176cfdSRui Paulo {
103132176cfdSRui Paulo struct ieee80211_mesh_route *rt;
103232176cfdSRui Paulo
103332176cfdSRui Paulo rt = ieee80211_mesh_rt_find(vap, dest);
103432176cfdSRui Paulo if (rt == NULL)
103532176cfdSRui Paulo return NULL;
1036085ff963SMatthew Dillon if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0) {
103732176cfdSRui Paulo IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, dest,
1038085ff963SMatthew Dillon "%s: !valid, flags 0x%x", __func__, rt->rt_flags);
103932176cfdSRui Paulo /* XXX stat */
104032176cfdSRui Paulo return NULL;
104132176cfdSRui Paulo }
1042085ff963SMatthew Dillon if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY) {
1043085ff963SMatthew Dillon rt = ieee80211_mesh_rt_find(vap, rt->rt_mesh_gate);
1044085ff963SMatthew Dillon if (rt == NULL) return NULL;
1045085ff963SMatthew Dillon if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0) {
1046085ff963SMatthew Dillon IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, dest,
1047085ff963SMatthew Dillon "%s: meshgate !valid, flags 0x%x", __func__,
1048085ff963SMatthew Dillon rt->rt_flags);
1049085ff963SMatthew Dillon /* XXX stat */
1050085ff963SMatthew Dillon return NULL;
1051085ff963SMatthew Dillon }
1052085ff963SMatthew Dillon }
105332176cfdSRui Paulo return ieee80211_find_txnode(vap, rt->rt_nexthop);
105432176cfdSRui Paulo }
105532176cfdSRui Paulo
1056085ff963SMatthew Dillon static void
mesh_transmit_to_gate(struct ieee80211vap * vap,struct mbuf * m,struct ieee80211_mesh_route * rt_gate)1057085ff963SMatthew Dillon mesh_transmit_to_gate(struct ieee80211vap *vap, struct mbuf *m,
1058085ff963SMatthew Dillon struct ieee80211_mesh_route *rt_gate)
1059085ff963SMatthew Dillon {
1060085ff963SMatthew Dillon struct ifnet *ifp = vap->iv_ifp;
1061085ff963SMatthew Dillon struct ieee80211_node *ni;
1062085ff963SMatthew Dillon
1063085ff963SMatthew Dillon IEEE80211_TX_UNLOCK_ASSERT(vap->iv_ic);
1064085ff963SMatthew Dillon
1065085ff963SMatthew Dillon ni = ieee80211_mesh_find_txnode(vap, rt_gate->rt_dest);
1066085ff963SMatthew Dillon if (ni == NULL) {
1067*4f655ef5SMatthew Dillon if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
1068085ff963SMatthew Dillon m_freem(m);
1069085ff963SMatthew Dillon return;
1070085ff963SMatthew Dillon }
1071085ff963SMatthew Dillon
1072085ff963SMatthew Dillon /*
1073085ff963SMatthew Dillon * Send through the VAP packet transmit path.
1074085ff963SMatthew Dillon * This consumes the node ref grabbed above and
1075085ff963SMatthew Dillon * the mbuf, regardless of whether there's a problem
1076085ff963SMatthew Dillon * or not.
1077085ff963SMatthew Dillon */
1078085ff963SMatthew Dillon (void) ieee80211_vap_pkt_send_dest(vap, m, ni);
1079085ff963SMatthew Dillon }
1080085ff963SMatthew Dillon
1081085ff963SMatthew Dillon /*
1082085ff963SMatthew Dillon * Forward the queued frames to known valid mesh gates.
1083085ff963SMatthew Dillon * Assume destination to be outside the MBSS (i.e. proxy entry),
1084085ff963SMatthew Dillon * If no valid mesh gates are known silently discard queued frames.
1085085ff963SMatthew Dillon * After transmitting frames to all known valid mesh gates, this route
1086085ff963SMatthew Dillon * will be marked invalid, and a new path discovery will happen in the hopes
1087085ff963SMatthew Dillon * that (at least) one of the mesh gates have a new proxy entry for us to use.
1088085ff963SMatthew Dillon */
1089085ff963SMatthew Dillon void
ieee80211_mesh_forward_to_gates(struct ieee80211vap * vap,struct ieee80211_mesh_route * rt_dest)1090085ff963SMatthew Dillon ieee80211_mesh_forward_to_gates(struct ieee80211vap *vap,
1091085ff963SMatthew Dillon struct ieee80211_mesh_route *rt_dest)
1092085ff963SMatthew Dillon {
1093085ff963SMatthew Dillon struct ieee80211com *ic = vap->iv_ic;
1094085ff963SMatthew Dillon struct ieee80211_mesh_state *ms = vap->iv_mesh;
1095085ff963SMatthew Dillon struct ieee80211_mesh_route *rt_gate;
1096085ff963SMatthew Dillon struct ieee80211_mesh_gate_route *gr = NULL, *gr_next;
1097085ff963SMatthew Dillon struct mbuf *m, *mcopy, *next;
1098085ff963SMatthew Dillon
1099085ff963SMatthew Dillon IEEE80211_TX_UNLOCK_ASSERT(ic);
1100085ff963SMatthew Dillon
1101085ff963SMatthew Dillon KASSERT( rt_dest->rt_flags == IEEE80211_MESHRT_FLAGS_DISCOVER,
1102085ff963SMatthew Dillon ("Route is not marked with IEEE80211_MESHRT_FLAGS_DISCOVER"));
1103085ff963SMatthew Dillon
1104085ff963SMatthew Dillon /* XXX: send to more than one valid mash gate */
1105085ff963SMatthew Dillon MESH_RT_LOCK(ms);
1106085ff963SMatthew Dillon
1107085ff963SMatthew Dillon m = ieee80211_ageq_remove(&ic->ic_stageq,
1108085ff963SMatthew Dillon (struct ieee80211_node *)(uintptr_t)
1109085ff963SMatthew Dillon ieee80211_mac_hash(ic, rt_dest->rt_dest));
1110085ff963SMatthew Dillon
1111085ff963SMatthew Dillon TAILQ_FOREACH_SAFE(gr, &ms->ms_known_gates, gr_next, gr_next) {
1112085ff963SMatthew Dillon rt_gate = gr->gr_route;
1113085ff963SMatthew Dillon if (rt_gate == NULL) {
1114*4f655ef5SMatthew Dillon #if defined(__DragonFly__)
1115085ff963SMatthew Dillon IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP,
1116085ff963SMatthew Dillon rt_dest->rt_dest,
1117f92fae3fSSascha Wildner "mesh gate with no path %s",
1118f92fae3fSSascha Wildner ether_sprintf(gr->gr_addr));
1119*4f655ef5SMatthew Dillon #else
1120*4f655ef5SMatthew Dillon IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP,
1121*4f655ef5SMatthew Dillon "mesh gate with no path %6D",
1122*4f655ef5SMatthew Dillon gr->gr_addr, ":");
1123*4f655ef5SMatthew Dillon #endif
1124085ff963SMatthew Dillon continue;
1125085ff963SMatthew Dillon }
1126085ff963SMatthew Dillon if ((rt_gate->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0)
1127085ff963SMatthew Dillon continue;
1128085ff963SMatthew Dillon KASSERT(rt_gate->rt_flags & IEEE80211_MESHRT_FLAGS_GATE,
1129085ff963SMatthew Dillon ("route not marked as a mesh gate"));
1130085ff963SMatthew Dillon KASSERT((rt_gate->rt_flags &
1131085ff963SMatthew Dillon IEEE80211_MESHRT_FLAGS_PROXY) == 0,
1132085ff963SMatthew Dillon ("found mesh gate that is also marked porxy"));
1133085ff963SMatthew Dillon /*
1134085ff963SMatthew Dillon * convert route to a proxy route gated by the current
1135085ff963SMatthew Dillon * mesh gate, this is needed so encap can built data
1136085ff963SMatthew Dillon * frame with correct address.
1137085ff963SMatthew Dillon */
1138085ff963SMatthew Dillon rt_dest->rt_flags = IEEE80211_MESHRT_FLAGS_PROXY |
1139085ff963SMatthew Dillon IEEE80211_MESHRT_FLAGS_VALID;
1140085ff963SMatthew Dillon rt_dest->rt_ext_seq = 1; /* random value */
1141085ff963SMatthew Dillon IEEE80211_ADDR_COPY(rt_dest->rt_mesh_gate, rt_gate->rt_dest);
1142085ff963SMatthew Dillon IEEE80211_ADDR_COPY(rt_dest->rt_nexthop, rt_gate->rt_nexthop);
1143085ff963SMatthew Dillon rt_dest->rt_metric = rt_gate->rt_metric;
1144085ff963SMatthew Dillon rt_dest->rt_nhops = rt_gate->rt_nhops;
1145085ff963SMatthew Dillon ieee80211_mesh_rt_update(rt_dest, ms->ms_ppath->mpp_inact);
1146085ff963SMatthew Dillon MESH_RT_UNLOCK(ms);
1147085ff963SMatthew Dillon /* XXX: lock?? */
1148b5523eacSSascha Wildner mcopy = m_dup(m, M_NOWAIT);
1149085ff963SMatthew Dillon for (; mcopy != NULL; mcopy = next) {
1150085ff963SMatthew Dillon next = mcopy->m_nextpkt;
1151085ff963SMatthew Dillon mcopy->m_nextpkt = NULL;
1152085ff963SMatthew Dillon IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP,
1153085ff963SMatthew Dillon rt_dest->rt_dest,
1154085ff963SMatthew Dillon "flush queued frame %p len %d", mcopy,
1155085ff963SMatthew Dillon mcopy->m_pkthdr.len);
1156085ff963SMatthew Dillon mesh_transmit_to_gate(vap, mcopy, rt_gate);
1157085ff963SMatthew Dillon }
1158085ff963SMatthew Dillon MESH_RT_LOCK(ms);
1159085ff963SMatthew Dillon }
1160085ff963SMatthew Dillon rt_dest->rt_flags = 0; /* Mark invalid */
1161085ff963SMatthew Dillon m_freem(m);
1162085ff963SMatthew Dillon MESH_RT_UNLOCK(ms);
1163085ff963SMatthew Dillon }
1164085ff963SMatthew Dillon
116532176cfdSRui Paulo /*
116632176cfdSRui Paulo * Forward the specified frame.
116732176cfdSRui Paulo * Decrement the TTL and set TA to our MAC address.
116832176cfdSRui Paulo */
116932176cfdSRui Paulo static void
mesh_forward(struct ieee80211vap * vap,struct mbuf * m,const struct ieee80211_meshcntl * mc)117032176cfdSRui Paulo mesh_forward(struct ieee80211vap *vap, struct mbuf *m,
117132176cfdSRui Paulo const struct ieee80211_meshcntl *mc)
117232176cfdSRui Paulo {
117332176cfdSRui Paulo struct ieee80211com *ic = vap->iv_ic;
117432176cfdSRui Paulo struct ieee80211_mesh_state *ms = vap->iv_mesh;
117532176cfdSRui Paulo struct ifnet *ifp = vap->iv_ifp;
117632176cfdSRui Paulo const struct ieee80211_frame *wh =
117732176cfdSRui Paulo mtod(m, const struct ieee80211_frame *);
117832176cfdSRui Paulo struct mbuf *mcopy;
117932176cfdSRui Paulo struct ieee80211_meshcntl *mccopy;
118032176cfdSRui Paulo struct ieee80211_frame *whcopy;
118132176cfdSRui Paulo struct ieee80211_node *ni;
118232176cfdSRui Paulo int err;
118332176cfdSRui Paulo
1184085ff963SMatthew Dillon /* This is called from the RX path - don't hold this lock */
1185085ff963SMatthew Dillon IEEE80211_TX_UNLOCK_ASSERT(ic);
1186085ff963SMatthew Dillon
1187085ff963SMatthew Dillon /*
1188*4f655ef5SMatthew Dillon * mesh ttl of 1 means we are the last one receiving it,
1189085ff963SMatthew Dillon * according to amendment we decrement and then check if
1190085ff963SMatthew Dillon * 0, if so we dont forward.
1191085ff963SMatthew Dillon */
1192085ff963SMatthew Dillon if (mc->mc_ttl < 1) {
119332176cfdSRui Paulo IEEE80211_NOTE_FRAME(vap, IEEE80211_MSG_MESH, wh,
1194085ff963SMatthew Dillon "%s", "frame not fwd'd, ttl 1");
119532176cfdSRui Paulo vap->iv_stats.is_mesh_fwd_ttl++;
119632176cfdSRui Paulo return;
119732176cfdSRui Paulo }
119832176cfdSRui Paulo if (!(ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) {
119932176cfdSRui Paulo IEEE80211_NOTE_FRAME(vap, IEEE80211_MSG_MESH, wh,
120032176cfdSRui Paulo "%s", "frame not fwd'd, fwding disabled");
120132176cfdSRui Paulo vap->iv_stats.is_mesh_fwd_disabled++;
120232176cfdSRui Paulo return;
120332176cfdSRui Paulo }
1204b5523eacSSascha Wildner mcopy = m_dup(m, M_NOWAIT);
120532176cfdSRui Paulo if (mcopy == NULL) {
120632176cfdSRui Paulo IEEE80211_NOTE_FRAME(vap, IEEE80211_MSG_MESH, wh,
120732176cfdSRui Paulo "%s", "frame not fwd'd, cannot dup");
120832176cfdSRui Paulo vap->iv_stats.is_mesh_fwd_nobuf++;
1209*4f655ef5SMatthew Dillon if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
121032176cfdSRui Paulo return;
121132176cfdSRui Paulo }
121232176cfdSRui Paulo mcopy = m_pullup(mcopy, ieee80211_hdrspace(ic, wh) +
121332176cfdSRui Paulo sizeof(struct ieee80211_meshcntl));
121432176cfdSRui Paulo if (mcopy == NULL) {
121532176cfdSRui Paulo IEEE80211_NOTE_FRAME(vap, IEEE80211_MSG_MESH, wh,
121632176cfdSRui Paulo "%s", "frame not fwd'd, too short");
121732176cfdSRui Paulo vap->iv_stats.is_mesh_fwd_tooshort++;
1218*4f655ef5SMatthew Dillon if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
121932176cfdSRui Paulo m_freem(mcopy);
122032176cfdSRui Paulo return;
122132176cfdSRui Paulo }
122232176cfdSRui Paulo whcopy = mtod(mcopy, struct ieee80211_frame *);
122332176cfdSRui Paulo mccopy = (struct ieee80211_meshcntl *)
122432176cfdSRui Paulo (mtod(mcopy, uint8_t *) + ieee80211_hdrspace(ic, wh));
122532176cfdSRui Paulo /* XXX clear other bits? */
122632176cfdSRui Paulo whcopy->i_fc[1] &= ~IEEE80211_FC1_RETRY;
122732176cfdSRui Paulo IEEE80211_ADDR_COPY(whcopy->i_addr2, vap->iv_myaddr);
122832176cfdSRui Paulo if (IEEE80211_IS_MULTICAST(wh->i_addr1)) {
122932176cfdSRui Paulo ni = ieee80211_ref_node(vap->iv_bss);
123032176cfdSRui Paulo mcopy->m_flags |= M_MCAST;
123132176cfdSRui Paulo } else {
1232085ff963SMatthew Dillon ni = ieee80211_mesh_find_txnode(vap, whcopy->i_addr3);
123332176cfdSRui Paulo if (ni == NULL) {
1234085ff963SMatthew Dillon /*
1235085ff963SMatthew Dillon * [Optional] any of the following three actions:
1236085ff963SMatthew Dillon * o silently discard
1237085ff963SMatthew Dillon * o trigger a path discovery
1238085ff963SMatthew Dillon * o inform TA that meshDA is unknown.
1239085ff963SMatthew Dillon */
124032176cfdSRui Paulo IEEE80211_NOTE_FRAME(vap, IEEE80211_MSG_MESH, wh,
124132176cfdSRui Paulo "%s", "frame not fwd'd, no path");
1242085ff963SMatthew Dillon ms->ms_ppath->mpp_senderror(vap, whcopy->i_addr3, NULL,
1243085ff963SMatthew Dillon IEEE80211_REASON_MESH_PERR_NO_FI);
124432176cfdSRui Paulo vap->iv_stats.is_mesh_fwd_nopath++;
124532176cfdSRui Paulo m_freem(mcopy);
124632176cfdSRui Paulo return;
124732176cfdSRui Paulo }
124832176cfdSRui Paulo IEEE80211_ADDR_COPY(whcopy->i_addr1, ni->ni_macaddr);
124932176cfdSRui Paulo }
125032176cfdSRui Paulo KASSERT(mccopy->mc_ttl > 0, ("%s called with wrong ttl", __func__));
125132176cfdSRui Paulo mccopy->mc_ttl--;
125232176cfdSRui Paulo
125332176cfdSRui Paulo /* XXX calculate priority so drivers can find the tx queue */
125432176cfdSRui Paulo M_WME_SETAC(mcopy, WME_AC_BE);
125532176cfdSRui Paulo
125632176cfdSRui Paulo /* XXX do we know m_nextpkt is NULL? */
125732176cfdSRui Paulo mcopy->m_pkthdr.rcvif = (void *) ni;
1258085ff963SMatthew Dillon
1259085ff963SMatthew Dillon /*
1260085ff963SMatthew Dillon * XXX this bypasses all of the VAP TX handling; it passes frames
1261085ff963SMatthew Dillon * directly to the parent interface.
1262085ff963SMatthew Dillon *
1263085ff963SMatthew Dillon * Because of this, there's no TX lock being held as there's no
1264085ff963SMatthew Dillon * encaps state being used.
1265085ff963SMatthew Dillon *
1266085ff963SMatthew Dillon * Doing a direct parent transmit may not be the correct thing
1267085ff963SMatthew Dillon * to do here; we'll have to re-think this soon.
1268085ff963SMatthew Dillon */
1269085ff963SMatthew Dillon IEEE80211_TX_LOCK(ic);
1270085ff963SMatthew Dillon err = ieee80211_parent_xmitpkt(ic, mcopy);
1271085ff963SMatthew Dillon IEEE80211_TX_UNLOCK(ic);
1272*4f655ef5SMatthew Dillon if (!err)
1273*4f655ef5SMatthew Dillon if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
127432176cfdSRui Paulo }
127532176cfdSRui Paulo
127632176cfdSRui Paulo static struct mbuf *
mesh_decap(struct ieee80211vap * vap,struct mbuf * m,int hdrlen,int meshdrlen)127732176cfdSRui Paulo mesh_decap(struct ieee80211vap *vap, struct mbuf *m, int hdrlen, int meshdrlen)
127832176cfdSRui Paulo {
127932176cfdSRui Paulo #define WHDIR(wh) ((wh)->i_fc[1] & IEEE80211_FC1_DIR_MASK)
1280085ff963SMatthew Dillon #define MC01(mc) ((const struct ieee80211_meshcntl_ae01 *)mc)
128132176cfdSRui Paulo uint8_t b[sizeof(struct ieee80211_qosframe_addr4) +
1282085ff963SMatthew Dillon sizeof(struct ieee80211_meshcntl_ae10)];
128332176cfdSRui Paulo const struct ieee80211_qosframe_addr4 *wh;
128432176cfdSRui Paulo const struct ieee80211_meshcntl_ae10 *mc;
128532176cfdSRui Paulo struct ether_header *eh;
128632176cfdSRui Paulo struct llc *llc;
128732176cfdSRui Paulo int ae;
128832176cfdSRui Paulo
128932176cfdSRui Paulo if (m->m_len < hdrlen + sizeof(*llc) &&
129032176cfdSRui Paulo (m = m_pullup(m, hdrlen + sizeof(*llc))) == NULL) {
129132176cfdSRui Paulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_ANY,
129232176cfdSRui Paulo "discard data frame: %s", "m_pullup failed");
129332176cfdSRui Paulo vap->iv_stats.is_rx_tooshort++;
129432176cfdSRui Paulo return NULL;
129532176cfdSRui Paulo }
129632176cfdSRui Paulo memcpy(b, mtod(m, caddr_t), hdrlen);
129732176cfdSRui Paulo wh = (const struct ieee80211_qosframe_addr4 *)&b[0];
129832176cfdSRui Paulo mc = (const struct ieee80211_meshcntl_ae10 *)&b[hdrlen - meshdrlen];
129932176cfdSRui Paulo KASSERT(WHDIR(wh) == IEEE80211_FC1_DIR_FROMDS ||
130032176cfdSRui Paulo WHDIR(wh) == IEEE80211_FC1_DIR_DSTODS,
130132176cfdSRui Paulo ("bogus dir, fc 0x%x:0x%x", wh->i_fc[0], wh->i_fc[1]));
130232176cfdSRui Paulo
130332176cfdSRui Paulo llc = (struct llc *)(mtod(m, caddr_t) + hdrlen);
130432176cfdSRui Paulo if (llc->llc_dsap == LLC_SNAP_LSAP && llc->llc_ssap == LLC_SNAP_LSAP &&
130532176cfdSRui Paulo llc->llc_control == LLC_UI && llc->llc_snap.org_code[0] == 0 &&
130632176cfdSRui Paulo llc->llc_snap.org_code[1] == 0 && llc->llc_snap.org_code[2] == 0 &&
1307085ff963SMatthew Dillon /* NB: preserve AppleTalk frames that have a native SNAP hdr */
1308085ff963SMatthew Dillon !(llc->llc_snap.ether_type == htons(ETHERTYPE_AARP) ||
1309085ff963SMatthew Dillon llc->llc_snap.ether_type == htons(ETHERTYPE_IPX))) {
131032176cfdSRui Paulo m_adj(m, hdrlen + sizeof(struct llc) - sizeof(*eh));
131132176cfdSRui Paulo llc = NULL;
131232176cfdSRui Paulo } else {
131332176cfdSRui Paulo m_adj(m, hdrlen - sizeof(*eh));
131432176cfdSRui Paulo }
131532176cfdSRui Paulo eh = mtod(m, struct ether_header *);
1316085ff963SMatthew Dillon ae = mc->mc_flags & IEEE80211_MESH_AE_MASK;
131732176cfdSRui Paulo if (WHDIR(wh) == IEEE80211_FC1_DIR_FROMDS) {
131832176cfdSRui Paulo IEEE80211_ADDR_COPY(eh->ether_dhost, wh->i_addr1);
1319085ff963SMatthew Dillon if (ae == IEEE80211_MESH_AE_00) {
132032176cfdSRui Paulo IEEE80211_ADDR_COPY(eh->ether_shost, wh->i_addr3);
1321085ff963SMatthew Dillon } else if (ae == IEEE80211_MESH_AE_01) {
1322085ff963SMatthew Dillon IEEE80211_ADDR_COPY(eh->ether_shost,
1323085ff963SMatthew Dillon MC01(mc)->mc_addr4);
132432176cfdSRui Paulo } else {
132532176cfdSRui Paulo IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
132632176cfdSRui Paulo (const struct ieee80211_frame *)wh, NULL,
132732176cfdSRui Paulo "bad AE %d", ae);
132832176cfdSRui Paulo vap->iv_stats.is_mesh_badae++;
132932176cfdSRui Paulo m_freem(m);
133032176cfdSRui Paulo return NULL;
133132176cfdSRui Paulo }
133232176cfdSRui Paulo } else {
1333085ff963SMatthew Dillon if (ae == IEEE80211_MESH_AE_00) {
133432176cfdSRui Paulo IEEE80211_ADDR_COPY(eh->ether_dhost, wh->i_addr3);
133532176cfdSRui Paulo IEEE80211_ADDR_COPY(eh->ether_shost, wh->i_addr4);
1336085ff963SMatthew Dillon } else if (ae == IEEE80211_MESH_AE_10) {
1337085ff963SMatthew Dillon IEEE80211_ADDR_COPY(eh->ether_dhost, mc->mc_addr5);
1338085ff963SMatthew Dillon IEEE80211_ADDR_COPY(eh->ether_shost, mc->mc_addr6);
133932176cfdSRui Paulo } else {
134032176cfdSRui Paulo IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
134132176cfdSRui Paulo (const struct ieee80211_frame *)wh, NULL,
134232176cfdSRui Paulo "bad AE %d", ae);
134332176cfdSRui Paulo vap->iv_stats.is_mesh_badae++;
134432176cfdSRui Paulo m_freem(m);
134532176cfdSRui Paulo return NULL;
134632176cfdSRui Paulo }
134732176cfdSRui Paulo }
1348085ff963SMatthew Dillon #ifndef __NO_STRICT_ALIGNMENT
134932176cfdSRui Paulo if (!ALIGNED_POINTER(mtod(m, caddr_t) + sizeof(*eh), uint32_t)) {
135032176cfdSRui Paulo m = ieee80211_realign(vap, m, sizeof(*eh));
135132176cfdSRui Paulo if (m == NULL)
135232176cfdSRui Paulo return NULL;
135332176cfdSRui Paulo }
1354085ff963SMatthew Dillon #endif /* !__NO_STRICT_ALIGNMENT */
135532176cfdSRui Paulo if (llc != NULL) {
135632176cfdSRui Paulo eh = mtod(m, struct ether_header *);
135732176cfdSRui Paulo eh->ether_type = htons(m->m_pkthdr.len - sizeof(*eh));
135832176cfdSRui Paulo }
135932176cfdSRui Paulo return m;
136032176cfdSRui Paulo #undef WDIR
1361085ff963SMatthew Dillon #undef MC01
136232176cfdSRui Paulo }
136332176cfdSRui Paulo
136432176cfdSRui Paulo /*
136532176cfdSRui Paulo * Return non-zero if the unicast mesh data frame should be processed
136632176cfdSRui Paulo * locally. Frames that are not proxy'd have our address, otherwise
136732176cfdSRui Paulo * we need to consult the routing table to look for a proxy entry.
136832176cfdSRui Paulo */
136932176cfdSRui Paulo static __inline int
mesh_isucastforme(struct ieee80211vap * vap,const struct ieee80211_frame * wh,const struct ieee80211_meshcntl * mc)137032176cfdSRui Paulo mesh_isucastforme(struct ieee80211vap *vap, const struct ieee80211_frame *wh,
137132176cfdSRui Paulo const struct ieee80211_meshcntl *mc)
137232176cfdSRui Paulo {
137332176cfdSRui Paulo int ae = mc->mc_flags & 3;
137432176cfdSRui Paulo
137532176cfdSRui Paulo KASSERT((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS,
137632176cfdSRui Paulo ("bad dir 0x%x:0x%x", wh->i_fc[0], wh->i_fc[1]));
1377085ff963SMatthew Dillon KASSERT(ae == IEEE80211_MESH_AE_00 || ae == IEEE80211_MESH_AE_10,
1378085ff963SMatthew Dillon ("bad AE %d", ae));
1379085ff963SMatthew Dillon if (ae == IEEE80211_MESH_AE_10) { /* ucast w/ proxy */
138032176cfdSRui Paulo const struct ieee80211_meshcntl_ae10 *mc10 =
138132176cfdSRui Paulo (const struct ieee80211_meshcntl_ae10 *) mc;
138232176cfdSRui Paulo struct ieee80211_mesh_route *rt =
1383085ff963SMatthew Dillon ieee80211_mesh_rt_find(vap, mc10->mc_addr5);
138432176cfdSRui Paulo /* check for proxy route to ourself */
138532176cfdSRui Paulo return (rt != NULL &&
138632176cfdSRui Paulo (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY));
138732176cfdSRui Paulo } else /* ucast w/o proxy */
138832176cfdSRui Paulo return IEEE80211_ADDR_EQ(wh->i_addr3, vap->iv_myaddr);
138932176cfdSRui Paulo }
139032176cfdSRui Paulo
1391085ff963SMatthew Dillon /*
1392085ff963SMatthew Dillon * Verifies transmitter, updates lifetime, precursor list and forwards data.
1393085ff963SMatthew Dillon * > 0 means we have forwarded data and no need to process locally
1394085ff963SMatthew Dillon * == 0 means we want to process locally (and we may have forwarded data
1395085ff963SMatthew Dillon * < 0 means there was an error and data should be discarded
1396085ff963SMatthew Dillon */
1397085ff963SMatthew Dillon static int
mesh_recv_indiv_data_to_fwrd(struct ieee80211vap * vap,struct mbuf * m,struct ieee80211_frame * wh,const struct ieee80211_meshcntl * mc)1398085ff963SMatthew Dillon mesh_recv_indiv_data_to_fwrd(struct ieee80211vap *vap, struct mbuf *m,
1399085ff963SMatthew Dillon struct ieee80211_frame *wh, const struct ieee80211_meshcntl *mc)
1400085ff963SMatthew Dillon {
1401085ff963SMatthew Dillon struct ieee80211_qosframe_addr4 *qwh;
1402085ff963SMatthew Dillon struct ieee80211_mesh_state *ms = vap->iv_mesh;
1403085ff963SMatthew Dillon struct ieee80211_mesh_route *rt_meshda, *rt_meshsa;
1404085ff963SMatthew Dillon
1405085ff963SMatthew Dillon /* This is called from the RX path - don't hold this lock */
1406085ff963SMatthew Dillon IEEE80211_TX_UNLOCK_ASSERT(vap->iv_ic);
1407085ff963SMatthew Dillon
1408085ff963SMatthew Dillon qwh = (struct ieee80211_qosframe_addr4 *)wh;
1409085ff963SMatthew Dillon
1410085ff963SMatthew Dillon /*
1411085ff963SMatthew Dillon * TODO:
1412085ff963SMatthew Dillon * o verify addr2 is a legitimate transmitter
1413085ff963SMatthew Dillon * o lifetime of precursor of addr3 (addr2) is max(init, curr)
1414085ff963SMatthew Dillon * o lifetime of precursor of addr4 (nexthop) is max(init, curr)
1415085ff963SMatthew Dillon */
1416085ff963SMatthew Dillon
1417085ff963SMatthew Dillon /* set lifetime of addr3 (meshDA) to initial value */
1418085ff963SMatthew Dillon rt_meshda = ieee80211_mesh_rt_find(vap, qwh->i_addr3);
1419085ff963SMatthew Dillon if (rt_meshda == NULL) {
1420*4f655ef5SMatthew Dillon #if defined(__DragonFly__)
1421085ff963SMatthew Dillon IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, qwh->i_addr2,
1422f92fae3fSSascha Wildner "no route to meshDA(%s)", ether_sprintf(qwh->i_addr3));
1423*4f655ef5SMatthew Dillon #else
1424*4f655ef5SMatthew Dillon IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, qwh->i_addr2,
1425*4f655ef5SMatthew Dillon "no route to meshDA(%6D)", qwh->i_addr3, ":");
1426*4f655ef5SMatthew Dillon #endif
1427085ff963SMatthew Dillon /*
1428085ff963SMatthew Dillon * [Optional] any of the following three actions:
1429085ff963SMatthew Dillon * o silently discard [X]
1430085ff963SMatthew Dillon * o trigger a path discovery [ ]
1431085ff963SMatthew Dillon * o inform TA that meshDA is unknown. [ ]
1432085ff963SMatthew Dillon */
1433085ff963SMatthew Dillon /* XXX: stats */
1434085ff963SMatthew Dillon return (-1);
1435085ff963SMatthew Dillon }
1436085ff963SMatthew Dillon
1437085ff963SMatthew Dillon ieee80211_mesh_rt_update(rt_meshda, ticks_to_msecs(
1438085ff963SMatthew Dillon ms->ms_ppath->mpp_inact));
1439085ff963SMatthew Dillon
1440085ff963SMatthew Dillon /* set lifetime of addr4 (meshSA) to initial value */
1441085ff963SMatthew Dillon rt_meshsa = ieee80211_mesh_rt_find(vap, qwh->i_addr4);
1442085ff963SMatthew Dillon KASSERT(rt_meshsa != NULL, ("no route"));
1443085ff963SMatthew Dillon ieee80211_mesh_rt_update(rt_meshsa, ticks_to_msecs(
1444085ff963SMatthew Dillon ms->ms_ppath->mpp_inact));
1445085ff963SMatthew Dillon
1446085ff963SMatthew Dillon mesh_forward(vap, m, mc);
1447085ff963SMatthew Dillon return (1); /* dont process locally */
1448085ff963SMatthew Dillon }
1449085ff963SMatthew Dillon
1450085ff963SMatthew Dillon /*
1451085ff963SMatthew Dillon * Verifies transmitter, updates lifetime, precursor list and process data
1452085ff963SMatthew Dillon * locally, if data is proxy with AE = 10 it could mean data should go
1453085ff963SMatthew Dillon * on another mesh path or data should be forwarded to the DS.
1454085ff963SMatthew Dillon *
1455085ff963SMatthew Dillon * > 0 means we have forwarded data and no need to process locally
1456085ff963SMatthew Dillon * == 0 means we want to process locally (and we may have forwarded data
1457085ff963SMatthew Dillon * < 0 means there was an error and data should be discarded
1458085ff963SMatthew Dillon */
1459085ff963SMatthew Dillon static int
mesh_recv_indiv_data_to_me(struct ieee80211vap * vap,struct mbuf * m,struct ieee80211_frame * wh,const struct ieee80211_meshcntl * mc)1460085ff963SMatthew Dillon mesh_recv_indiv_data_to_me(struct ieee80211vap *vap, struct mbuf *m,
1461085ff963SMatthew Dillon struct ieee80211_frame *wh, const struct ieee80211_meshcntl *mc)
1462085ff963SMatthew Dillon {
1463085ff963SMatthew Dillon struct ieee80211_qosframe_addr4 *qwh;
1464085ff963SMatthew Dillon const struct ieee80211_meshcntl_ae10 *mc10;
1465085ff963SMatthew Dillon struct ieee80211_mesh_state *ms = vap->iv_mesh;
1466085ff963SMatthew Dillon struct ieee80211_mesh_route *rt;
1467085ff963SMatthew Dillon int ae;
1468085ff963SMatthew Dillon
1469085ff963SMatthew Dillon /* This is called from the RX path - don't hold this lock */
1470085ff963SMatthew Dillon IEEE80211_TX_UNLOCK_ASSERT(vap->iv_ic);
1471085ff963SMatthew Dillon
1472085ff963SMatthew Dillon qwh = (struct ieee80211_qosframe_addr4 *)wh;
1473085ff963SMatthew Dillon mc10 = (const struct ieee80211_meshcntl_ae10 *)mc;
1474085ff963SMatthew Dillon
1475085ff963SMatthew Dillon /*
1476085ff963SMatthew Dillon * TODO:
1477085ff963SMatthew Dillon * o verify addr2 is a legitimate transmitter
1478085ff963SMatthew Dillon * o lifetime of precursor entry is max(init, curr)
1479085ff963SMatthew Dillon */
1480085ff963SMatthew Dillon
1481085ff963SMatthew Dillon /* set lifetime of addr4 (meshSA) to initial value */
1482085ff963SMatthew Dillon rt = ieee80211_mesh_rt_find(vap, qwh->i_addr4);
1483085ff963SMatthew Dillon KASSERT(rt != NULL, ("no route"));
1484085ff963SMatthew Dillon ieee80211_mesh_rt_update(rt, ticks_to_msecs(ms->ms_ppath->mpp_inact));
1485085ff963SMatthew Dillon rt = NULL;
1486085ff963SMatthew Dillon
1487085ff963SMatthew Dillon ae = mc10->mc_flags & IEEE80211_MESH_AE_MASK;
1488085ff963SMatthew Dillon KASSERT(ae == IEEE80211_MESH_AE_00 ||
1489085ff963SMatthew Dillon ae == IEEE80211_MESH_AE_10, ("bad AE %d", ae));
1490085ff963SMatthew Dillon if (ae == IEEE80211_MESH_AE_10) {
1491085ff963SMatthew Dillon if (IEEE80211_ADDR_EQ(mc10->mc_addr5, qwh->i_addr3)) {
1492085ff963SMatthew Dillon return (0); /* process locally */
1493085ff963SMatthew Dillon }
1494085ff963SMatthew Dillon
1495085ff963SMatthew Dillon rt = ieee80211_mesh_rt_find(vap, mc10->mc_addr5);
1496085ff963SMatthew Dillon if (rt != NULL &&
1497085ff963SMatthew Dillon (rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) &&
1498085ff963SMatthew Dillon (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY) == 0) {
1499085ff963SMatthew Dillon /*
1500085ff963SMatthew Dillon * Forward on another mesh-path, according to
1501085ff963SMatthew Dillon * amendment as specified in 9.32.4.1
1502085ff963SMatthew Dillon */
1503085ff963SMatthew Dillon IEEE80211_ADDR_COPY(qwh->i_addr3, mc10->mc_addr5);
1504085ff963SMatthew Dillon mesh_forward(vap, m,
1505085ff963SMatthew Dillon (const struct ieee80211_meshcntl *)mc10);
1506085ff963SMatthew Dillon return (1); /* dont process locally */
1507085ff963SMatthew Dillon }
1508085ff963SMatthew Dillon /*
1509085ff963SMatthew Dillon * All other cases: forward of MSDUs from the MBSS to DS indiv.
1510085ff963SMatthew Dillon * addressed according to 13.11.3.2.
1511085ff963SMatthew Dillon */
1512*4f655ef5SMatthew Dillon #if defined(__DragonFly__)
1513085ff963SMatthew Dillon IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_OUTPUT, qwh->i_addr2,
1514f92fae3fSSascha Wildner "forward frame to DS, SA(%s) DA(%s)",
1515f92fae3fSSascha Wildner ether_sprintf(mc10->mc_addr6),
1516f92fae3fSSascha Wildner ether_sprintf(mc10->mc_addr5));
1517*4f655ef5SMatthew Dillon #else
1518*4f655ef5SMatthew Dillon IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_OUTPUT, qwh->i_addr2,
1519*4f655ef5SMatthew Dillon "forward frame to DS, SA(%6D) DA(%6D)",
1520*4f655ef5SMatthew Dillon mc10->mc_addr6, ":", mc10->mc_addr5, ":");
1521*4f655ef5SMatthew Dillon #endif
1522085ff963SMatthew Dillon }
1523085ff963SMatthew Dillon return (0); /* process locally */
1524085ff963SMatthew Dillon }
1525085ff963SMatthew Dillon
1526085ff963SMatthew Dillon /*
1527085ff963SMatthew Dillon * Try to forward the group addressed data on to other mesh STAs, and
1528085ff963SMatthew Dillon * also to the DS.
1529085ff963SMatthew Dillon *
1530085ff963SMatthew Dillon * > 0 means we have forwarded data and no need to process locally
1531085ff963SMatthew Dillon * == 0 means we want to process locally (and we may have forwarded data
1532085ff963SMatthew Dillon * < 0 means there was an error and data should be discarded
1533085ff963SMatthew Dillon */
1534085ff963SMatthew Dillon static int
mesh_recv_group_data(struct ieee80211vap * vap,struct mbuf * m,struct ieee80211_frame * wh,const struct ieee80211_meshcntl * mc)1535085ff963SMatthew Dillon mesh_recv_group_data(struct ieee80211vap *vap, struct mbuf *m,
1536085ff963SMatthew Dillon struct ieee80211_frame *wh, const struct ieee80211_meshcntl *mc)
1537085ff963SMatthew Dillon {
1538085ff963SMatthew Dillon #define MC01(mc) ((const struct ieee80211_meshcntl_ae01 *)mc)
1539085ff963SMatthew Dillon struct ieee80211_mesh_state *ms = vap->iv_mesh;
1540085ff963SMatthew Dillon
1541085ff963SMatthew Dillon /* This is called from the RX path - don't hold this lock */
1542085ff963SMatthew Dillon IEEE80211_TX_UNLOCK_ASSERT(vap->iv_ic);
1543085ff963SMatthew Dillon
1544085ff963SMatthew Dillon mesh_forward(vap, m, mc);
1545085ff963SMatthew Dillon
1546085ff963SMatthew Dillon if(mc->mc_ttl > 0) {
1547085ff963SMatthew Dillon if (mc->mc_flags & IEEE80211_MESH_AE_01) {
1548085ff963SMatthew Dillon /*
1549085ff963SMatthew Dillon * Forward of MSDUs from the MBSS to DS group addressed
1550085ff963SMatthew Dillon * (according to 13.11.3.2)
1551085ff963SMatthew Dillon * This happens by delivering the packet, and a bridge
1552085ff963SMatthew Dillon * will sent it on another port member.
1553085ff963SMatthew Dillon */
1554085ff963SMatthew Dillon if (ms->ms_flags & IEEE80211_MESHFLAGS_GATE &&
1555085ff963SMatthew Dillon ms->ms_flags & IEEE80211_MESHFLAGS_FWD)
1556085ff963SMatthew Dillon IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH,
1557085ff963SMatthew Dillon MC01(mc)->mc_addr4, "%s",
1558085ff963SMatthew Dillon "forward from MBSS to the DS");
1559085ff963SMatthew Dillon }
1560085ff963SMatthew Dillon }
1561085ff963SMatthew Dillon return (0); /* process locally */
1562085ff963SMatthew Dillon #undef MC01
1563085ff963SMatthew Dillon }
1564085ff963SMatthew Dillon
156532176cfdSRui Paulo static int
mesh_input(struct ieee80211_node * ni,struct mbuf * m,const struct ieee80211_rx_stats * rxs,int rssi,int nf)15664f898719SImre Vadász mesh_input(struct ieee80211_node *ni, struct mbuf *m,
15674f898719SImre Vadász const struct ieee80211_rx_stats *rxs, int rssi, int nf)
156832176cfdSRui Paulo {
156932176cfdSRui Paulo #define HAS_SEQ(type) ((type & 0x4) == 0)
1570085ff963SMatthew Dillon #define MC01(mc) ((const struct ieee80211_meshcntl_ae01 *)mc)
1571085ff963SMatthew Dillon #define MC10(mc) ((const struct ieee80211_meshcntl_ae10 *)mc)
157232176cfdSRui Paulo struct ieee80211vap *vap = ni->ni_vap;
157332176cfdSRui Paulo struct ieee80211com *ic = ni->ni_ic;
157432176cfdSRui Paulo struct ifnet *ifp = vap->iv_ifp;
157532176cfdSRui Paulo struct ieee80211_frame *wh;
157632176cfdSRui Paulo const struct ieee80211_meshcntl *mc;
1577085ff963SMatthew Dillon int hdrspace, meshdrlen, need_tap, error;
1578085ff963SMatthew Dillon uint8_t dir, type, subtype, ae;
157932176cfdSRui Paulo uint32_t seq;
1580085ff963SMatthew Dillon const uint8_t *addr;
1581085ff963SMatthew Dillon uint8_t qos[2];
158232176cfdSRui Paulo
158332176cfdSRui Paulo KASSERT(ni != NULL, ("null node"));
158432176cfdSRui Paulo ni->ni_inact = ni->ni_inact_reload;
158532176cfdSRui Paulo
158632176cfdSRui Paulo need_tap = 1; /* mbuf need to be tapped. */
158732176cfdSRui Paulo type = -1; /* undefined */
158832176cfdSRui Paulo
1589085ff963SMatthew Dillon /* This is called from the RX path - don't hold this lock */
1590085ff963SMatthew Dillon IEEE80211_TX_UNLOCK_ASSERT(ic);
1591085ff963SMatthew Dillon
159232176cfdSRui Paulo if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) {
159332176cfdSRui Paulo IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
159432176cfdSRui Paulo ni->ni_macaddr, NULL,
159532176cfdSRui Paulo "too short (1): len %u", m->m_pkthdr.len);
159632176cfdSRui Paulo vap->iv_stats.is_rx_tooshort++;
159732176cfdSRui Paulo goto out;
159832176cfdSRui Paulo }
159932176cfdSRui Paulo /*
160032176cfdSRui Paulo * Bit of a cheat here, we use a pointer for a 3-address
160132176cfdSRui Paulo * frame format but don't reference fields past outside
160232176cfdSRui Paulo * ieee80211_frame_min w/o first validating the data is
160332176cfdSRui Paulo * present.
160432176cfdSRui Paulo */
160532176cfdSRui Paulo wh = mtod(m, struct ieee80211_frame *);
160632176cfdSRui Paulo
160732176cfdSRui Paulo if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
160832176cfdSRui Paulo IEEE80211_FC0_VERSION_0) {
160932176cfdSRui Paulo IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
161032176cfdSRui Paulo ni->ni_macaddr, NULL, "wrong version %x", wh->i_fc[0]);
161132176cfdSRui Paulo vap->iv_stats.is_rx_badversion++;
161232176cfdSRui Paulo goto err;
161332176cfdSRui Paulo }
161432176cfdSRui Paulo dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
161532176cfdSRui Paulo type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
161632176cfdSRui Paulo subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
161732176cfdSRui Paulo if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
161832176cfdSRui Paulo IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi);
161932176cfdSRui Paulo ni->ni_noise = nf;
162032176cfdSRui Paulo if (HAS_SEQ(type)) {
162132176cfdSRui Paulo uint8_t tid = ieee80211_gettid(wh);
162232176cfdSRui Paulo
162332176cfdSRui Paulo if (IEEE80211_QOS_HAS_SEQ(wh) &&
162432176cfdSRui Paulo TID_TO_WME_AC(tid) >= WME_AC_VI)
162532176cfdSRui Paulo ic->ic_wme.wme_hipri_traffic++;
1626*4f655ef5SMatthew Dillon if (! ieee80211_check_rxseq(ni, wh, wh->i_addr1))
162732176cfdSRui Paulo goto out;
162832176cfdSRui Paulo }
162932176cfdSRui Paulo }
163032176cfdSRui Paulo #ifdef IEEE80211_DEBUG
163132176cfdSRui Paulo /*
163232176cfdSRui Paulo * It's easier, but too expensive, to simulate different mesh
163332176cfdSRui Paulo * topologies by consulting the ACL policy very early, so do this
163432176cfdSRui Paulo * only under DEBUG.
163532176cfdSRui Paulo *
163632176cfdSRui Paulo * NB: this check is also done upon peering link initiation.
163732176cfdSRui Paulo */
1638085ff963SMatthew Dillon if (vap->iv_acl != NULL && !vap->iv_acl->iac_check(vap, wh)) {
163932176cfdSRui Paulo IEEE80211_DISCARD(vap, IEEE80211_MSG_ACL,
164032176cfdSRui Paulo wh, NULL, "%s", "disallowed by ACL");
164132176cfdSRui Paulo vap->iv_stats.is_rx_acl++;
164232176cfdSRui Paulo goto out;
164332176cfdSRui Paulo }
164432176cfdSRui Paulo #endif
164532176cfdSRui Paulo switch (type) {
164632176cfdSRui Paulo case IEEE80211_FC0_TYPE_DATA:
164732176cfdSRui Paulo if (ni == vap->iv_bss)
164832176cfdSRui Paulo goto out;
164932176cfdSRui Paulo if (ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED) {
165032176cfdSRui Paulo IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_MESH,
165132176cfdSRui Paulo ni->ni_macaddr, NULL,
165232176cfdSRui Paulo "peer link not yet established (%d)",
165332176cfdSRui Paulo ni->ni_mlstate);
165432176cfdSRui Paulo vap->iv_stats.is_mesh_nolink++;
165532176cfdSRui Paulo goto out;
165632176cfdSRui Paulo }
165732176cfdSRui Paulo if (dir != IEEE80211_FC1_DIR_FROMDS &&
165832176cfdSRui Paulo dir != IEEE80211_FC1_DIR_DSTODS) {
165932176cfdSRui Paulo IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
166032176cfdSRui Paulo wh, "data", "incorrect dir 0x%x", dir);
166132176cfdSRui Paulo vap->iv_stats.is_rx_wrongdir++;
166232176cfdSRui Paulo goto err;
166332176cfdSRui Paulo }
1664085ff963SMatthew Dillon
1665085ff963SMatthew Dillon /* All Mesh data frames are QoS subtype */
1666085ff963SMatthew Dillon if (!HAS_SEQ(type)) {
1667085ff963SMatthew Dillon IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
1668085ff963SMatthew Dillon wh, "data", "incorrect subtype 0x%x", subtype);
1669085ff963SMatthew Dillon vap->iv_stats.is_rx_badsubtype++;
1670085ff963SMatthew Dillon goto err;
1671085ff963SMatthew Dillon }
1672085ff963SMatthew Dillon
1673085ff963SMatthew Dillon /*
1674085ff963SMatthew Dillon * Next up, any fragmentation.
1675085ff963SMatthew Dillon * XXX: we defrag before we even try to forward,
1676085ff963SMatthew Dillon * Mesh Control field is not present in sub-sequent
1677085ff963SMatthew Dillon * fragmented frames. This is in contrast to Draft 4.0.
1678085ff963SMatthew Dillon */
167932176cfdSRui Paulo hdrspace = ieee80211_hdrspace(ic, wh);
1680085ff963SMatthew Dillon if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
1681085ff963SMatthew Dillon m = ieee80211_defrag(ni, m, hdrspace);
1682085ff963SMatthew Dillon if (m == NULL) {
1683085ff963SMatthew Dillon /* Fragment dropped or frame not complete yet */
1684085ff963SMatthew Dillon goto out;
1685085ff963SMatthew Dillon }
1686085ff963SMatthew Dillon }
1687085ff963SMatthew Dillon wh = mtod(m, struct ieee80211_frame *); /* NB: after defrag */
1688085ff963SMatthew Dillon
1689085ff963SMatthew Dillon /*
1690085ff963SMatthew Dillon * Now we have a complete Mesh Data frame.
1691085ff963SMatthew Dillon */
1692085ff963SMatthew Dillon
1693085ff963SMatthew Dillon /*
1694085ff963SMatthew Dillon * Only fromDStoDS data frames use 4 address qos frames
1695085ff963SMatthew Dillon * as specified in amendment. Otherwise addr4 is located
1696085ff963SMatthew Dillon * in the Mesh Control field and a 3 address qos frame
1697085ff963SMatthew Dillon * is used.
1698085ff963SMatthew Dillon */
1699085ff963SMatthew Dillon if (IEEE80211_IS_DSTODS(wh))
1700085ff963SMatthew Dillon *(uint16_t *)qos = *(uint16_t *)
1701085ff963SMatthew Dillon ((struct ieee80211_qosframe_addr4 *)wh)->i_qos;
1702085ff963SMatthew Dillon else
1703085ff963SMatthew Dillon *(uint16_t *)qos = *(uint16_t *)
1704085ff963SMatthew Dillon ((struct ieee80211_qosframe *)wh)->i_qos;
1705085ff963SMatthew Dillon
1706085ff963SMatthew Dillon /*
1707085ff963SMatthew Dillon * NB: The mesh STA sets the Mesh Control Present
1708085ff963SMatthew Dillon * subfield to 1 in the Mesh Data frame containing
1709085ff963SMatthew Dillon * an unfragmented MSDU, an A-MSDU, or the first
1710085ff963SMatthew Dillon * fragment of an MSDU.
1711085ff963SMatthew Dillon * After defrag it should always be present.
1712085ff963SMatthew Dillon */
1713085ff963SMatthew Dillon if (!(qos[1] & IEEE80211_QOS_MC)) {
1714085ff963SMatthew Dillon IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_MESH,
1715085ff963SMatthew Dillon ni->ni_macaddr, NULL,
1716085ff963SMatthew Dillon "%s", "Mesh control field not present");
1717085ff963SMatthew Dillon vap->iv_stats.is_rx_elem_missing++; /* XXX: kinda */
1718085ff963SMatthew Dillon goto err;
1719085ff963SMatthew Dillon }
1720085ff963SMatthew Dillon
1721085ff963SMatthew Dillon /* pull up enough to get to the mesh control */
172232176cfdSRui Paulo if (m->m_len < hdrspace + sizeof(struct ieee80211_meshcntl) &&
172332176cfdSRui Paulo (m = m_pullup(m, hdrspace +
172432176cfdSRui Paulo sizeof(struct ieee80211_meshcntl))) == NULL) {
172532176cfdSRui Paulo IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
172632176cfdSRui Paulo ni->ni_macaddr, NULL,
172732176cfdSRui Paulo "data too short: expecting %u", hdrspace);
172832176cfdSRui Paulo vap->iv_stats.is_rx_tooshort++;
172932176cfdSRui Paulo goto out; /* XXX */
173032176cfdSRui Paulo }
173132176cfdSRui Paulo /*
173232176cfdSRui Paulo * Now calculate the full extent of the headers. Note
173332176cfdSRui Paulo * mesh_decap will pull up anything we didn't get
173432176cfdSRui Paulo * above when it strips the 802.11 headers.
173532176cfdSRui Paulo */
173632176cfdSRui Paulo mc = (const struct ieee80211_meshcntl *)
173732176cfdSRui Paulo (mtod(m, const uint8_t *) + hdrspace);
1738085ff963SMatthew Dillon ae = mc->mc_flags & IEEE80211_MESH_AE_MASK;
173932176cfdSRui Paulo meshdrlen = sizeof(struct ieee80211_meshcntl) +
1740085ff963SMatthew Dillon ae * IEEE80211_ADDR_LEN;
174132176cfdSRui Paulo hdrspace += meshdrlen;
1742085ff963SMatthew Dillon
1743085ff963SMatthew Dillon /* pull complete hdrspace = ieee80211_hdrspace + meshcontrol */
1744085ff963SMatthew Dillon if ((meshdrlen > sizeof(struct ieee80211_meshcntl)) &&
1745085ff963SMatthew Dillon (m->m_len < hdrspace) &&
1746085ff963SMatthew Dillon ((m = m_pullup(m, hdrspace)) == NULL)) {
1747085ff963SMatthew Dillon IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
1748085ff963SMatthew Dillon ni->ni_macaddr, NULL,
1749085ff963SMatthew Dillon "data too short: expecting %u", hdrspace);
1750085ff963SMatthew Dillon vap->iv_stats.is_rx_tooshort++;
1751085ff963SMatthew Dillon goto out; /* XXX */
1752085ff963SMatthew Dillon }
1753085ff963SMatthew Dillon /* XXX: are we sure there is no reallocating after m_pullup? */
1754085ff963SMatthew Dillon
1755*4f655ef5SMatthew Dillon seq = le32dec(mc->mc_seq);
175632176cfdSRui Paulo if (IEEE80211_IS_MULTICAST(wh->i_addr1))
175732176cfdSRui Paulo addr = wh->i_addr3;
1758085ff963SMatthew Dillon else if (ae == IEEE80211_MESH_AE_01)
1759085ff963SMatthew Dillon addr = MC01(mc)->mc_addr4;
176032176cfdSRui Paulo else
176132176cfdSRui Paulo addr = ((struct ieee80211_qosframe_addr4 *)wh)->i_addr4;
176232176cfdSRui Paulo if (IEEE80211_ADDR_EQ(vap->iv_myaddr, addr)) {
176332176cfdSRui Paulo IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
176432176cfdSRui Paulo addr, "data", "%s", "not to me");
176532176cfdSRui Paulo vap->iv_stats.is_rx_wrongbss++; /* XXX kinda */
176632176cfdSRui Paulo goto out;
176732176cfdSRui Paulo }
176832176cfdSRui Paulo if (mesh_checkpseq(vap, addr, seq) != 0) {
176932176cfdSRui Paulo vap->iv_stats.is_rx_dup++;
177032176cfdSRui Paulo goto out;
177132176cfdSRui Paulo }
177232176cfdSRui Paulo
1773085ff963SMatthew Dillon /* This code "routes" the frame to the right control path */
177432176cfdSRui Paulo if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
1775085ff963SMatthew Dillon if (IEEE80211_ADDR_EQ(vap->iv_myaddr, wh->i_addr3))
1776085ff963SMatthew Dillon error =
1777085ff963SMatthew Dillon mesh_recv_indiv_data_to_me(vap, m, wh, mc);
1778085ff963SMatthew Dillon else if (IEEE80211_IS_MULTICAST(wh->i_addr3))
1779085ff963SMatthew Dillon error = mesh_recv_group_data(vap, m, wh, mc);
1780085ff963SMatthew Dillon else
1781085ff963SMatthew Dillon error = mesh_recv_indiv_data_to_fwrd(vap, m,
1782085ff963SMatthew Dillon wh, mc);
1783085ff963SMatthew Dillon } else
1784085ff963SMatthew Dillon error = mesh_recv_group_data(vap, m, wh, mc);
1785085ff963SMatthew Dillon if (error < 0)
1786085ff963SMatthew Dillon goto err;
1787085ff963SMatthew Dillon else if (error > 0)
178832176cfdSRui Paulo goto out;
178932176cfdSRui Paulo
179032176cfdSRui Paulo if (ieee80211_radiotap_active_vap(vap))
179132176cfdSRui Paulo ieee80211_radiotap_rx(vap, m);
179232176cfdSRui Paulo need_tap = 0;
179332176cfdSRui Paulo
179432176cfdSRui Paulo /*
179532176cfdSRui Paulo * Finally, strip the 802.11 header.
179632176cfdSRui Paulo */
179732176cfdSRui Paulo m = mesh_decap(vap, m, hdrspace, meshdrlen);
179832176cfdSRui Paulo if (m == NULL) {
179932176cfdSRui Paulo /* XXX mask bit to check for both */
180032176cfdSRui Paulo /* don't count Null data frames as errors */
180132176cfdSRui Paulo if (subtype == IEEE80211_FC0_SUBTYPE_NODATA ||
180232176cfdSRui Paulo subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL)
180332176cfdSRui Paulo goto out;
180432176cfdSRui Paulo IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
180532176cfdSRui Paulo ni->ni_macaddr, "data", "%s", "decap error");
180632176cfdSRui Paulo vap->iv_stats.is_rx_decap++;
180732176cfdSRui Paulo IEEE80211_NODE_STAT(ni, rx_decap);
180832176cfdSRui Paulo goto err;
180932176cfdSRui Paulo }
1810085ff963SMatthew Dillon if (qos[0] & IEEE80211_QOS_AMSDU) {
181132176cfdSRui Paulo m = ieee80211_decap_amsdu(ni, m);
181232176cfdSRui Paulo if (m == NULL)
181332176cfdSRui Paulo return IEEE80211_FC0_TYPE_DATA;
181432176cfdSRui Paulo }
181532176cfdSRui Paulo ieee80211_deliver_data(vap, ni, m);
181632176cfdSRui Paulo return type;
181732176cfdSRui Paulo case IEEE80211_FC0_TYPE_MGT:
181832176cfdSRui Paulo vap->iv_stats.is_rx_mgmt++;
181932176cfdSRui Paulo IEEE80211_NODE_STAT(ni, rx_mgmt);
182032176cfdSRui Paulo if (dir != IEEE80211_FC1_DIR_NODS) {
182132176cfdSRui Paulo IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
182232176cfdSRui Paulo wh, "mgt", "incorrect dir 0x%x", dir);
182332176cfdSRui Paulo vap->iv_stats.is_rx_wrongdir++;
182432176cfdSRui Paulo goto err;
182532176cfdSRui Paulo }
182632176cfdSRui Paulo if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) {
182732176cfdSRui Paulo IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
182832176cfdSRui Paulo ni->ni_macaddr, "mgt", "too short: len %u",
182932176cfdSRui Paulo m->m_pkthdr.len);
183032176cfdSRui Paulo vap->iv_stats.is_rx_tooshort++;
183132176cfdSRui Paulo goto out;
183232176cfdSRui Paulo }
183332176cfdSRui Paulo #ifdef IEEE80211_DEBUG
183432176cfdSRui Paulo if ((ieee80211_msg_debug(vap) &&
183532176cfdSRui Paulo (vap->iv_ic->ic_flags & IEEE80211_F_SCAN)) ||
183632176cfdSRui Paulo ieee80211_msg_dumppkts(vap)) {
18371e290df3SAntonio Huete Jimenez if_printf(ifp, "received %s from %s rssi %d\n",
1838*4f655ef5SMatthew Dillon ieee80211_mgt_subtype_name(subtype),
1839085ff963SMatthew Dillon ether_sprintf(wh->i_addr2), rssi);
184032176cfdSRui Paulo }
184132176cfdSRui Paulo #endif
1842085ff963SMatthew Dillon if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
184332176cfdSRui Paulo IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
184432176cfdSRui Paulo wh, NULL, "%s", "WEP set but not permitted");
184532176cfdSRui Paulo vap->iv_stats.is_rx_mgtdiscard++; /* XXX */
184632176cfdSRui Paulo goto out;
184732176cfdSRui Paulo }
18484f898719SImre Vadász vap->iv_recv_mgmt(ni, m, subtype, rxs, rssi, nf);
184932176cfdSRui Paulo goto out;
185032176cfdSRui Paulo case IEEE80211_FC0_TYPE_CTL:
185132176cfdSRui Paulo vap->iv_stats.is_rx_ctl++;
185232176cfdSRui Paulo IEEE80211_NODE_STAT(ni, rx_ctrl);
185332176cfdSRui Paulo goto out;
185432176cfdSRui Paulo default:
185532176cfdSRui Paulo IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
185632176cfdSRui Paulo wh, "bad", "frame type 0x%x", type);
185732176cfdSRui Paulo /* should not come here */
185832176cfdSRui Paulo break;
185932176cfdSRui Paulo }
186032176cfdSRui Paulo err:
1861*4f655ef5SMatthew Dillon if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
186232176cfdSRui Paulo out:
186332176cfdSRui Paulo if (m != NULL) {
186432176cfdSRui Paulo if (need_tap && ieee80211_radiotap_active_vap(vap))
186532176cfdSRui Paulo ieee80211_radiotap_rx(vap, m);
186632176cfdSRui Paulo m_freem(m);
186732176cfdSRui Paulo }
186832176cfdSRui Paulo return type;
1869085ff963SMatthew Dillon #undef HAS_SEQ
1870085ff963SMatthew Dillon #undef MC01
1871085ff963SMatthew Dillon #undef MC10
187232176cfdSRui Paulo }
187332176cfdSRui Paulo
187432176cfdSRui Paulo static void
mesh_recv_mgmt(struct ieee80211_node * ni,struct mbuf * m0,int subtype,const struct ieee80211_rx_stats * rxs,int rssi,int nf)187532176cfdSRui Paulo mesh_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, int subtype,
18764f898719SImre Vadász const struct ieee80211_rx_stats *rxs, int rssi, int nf)
187732176cfdSRui Paulo {
187832176cfdSRui Paulo struct ieee80211vap *vap = ni->ni_vap;
187932176cfdSRui Paulo struct ieee80211_mesh_state *ms = vap->iv_mesh;
188032176cfdSRui Paulo struct ieee80211com *ic = ni->ni_ic;
18814f898719SImre Vadász struct ieee80211_channel *rxchan = ic->ic_curchan;
188232176cfdSRui Paulo struct ieee80211_frame *wh;
1883085ff963SMatthew Dillon struct ieee80211_mesh_route *rt;
188432176cfdSRui Paulo uint8_t *frm, *efrm;
188532176cfdSRui Paulo
188632176cfdSRui Paulo wh = mtod(m0, struct ieee80211_frame *);
188732176cfdSRui Paulo frm = (uint8_t *)&wh[1];
188832176cfdSRui Paulo efrm = mtod(m0, uint8_t *) + m0->m_len;
188932176cfdSRui Paulo switch (subtype) {
189032176cfdSRui Paulo case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
189132176cfdSRui Paulo case IEEE80211_FC0_SUBTYPE_BEACON:
189232176cfdSRui Paulo {
189332176cfdSRui Paulo struct ieee80211_scanparams scan;
18944f898719SImre Vadász struct ieee80211_channel *c;
189532176cfdSRui Paulo /*
189632176cfdSRui Paulo * We process beacon/probe response
189732176cfdSRui Paulo * frames to discover neighbors.
189832176cfdSRui Paulo */
18994f898719SImre Vadász if (rxs != NULL) {
19004f898719SImre Vadász c = ieee80211_lookup_channel_rxstatus(vap, rxs);
19014f898719SImre Vadász if (c != NULL)
19024f898719SImre Vadász rxchan = c;
19034f898719SImre Vadász }
19044f898719SImre Vadász if (ieee80211_parse_beacon(ni, m0, rxchan, &scan) != 0)
190532176cfdSRui Paulo return;
190632176cfdSRui Paulo /*
190732176cfdSRui Paulo * Count frame now that we know it's to be processed.
190832176cfdSRui Paulo */
190932176cfdSRui Paulo if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) {
191032176cfdSRui Paulo vap->iv_stats.is_rx_beacon++; /* XXX remove */
191132176cfdSRui Paulo IEEE80211_NODE_STAT(ni, rx_beacons);
191232176cfdSRui Paulo } else
191332176cfdSRui Paulo IEEE80211_NODE_STAT(ni, rx_proberesp);
191432176cfdSRui Paulo /*
191532176cfdSRui Paulo * If scanning, just pass information to the scan module.
191632176cfdSRui Paulo */
191732176cfdSRui Paulo if (ic->ic_flags & IEEE80211_F_SCAN) {
191832176cfdSRui Paulo if (ic->ic_flags_ext & IEEE80211_FEXT_PROBECHAN) {
191932176cfdSRui Paulo /*
192032176cfdSRui Paulo * Actively scanning a channel marked passive;
192132176cfdSRui Paulo * send a probe request now that we know there
192232176cfdSRui Paulo * is 802.11 traffic present.
192332176cfdSRui Paulo *
192432176cfdSRui Paulo * XXX check if the beacon we recv'd gives
192532176cfdSRui Paulo * us what we need and suppress the probe req
192632176cfdSRui Paulo */
192732176cfdSRui Paulo ieee80211_probe_curchan(vap, 1);
192832176cfdSRui Paulo ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN;
192932176cfdSRui Paulo }
19304f898719SImre Vadász ieee80211_add_scan(vap, rxchan, &scan, wh,
193132176cfdSRui Paulo subtype, rssi, nf);
193232176cfdSRui Paulo return;
193332176cfdSRui Paulo }
193432176cfdSRui Paulo
193532176cfdSRui Paulo /* The rest of this code assumes we are running */
193632176cfdSRui Paulo if (vap->iv_state != IEEE80211_S_RUN)
193732176cfdSRui Paulo return;
193832176cfdSRui Paulo /*
193932176cfdSRui Paulo * Ignore non-mesh STAs.
194032176cfdSRui Paulo */
194132176cfdSRui Paulo if ((scan.capinfo &
194232176cfdSRui Paulo (IEEE80211_CAPINFO_ESS|IEEE80211_CAPINFO_IBSS)) ||
194332176cfdSRui Paulo scan.meshid == NULL || scan.meshconf == NULL) {
194432176cfdSRui Paulo IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
194532176cfdSRui Paulo wh, "beacon", "%s", "not a mesh sta");
194632176cfdSRui Paulo vap->iv_stats.is_mesh_wrongmesh++;
194732176cfdSRui Paulo return;
194832176cfdSRui Paulo }
194932176cfdSRui Paulo /*
195032176cfdSRui Paulo * Ignore STAs for other mesh networks.
195132176cfdSRui Paulo */
195232176cfdSRui Paulo if (memcmp(scan.meshid+2, ms->ms_id, ms->ms_idlen) != 0 ||
195332176cfdSRui Paulo mesh_verify_meshconf(vap, scan.meshconf)) {
195432176cfdSRui Paulo IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
195532176cfdSRui Paulo wh, "beacon", "%s", "not for our mesh");
195632176cfdSRui Paulo vap->iv_stats.is_mesh_wrongmesh++;
195732176cfdSRui Paulo return;
195832176cfdSRui Paulo }
195932176cfdSRui Paulo /*
196032176cfdSRui Paulo * Peer only based on the current ACL policy.
196132176cfdSRui Paulo */
1962085ff963SMatthew Dillon if (vap->iv_acl != NULL && !vap->iv_acl->iac_check(vap, wh)) {
196332176cfdSRui Paulo IEEE80211_DISCARD(vap, IEEE80211_MSG_ACL,
196432176cfdSRui Paulo wh, NULL, "%s", "disallowed by ACL");
196532176cfdSRui Paulo vap->iv_stats.is_rx_acl++;
196632176cfdSRui Paulo return;
196732176cfdSRui Paulo }
196832176cfdSRui Paulo /*
196932176cfdSRui Paulo * Do neighbor discovery.
197032176cfdSRui Paulo */
197132176cfdSRui Paulo if (!IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) {
197232176cfdSRui Paulo /*
197332176cfdSRui Paulo * Create a new entry in the neighbor table.
197432176cfdSRui Paulo */
197532176cfdSRui Paulo ni = ieee80211_add_neighbor(vap, wh, &scan);
197632176cfdSRui Paulo }
197732176cfdSRui Paulo /*
197832176cfdSRui Paulo * Automatically peer with discovered nodes if possible.
197932176cfdSRui Paulo */
198032176cfdSRui Paulo if (ni != vap->iv_bss &&
1981085ff963SMatthew Dillon (ms->ms_flags & IEEE80211_MESHFLAGS_AP)) {
1982085ff963SMatthew Dillon switch (ni->ni_mlstate) {
1983085ff963SMatthew Dillon case IEEE80211_NODE_MESH_IDLE:
1984085ff963SMatthew Dillon {
198532176cfdSRui Paulo uint16_t args[1];
198632176cfdSRui Paulo
1987085ff963SMatthew Dillon /* Wait for backoff callout to reset counter */
1988085ff963SMatthew Dillon if (ni->ni_mlhcnt >= ieee80211_mesh_maxholding)
1989085ff963SMatthew Dillon return;
1990085ff963SMatthew Dillon
199132176cfdSRui Paulo ni->ni_mlpid = mesh_generateid(vap);
199232176cfdSRui Paulo if (ni->ni_mlpid == 0)
199332176cfdSRui Paulo return;
199432176cfdSRui Paulo mesh_linkchange(ni, IEEE80211_NODE_MESH_OPENSNT);
199532176cfdSRui Paulo args[0] = ni->ni_mlpid;
199632176cfdSRui Paulo ieee80211_send_action(ni,
1997085ff963SMatthew Dillon IEEE80211_ACTION_CAT_SELF_PROT,
199832176cfdSRui Paulo IEEE80211_ACTION_MESHPEERING_OPEN, args);
199932176cfdSRui Paulo ni->ni_mlrcnt = 0;
200032176cfdSRui Paulo mesh_peer_timeout_setup(ni);
2001085ff963SMatthew Dillon break;
2002085ff963SMatthew Dillon }
2003085ff963SMatthew Dillon case IEEE80211_NODE_MESH_ESTABLISHED:
2004085ff963SMatthew Dillon {
2005085ff963SMatthew Dillon /*
2006085ff963SMatthew Dillon * Valid beacon from a peer mesh STA
2007085ff963SMatthew Dillon * bump TA lifetime
2008085ff963SMatthew Dillon */
2009085ff963SMatthew Dillon rt = ieee80211_mesh_rt_find(vap, wh->i_addr2);
2010085ff963SMatthew Dillon if(rt != NULL) {
2011085ff963SMatthew Dillon ieee80211_mesh_rt_update(rt,
2012085ff963SMatthew Dillon ticks_to_msecs(
2013085ff963SMatthew Dillon ms->ms_ppath->mpp_inact));
2014085ff963SMatthew Dillon }
2015085ff963SMatthew Dillon break;
2016085ff963SMatthew Dillon }
2017085ff963SMatthew Dillon default:
2018085ff963SMatthew Dillon break; /* ignore */
2019085ff963SMatthew Dillon }
202032176cfdSRui Paulo }
202132176cfdSRui Paulo break;
202232176cfdSRui Paulo }
202332176cfdSRui Paulo case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
202432176cfdSRui Paulo {
202532176cfdSRui Paulo uint8_t *ssid, *meshid, *rates, *xrates;
2026085ff963SMatthew Dillon uint8_t *sfrm;
202732176cfdSRui Paulo
202832176cfdSRui Paulo if (vap->iv_state != IEEE80211_S_RUN) {
202932176cfdSRui Paulo IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
203032176cfdSRui Paulo wh, NULL, "wrong state %s",
203132176cfdSRui Paulo ieee80211_state_name[vap->iv_state]);
203232176cfdSRui Paulo vap->iv_stats.is_rx_mgtdiscard++;
203332176cfdSRui Paulo return;
203432176cfdSRui Paulo }
203532176cfdSRui Paulo if (IEEE80211_IS_MULTICAST(wh->i_addr2)) {
203632176cfdSRui Paulo /* frame must be directed */
203732176cfdSRui Paulo IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
203832176cfdSRui Paulo wh, NULL, "%s", "not unicast");
203932176cfdSRui Paulo vap->iv_stats.is_rx_mgtdiscard++; /* XXX stat */
204032176cfdSRui Paulo return;
204132176cfdSRui Paulo }
204232176cfdSRui Paulo /*
204332176cfdSRui Paulo * prreq frame format
204432176cfdSRui Paulo * [tlv] ssid
204532176cfdSRui Paulo * [tlv] supported rates
204632176cfdSRui Paulo * [tlv] extended supported rates
204732176cfdSRui Paulo * [tlv] mesh id
204832176cfdSRui Paulo */
204932176cfdSRui Paulo ssid = meshid = rates = xrates = NULL;
2050085ff963SMatthew Dillon sfrm = frm;
205132176cfdSRui Paulo while (efrm - frm > 1) {
205232176cfdSRui Paulo IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return);
205332176cfdSRui Paulo switch (*frm) {
205432176cfdSRui Paulo case IEEE80211_ELEMID_SSID:
205532176cfdSRui Paulo ssid = frm;
205632176cfdSRui Paulo break;
205732176cfdSRui Paulo case IEEE80211_ELEMID_RATES:
205832176cfdSRui Paulo rates = frm;
205932176cfdSRui Paulo break;
206032176cfdSRui Paulo case IEEE80211_ELEMID_XRATES:
206132176cfdSRui Paulo xrates = frm;
206232176cfdSRui Paulo break;
206332176cfdSRui Paulo case IEEE80211_ELEMID_MESHID:
206432176cfdSRui Paulo meshid = frm;
206532176cfdSRui Paulo break;
206632176cfdSRui Paulo }
2067085ff963SMatthew Dillon frm += frm[1] + 2;
206832176cfdSRui Paulo }
206932176cfdSRui Paulo IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN, return);
207032176cfdSRui Paulo IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE, return);
207132176cfdSRui Paulo if (xrates != NULL)
207232176cfdSRui Paulo IEEE80211_VERIFY_ELEMENT(xrates,
207332176cfdSRui Paulo IEEE80211_RATE_MAXSIZE - rates[1], return);
207432176cfdSRui Paulo if (meshid != NULL) {
207532176cfdSRui Paulo IEEE80211_VERIFY_ELEMENT(meshid,
207632176cfdSRui Paulo IEEE80211_MESHID_LEN, return);
207732176cfdSRui Paulo /* NB: meshid, not ssid */
207832176cfdSRui Paulo IEEE80211_VERIFY_SSID(vap->iv_bss, meshid, return);
207932176cfdSRui Paulo }
208032176cfdSRui Paulo
208132176cfdSRui Paulo /* XXX find a better class or define it's own */
208232176cfdSRui Paulo IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_INPUT, wh->i_addr2,
208332176cfdSRui Paulo "%s", "recv probe req");
208432176cfdSRui Paulo /*
208532176cfdSRui Paulo * Some legacy 11b clients cannot hack a complete
208632176cfdSRui Paulo * probe response frame. When the request includes
208732176cfdSRui Paulo * only a bare-bones rate set, communicate this to
208832176cfdSRui Paulo * the transmit side.
208932176cfdSRui Paulo */
209032176cfdSRui Paulo ieee80211_send_proberesp(vap, wh->i_addr2, 0);
209132176cfdSRui Paulo break;
209232176cfdSRui Paulo }
2093085ff963SMatthew Dillon
209432176cfdSRui Paulo case IEEE80211_FC0_SUBTYPE_ACTION:
2095085ff963SMatthew Dillon case IEEE80211_FC0_SUBTYPE_ACTION_NOACK:
209632176cfdSRui Paulo if (ni == vap->iv_bss) {
2097085ff963SMatthew Dillon IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
209832176cfdSRui Paulo wh, NULL, "%s", "unknown node");
209932176cfdSRui Paulo vap->iv_stats.is_rx_mgtdiscard++;
2100085ff963SMatthew Dillon } else if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, wh->i_addr1) &&
210132176cfdSRui Paulo !IEEE80211_IS_MULTICAST(wh->i_addr1)) {
2102085ff963SMatthew Dillon IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
2103085ff963SMatthew Dillon wh, NULL, "%s", "not for us");
210432176cfdSRui Paulo vap->iv_stats.is_rx_mgtdiscard++;
2105085ff963SMatthew Dillon } else if (vap->iv_state != IEEE80211_S_RUN) {
2106085ff963SMatthew Dillon IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
2107085ff963SMatthew Dillon wh, NULL, "wrong state %s",
2108085ff963SMatthew Dillon ieee80211_state_name[vap->iv_state]);
2109085ff963SMatthew Dillon vap->iv_stats.is_rx_mgtdiscard++;
2110085ff963SMatthew Dillon } else {
211132176cfdSRui Paulo if (ieee80211_parse_action(ni, m0) == 0)
2112085ff963SMatthew Dillon (void)ic->ic_recv_action(ni, wh, frm, efrm);
2113085ff963SMatthew Dillon }
211432176cfdSRui Paulo break;
2115085ff963SMatthew Dillon
211632176cfdSRui Paulo case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
211732176cfdSRui Paulo case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
2118085ff963SMatthew Dillon case IEEE80211_FC0_SUBTYPE_REASSOC_REQ:
211932176cfdSRui Paulo case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
2120*4f655ef5SMatthew Dillon case IEEE80211_FC0_SUBTYPE_TIMING_ADV:
2121085ff963SMatthew Dillon case IEEE80211_FC0_SUBTYPE_ATIM:
212232176cfdSRui Paulo case IEEE80211_FC0_SUBTYPE_DISASSOC:
2123085ff963SMatthew Dillon case IEEE80211_FC0_SUBTYPE_AUTH:
2124085ff963SMatthew Dillon case IEEE80211_FC0_SUBTYPE_DEAUTH:
212532176cfdSRui Paulo IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
212632176cfdSRui Paulo wh, NULL, "%s", "not handled");
212732176cfdSRui Paulo vap->iv_stats.is_rx_mgtdiscard++;
2128085ff963SMatthew Dillon break;
2129085ff963SMatthew Dillon
213032176cfdSRui Paulo default:
213132176cfdSRui Paulo IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
213232176cfdSRui Paulo wh, "mgt", "subtype 0x%x not handled", subtype);
213332176cfdSRui Paulo vap->iv_stats.is_rx_badsubtype++;
213432176cfdSRui Paulo break;
213532176cfdSRui Paulo }
213632176cfdSRui Paulo }
213732176cfdSRui Paulo
2138085ff963SMatthew Dillon static void
mesh_recv_ctl(struct ieee80211_node * ni,struct mbuf * m,int subtype)2139085ff963SMatthew Dillon mesh_recv_ctl(struct ieee80211_node *ni, struct mbuf *m, int subtype)
2140085ff963SMatthew Dillon {
2141085ff963SMatthew Dillon
2142085ff963SMatthew Dillon switch (subtype) {
2143085ff963SMatthew Dillon case IEEE80211_FC0_SUBTYPE_BAR:
2144085ff963SMatthew Dillon ieee80211_recv_bar(ni, m);
2145085ff963SMatthew Dillon break;
2146085ff963SMatthew Dillon }
2147085ff963SMatthew Dillon }
2148085ff963SMatthew Dillon
214932176cfdSRui Paulo /*
2150085ff963SMatthew Dillon * Parse meshpeering action ie's for MPM frames
215132176cfdSRui Paulo */
215232176cfdSRui Paulo static const struct ieee80211_meshpeer_ie *
mesh_parse_meshpeering_action(struct ieee80211_node * ni,const struct ieee80211_frame * wh,const uint8_t * frm,const uint8_t * efrm,struct ieee80211_meshpeer_ie * mp,uint8_t subtype)215332176cfdSRui Paulo mesh_parse_meshpeering_action(struct ieee80211_node *ni,
215432176cfdSRui Paulo const struct ieee80211_frame *wh, /* XXX for VERIFY_LENGTH */
215532176cfdSRui Paulo const uint8_t *frm, const uint8_t *efrm,
215632176cfdSRui Paulo struct ieee80211_meshpeer_ie *mp, uint8_t subtype)
215732176cfdSRui Paulo {
215832176cfdSRui Paulo struct ieee80211vap *vap = ni->ni_vap;
215932176cfdSRui Paulo const struct ieee80211_meshpeer_ie *mpie;
2160085ff963SMatthew Dillon uint16_t args[3];
216132176cfdSRui Paulo const uint8_t *meshid, *meshconf, *meshpeer;
2162085ff963SMatthew Dillon uint8_t sendclose = 0; /* 1 = MPM frame rejected, close will be sent */
216332176cfdSRui Paulo
216432176cfdSRui Paulo meshid = meshconf = meshpeer = NULL;
216532176cfdSRui Paulo while (efrm - frm > 1) {
216632176cfdSRui Paulo IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return NULL);
216732176cfdSRui Paulo switch (*frm) {
216832176cfdSRui Paulo case IEEE80211_ELEMID_MESHID:
216932176cfdSRui Paulo meshid = frm;
217032176cfdSRui Paulo break;
217132176cfdSRui Paulo case IEEE80211_ELEMID_MESHCONF:
217232176cfdSRui Paulo meshconf = frm;
217332176cfdSRui Paulo break;
217432176cfdSRui Paulo case IEEE80211_ELEMID_MESHPEER:
217532176cfdSRui Paulo meshpeer = frm;
217632176cfdSRui Paulo mpie = (const struct ieee80211_meshpeer_ie *) frm;
217732176cfdSRui Paulo memset(mp, 0, sizeof(*mp));
2178085ff963SMatthew Dillon mp->peer_len = mpie->peer_len;
2179*4f655ef5SMatthew Dillon mp->peer_proto = le16dec(&mpie->peer_proto);
2180*4f655ef5SMatthew Dillon mp->peer_llinkid = le16dec(&mpie->peer_llinkid);
2181085ff963SMatthew Dillon switch (subtype) {
2182085ff963SMatthew Dillon case IEEE80211_ACTION_MESHPEERING_CONFIRM:
2183085ff963SMatthew Dillon mp->peer_linkid =
2184*4f655ef5SMatthew Dillon le16dec(&mpie->peer_linkid);
2185085ff963SMatthew Dillon break;
2186085ff963SMatthew Dillon case IEEE80211_ACTION_MESHPEERING_CLOSE:
2187085ff963SMatthew Dillon /* NB: peer link ID is optional */
2188085ff963SMatthew Dillon if (mpie->peer_len ==
2189085ff963SMatthew Dillon (IEEE80211_MPM_BASE_SZ + 2)) {
219032176cfdSRui Paulo mp->peer_linkid = 0;
2191085ff963SMatthew Dillon mp->peer_rcode =
2192*4f655ef5SMatthew Dillon le16dec(&mpie->peer_linkid);
219332176cfdSRui Paulo } else {
2194085ff963SMatthew Dillon mp->peer_linkid =
2195*4f655ef5SMatthew Dillon le16dec(&mpie->peer_linkid);
2196085ff963SMatthew Dillon mp->peer_rcode =
2197*4f655ef5SMatthew Dillon le16dec(&mpie->peer_rcode);
2198085ff963SMatthew Dillon }
2199085ff963SMatthew Dillon break;
220032176cfdSRui Paulo }
220132176cfdSRui Paulo break;
220232176cfdSRui Paulo }
220332176cfdSRui Paulo frm += frm[1] + 2;
220432176cfdSRui Paulo }
220532176cfdSRui Paulo
220632176cfdSRui Paulo /*
2207085ff963SMatthew Dillon * Verify the contents of the frame.
2208085ff963SMatthew Dillon * If it fails validation, close the peer link.
220932176cfdSRui Paulo */
2210085ff963SMatthew Dillon if (mesh_verify_meshpeer(vap, subtype, (const uint8_t *)mp)) {
2211085ff963SMatthew Dillon sendclose = 1;
2212085ff963SMatthew Dillon IEEE80211_DISCARD(vap,
2213085ff963SMatthew Dillon IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH,
2214085ff963SMatthew Dillon wh, NULL, "%s", "MPM validation failed");
2215085ff963SMatthew Dillon }
221632176cfdSRui Paulo
2217085ff963SMatthew Dillon /* If meshid is not the same reject any frames type. */
2218085ff963SMatthew Dillon if (sendclose == 0 && mesh_verify_meshid(vap, meshid)) {
2219085ff963SMatthew Dillon sendclose = 1;
222032176cfdSRui Paulo IEEE80211_DISCARD(vap,
222132176cfdSRui Paulo IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH,
222232176cfdSRui Paulo wh, NULL, "%s", "not for our mesh");
2223085ff963SMatthew Dillon if (subtype == IEEE80211_ACTION_MESHPEERING_CLOSE) {
2224085ff963SMatthew Dillon /*
2225085ff963SMatthew Dillon * Standard not clear about this, if we dont ignore
2226085ff963SMatthew Dillon * there will be an endless loop between nodes sending
2227085ff963SMatthew Dillon * CLOSE frames between each other with wrong meshid.
2228085ff963SMatthew Dillon * Discard and timers will bring FSM to IDLE state.
2229085ff963SMatthew Dillon */
2230085ff963SMatthew Dillon return NULL;
2231085ff963SMatthew Dillon }
2232085ff963SMatthew Dillon }
2233085ff963SMatthew Dillon
2234085ff963SMatthew Dillon /*
2235085ff963SMatthew Dillon * Close frames are accepted if meshid is the same.
2236085ff963SMatthew Dillon * Verify the other two types.
2237085ff963SMatthew Dillon */
2238085ff963SMatthew Dillon if (sendclose == 0 && subtype != IEEE80211_ACTION_MESHPEERING_CLOSE &&
2239085ff963SMatthew Dillon mesh_verify_meshconf(vap, meshconf)) {
2240085ff963SMatthew Dillon sendclose = 1;
2241085ff963SMatthew Dillon IEEE80211_DISCARD(vap,
2242085ff963SMatthew Dillon IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH,
2243085ff963SMatthew Dillon wh, NULL, "%s", "configuration missmatch");
2244085ff963SMatthew Dillon }
2245085ff963SMatthew Dillon
2246085ff963SMatthew Dillon if (sendclose) {
224732176cfdSRui Paulo vap->iv_stats.is_rx_mgtdiscard++;
224832176cfdSRui Paulo switch (ni->ni_mlstate) {
224932176cfdSRui Paulo case IEEE80211_NODE_MESH_IDLE:
225032176cfdSRui Paulo case IEEE80211_NODE_MESH_ESTABLISHED:
225132176cfdSRui Paulo case IEEE80211_NODE_MESH_HOLDING:
225232176cfdSRui Paulo /* ignore */
225332176cfdSRui Paulo break;
225432176cfdSRui Paulo case IEEE80211_NODE_MESH_OPENSNT:
225532176cfdSRui Paulo case IEEE80211_NODE_MESH_OPENRCV:
225632176cfdSRui Paulo case IEEE80211_NODE_MESH_CONFIRMRCV:
225732176cfdSRui Paulo args[0] = ni->ni_mlpid;
225832176cfdSRui Paulo args[1] = ni->ni_mllid;
2259085ff963SMatthew Dillon /* Reason codes for rejection */
2260085ff963SMatthew Dillon switch (subtype) {
2261085ff963SMatthew Dillon case IEEE80211_ACTION_MESHPEERING_OPEN:
2262085ff963SMatthew Dillon args[2] = IEEE80211_REASON_MESH_CPVIOLATION;
2263085ff963SMatthew Dillon break;
2264085ff963SMatthew Dillon case IEEE80211_ACTION_MESHPEERING_CONFIRM:
2265085ff963SMatthew Dillon args[2] = IEEE80211_REASON_MESH_INCONS_PARAMS;
2266085ff963SMatthew Dillon break;
2267085ff963SMatthew Dillon }
226832176cfdSRui Paulo ieee80211_send_action(ni,
2269085ff963SMatthew Dillon IEEE80211_ACTION_CAT_SELF_PROT,
227032176cfdSRui Paulo IEEE80211_ACTION_MESHPEERING_CLOSE,
227132176cfdSRui Paulo args);
227232176cfdSRui Paulo mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING);
227332176cfdSRui Paulo mesh_peer_timeout_setup(ni);
227432176cfdSRui Paulo break;
227532176cfdSRui Paulo }
227632176cfdSRui Paulo return NULL;
227732176cfdSRui Paulo }
2278085ff963SMatthew Dillon
227932176cfdSRui Paulo return (const struct ieee80211_meshpeer_ie *) mp;
228032176cfdSRui Paulo }
228132176cfdSRui Paulo
228232176cfdSRui Paulo static int
mesh_recv_action_meshpeering_open(struct ieee80211_node * ni,const struct ieee80211_frame * wh,const uint8_t * frm,const uint8_t * efrm)228332176cfdSRui Paulo mesh_recv_action_meshpeering_open(struct ieee80211_node *ni,
228432176cfdSRui Paulo const struct ieee80211_frame *wh,
228532176cfdSRui Paulo const uint8_t *frm, const uint8_t *efrm)
228632176cfdSRui Paulo {
228732176cfdSRui Paulo struct ieee80211vap *vap = ni->ni_vap;
2288085ff963SMatthew Dillon struct ieee80211_mesh_state *ms = vap->iv_mesh;
228932176cfdSRui Paulo struct ieee80211_meshpeer_ie ie;
229032176cfdSRui Paulo const struct ieee80211_meshpeer_ie *meshpeer;
229132176cfdSRui Paulo uint16_t args[3];
229232176cfdSRui Paulo
229332176cfdSRui Paulo /* +2+2 for action + code + capabilites */
229432176cfdSRui Paulo meshpeer = mesh_parse_meshpeering_action(ni, wh, frm+2+2, efrm, &ie,
229532176cfdSRui Paulo IEEE80211_ACTION_MESHPEERING_OPEN);
229632176cfdSRui Paulo if (meshpeer == NULL) {
229732176cfdSRui Paulo return 0;
229832176cfdSRui Paulo }
229932176cfdSRui Paulo
230032176cfdSRui Paulo /* XXX move up */
230132176cfdSRui Paulo IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, ni,
230232176cfdSRui Paulo "recv PEER OPEN, lid 0x%x", meshpeer->peer_llinkid);
230332176cfdSRui Paulo
230432176cfdSRui Paulo switch (ni->ni_mlstate) {
230532176cfdSRui Paulo case IEEE80211_NODE_MESH_IDLE:
2306085ff963SMatthew Dillon /* Reject open request if reached our maximum neighbor count */
2307085ff963SMatthew Dillon if (ms->ms_neighbors >= IEEE80211_MESH_MAX_NEIGHBORS) {
2308085ff963SMatthew Dillon args[0] = meshpeer->peer_llinkid;
2309085ff963SMatthew Dillon args[1] = 0;
2310085ff963SMatthew Dillon args[2] = IEEE80211_REASON_MESH_MAX_PEERS;
2311085ff963SMatthew Dillon ieee80211_send_action(ni,
2312085ff963SMatthew Dillon IEEE80211_ACTION_CAT_SELF_PROT,
2313085ff963SMatthew Dillon IEEE80211_ACTION_MESHPEERING_CLOSE,
2314085ff963SMatthew Dillon args);
2315085ff963SMatthew Dillon /* stay in IDLE state */
2316085ff963SMatthew Dillon return (0);
2317085ff963SMatthew Dillon }
2318085ff963SMatthew Dillon /* Open frame accepted */
231932176cfdSRui Paulo mesh_linkchange(ni, IEEE80211_NODE_MESH_OPENRCV);
232032176cfdSRui Paulo ni->ni_mllid = meshpeer->peer_llinkid;
232132176cfdSRui Paulo ni->ni_mlpid = mesh_generateid(vap);
232232176cfdSRui Paulo if (ni->ni_mlpid == 0)
232332176cfdSRui Paulo return 0; /* XXX */
232432176cfdSRui Paulo args[0] = ni->ni_mlpid;
232532176cfdSRui Paulo /* Announce we're open too... */
232632176cfdSRui Paulo ieee80211_send_action(ni,
2327085ff963SMatthew Dillon IEEE80211_ACTION_CAT_SELF_PROT,
232832176cfdSRui Paulo IEEE80211_ACTION_MESHPEERING_OPEN, args);
232932176cfdSRui Paulo /* ...and confirm the link. */
233032176cfdSRui Paulo args[0] = ni->ni_mlpid;
233132176cfdSRui Paulo args[1] = ni->ni_mllid;
233232176cfdSRui Paulo ieee80211_send_action(ni,
2333085ff963SMatthew Dillon IEEE80211_ACTION_CAT_SELF_PROT,
233432176cfdSRui Paulo IEEE80211_ACTION_MESHPEERING_CONFIRM,
233532176cfdSRui Paulo args);
233632176cfdSRui Paulo mesh_peer_timeout_setup(ni);
233732176cfdSRui Paulo break;
233832176cfdSRui Paulo case IEEE80211_NODE_MESH_OPENRCV:
233932176cfdSRui Paulo /* Wrong Link ID */
234032176cfdSRui Paulo if (ni->ni_mllid != meshpeer->peer_llinkid) {
234132176cfdSRui Paulo args[0] = ni->ni_mllid;
234232176cfdSRui Paulo args[1] = ni->ni_mlpid;
234332176cfdSRui Paulo args[2] = IEEE80211_REASON_PEER_LINK_CANCELED;
234432176cfdSRui Paulo ieee80211_send_action(ni,
2345085ff963SMatthew Dillon IEEE80211_ACTION_CAT_SELF_PROT,
234632176cfdSRui Paulo IEEE80211_ACTION_MESHPEERING_CLOSE,
234732176cfdSRui Paulo args);
234832176cfdSRui Paulo mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING);
234932176cfdSRui Paulo mesh_peer_timeout_setup(ni);
235032176cfdSRui Paulo break;
235132176cfdSRui Paulo }
235232176cfdSRui Paulo /* Duplicate open, confirm again. */
235332176cfdSRui Paulo args[0] = ni->ni_mlpid;
235432176cfdSRui Paulo args[1] = ni->ni_mllid;
235532176cfdSRui Paulo ieee80211_send_action(ni,
2356085ff963SMatthew Dillon IEEE80211_ACTION_CAT_SELF_PROT,
235732176cfdSRui Paulo IEEE80211_ACTION_MESHPEERING_CONFIRM,
235832176cfdSRui Paulo args);
235932176cfdSRui Paulo break;
236032176cfdSRui Paulo case IEEE80211_NODE_MESH_OPENSNT:
236132176cfdSRui Paulo ni->ni_mllid = meshpeer->peer_llinkid;
236232176cfdSRui Paulo mesh_linkchange(ni, IEEE80211_NODE_MESH_OPENRCV);
236332176cfdSRui Paulo args[0] = ni->ni_mlpid;
236432176cfdSRui Paulo args[1] = ni->ni_mllid;
236532176cfdSRui Paulo ieee80211_send_action(ni,
2366085ff963SMatthew Dillon IEEE80211_ACTION_CAT_SELF_PROT,
236732176cfdSRui Paulo IEEE80211_ACTION_MESHPEERING_CONFIRM,
236832176cfdSRui Paulo args);
236932176cfdSRui Paulo /* NB: don't setup/clear any timeout */
237032176cfdSRui Paulo break;
237132176cfdSRui Paulo case IEEE80211_NODE_MESH_CONFIRMRCV:
237232176cfdSRui Paulo if (ni->ni_mlpid != meshpeer->peer_linkid ||
237332176cfdSRui Paulo ni->ni_mllid != meshpeer->peer_llinkid) {
237432176cfdSRui Paulo args[0] = ni->ni_mlpid;
237532176cfdSRui Paulo args[1] = ni->ni_mllid;
237632176cfdSRui Paulo args[2] = IEEE80211_REASON_PEER_LINK_CANCELED;
237732176cfdSRui Paulo ieee80211_send_action(ni,
2378085ff963SMatthew Dillon IEEE80211_ACTION_CAT_SELF_PROT,
237932176cfdSRui Paulo IEEE80211_ACTION_MESHPEERING_CLOSE,
238032176cfdSRui Paulo args);
238132176cfdSRui Paulo mesh_linkchange(ni,
238232176cfdSRui Paulo IEEE80211_NODE_MESH_HOLDING);
238332176cfdSRui Paulo mesh_peer_timeout_setup(ni);
238432176cfdSRui Paulo break;
238532176cfdSRui Paulo }
238632176cfdSRui Paulo mesh_linkchange(ni, IEEE80211_NODE_MESH_ESTABLISHED);
238732176cfdSRui Paulo ni->ni_mllid = meshpeer->peer_llinkid;
238832176cfdSRui Paulo args[0] = ni->ni_mlpid;
238932176cfdSRui Paulo args[1] = ni->ni_mllid;
239032176cfdSRui Paulo ieee80211_send_action(ni,
2391085ff963SMatthew Dillon IEEE80211_ACTION_CAT_SELF_PROT,
239232176cfdSRui Paulo IEEE80211_ACTION_MESHPEERING_CONFIRM,
239332176cfdSRui Paulo args);
239432176cfdSRui Paulo mesh_peer_timeout_stop(ni);
239532176cfdSRui Paulo break;
239632176cfdSRui Paulo case IEEE80211_NODE_MESH_ESTABLISHED:
239732176cfdSRui Paulo if (ni->ni_mllid != meshpeer->peer_llinkid) {
239832176cfdSRui Paulo args[0] = ni->ni_mllid;
239932176cfdSRui Paulo args[1] = ni->ni_mlpid;
240032176cfdSRui Paulo args[2] = IEEE80211_REASON_PEER_LINK_CANCELED;
240132176cfdSRui Paulo ieee80211_send_action(ni,
2402085ff963SMatthew Dillon IEEE80211_ACTION_CAT_SELF_PROT,
240332176cfdSRui Paulo IEEE80211_ACTION_MESHPEERING_CLOSE,
240432176cfdSRui Paulo args);
240532176cfdSRui Paulo mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING);
240632176cfdSRui Paulo mesh_peer_timeout_setup(ni);
240732176cfdSRui Paulo break;
240832176cfdSRui Paulo }
240932176cfdSRui Paulo args[0] = ni->ni_mlpid;
241032176cfdSRui Paulo args[1] = ni->ni_mllid;
241132176cfdSRui Paulo ieee80211_send_action(ni,
2412085ff963SMatthew Dillon IEEE80211_ACTION_CAT_SELF_PROT,
241332176cfdSRui Paulo IEEE80211_ACTION_MESHPEERING_CONFIRM,
241432176cfdSRui Paulo args);
241532176cfdSRui Paulo break;
241632176cfdSRui Paulo case IEEE80211_NODE_MESH_HOLDING:
241732176cfdSRui Paulo args[0] = ni->ni_mlpid;
241832176cfdSRui Paulo args[1] = meshpeer->peer_llinkid;
2419085ff963SMatthew Dillon /* Standard not clear about what the reaason code should be */
2420085ff963SMatthew Dillon args[2] = IEEE80211_REASON_PEER_LINK_CANCELED;
242132176cfdSRui Paulo ieee80211_send_action(ni,
2422085ff963SMatthew Dillon IEEE80211_ACTION_CAT_SELF_PROT,
242332176cfdSRui Paulo IEEE80211_ACTION_MESHPEERING_CLOSE,
242432176cfdSRui Paulo args);
242532176cfdSRui Paulo break;
242632176cfdSRui Paulo }
242732176cfdSRui Paulo return 0;
242832176cfdSRui Paulo }
242932176cfdSRui Paulo
243032176cfdSRui Paulo static int
mesh_recv_action_meshpeering_confirm(struct ieee80211_node * ni,const struct ieee80211_frame * wh,const uint8_t * frm,const uint8_t * efrm)243132176cfdSRui Paulo mesh_recv_action_meshpeering_confirm(struct ieee80211_node *ni,
243232176cfdSRui Paulo const struct ieee80211_frame *wh,
243332176cfdSRui Paulo const uint8_t *frm, const uint8_t *efrm)
243432176cfdSRui Paulo {
243532176cfdSRui Paulo struct ieee80211vap *vap = ni->ni_vap;
243632176cfdSRui Paulo struct ieee80211_meshpeer_ie ie;
243732176cfdSRui Paulo const struct ieee80211_meshpeer_ie *meshpeer;
243832176cfdSRui Paulo uint16_t args[3];
243932176cfdSRui Paulo
244032176cfdSRui Paulo /* +2+2+2+2 for action + code + capabilites + status code + AID */
244132176cfdSRui Paulo meshpeer = mesh_parse_meshpeering_action(ni, wh, frm+2+2+2+2, efrm, &ie,
244232176cfdSRui Paulo IEEE80211_ACTION_MESHPEERING_CONFIRM);
244332176cfdSRui Paulo if (meshpeer == NULL) {
244432176cfdSRui Paulo return 0;
244532176cfdSRui Paulo }
244632176cfdSRui Paulo
244732176cfdSRui Paulo IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, ni,
244832176cfdSRui Paulo "recv PEER CONFIRM, local id 0x%x, peer id 0x%x",
244932176cfdSRui Paulo meshpeer->peer_llinkid, meshpeer->peer_linkid);
245032176cfdSRui Paulo
245132176cfdSRui Paulo switch (ni->ni_mlstate) {
245232176cfdSRui Paulo case IEEE80211_NODE_MESH_OPENRCV:
245332176cfdSRui Paulo mesh_linkchange(ni, IEEE80211_NODE_MESH_ESTABLISHED);
245432176cfdSRui Paulo mesh_peer_timeout_stop(ni);
245532176cfdSRui Paulo break;
245632176cfdSRui Paulo case IEEE80211_NODE_MESH_OPENSNT:
245732176cfdSRui Paulo mesh_linkchange(ni, IEEE80211_NODE_MESH_CONFIRMRCV);
2458085ff963SMatthew Dillon mesh_peer_timeout_setup(ni);
245932176cfdSRui Paulo break;
246032176cfdSRui Paulo case IEEE80211_NODE_MESH_HOLDING:
246132176cfdSRui Paulo args[0] = ni->ni_mlpid;
246232176cfdSRui Paulo args[1] = meshpeer->peer_llinkid;
2463085ff963SMatthew Dillon /* Standard not clear about what the reaason code should be */
2464085ff963SMatthew Dillon args[2] = IEEE80211_REASON_PEER_LINK_CANCELED;
246532176cfdSRui Paulo ieee80211_send_action(ni,
2466085ff963SMatthew Dillon IEEE80211_ACTION_CAT_SELF_PROT,
246732176cfdSRui Paulo IEEE80211_ACTION_MESHPEERING_CLOSE,
246832176cfdSRui Paulo args);
246932176cfdSRui Paulo break;
247032176cfdSRui Paulo case IEEE80211_NODE_MESH_CONFIRMRCV:
247132176cfdSRui Paulo if (ni->ni_mllid != meshpeer->peer_llinkid) {
247232176cfdSRui Paulo args[0] = ni->ni_mlpid;
247332176cfdSRui Paulo args[1] = ni->ni_mllid;
247432176cfdSRui Paulo args[2] = IEEE80211_REASON_PEER_LINK_CANCELED;
247532176cfdSRui Paulo ieee80211_send_action(ni,
2476085ff963SMatthew Dillon IEEE80211_ACTION_CAT_SELF_PROT,
247732176cfdSRui Paulo IEEE80211_ACTION_MESHPEERING_CLOSE,
247832176cfdSRui Paulo args);
247932176cfdSRui Paulo mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING);
248032176cfdSRui Paulo mesh_peer_timeout_setup(ni);
248132176cfdSRui Paulo }
248232176cfdSRui Paulo break;
248332176cfdSRui Paulo default:
248432176cfdSRui Paulo IEEE80211_DISCARD(vap,
248532176cfdSRui Paulo IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH,
248632176cfdSRui Paulo wh, NULL, "received confirm in invalid state %d",
248732176cfdSRui Paulo ni->ni_mlstate);
248832176cfdSRui Paulo vap->iv_stats.is_rx_mgtdiscard++;
248932176cfdSRui Paulo break;
249032176cfdSRui Paulo }
249132176cfdSRui Paulo return 0;
249232176cfdSRui Paulo }
249332176cfdSRui Paulo
249432176cfdSRui Paulo static int
mesh_recv_action_meshpeering_close(struct ieee80211_node * ni,const struct ieee80211_frame * wh,const uint8_t * frm,const uint8_t * efrm)249532176cfdSRui Paulo mesh_recv_action_meshpeering_close(struct ieee80211_node *ni,
249632176cfdSRui Paulo const struct ieee80211_frame *wh,
249732176cfdSRui Paulo const uint8_t *frm, const uint8_t *efrm)
249832176cfdSRui Paulo {
2499085ff963SMatthew Dillon struct ieee80211_meshpeer_ie ie;
2500085ff963SMatthew Dillon const struct ieee80211_meshpeer_ie *meshpeer;
250132176cfdSRui Paulo uint16_t args[3];
250232176cfdSRui Paulo
2503085ff963SMatthew Dillon /* +2 for action + code */
2504085ff963SMatthew Dillon meshpeer = mesh_parse_meshpeering_action(ni, wh, frm+2, efrm, &ie,
2505085ff963SMatthew Dillon IEEE80211_ACTION_MESHPEERING_CLOSE);
2506085ff963SMatthew Dillon if (meshpeer == NULL) {
2507085ff963SMatthew Dillon return 0;
2508085ff963SMatthew Dillon }
2509085ff963SMatthew Dillon
2510085ff963SMatthew Dillon /*
2511085ff963SMatthew Dillon * XXX: check reason code, for example we could receive
2512085ff963SMatthew Dillon * IEEE80211_REASON_MESH_MAX_PEERS then we should not attempt
2513085ff963SMatthew Dillon * to peer again.
2514085ff963SMatthew Dillon */
2515085ff963SMatthew Dillon
251632176cfdSRui Paulo IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH,
251732176cfdSRui Paulo ni, "%s", "recv PEER CLOSE");
251832176cfdSRui Paulo
251932176cfdSRui Paulo switch (ni->ni_mlstate) {
252032176cfdSRui Paulo case IEEE80211_NODE_MESH_IDLE:
252132176cfdSRui Paulo /* ignore */
252232176cfdSRui Paulo break;
252332176cfdSRui Paulo case IEEE80211_NODE_MESH_OPENRCV:
252432176cfdSRui Paulo case IEEE80211_NODE_MESH_OPENSNT:
252532176cfdSRui Paulo case IEEE80211_NODE_MESH_CONFIRMRCV:
252632176cfdSRui Paulo case IEEE80211_NODE_MESH_ESTABLISHED:
252732176cfdSRui Paulo args[0] = ni->ni_mlpid;
252832176cfdSRui Paulo args[1] = ni->ni_mllid;
252932176cfdSRui Paulo args[2] = IEEE80211_REASON_MESH_CLOSE_RCVD;
253032176cfdSRui Paulo ieee80211_send_action(ni,
2531085ff963SMatthew Dillon IEEE80211_ACTION_CAT_SELF_PROT,
253232176cfdSRui Paulo IEEE80211_ACTION_MESHPEERING_CLOSE,
253332176cfdSRui Paulo args);
253432176cfdSRui Paulo mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING);
253532176cfdSRui Paulo mesh_peer_timeout_setup(ni);
253632176cfdSRui Paulo break;
253732176cfdSRui Paulo case IEEE80211_NODE_MESH_HOLDING:
253832176cfdSRui Paulo mesh_linkchange(ni, IEEE80211_NODE_MESH_IDLE);
2539085ff963SMatthew Dillon mesh_peer_timeout_stop(ni);
254032176cfdSRui Paulo break;
254132176cfdSRui Paulo }
254232176cfdSRui Paulo return 0;
254332176cfdSRui Paulo }
254432176cfdSRui Paulo
254532176cfdSRui Paulo /*
254632176cfdSRui Paulo * Link Metric handling.
254732176cfdSRui Paulo */
254832176cfdSRui Paulo static int
mesh_recv_action_meshlmetric(struct ieee80211_node * ni,const struct ieee80211_frame * wh,const uint8_t * frm,const uint8_t * efrm)2549085ff963SMatthew Dillon mesh_recv_action_meshlmetric(struct ieee80211_node *ni,
255032176cfdSRui Paulo const struct ieee80211_frame *wh,
255132176cfdSRui Paulo const uint8_t *frm, const uint8_t *efrm)
255232176cfdSRui Paulo {
2553085ff963SMatthew Dillon const struct ieee80211_meshlmetric_ie *ie =
2554085ff963SMatthew Dillon (const struct ieee80211_meshlmetric_ie *)
2555085ff963SMatthew Dillon (frm+2); /* action + code */
2556085ff963SMatthew Dillon struct ieee80211_meshlmetric_ie lm_rep;
255732176cfdSRui Paulo
2558085ff963SMatthew Dillon if (ie->lm_flags & IEEE80211_MESH_LMETRIC_FLAGS_REQ) {
2559085ff963SMatthew Dillon lm_rep.lm_flags = 0;
2560085ff963SMatthew Dillon lm_rep.lm_metric = mesh_airtime_calc(ni);
256132176cfdSRui Paulo ieee80211_send_action(ni,
2562085ff963SMatthew Dillon IEEE80211_ACTION_CAT_MESH,
2563085ff963SMatthew Dillon IEEE80211_ACTION_MESH_LMETRIC,
2564085ff963SMatthew Dillon &lm_rep);
2565085ff963SMatthew Dillon }
2566085ff963SMatthew Dillon /* XXX: else do nothing for now */
256732176cfdSRui Paulo return 0;
256832176cfdSRui Paulo }
256932176cfdSRui Paulo
2570085ff963SMatthew Dillon /*
2571085ff963SMatthew Dillon * Parse meshgate action ie's for GANN frames.
2572085ff963SMatthew Dillon * Returns -1 if parsing fails, otherwise 0.
2573085ff963SMatthew Dillon */
257432176cfdSRui Paulo static int
mesh_parse_meshgate_action(struct ieee80211_node * ni,const struct ieee80211_frame * wh,struct ieee80211_meshgann_ie * ie,const uint8_t * frm,const uint8_t * efrm)2575085ff963SMatthew Dillon mesh_parse_meshgate_action(struct ieee80211_node *ni,
2576085ff963SMatthew Dillon const struct ieee80211_frame *wh, /* XXX for VERIFY_LENGTH */
2577085ff963SMatthew Dillon struct ieee80211_meshgann_ie *ie, const uint8_t *frm, const uint8_t *efrm)
2578085ff963SMatthew Dillon {
2579085ff963SMatthew Dillon struct ieee80211vap *vap = ni->ni_vap;
2580085ff963SMatthew Dillon const struct ieee80211_meshgann_ie *gannie;
2581085ff963SMatthew Dillon
2582085ff963SMatthew Dillon while (efrm - frm > 1) {
2583085ff963SMatthew Dillon IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return -1);
2584085ff963SMatthew Dillon switch (*frm) {
2585085ff963SMatthew Dillon case IEEE80211_ELEMID_MESHGANN:
2586085ff963SMatthew Dillon gannie = (const struct ieee80211_meshgann_ie *) frm;
2587085ff963SMatthew Dillon memset(ie, 0, sizeof(*ie));
2588085ff963SMatthew Dillon ie->gann_ie = gannie->gann_ie;
2589085ff963SMatthew Dillon ie->gann_len = gannie->gann_len;
2590085ff963SMatthew Dillon ie->gann_flags = gannie->gann_flags;
2591085ff963SMatthew Dillon ie->gann_hopcount = gannie->gann_hopcount;
2592085ff963SMatthew Dillon ie->gann_ttl = gannie->gann_ttl;
2593085ff963SMatthew Dillon IEEE80211_ADDR_COPY(ie->gann_addr, gannie->gann_addr);
2594*4f655ef5SMatthew Dillon ie->gann_seq = le32dec(&gannie->gann_seq);
2595*4f655ef5SMatthew Dillon ie->gann_interval = le16dec(&gannie->gann_interval);
2596085ff963SMatthew Dillon break;
2597085ff963SMatthew Dillon }
2598085ff963SMatthew Dillon frm += frm[1] + 2;
2599085ff963SMatthew Dillon }
2600085ff963SMatthew Dillon
2601085ff963SMatthew Dillon return 0;
2602085ff963SMatthew Dillon }
2603085ff963SMatthew Dillon
2604085ff963SMatthew Dillon /*
2605085ff963SMatthew Dillon * Mesh Gate Announcement handling.
2606085ff963SMatthew Dillon */
2607085ff963SMatthew Dillon static int
mesh_recv_action_meshgate(struct ieee80211_node * ni,const struct ieee80211_frame * wh,const uint8_t * frm,const uint8_t * efrm)2608085ff963SMatthew Dillon mesh_recv_action_meshgate(struct ieee80211_node *ni,
260932176cfdSRui Paulo const struct ieee80211_frame *wh,
261032176cfdSRui Paulo const uint8_t *frm, const uint8_t *efrm)
261132176cfdSRui Paulo {
2612085ff963SMatthew Dillon struct ieee80211vap *vap = ni->ni_vap;
2613085ff963SMatthew Dillon struct ieee80211_mesh_state *ms = vap->iv_mesh;
2614085ff963SMatthew Dillon struct ieee80211_mesh_gate_route *gr, *next;
2615085ff963SMatthew Dillon struct ieee80211_mesh_route *rt_gate;
2616085ff963SMatthew Dillon struct ieee80211_meshgann_ie pgann;
2617085ff963SMatthew Dillon struct ieee80211_meshgann_ie ie;
2618085ff963SMatthew Dillon int found = 0;
2619085ff963SMatthew Dillon
2620085ff963SMatthew Dillon /* +2 for action + code */
2621085ff963SMatthew Dillon if (mesh_parse_meshgate_action(ni, wh, &ie, frm+2, efrm) != 0) {
2622085ff963SMatthew Dillon IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_MESH,
2623085ff963SMatthew Dillon ni->ni_macaddr, NULL, "%s",
2624085ff963SMatthew Dillon "GANN parsing failed");
2625085ff963SMatthew Dillon vap->iv_stats.is_rx_mgtdiscard++;
2626085ff963SMatthew Dillon return (0);
2627085ff963SMatthew Dillon }
2628085ff963SMatthew Dillon
2629085ff963SMatthew Dillon if (IEEE80211_ADDR_EQ(vap->iv_myaddr, ie.gann_addr))
2630085ff963SMatthew Dillon return 0;
2631085ff963SMatthew Dillon
2632*4f655ef5SMatthew Dillon #if defined(__DragonFly__)
2633085ff963SMatthew Dillon IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, ni->ni_macaddr,
2634f92fae3fSSascha Wildner "received GANN, meshgate: %s (seq %u)",
2635f92fae3fSSascha Wildner ether_sprintf(ie.gann_addr),
2636085ff963SMatthew Dillon ie.gann_seq);
2637*4f655ef5SMatthew Dillon #else
2638*4f655ef5SMatthew Dillon IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, ni->ni_macaddr,
2639*4f655ef5SMatthew Dillon "received GANN, meshgate: %6D (seq %u)", ie.gann_addr, ":",
2640*4f655ef5SMatthew Dillon ie.gann_seq);
2641*4f655ef5SMatthew Dillon #endif
2642085ff963SMatthew Dillon
2643085ff963SMatthew Dillon if (ms == NULL)
2644085ff963SMatthew Dillon return (0);
2645085ff963SMatthew Dillon MESH_RT_LOCK(ms);
2646085ff963SMatthew Dillon TAILQ_FOREACH_SAFE(gr, &ms->ms_known_gates, gr_next, next) {
2647085ff963SMatthew Dillon if (!IEEE80211_ADDR_EQ(gr->gr_addr, ie.gann_addr))
2648085ff963SMatthew Dillon continue;
2649085ff963SMatthew Dillon if (ie.gann_seq <= gr->gr_lastseq) {
2650085ff963SMatthew Dillon IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_MESH,
2651085ff963SMatthew Dillon ni->ni_macaddr, NULL,
2652085ff963SMatthew Dillon "GANN old seqno %u <= %u",
2653085ff963SMatthew Dillon ie.gann_seq, gr->gr_lastseq);
2654085ff963SMatthew Dillon MESH_RT_UNLOCK(ms);
2655085ff963SMatthew Dillon return (0);
2656085ff963SMatthew Dillon }
2657085ff963SMatthew Dillon /* corresponding mesh gate found & GANN accepted */
2658085ff963SMatthew Dillon found = 1;
2659085ff963SMatthew Dillon break;
2660085ff963SMatthew Dillon
2661085ff963SMatthew Dillon }
2662085ff963SMatthew Dillon if (found == 0) {
2663085ff963SMatthew Dillon /* this GANN is from a new mesh Gate add it to known table. */
2664085ff963SMatthew Dillon IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, ie.gann_addr,
2665085ff963SMatthew Dillon "stored new GANN information, seq %u.", ie.gann_seq);
2666*4f655ef5SMatthew Dillon #if defined(__DragonFly__)
2667085ff963SMatthew Dillon gr = kmalloc(ALIGN(sizeof(struct ieee80211_mesh_gate_route)),
2668085ff963SMatthew Dillon M_80211_MESH_GT_RT, M_INTWAIT | M_ZERO);
2669*4f655ef5SMatthew Dillon #else
2670*4f655ef5SMatthew Dillon gr = IEEE80211_MALLOC(ALIGN(sizeof(struct ieee80211_mesh_gate_route)),
2671*4f655ef5SMatthew Dillon M_80211_MESH_GT_RT,
2672*4f655ef5SMatthew Dillon IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
2673*4f655ef5SMatthew Dillon #endif
2674085ff963SMatthew Dillon IEEE80211_ADDR_COPY(gr->gr_addr, ie.gann_addr);
2675085ff963SMatthew Dillon TAILQ_INSERT_TAIL(&ms->ms_known_gates, gr, gr_next);
2676085ff963SMatthew Dillon }
2677085ff963SMatthew Dillon gr->gr_lastseq = ie.gann_seq;
2678085ff963SMatthew Dillon
2679085ff963SMatthew Dillon /* check if we have a path to this gate */
2680085ff963SMatthew Dillon rt_gate = mesh_rt_find_locked(ms, gr->gr_addr);
2681085ff963SMatthew Dillon if (rt_gate != NULL &&
2682085ff963SMatthew Dillon rt_gate->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) {
2683085ff963SMatthew Dillon gr->gr_route = rt_gate;
2684085ff963SMatthew Dillon rt_gate->rt_flags |= IEEE80211_MESHRT_FLAGS_GATE;
2685085ff963SMatthew Dillon }
2686085ff963SMatthew Dillon
2687085ff963SMatthew Dillon MESH_RT_UNLOCK(ms);
2688085ff963SMatthew Dillon
2689085ff963SMatthew Dillon /* popagate only if decremented ttl >= 1 && forwarding is enabled */
2690085ff963SMatthew Dillon if ((ie.gann_ttl - 1) < 1 && !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD))
2691085ff963SMatthew Dillon return 0;
2692085ff963SMatthew Dillon pgann.gann_flags = ie.gann_flags; /* Reserved */
2693085ff963SMatthew Dillon pgann.gann_hopcount = ie.gann_hopcount + 1;
2694085ff963SMatthew Dillon pgann.gann_ttl = ie.gann_ttl - 1;
2695085ff963SMatthew Dillon IEEE80211_ADDR_COPY(pgann.gann_addr, ie.gann_addr);
2696085ff963SMatthew Dillon pgann.gann_seq = ie.gann_seq;
2697085ff963SMatthew Dillon pgann.gann_interval = ie.gann_interval;
2698085ff963SMatthew Dillon
2699085ff963SMatthew Dillon IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, ie.gann_addr,
2700085ff963SMatthew Dillon "%s", "propagate GANN");
2701085ff963SMatthew Dillon
2702085ff963SMatthew Dillon ieee80211_send_action(vap->iv_bss, IEEE80211_ACTION_CAT_MESH,
2703085ff963SMatthew Dillon IEEE80211_ACTION_MESH_GANN, &pgann);
2704085ff963SMatthew Dillon
270532176cfdSRui Paulo return 0;
270632176cfdSRui Paulo }
270732176cfdSRui Paulo
270832176cfdSRui Paulo static int
mesh_send_action(struct ieee80211_node * ni,const uint8_t sa[IEEE80211_ADDR_LEN],const uint8_t da[IEEE80211_ADDR_LEN],struct mbuf * m)2709085ff963SMatthew Dillon mesh_send_action(struct ieee80211_node *ni,
2710085ff963SMatthew Dillon const uint8_t sa[IEEE80211_ADDR_LEN],
2711085ff963SMatthew Dillon const uint8_t da[IEEE80211_ADDR_LEN],
2712085ff963SMatthew Dillon struct mbuf *m)
271332176cfdSRui Paulo {
2714085ff963SMatthew Dillon struct ieee80211vap *vap = ni->ni_vap;
2715085ff963SMatthew Dillon struct ieee80211com *ic = ni->ni_ic;
271632176cfdSRui Paulo struct ieee80211_bpf_params params;
2717085ff963SMatthew Dillon struct ieee80211_frame *wh;
2718085ff963SMatthew Dillon int ret;
2719085ff963SMatthew Dillon
2720085ff963SMatthew Dillon KASSERT(ni != NULL, ("null node"));
2721085ff963SMatthew Dillon
2722085ff963SMatthew Dillon if (vap->iv_state == IEEE80211_S_CAC) {
2723085ff963SMatthew Dillon IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, ni,
2724085ff963SMatthew Dillon "block %s frame in CAC state", "Mesh action");
2725085ff963SMatthew Dillon vap->iv_stats.is_tx_badstate++;
2726085ff963SMatthew Dillon ieee80211_free_node(ni);
2727085ff963SMatthew Dillon m_freem(m);
2728085ff963SMatthew Dillon return EIO; /* XXX */
2729085ff963SMatthew Dillon }
2730085ff963SMatthew Dillon
2731b5523eacSSascha Wildner M_PREPEND(m, sizeof(struct ieee80211_frame), M_NOWAIT);
2732085ff963SMatthew Dillon if (m == NULL) {
2733085ff963SMatthew Dillon ieee80211_free_node(ni);
2734085ff963SMatthew Dillon return ENOMEM;
2735085ff963SMatthew Dillon }
2736085ff963SMatthew Dillon
2737085ff963SMatthew Dillon IEEE80211_TX_LOCK(ic);
2738085ff963SMatthew Dillon wh = mtod(m, struct ieee80211_frame *);
2739085ff963SMatthew Dillon ieee80211_send_setup(ni, m,
2740085ff963SMatthew Dillon IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_ACTION,
2741085ff963SMatthew Dillon IEEE80211_NONQOS_TID, sa, da, sa);
2742085ff963SMatthew Dillon m->m_flags |= M_ENCAP; /* mark encapsulated */
274332176cfdSRui Paulo
274432176cfdSRui Paulo memset(¶ms, 0, sizeof(params));
274532176cfdSRui Paulo params.ibp_pri = WME_AC_VO;
274632176cfdSRui Paulo params.ibp_rate0 = ni->ni_txparms->mgmtrate;
2747085ff963SMatthew Dillon if (IEEE80211_IS_MULTICAST(da))
2748085ff963SMatthew Dillon params.ibp_try0 = 1;
2749085ff963SMatthew Dillon else
275032176cfdSRui Paulo params.ibp_try0 = ni->ni_txparms->maxretry;
275132176cfdSRui Paulo params.ibp_power = ni->ni_txpower;
2752085ff963SMatthew Dillon
2753085ff963SMatthew Dillon IEEE80211_NODE_STAT(ni, tx_mgmt);
2754085ff963SMatthew Dillon
2755085ff963SMatthew Dillon ret = ieee80211_raw_output(vap, ni, m, ¶ms);
2756085ff963SMatthew Dillon IEEE80211_TX_UNLOCK(ic);
2757085ff963SMatthew Dillon return (ret);
275832176cfdSRui Paulo }
275932176cfdSRui Paulo
276032176cfdSRui Paulo #define ADDSHORT(frm, v) do { \
276132176cfdSRui Paulo frm[0] = (v) & 0xff; \
276232176cfdSRui Paulo frm[1] = (v) >> 8; \
276332176cfdSRui Paulo frm += 2; \
276432176cfdSRui Paulo } while (0)
276532176cfdSRui Paulo #define ADDWORD(frm, v) do { \
276632176cfdSRui Paulo frm[0] = (v) & 0xff; \
276732176cfdSRui Paulo frm[1] = ((v) >> 8) & 0xff; \
276832176cfdSRui Paulo frm[2] = ((v) >> 16) & 0xff; \
276932176cfdSRui Paulo frm[3] = ((v) >> 24) & 0xff; \
277032176cfdSRui Paulo frm += 4; \
277132176cfdSRui Paulo } while (0)
277232176cfdSRui Paulo
277332176cfdSRui Paulo static int
mesh_send_action_meshpeering_open(struct ieee80211_node * ni,int category,int action,void * args0)277432176cfdSRui Paulo mesh_send_action_meshpeering_open(struct ieee80211_node *ni,
277532176cfdSRui Paulo int category, int action, void *args0)
277632176cfdSRui Paulo {
277732176cfdSRui Paulo struct ieee80211vap *vap = ni->ni_vap;
277832176cfdSRui Paulo struct ieee80211com *ic = ni->ni_ic;
277932176cfdSRui Paulo uint16_t *args = args0;
278032176cfdSRui Paulo const struct ieee80211_rateset *rs;
278132176cfdSRui Paulo struct mbuf *m;
278232176cfdSRui Paulo uint8_t *frm;
278332176cfdSRui Paulo
278432176cfdSRui Paulo IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, ni,
278532176cfdSRui Paulo "send PEER OPEN action: localid 0x%x", args[0]);
278632176cfdSRui Paulo
278732176cfdSRui Paulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
27881e290df3SAntonio Huete Jimenez "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__,
2789085ff963SMatthew Dillon ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1);
279032176cfdSRui Paulo ieee80211_ref_node(ni);
279132176cfdSRui Paulo
279232176cfdSRui Paulo m = ieee80211_getmgtframe(&frm,
279332176cfdSRui Paulo ic->ic_headroom + sizeof(struct ieee80211_frame),
279432176cfdSRui Paulo sizeof(uint16_t) /* action+category */
279532176cfdSRui Paulo + sizeof(uint16_t) /* capabilites */
279632176cfdSRui Paulo + 2 + IEEE80211_RATE_SIZE
279732176cfdSRui Paulo + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
279832176cfdSRui Paulo + 2 + IEEE80211_MESHID_LEN
279932176cfdSRui Paulo + sizeof(struct ieee80211_meshconf_ie)
280032176cfdSRui Paulo + sizeof(struct ieee80211_meshpeer_ie)
280132176cfdSRui Paulo );
280232176cfdSRui Paulo if (m != NULL) {
280332176cfdSRui Paulo /*
280432176cfdSRui Paulo * mesh peer open action frame format:
280532176cfdSRui Paulo * [1] category
280632176cfdSRui Paulo * [1] action
280732176cfdSRui Paulo * [2] capabilities
280832176cfdSRui Paulo * [tlv] rates
280932176cfdSRui Paulo * [tlv] xrates
281032176cfdSRui Paulo * [tlv] mesh id
281132176cfdSRui Paulo * [tlv] mesh conf
281232176cfdSRui Paulo * [tlv] mesh peer link mgmt
281332176cfdSRui Paulo */
281432176cfdSRui Paulo *frm++ = category;
281532176cfdSRui Paulo *frm++ = action;
281632176cfdSRui Paulo ADDSHORT(frm, ieee80211_getcapinfo(vap, ni->ni_chan));
281732176cfdSRui Paulo rs = ieee80211_get_suprates(ic, ic->ic_curchan);
281832176cfdSRui Paulo frm = ieee80211_add_rates(frm, rs);
281932176cfdSRui Paulo frm = ieee80211_add_xrates(frm, rs);
282032176cfdSRui Paulo frm = ieee80211_add_meshid(frm, vap);
282132176cfdSRui Paulo frm = ieee80211_add_meshconf(frm, vap);
2822085ff963SMatthew Dillon frm = ieee80211_add_meshpeer(frm, IEEE80211_ACTION_MESHPEERING_OPEN,
282332176cfdSRui Paulo args[0], 0, 0);
282432176cfdSRui Paulo m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
2825085ff963SMatthew Dillon return mesh_send_action(ni, vap->iv_myaddr, ni->ni_macaddr, m);
282632176cfdSRui Paulo } else {
282732176cfdSRui Paulo vap->iv_stats.is_tx_nobuf++;
282832176cfdSRui Paulo ieee80211_free_node(ni);
282932176cfdSRui Paulo return ENOMEM;
283032176cfdSRui Paulo }
283132176cfdSRui Paulo }
283232176cfdSRui Paulo
283332176cfdSRui Paulo static int
mesh_send_action_meshpeering_confirm(struct ieee80211_node * ni,int category,int action,void * args0)283432176cfdSRui Paulo mesh_send_action_meshpeering_confirm(struct ieee80211_node *ni,
283532176cfdSRui Paulo int category, int action, void *args0)
283632176cfdSRui Paulo {
283732176cfdSRui Paulo struct ieee80211vap *vap = ni->ni_vap;
283832176cfdSRui Paulo struct ieee80211com *ic = ni->ni_ic;
283932176cfdSRui Paulo uint16_t *args = args0;
284032176cfdSRui Paulo const struct ieee80211_rateset *rs;
284132176cfdSRui Paulo struct mbuf *m;
284232176cfdSRui Paulo uint8_t *frm;
284332176cfdSRui Paulo
284432176cfdSRui Paulo IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, ni,
284532176cfdSRui Paulo "send PEER CONFIRM action: localid 0x%x, peerid 0x%x",
284632176cfdSRui Paulo args[0], args[1]);
284732176cfdSRui Paulo
284832176cfdSRui Paulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
28491e290df3SAntonio Huete Jimenez "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__,
2850085ff963SMatthew Dillon ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1);
285132176cfdSRui Paulo ieee80211_ref_node(ni);
285232176cfdSRui Paulo
285332176cfdSRui Paulo m = ieee80211_getmgtframe(&frm,
285432176cfdSRui Paulo ic->ic_headroom + sizeof(struct ieee80211_frame),
285532176cfdSRui Paulo sizeof(uint16_t) /* action+category */
285632176cfdSRui Paulo + sizeof(uint16_t) /* capabilites */
285732176cfdSRui Paulo + sizeof(uint16_t) /* status code */
285832176cfdSRui Paulo + sizeof(uint16_t) /* AID */
285932176cfdSRui Paulo + 2 + IEEE80211_RATE_SIZE
286032176cfdSRui Paulo + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
286132176cfdSRui Paulo + 2 + IEEE80211_MESHID_LEN
286232176cfdSRui Paulo + sizeof(struct ieee80211_meshconf_ie)
286332176cfdSRui Paulo + sizeof(struct ieee80211_meshpeer_ie)
286432176cfdSRui Paulo );
286532176cfdSRui Paulo if (m != NULL) {
286632176cfdSRui Paulo /*
286732176cfdSRui Paulo * mesh peer confirm action frame format:
286832176cfdSRui Paulo * [1] category
286932176cfdSRui Paulo * [1] action
287032176cfdSRui Paulo * [2] capabilities
287132176cfdSRui Paulo * [2] status code
287232176cfdSRui Paulo * [2] association id (peer ID)
287332176cfdSRui Paulo * [tlv] rates
287432176cfdSRui Paulo * [tlv] xrates
287532176cfdSRui Paulo * [tlv] mesh id
287632176cfdSRui Paulo * [tlv] mesh conf
287732176cfdSRui Paulo * [tlv] mesh peer link mgmt
287832176cfdSRui Paulo */
287932176cfdSRui Paulo *frm++ = category;
288032176cfdSRui Paulo *frm++ = action;
288132176cfdSRui Paulo ADDSHORT(frm, ieee80211_getcapinfo(vap, ni->ni_chan));
288232176cfdSRui Paulo ADDSHORT(frm, 0); /* status code */
288332176cfdSRui Paulo ADDSHORT(frm, args[1]); /* AID */
288432176cfdSRui Paulo rs = ieee80211_get_suprates(ic, ic->ic_curchan);
288532176cfdSRui Paulo frm = ieee80211_add_rates(frm, rs);
288632176cfdSRui Paulo frm = ieee80211_add_xrates(frm, rs);
288732176cfdSRui Paulo frm = ieee80211_add_meshid(frm, vap);
288832176cfdSRui Paulo frm = ieee80211_add_meshconf(frm, vap);
288932176cfdSRui Paulo frm = ieee80211_add_meshpeer(frm,
2890085ff963SMatthew Dillon IEEE80211_ACTION_MESHPEERING_CONFIRM,
289132176cfdSRui Paulo args[0], args[1], 0);
289232176cfdSRui Paulo m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
2893085ff963SMatthew Dillon return mesh_send_action(ni, vap->iv_myaddr, ni->ni_macaddr, m);
289432176cfdSRui Paulo } else {
289532176cfdSRui Paulo vap->iv_stats.is_tx_nobuf++;
289632176cfdSRui Paulo ieee80211_free_node(ni);
289732176cfdSRui Paulo return ENOMEM;
289832176cfdSRui Paulo }
289932176cfdSRui Paulo }
290032176cfdSRui Paulo
290132176cfdSRui Paulo static int
mesh_send_action_meshpeering_close(struct ieee80211_node * ni,int category,int action,void * args0)290232176cfdSRui Paulo mesh_send_action_meshpeering_close(struct ieee80211_node *ni,
290332176cfdSRui Paulo int category, int action, void *args0)
290432176cfdSRui Paulo {
290532176cfdSRui Paulo struct ieee80211vap *vap = ni->ni_vap;
290632176cfdSRui Paulo struct ieee80211com *ic = ni->ni_ic;
290732176cfdSRui Paulo uint16_t *args = args0;
290832176cfdSRui Paulo struct mbuf *m;
290932176cfdSRui Paulo uint8_t *frm;
291032176cfdSRui Paulo
291132176cfdSRui Paulo IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, ni,
2912*4f655ef5SMatthew Dillon "send PEER CLOSE action: localid 0x%x, peerid 0x%x reason %d (%s)",
2913*4f655ef5SMatthew Dillon args[0], args[1], args[2], ieee80211_reason_to_string(args[2]));
291432176cfdSRui Paulo
291532176cfdSRui Paulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
29161e290df3SAntonio Huete Jimenez "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__,
2917085ff963SMatthew Dillon ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1);
291832176cfdSRui Paulo ieee80211_ref_node(ni);
291932176cfdSRui Paulo
292032176cfdSRui Paulo m = ieee80211_getmgtframe(&frm,
292132176cfdSRui Paulo ic->ic_headroom + sizeof(struct ieee80211_frame),
292232176cfdSRui Paulo sizeof(uint16_t) /* action+category */
292332176cfdSRui Paulo + sizeof(uint16_t) /* reason code */
292432176cfdSRui Paulo + 2 + IEEE80211_MESHID_LEN
292532176cfdSRui Paulo + sizeof(struct ieee80211_meshpeer_ie)
292632176cfdSRui Paulo );
292732176cfdSRui Paulo if (m != NULL) {
292832176cfdSRui Paulo /*
292932176cfdSRui Paulo * mesh peer close action frame format:
293032176cfdSRui Paulo * [1] category
293132176cfdSRui Paulo * [1] action
293232176cfdSRui Paulo * [tlv] mesh id
293332176cfdSRui Paulo * [tlv] mesh peer link mgmt
293432176cfdSRui Paulo */
293532176cfdSRui Paulo *frm++ = category;
293632176cfdSRui Paulo *frm++ = action;
293732176cfdSRui Paulo frm = ieee80211_add_meshid(frm, vap);
293832176cfdSRui Paulo frm = ieee80211_add_meshpeer(frm,
2939085ff963SMatthew Dillon IEEE80211_ACTION_MESHPEERING_CLOSE,
294032176cfdSRui Paulo args[0], args[1], args[2]);
294132176cfdSRui Paulo m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
2942085ff963SMatthew Dillon return mesh_send_action(ni, vap->iv_myaddr, ni->ni_macaddr, m);
294332176cfdSRui Paulo } else {
294432176cfdSRui Paulo vap->iv_stats.is_tx_nobuf++;
294532176cfdSRui Paulo ieee80211_free_node(ni);
294632176cfdSRui Paulo return ENOMEM;
294732176cfdSRui Paulo }
294832176cfdSRui Paulo }
294932176cfdSRui Paulo
295032176cfdSRui Paulo static int
mesh_send_action_meshlmetric(struct ieee80211_node * ni,int category,int action,void * arg0)2951085ff963SMatthew Dillon mesh_send_action_meshlmetric(struct ieee80211_node *ni,
295232176cfdSRui Paulo int category, int action, void *arg0)
295332176cfdSRui Paulo {
295432176cfdSRui Paulo struct ieee80211vap *vap = ni->ni_vap;
295532176cfdSRui Paulo struct ieee80211com *ic = ni->ni_ic;
2956085ff963SMatthew Dillon struct ieee80211_meshlmetric_ie *ie = arg0;
295732176cfdSRui Paulo struct mbuf *m;
295832176cfdSRui Paulo uint8_t *frm;
295932176cfdSRui Paulo
2960085ff963SMatthew Dillon if (ie->lm_flags & IEEE80211_MESH_LMETRIC_FLAGS_REQ) {
2961085ff963SMatthew Dillon IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH,
2962085ff963SMatthew Dillon ni, "%s", "send LINK METRIC REQUEST action");
296332176cfdSRui Paulo } else {
2964085ff963SMatthew Dillon IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH,
2965085ff963SMatthew Dillon ni, "send LINK METRIC REPLY action: metric 0x%x",
2966085ff963SMatthew Dillon ie->lm_metric);
296732176cfdSRui Paulo }
296832176cfdSRui Paulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
29691e290df3SAntonio Huete Jimenez "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__,
2970085ff963SMatthew Dillon ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1);
297132176cfdSRui Paulo ieee80211_ref_node(ni);
297232176cfdSRui Paulo
297332176cfdSRui Paulo m = ieee80211_getmgtframe(&frm,
297432176cfdSRui Paulo ic->ic_headroom + sizeof(struct ieee80211_frame),
2975085ff963SMatthew Dillon sizeof(uint16_t) + /* action+category */
2976085ff963SMatthew Dillon sizeof(struct ieee80211_meshlmetric_ie)
297732176cfdSRui Paulo );
297832176cfdSRui Paulo if (m != NULL) {
297932176cfdSRui Paulo /*
2980085ff963SMatthew Dillon * mesh link metric
298132176cfdSRui Paulo * [1] category
298232176cfdSRui Paulo * [1] action
298332176cfdSRui Paulo * [tlv] mesh link metric
298432176cfdSRui Paulo */
298532176cfdSRui Paulo *frm++ = category;
298632176cfdSRui Paulo *frm++ = action;
2987085ff963SMatthew Dillon frm = ieee80211_add_meshlmetric(frm,
2988085ff963SMatthew Dillon ie->lm_flags, ie->lm_metric);
298932176cfdSRui Paulo m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
2990085ff963SMatthew Dillon return mesh_send_action(ni, vap->iv_myaddr, ni->ni_macaddr, m);
2991085ff963SMatthew Dillon } else {
2992085ff963SMatthew Dillon vap->iv_stats.is_tx_nobuf++;
2993085ff963SMatthew Dillon ieee80211_free_node(ni);
2994085ff963SMatthew Dillon return ENOMEM;
2995085ff963SMatthew Dillon }
2996085ff963SMatthew Dillon }
2997085ff963SMatthew Dillon
2998085ff963SMatthew Dillon static int
mesh_send_action_meshgate(struct ieee80211_node * ni,int category,int action,void * arg0)2999085ff963SMatthew Dillon mesh_send_action_meshgate(struct ieee80211_node *ni,
3000085ff963SMatthew Dillon int category, int action, void *arg0)
3001085ff963SMatthew Dillon {
3002085ff963SMatthew Dillon struct ieee80211vap *vap = ni->ni_vap;
3003085ff963SMatthew Dillon struct ieee80211com *ic = ni->ni_ic;
3004085ff963SMatthew Dillon struct ieee80211_meshgann_ie *ie = arg0;
3005085ff963SMatthew Dillon struct mbuf *m;
3006085ff963SMatthew Dillon uint8_t *frm;
3007085ff963SMatthew Dillon
3008085ff963SMatthew Dillon IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
3009085ff963SMatthew Dillon "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__,
3010085ff963SMatthew Dillon ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1);
3011085ff963SMatthew Dillon ieee80211_ref_node(ni);
3012085ff963SMatthew Dillon
3013085ff963SMatthew Dillon m = ieee80211_getmgtframe(&frm,
3014085ff963SMatthew Dillon ic->ic_headroom + sizeof(struct ieee80211_frame),
3015085ff963SMatthew Dillon sizeof(uint16_t) + /* action+category */
3016085ff963SMatthew Dillon IEEE80211_MESHGANN_BASE_SZ
3017085ff963SMatthew Dillon );
3018085ff963SMatthew Dillon if (m != NULL) {
3019085ff963SMatthew Dillon /*
3020085ff963SMatthew Dillon * mesh link metric
3021085ff963SMatthew Dillon * [1] category
3022085ff963SMatthew Dillon * [1] action
3023085ff963SMatthew Dillon * [tlv] mesh gate annoucement
3024085ff963SMatthew Dillon */
3025085ff963SMatthew Dillon *frm++ = category;
3026085ff963SMatthew Dillon *frm++ = action;
3027085ff963SMatthew Dillon frm = ieee80211_add_meshgate(frm, ie);
3028085ff963SMatthew Dillon m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
3029085ff963SMatthew Dillon return mesh_send_action(ni, vap->iv_myaddr, broadcastaddr, m);
303032176cfdSRui Paulo } else {
303132176cfdSRui Paulo vap->iv_stats.is_tx_nobuf++;
303232176cfdSRui Paulo ieee80211_free_node(ni);
303332176cfdSRui Paulo return ENOMEM;
303432176cfdSRui Paulo }
303532176cfdSRui Paulo }
303632176cfdSRui Paulo
303732176cfdSRui Paulo static void
mesh_peer_timeout_setup(struct ieee80211_node * ni)303832176cfdSRui Paulo mesh_peer_timeout_setup(struct ieee80211_node *ni)
303932176cfdSRui Paulo {
304032176cfdSRui Paulo switch (ni->ni_mlstate) {
304132176cfdSRui Paulo case IEEE80211_NODE_MESH_HOLDING:
304232176cfdSRui Paulo ni->ni_mltval = ieee80211_mesh_holdingtimeout;
304332176cfdSRui Paulo break;
304432176cfdSRui Paulo case IEEE80211_NODE_MESH_CONFIRMRCV:
304532176cfdSRui Paulo ni->ni_mltval = ieee80211_mesh_confirmtimeout;
304632176cfdSRui Paulo break;
304732176cfdSRui Paulo case IEEE80211_NODE_MESH_IDLE:
304832176cfdSRui Paulo ni->ni_mltval = 0;
304932176cfdSRui Paulo break;
305032176cfdSRui Paulo default:
305132176cfdSRui Paulo ni->ni_mltval = ieee80211_mesh_retrytimeout;
305232176cfdSRui Paulo break;
305332176cfdSRui Paulo }
3054085ff963SMatthew Dillon if (ni->ni_mltval)
305532176cfdSRui Paulo callout_reset(&ni->ni_mltimer, ni->ni_mltval,
3056085ff963SMatthew Dillon mesh_peer_timeout_cb, ni);
305732176cfdSRui Paulo }
305832176cfdSRui Paulo
305932176cfdSRui Paulo /*
306032176cfdSRui Paulo * Same as above but backoffs timer statisically 50%.
306132176cfdSRui Paulo */
306232176cfdSRui Paulo static void
mesh_peer_timeout_backoff(struct ieee80211_node * ni)306332176cfdSRui Paulo mesh_peer_timeout_backoff(struct ieee80211_node *ni)
306432176cfdSRui Paulo {
306532176cfdSRui Paulo uint32_t r;
306632176cfdSRui Paulo
3067085ff963SMatthew Dillon r = arc4random();
306832176cfdSRui Paulo ni->ni_mltval += r % ni->ni_mltval;
3069085ff963SMatthew Dillon callout_reset(&ni->ni_mltimer, ni->ni_mltval, mesh_peer_timeout_cb,
3070085ff963SMatthew Dillon ni);
307132176cfdSRui Paulo }
307232176cfdSRui Paulo
307332176cfdSRui Paulo static __inline void
mesh_peer_timeout_stop(struct ieee80211_node * ni)307432176cfdSRui Paulo mesh_peer_timeout_stop(struct ieee80211_node *ni)
307532176cfdSRui Paulo {
3076085ff963SMatthew Dillon callout_drain(&ni->ni_mltimer);
3077085ff963SMatthew Dillon }
3078085ff963SMatthew Dillon
3079085ff963SMatthew Dillon static void
mesh_peer_backoff_cb(void * arg)3080085ff963SMatthew Dillon mesh_peer_backoff_cb(void *arg)
3081085ff963SMatthew Dillon {
3082085ff963SMatthew Dillon struct ieee80211_node *ni = (struct ieee80211_node *)arg;
3083085ff963SMatthew Dillon
3084085ff963SMatthew Dillon /* After backoff timeout, try to peer automatically again. */
3085085ff963SMatthew Dillon ni->ni_mlhcnt = 0;
308632176cfdSRui Paulo }
308732176cfdSRui Paulo
308832176cfdSRui Paulo /*
308932176cfdSRui Paulo * Mesh Peer Link Management FSM timeout handling.
309032176cfdSRui Paulo */
309132176cfdSRui Paulo static void
mesh_peer_timeout_cb(void * arg)3092085ff963SMatthew Dillon mesh_peer_timeout_cb(void *arg)
309332176cfdSRui Paulo {
309432176cfdSRui Paulo struct ieee80211_node *ni = (struct ieee80211_node *)arg;
309532176cfdSRui Paulo uint16_t args[3];
309632176cfdSRui Paulo
309732176cfdSRui Paulo IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_MESH,
309832176cfdSRui Paulo ni, "mesh link timeout, state %d, retry counter %d",
309932176cfdSRui Paulo ni->ni_mlstate, ni->ni_mlrcnt);
310032176cfdSRui Paulo
310132176cfdSRui Paulo switch (ni->ni_mlstate) {
310232176cfdSRui Paulo case IEEE80211_NODE_MESH_IDLE:
310332176cfdSRui Paulo case IEEE80211_NODE_MESH_ESTABLISHED:
310432176cfdSRui Paulo break;
310532176cfdSRui Paulo case IEEE80211_NODE_MESH_OPENSNT:
310632176cfdSRui Paulo case IEEE80211_NODE_MESH_OPENRCV:
310732176cfdSRui Paulo if (ni->ni_mlrcnt == ieee80211_mesh_maxretries) {
310832176cfdSRui Paulo args[0] = ni->ni_mlpid;
310932176cfdSRui Paulo args[2] = IEEE80211_REASON_MESH_MAX_RETRIES;
311032176cfdSRui Paulo ieee80211_send_action(ni,
3111085ff963SMatthew Dillon IEEE80211_ACTION_CAT_SELF_PROT,
311232176cfdSRui Paulo IEEE80211_ACTION_MESHPEERING_CLOSE, args);
311332176cfdSRui Paulo ni->ni_mlrcnt = 0;
311432176cfdSRui Paulo mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING);
311532176cfdSRui Paulo mesh_peer_timeout_setup(ni);
311632176cfdSRui Paulo } else {
311732176cfdSRui Paulo args[0] = ni->ni_mlpid;
311832176cfdSRui Paulo ieee80211_send_action(ni,
3119085ff963SMatthew Dillon IEEE80211_ACTION_CAT_SELF_PROT,
312032176cfdSRui Paulo IEEE80211_ACTION_MESHPEERING_OPEN, args);
312132176cfdSRui Paulo ni->ni_mlrcnt++;
312232176cfdSRui Paulo mesh_peer_timeout_backoff(ni);
312332176cfdSRui Paulo }
312432176cfdSRui Paulo break;
312532176cfdSRui Paulo case IEEE80211_NODE_MESH_CONFIRMRCV:
312632176cfdSRui Paulo args[0] = ni->ni_mlpid;
312732176cfdSRui Paulo args[2] = IEEE80211_REASON_MESH_CONFIRM_TIMEOUT;
312832176cfdSRui Paulo ieee80211_send_action(ni,
3129085ff963SMatthew Dillon IEEE80211_ACTION_CAT_SELF_PROT,
313032176cfdSRui Paulo IEEE80211_ACTION_MESHPEERING_CLOSE, args);
313132176cfdSRui Paulo mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING);
313232176cfdSRui Paulo mesh_peer_timeout_setup(ni);
313332176cfdSRui Paulo break;
313432176cfdSRui Paulo case IEEE80211_NODE_MESH_HOLDING:
3135085ff963SMatthew Dillon ni->ni_mlhcnt++;
3136085ff963SMatthew Dillon if (ni->ni_mlhcnt >= ieee80211_mesh_maxholding)
3137085ff963SMatthew Dillon callout_reset(&ni->ni_mlhtimer,
3138085ff963SMatthew Dillon ieee80211_mesh_backofftimeout,
3139085ff963SMatthew Dillon mesh_peer_backoff_cb, ni);
314032176cfdSRui Paulo mesh_linkchange(ni, IEEE80211_NODE_MESH_IDLE);
314132176cfdSRui Paulo break;
314232176cfdSRui Paulo }
314332176cfdSRui Paulo }
314432176cfdSRui Paulo
314532176cfdSRui Paulo static int
mesh_verify_meshid(struct ieee80211vap * vap,const uint8_t * ie)314632176cfdSRui Paulo mesh_verify_meshid(struct ieee80211vap *vap, const uint8_t *ie)
314732176cfdSRui Paulo {
314832176cfdSRui Paulo struct ieee80211_mesh_state *ms = vap->iv_mesh;
314932176cfdSRui Paulo
315032176cfdSRui Paulo if (ie == NULL || ie[1] != ms->ms_idlen)
315132176cfdSRui Paulo return 1;
315232176cfdSRui Paulo return memcmp(ms->ms_id, ie + 2, ms->ms_idlen);
315332176cfdSRui Paulo }
315432176cfdSRui Paulo
315532176cfdSRui Paulo /*
315632176cfdSRui Paulo * Check if we are using the same algorithms for this mesh.
315732176cfdSRui Paulo */
315832176cfdSRui Paulo static int
mesh_verify_meshconf(struct ieee80211vap * vap,const uint8_t * ie)315932176cfdSRui Paulo mesh_verify_meshconf(struct ieee80211vap *vap, const uint8_t *ie)
316032176cfdSRui Paulo {
316132176cfdSRui Paulo const struct ieee80211_meshconf_ie *meshconf =
316232176cfdSRui Paulo (const struct ieee80211_meshconf_ie *) ie;
316332176cfdSRui Paulo const struct ieee80211_mesh_state *ms = vap->iv_mesh;
316432176cfdSRui Paulo
316532176cfdSRui Paulo if (meshconf == NULL)
316632176cfdSRui Paulo return 1;
316732176cfdSRui Paulo if (meshconf->conf_pselid != ms->ms_ppath->mpp_ie) {
316832176cfdSRui Paulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_MESH,
316932176cfdSRui Paulo "unknown path selection algorithm: 0x%x\n",
317032176cfdSRui Paulo meshconf->conf_pselid);
317132176cfdSRui Paulo return 1;
317232176cfdSRui Paulo }
317332176cfdSRui Paulo if (meshconf->conf_pmetid != ms->ms_pmetric->mpm_ie) {
317432176cfdSRui Paulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_MESH,
317532176cfdSRui Paulo "unknown path metric algorithm: 0x%x\n",
317632176cfdSRui Paulo meshconf->conf_pmetid);
317732176cfdSRui Paulo return 1;
317832176cfdSRui Paulo }
317932176cfdSRui Paulo if (meshconf->conf_ccid != 0) {
318032176cfdSRui Paulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_MESH,
318132176cfdSRui Paulo "unknown congestion control algorithm: 0x%x\n",
318232176cfdSRui Paulo meshconf->conf_ccid);
318332176cfdSRui Paulo return 1;
318432176cfdSRui Paulo }
318532176cfdSRui Paulo if (meshconf->conf_syncid != IEEE80211_MESHCONF_SYNC_NEIGHOFF) {
318632176cfdSRui Paulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_MESH,
318732176cfdSRui Paulo "unknown sync algorithm: 0x%x\n",
318832176cfdSRui Paulo meshconf->conf_syncid);
318932176cfdSRui Paulo return 1;
319032176cfdSRui Paulo }
319132176cfdSRui Paulo if (meshconf->conf_authid != 0) {
319232176cfdSRui Paulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_MESH,
3193085ff963SMatthew Dillon "unknown auth auth algorithm: 0x%x\n",
319432176cfdSRui Paulo meshconf->conf_pselid);
319532176cfdSRui Paulo return 1;
319632176cfdSRui Paulo }
319732176cfdSRui Paulo /* Not accepting peers */
3198085ff963SMatthew Dillon if (!(meshconf->conf_cap & IEEE80211_MESHCONF_CAP_AP)) {
319932176cfdSRui Paulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_MESH,
320032176cfdSRui Paulo "not accepting peers: 0x%x\n", meshconf->conf_cap);
320132176cfdSRui Paulo return 1;
320232176cfdSRui Paulo }
320332176cfdSRui Paulo return 0;
320432176cfdSRui Paulo }
320532176cfdSRui Paulo
320632176cfdSRui Paulo static int
mesh_verify_meshpeer(struct ieee80211vap * vap,uint8_t subtype,const uint8_t * ie)320732176cfdSRui Paulo mesh_verify_meshpeer(struct ieee80211vap *vap, uint8_t subtype,
320832176cfdSRui Paulo const uint8_t *ie)
320932176cfdSRui Paulo {
321032176cfdSRui Paulo const struct ieee80211_meshpeer_ie *meshpeer =
321132176cfdSRui Paulo (const struct ieee80211_meshpeer_ie *) ie;
321232176cfdSRui Paulo
3213085ff963SMatthew Dillon if (meshpeer == NULL ||
3214085ff963SMatthew Dillon meshpeer->peer_len < IEEE80211_MPM_BASE_SZ ||
3215085ff963SMatthew Dillon meshpeer->peer_len > IEEE80211_MPM_MAX_SZ)
321632176cfdSRui Paulo return 1;
3217085ff963SMatthew Dillon if (meshpeer->peer_proto != IEEE80211_MPPID_MPM) {
3218085ff963SMatthew Dillon IEEE80211_DPRINTF(vap,
3219085ff963SMatthew Dillon IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH,
3220085ff963SMatthew Dillon "Only MPM protocol is supported (proto: 0x%02X)",
3221085ff963SMatthew Dillon meshpeer->peer_proto);
3222085ff963SMatthew Dillon return 1;
3223085ff963SMatthew Dillon }
322432176cfdSRui Paulo switch (subtype) {
3225085ff963SMatthew Dillon case IEEE80211_ACTION_MESHPEERING_OPEN:
3226085ff963SMatthew Dillon if (meshpeer->peer_len != IEEE80211_MPM_BASE_SZ)
322732176cfdSRui Paulo return 1;
322832176cfdSRui Paulo break;
3229085ff963SMatthew Dillon case IEEE80211_ACTION_MESHPEERING_CONFIRM:
3230085ff963SMatthew Dillon if (meshpeer->peer_len != IEEE80211_MPM_BASE_SZ + 2)
323132176cfdSRui Paulo return 1;
323232176cfdSRui Paulo break;
3233085ff963SMatthew Dillon case IEEE80211_ACTION_MESHPEERING_CLOSE:
3234085ff963SMatthew Dillon if (meshpeer->peer_len < IEEE80211_MPM_BASE_SZ + 2)
323532176cfdSRui Paulo return 1;
3236085ff963SMatthew Dillon if (meshpeer->peer_len == (IEEE80211_MPM_BASE_SZ + 2) &&
3237085ff963SMatthew Dillon meshpeer->peer_linkid != 0)
323832176cfdSRui Paulo return 1;
323932176cfdSRui Paulo if (meshpeer->peer_rcode == 0)
324032176cfdSRui Paulo return 1;
324132176cfdSRui Paulo break;
324232176cfdSRui Paulo }
324332176cfdSRui Paulo return 0;
324432176cfdSRui Paulo }
324532176cfdSRui Paulo
324632176cfdSRui Paulo /*
324732176cfdSRui Paulo * Add a Mesh ID IE to a frame.
324832176cfdSRui Paulo */
324932176cfdSRui Paulo uint8_t *
ieee80211_add_meshid(uint8_t * frm,struct ieee80211vap * vap)325032176cfdSRui Paulo ieee80211_add_meshid(uint8_t *frm, struct ieee80211vap *vap)
325132176cfdSRui Paulo {
325232176cfdSRui Paulo struct ieee80211_mesh_state *ms = vap->iv_mesh;
325332176cfdSRui Paulo
325432176cfdSRui Paulo KASSERT(vap->iv_opmode == IEEE80211_M_MBSS, ("not a mbss vap"));
325532176cfdSRui Paulo
325632176cfdSRui Paulo *frm++ = IEEE80211_ELEMID_MESHID;
325732176cfdSRui Paulo *frm++ = ms->ms_idlen;
325832176cfdSRui Paulo memcpy(frm, ms->ms_id, ms->ms_idlen);
325932176cfdSRui Paulo return frm + ms->ms_idlen;
326032176cfdSRui Paulo }
326132176cfdSRui Paulo
326232176cfdSRui Paulo /*
326332176cfdSRui Paulo * Add a Mesh Configuration IE to a frame.
326432176cfdSRui Paulo * For now just use HWMP routing, Airtime link metric, Null Congestion
326532176cfdSRui Paulo * Signaling, Null Sync Protocol and Null Authentication.
326632176cfdSRui Paulo */
326732176cfdSRui Paulo uint8_t *
ieee80211_add_meshconf(uint8_t * frm,struct ieee80211vap * vap)326832176cfdSRui Paulo ieee80211_add_meshconf(uint8_t *frm, struct ieee80211vap *vap)
326932176cfdSRui Paulo {
327032176cfdSRui Paulo const struct ieee80211_mesh_state *ms = vap->iv_mesh;
327132176cfdSRui Paulo uint16_t caps;
327232176cfdSRui Paulo
327332176cfdSRui Paulo KASSERT(vap->iv_opmode == IEEE80211_M_MBSS, ("not a MBSS vap"));
327432176cfdSRui Paulo
327532176cfdSRui Paulo *frm++ = IEEE80211_ELEMID_MESHCONF;
3276085ff963SMatthew Dillon *frm++ = IEEE80211_MESH_CONF_SZ;
327732176cfdSRui Paulo *frm++ = ms->ms_ppath->mpp_ie; /* path selection */
327832176cfdSRui Paulo *frm++ = ms->ms_pmetric->mpm_ie; /* link metric */
327932176cfdSRui Paulo *frm++ = IEEE80211_MESHCONF_CC_DISABLED;
328032176cfdSRui Paulo *frm++ = IEEE80211_MESHCONF_SYNC_NEIGHOFF;
328132176cfdSRui Paulo *frm++ = IEEE80211_MESHCONF_AUTH_DISABLED;
328232176cfdSRui Paulo /* NB: set the number of neighbors before the rest */
3283085ff963SMatthew Dillon *frm = (ms->ms_neighbors > IEEE80211_MESH_MAX_NEIGHBORS ?
3284085ff963SMatthew Dillon IEEE80211_MESH_MAX_NEIGHBORS : ms->ms_neighbors) << 1;
3285085ff963SMatthew Dillon if (ms->ms_flags & IEEE80211_MESHFLAGS_GATE)
3286085ff963SMatthew Dillon *frm |= IEEE80211_MESHCONF_FORM_GATE;
328732176cfdSRui Paulo frm += 1;
328832176cfdSRui Paulo caps = 0;
328932176cfdSRui Paulo if (ms->ms_flags & IEEE80211_MESHFLAGS_AP)
329032176cfdSRui Paulo caps |= IEEE80211_MESHCONF_CAP_AP;
329132176cfdSRui Paulo if (ms->ms_flags & IEEE80211_MESHFLAGS_FWD)
329232176cfdSRui Paulo caps |= IEEE80211_MESHCONF_CAP_FWRD;
3293085ff963SMatthew Dillon *frm++ = caps;
329432176cfdSRui Paulo return frm;
329532176cfdSRui Paulo }
329632176cfdSRui Paulo
329732176cfdSRui Paulo /*
329832176cfdSRui Paulo * Add a Mesh Peer Management IE to a frame.
329932176cfdSRui Paulo */
330032176cfdSRui Paulo uint8_t *
ieee80211_add_meshpeer(uint8_t * frm,uint8_t subtype,uint16_t localid,uint16_t peerid,uint16_t reason)330132176cfdSRui Paulo ieee80211_add_meshpeer(uint8_t *frm, uint8_t subtype, uint16_t localid,
330232176cfdSRui Paulo uint16_t peerid, uint16_t reason)
330332176cfdSRui Paulo {
330432176cfdSRui Paulo
330532176cfdSRui Paulo KASSERT(localid != 0, ("localid == 0"));
330632176cfdSRui Paulo
330732176cfdSRui Paulo *frm++ = IEEE80211_ELEMID_MESHPEER;
330832176cfdSRui Paulo switch (subtype) {
3309085ff963SMatthew Dillon case IEEE80211_ACTION_MESHPEERING_OPEN:
3310085ff963SMatthew Dillon *frm++ = IEEE80211_MPM_BASE_SZ; /* length */
3311085ff963SMatthew Dillon ADDSHORT(frm, IEEE80211_MPPID_MPM); /* proto */
331232176cfdSRui Paulo ADDSHORT(frm, localid); /* local ID */
331332176cfdSRui Paulo break;
3314085ff963SMatthew Dillon case IEEE80211_ACTION_MESHPEERING_CONFIRM:
331532176cfdSRui Paulo KASSERT(peerid != 0, ("sending peer confirm without peer id"));
3316085ff963SMatthew Dillon *frm++ = IEEE80211_MPM_BASE_SZ + 2; /* length */
3317085ff963SMatthew Dillon ADDSHORT(frm, IEEE80211_MPPID_MPM); /* proto */
331832176cfdSRui Paulo ADDSHORT(frm, localid); /* local ID */
331932176cfdSRui Paulo ADDSHORT(frm, peerid); /* peer ID */
332032176cfdSRui Paulo break;
3321085ff963SMatthew Dillon case IEEE80211_ACTION_MESHPEERING_CLOSE:
332232176cfdSRui Paulo if (peerid)
3323085ff963SMatthew Dillon *frm++ = IEEE80211_MPM_MAX_SZ; /* length */
332432176cfdSRui Paulo else
3325085ff963SMatthew Dillon *frm++ = IEEE80211_MPM_BASE_SZ + 2; /* length */
3326085ff963SMatthew Dillon ADDSHORT(frm, IEEE80211_MPPID_MPM); /* proto */
332732176cfdSRui Paulo ADDSHORT(frm, localid); /* local ID */
332832176cfdSRui Paulo if (peerid)
332932176cfdSRui Paulo ADDSHORT(frm, peerid); /* peer ID */
333032176cfdSRui Paulo ADDSHORT(frm, reason);
333132176cfdSRui Paulo break;
333232176cfdSRui Paulo }
333332176cfdSRui Paulo return frm;
333432176cfdSRui Paulo }
333532176cfdSRui Paulo
333632176cfdSRui Paulo /*
333732176cfdSRui Paulo * Compute an Airtime Link Metric for the link with this node.
333832176cfdSRui Paulo *
333932176cfdSRui Paulo * Based on Draft 3.0 spec (11B.10, p.149).
334032176cfdSRui Paulo */
334132176cfdSRui Paulo /*
334232176cfdSRui Paulo * Max 802.11s overhead.
334332176cfdSRui Paulo */
334432176cfdSRui Paulo #define IEEE80211_MESH_MAXOVERHEAD \
334532176cfdSRui Paulo (sizeof(struct ieee80211_qosframe_addr4) \
3346085ff963SMatthew Dillon + sizeof(struct ieee80211_meshcntl_ae10) \
334732176cfdSRui Paulo + sizeof(struct llc) \
334832176cfdSRui Paulo + IEEE80211_ADDR_LEN \
334932176cfdSRui Paulo + IEEE80211_WEP_IVLEN \
335032176cfdSRui Paulo + IEEE80211_WEP_KIDLEN \
335132176cfdSRui Paulo + IEEE80211_WEP_CRCLEN \
335232176cfdSRui Paulo + IEEE80211_WEP_MICLEN \
335332176cfdSRui Paulo + IEEE80211_CRC_LEN)
335432176cfdSRui Paulo uint32_t
mesh_airtime_calc(struct ieee80211_node * ni)335532176cfdSRui Paulo mesh_airtime_calc(struct ieee80211_node *ni)
335632176cfdSRui Paulo {
335732176cfdSRui Paulo #define M_BITS 8
335832176cfdSRui Paulo #define S_FACTOR (2 * M_BITS)
335932176cfdSRui Paulo struct ieee80211com *ic = ni->ni_ic;
336032176cfdSRui Paulo struct ifnet *ifp = ni->ni_vap->iv_ifp;
3361db11cb20SSascha Wildner static const int nbits = 8192 << M_BITS;
336232176cfdSRui Paulo uint32_t overhead, rate, errrate;
336332176cfdSRui Paulo uint64_t res;
336432176cfdSRui Paulo
336532176cfdSRui Paulo /* Time to transmit a frame */
336632176cfdSRui Paulo rate = ni->ni_txrate;
336732176cfdSRui Paulo overhead = ieee80211_compute_duration(ic->ic_rt,
336832176cfdSRui Paulo ifp->if_mtu + IEEE80211_MESH_MAXOVERHEAD, rate, 0) << M_BITS;
336932176cfdSRui Paulo /* Error rate in percentage */
337032176cfdSRui Paulo /* XXX assuming small failures are ok */
3371085ff963SMatthew Dillon #if defined(__DragonFly__)
3372085ff963SMatthew Dillon u_long icount;
3373085ff963SMatthew Dillon u_long ocount;
3374085ff963SMatthew Dillon IFNET_STAT_GET(ifp, ierrors, icount);
3375085ff963SMatthew Dillon IFNET_STAT_GET(ifp, oerrors, ocount);
3376085ff963SMatthew Dillon errrate = (((ocount + icount) / 100) << M_BITS)
3377085ff963SMatthew Dillon / 100;
3378085ff963SMatthew Dillon #else
3379085ff963SMatthew Dillon errrate = (((ifp->if_get_counter(ifp, IFCOUNTER_OERRORS) +
3380085ff963SMatthew Dillon ifp->if_get_counter(ifp, IFCOUNTER_IERRORS)) / 100) << M_BITS)
3381085ff963SMatthew Dillon / 100;
3382085ff963SMatthew Dillon #endif
338332176cfdSRui Paulo res = (overhead + (nbits / rate)) *
338432176cfdSRui Paulo ((1 << S_FACTOR) / ((1 << M_BITS) - errrate));
338532176cfdSRui Paulo
338632176cfdSRui Paulo return (uint32_t)(res >> S_FACTOR);
338732176cfdSRui Paulo #undef M_BITS
338832176cfdSRui Paulo #undef S_FACTOR
338932176cfdSRui Paulo }
339032176cfdSRui Paulo
339132176cfdSRui Paulo /*
339232176cfdSRui Paulo * Add a Mesh Link Metric report IE to a frame.
339332176cfdSRui Paulo */
339432176cfdSRui Paulo uint8_t *
ieee80211_add_meshlmetric(uint8_t * frm,uint8_t flags,uint32_t metric)3395085ff963SMatthew Dillon ieee80211_add_meshlmetric(uint8_t *frm, uint8_t flags, uint32_t metric)
339632176cfdSRui Paulo {
339732176cfdSRui Paulo *frm++ = IEEE80211_ELEMID_MESHLINK;
3398085ff963SMatthew Dillon *frm++ = 5;
3399085ff963SMatthew Dillon *frm++ = flags;
340032176cfdSRui Paulo ADDWORD(frm, metric);
340132176cfdSRui Paulo return frm;
340232176cfdSRui Paulo }
3403085ff963SMatthew Dillon
3404085ff963SMatthew Dillon /*
3405085ff963SMatthew Dillon * Add a Mesh Gate Announcement IE to a frame.
3406085ff963SMatthew Dillon */
3407085ff963SMatthew Dillon uint8_t *
ieee80211_add_meshgate(uint8_t * frm,struct ieee80211_meshgann_ie * ie)3408085ff963SMatthew Dillon ieee80211_add_meshgate(uint8_t *frm, struct ieee80211_meshgann_ie *ie)
3409085ff963SMatthew Dillon {
3410085ff963SMatthew Dillon *frm++ = IEEE80211_ELEMID_MESHGANN; /* ie */
3411085ff963SMatthew Dillon *frm++ = IEEE80211_MESHGANN_BASE_SZ; /* len */
3412085ff963SMatthew Dillon *frm++ = ie->gann_flags;
3413085ff963SMatthew Dillon *frm++ = ie->gann_hopcount;
3414085ff963SMatthew Dillon *frm++ = ie->gann_ttl;
3415085ff963SMatthew Dillon IEEE80211_ADDR_COPY(frm, ie->gann_addr);
3416085ff963SMatthew Dillon frm += 6;
3417085ff963SMatthew Dillon ADDWORD(frm, ie->gann_seq);
3418085ff963SMatthew Dillon ADDSHORT(frm, ie->gann_interval);
3419085ff963SMatthew Dillon return frm;
3420085ff963SMatthew Dillon }
342132176cfdSRui Paulo #undef ADDSHORT
342232176cfdSRui Paulo #undef ADDWORD
342332176cfdSRui Paulo
342432176cfdSRui Paulo /*
342532176cfdSRui Paulo * Initialize any mesh-specific node state.
342632176cfdSRui Paulo */
342732176cfdSRui Paulo void
ieee80211_mesh_node_init(struct ieee80211vap * vap,struct ieee80211_node * ni)342832176cfdSRui Paulo ieee80211_mesh_node_init(struct ieee80211vap *vap, struct ieee80211_node *ni)
342932176cfdSRui Paulo {
343032176cfdSRui Paulo ni->ni_flags |= IEEE80211_NODE_QOS;
3431*4f655ef5SMatthew Dillon #if defined(__DragonFly__)
343234a60cf6SRui Paulo callout_init_mp(&ni->ni_mltimer);
3433085ff963SMatthew Dillon callout_init_mp(&ni->ni_mlhtimer);
3434*4f655ef5SMatthew Dillon #else
3435*4f655ef5SMatthew Dillon callout_init(&ni->ni_mltimer, 1);
3436*4f655ef5SMatthew Dillon callout_init(&ni->ni_mlhtimer, 1);
3437*4f655ef5SMatthew Dillon #endif
343832176cfdSRui Paulo }
343932176cfdSRui Paulo
344032176cfdSRui Paulo /*
344132176cfdSRui Paulo * Cleanup any mesh-specific node state.
344232176cfdSRui Paulo */
344332176cfdSRui Paulo void
ieee80211_mesh_node_cleanup(struct ieee80211_node * ni)344432176cfdSRui Paulo ieee80211_mesh_node_cleanup(struct ieee80211_node *ni)
344532176cfdSRui Paulo {
344632176cfdSRui Paulo struct ieee80211vap *vap = ni->ni_vap;
344732176cfdSRui Paulo struct ieee80211_mesh_state *ms = vap->iv_mesh;
344832176cfdSRui Paulo
3449085ff963SMatthew Dillon callout_drain(&ni->ni_mltimer);
3450085ff963SMatthew Dillon callout_drain(&ni->ni_mlhtimer);
345132176cfdSRui Paulo /* NB: short-circuit callbacks after mesh_vdetach */
345232176cfdSRui Paulo if (vap->iv_mesh != NULL)
345332176cfdSRui Paulo ms->ms_ppath->mpp_peerdown(ni);
345432176cfdSRui Paulo }
345532176cfdSRui Paulo
345632176cfdSRui Paulo void
ieee80211_parse_meshid(struct ieee80211_node * ni,const uint8_t * ie)345732176cfdSRui Paulo ieee80211_parse_meshid(struct ieee80211_node *ni, const uint8_t *ie)
345832176cfdSRui Paulo {
345932176cfdSRui Paulo ni->ni_meshidlen = ie[1];
346032176cfdSRui Paulo memcpy(ni->ni_meshid, ie + 2, ie[1]);
346132176cfdSRui Paulo }
346232176cfdSRui Paulo
346332176cfdSRui Paulo /*
346432176cfdSRui Paulo * Setup mesh-specific node state on neighbor discovery.
346532176cfdSRui Paulo */
346632176cfdSRui Paulo void
ieee80211_mesh_init_neighbor(struct ieee80211_node * ni,const struct ieee80211_frame * wh,const struct ieee80211_scanparams * sp)346732176cfdSRui Paulo ieee80211_mesh_init_neighbor(struct ieee80211_node *ni,
346832176cfdSRui Paulo const struct ieee80211_frame *wh,
346932176cfdSRui Paulo const struct ieee80211_scanparams *sp)
347032176cfdSRui Paulo {
347132176cfdSRui Paulo ieee80211_parse_meshid(ni, sp->meshid);
347232176cfdSRui Paulo }
347332176cfdSRui Paulo
347432176cfdSRui Paulo void
ieee80211_mesh_update_beacon(struct ieee80211vap * vap,struct ieee80211_beacon_offsets * bo)347532176cfdSRui Paulo ieee80211_mesh_update_beacon(struct ieee80211vap *vap,
347632176cfdSRui Paulo struct ieee80211_beacon_offsets *bo)
347732176cfdSRui Paulo {
347832176cfdSRui Paulo KASSERT(vap->iv_opmode == IEEE80211_M_MBSS, ("not a MBSS vap"));
347932176cfdSRui Paulo
348032176cfdSRui Paulo if (isset(bo->bo_flags, IEEE80211_BEACON_MESHCONF)) {
348132176cfdSRui Paulo (void)ieee80211_add_meshconf(bo->bo_meshconf, vap);
348232176cfdSRui Paulo clrbit(bo->bo_flags, IEEE80211_BEACON_MESHCONF);
348332176cfdSRui Paulo }
348432176cfdSRui Paulo }
348532176cfdSRui Paulo
348632176cfdSRui Paulo static int
mesh_ioctl_get80211(struct ieee80211vap * vap,struct ieee80211req * ireq)348732176cfdSRui Paulo mesh_ioctl_get80211(struct ieee80211vap *vap, struct ieee80211req *ireq)
348832176cfdSRui Paulo {
348932176cfdSRui Paulo struct ieee80211_mesh_state *ms = vap->iv_mesh;
349032176cfdSRui Paulo uint8_t tmpmeshid[IEEE80211_NWID_LEN];
349132176cfdSRui Paulo struct ieee80211_mesh_route *rt;
349232176cfdSRui Paulo struct ieee80211req_mesh_route *imr;
349332176cfdSRui Paulo size_t len, off;
349432176cfdSRui Paulo uint8_t *p;
349532176cfdSRui Paulo int error;
349632176cfdSRui Paulo
349732176cfdSRui Paulo if (vap->iv_opmode != IEEE80211_M_MBSS)
349832176cfdSRui Paulo return ENOSYS;
349932176cfdSRui Paulo
350032176cfdSRui Paulo error = 0;
350132176cfdSRui Paulo switch (ireq->i_type) {
350232176cfdSRui Paulo case IEEE80211_IOC_MESH_ID:
350332176cfdSRui Paulo ireq->i_len = ms->ms_idlen;
350432176cfdSRui Paulo memcpy(tmpmeshid, ms->ms_id, ireq->i_len);
350532176cfdSRui Paulo error = copyout(tmpmeshid, ireq->i_data, ireq->i_len);
350632176cfdSRui Paulo break;
350732176cfdSRui Paulo case IEEE80211_IOC_MESH_AP:
350832176cfdSRui Paulo ireq->i_val = (ms->ms_flags & IEEE80211_MESHFLAGS_AP) != 0;
350932176cfdSRui Paulo break;
351032176cfdSRui Paulo case IEEE80211_IOC_MESH_FWRD:
351132176cfdSRui Paulo ireq->i_val = (ms->ms_flags & IEEE80211_MESHFLAGS_FWD) != 0;
351232176cfdSRui Paulo break;
3513085ff963SMatthew Dillon case IEEE80211_IOC_MESH_GATE:
3514085ff963SMatthew Dillon ireq->i_val = (ms->ms_flags & IEEE80211_MESHFLAGS_GATE) != 0;
3515085ff963SMatthew Dillon break;
351632176cfdSRui Paulo case IEEE80211_IOC_MESH_TTL:
351732176cfdSRui Paulo ireq->i_val = ms->ms_ttl;
351832176cfdSRui Paulo break;
351932176cfdSRui Paulo case IEEE80211_IOC_MESH_RTCMD:
352032176cfdSRui Paulo switch (ireq->i_val) {
352132176cfdSRui Paulo case IEEE80211_MESH_RTCMD_LIST:
352232176cfdSRui Paulo len = 0;
3523085ff963SMatthew Dillon MESH_RT_LOCK(ms);
352432176cfdSRui Paulo TAILQ_FOREACH(rt, &ms->ms_routes, rt_next) {
352532176cfdSRui Paulo len += sizeof(*imr);
352632176cfdSRui Paulo }
3527085ff963SMatthew Dillon MESH_RT_UNLOCK(ms);
352832176cfdSRui Paulo if (len > ireq->i_len || ireq->i_len < sizeof(*imr)) {
352932176cfdSRui Paulo ireq->i_len = len;
353032176cfdSRui Paulo return ENOMEM;
353132176cfdSRui Paulo }
353232176cfdSRui Paulo ireq->i_len = len;
353332176cfdSRui Paulo /* XXX M_WAIT? */
3534*4f655ef5SMatthew Dillon #if defined(__DragonFly__)
3535fcaa651dSRui Paulo p = kmalloc(len, M_TEMP, M_INTWAIT | M_ZERO);
3536*4f655ef5SMatthew Dillon #else
3537*4f655ef5SMatthew Dillon p = IEEE80211_MALLOC(len, M_TEMP,
3538*4f655ef5SMatthew Dillon IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
3539*4f655ef5SMatthew Dillon #endif
3540085ff963SMatthew Dillon if (p == NULL)
3541085ff963SMatthew Dillon return ENOMEM;
354232176cfdSRui Paulo off = 0;
3543085ff963SMatthew Dillon MESH_RT_LOCK(ms);
354432176cfdSRui Paulo TAILQ_FOREACH(rt, &ms->ms_routes, rt_next) {
354532176cfdSRui Paulo if (off >= len)
354632176cfdSRui Paulo break;
354732176cfdSRui Paulo imr = (struct ieee80211req_mesh_route *)
354832176cfdSRui Paulo (p + off);
354932176cfdSRui Paulo IEEE80211_ADDR_COPY(imr->imr_dest,
355032176cfdSRui Paulo rt->rt_dest);
355132176cfdSRui Paulo IEEE80211_ADDR_COPY(imr->imr_nexthop,
355232176cfdSRui Paulo rt->rt_nexthop);
355332176cfdSRui Paulo imr->imr_metric = rt->rt_metric;
355432176cfdSRui Paulo imr->imr_nhops = rt->rt_nhops;
3555085ff963SMatthew Dillon imr->imr_lifetime =
3556085ff963SMatthew Dillon ieee80211_mesh_rt_update(rt, 0);
355732176cfdSRui Paulo imr->imr_lastmseq = rt->rt_lastmseq;
3558085ff963SMatthew Dillon imr->imr_flags = rt->rt_flags; /* last */
355932176cfdSRui Paulo off += sizeof(*imr);
356032176cfdSRui Paulo }
3561085ff963SMatthew Dillon MESH_RT_UNLOCK(ms);
356232176cfdSRui Paulo error = copyout(p, (uint8_t *)ireq->i_data,
356332176cfdSRui Paulo ireq->i_len);
3564*4f655ef5SMatthew Dillon IEEE80211_FREE(p, M_TEMP);
356532176cfdSRui Paulo break;
356632176cfdSRui Paulo case IEEE80211_MESH_RTCMD_FLUSH:
356732176cfdSRui Paulo case IEEE80211_MESH_RTCMD_ADD:
356832176cfdSRui Paulo case IEEE80211_MESH_RTCMD_DELETE:
356932176cfdSRui Paulo return EINVAL;
357032176cfdSRui Paulo default:
357132176cfdSRui Paulo return ENOSYS;
357232176cfdSRui Paulo }
357332176cfdSRui Paulo break;
357432176cfdSRui Paulo case IEEE80211_IOC_MESH_PR_METRIC:
357532176cfdSRui Paulo len = strlen(ms->ms_pmetric->mpm_descr);
357632176cfdSRui Paulo if (ireq->i_len < len)
357732176cfdSRui Paulo return EINVAL;
357832176cfdSRui Paulo ireq->i_len = len;
357932176cfdSRui Paulo error = copyout(ms->ms_pmetric->mpm_descr,
358032176cfdSRui Paulo (uint8_t *)ireq->i_data, len);
358132176cfdSRui Paulo break;
358232176cfdSRui Paulo case IEEE80211_IOC_MESH_PR_PATH:
358332176cfdSRui Paulo len = strlen(ms->ms_ppath->mpp_descr);
358432176cfdSRui Paulo if (ireq->i_len < len)
358532176cfdSRui Paulo return EINVAL;
358632176cfdSRui Paulo ireq->i_len = len;
358732176cfdSRui Paulo error = copyout(ms->ms_ppath->mpp_descr,
358832176cfdSRui Paulo (uint8_t *)ireq->i_data, len);
358932176cfdSRui Paulo break;
359032176cfdSRui Paulo default:
359132176cfdSRui Paulo return ENOSYS;
359232176cfdSRui Paulo }
359332176cfdSRui Paulo
359432176cfdSRui Paulo return error;
359532176cfdSRui Paulo }
359632176cfdSRui Paulo IEEE80211_IOCTL_GET(mesh, mesh_ioctl_get80211);
359732176cfdSRui Paulo
359832176cfdSRui Paulo static int
mesh_ioctl_set80211(struct ieee80211vap * vap,struct ieee80211req * ireq)359932176cfdSRui Paulo mesh_ioctl_set80211(struct ieee80211vap *vap, struct ieee80211req *ireq)
360032176cfdSRui Paulo {
360132176cfdSRui Paulo struct ieee80211_mesh_state *ms = vap->iv_mesh;
360232176cfdSRui Paulo uint8_t tmpmeshid[IEEE80211_NWID_LEN];
360332176cfdSRui Paulo uint8_t tmpaddr[IEEE80211_ADDR_LEN];
360432176cfdSRui Paulo char tmpproto[IEEE80211_MESH_PROTO_DSZ];
360532176cfdSRui Paulo int error;
360632176cfdSRui Paulo
360732176cfdSRui Paulo if (vap->iv_opmode != IEEE80211_M_MBSS)
360832176cfdSRui Paulo return ENOSYS;
360932176cfdSRui Paulo
361032176cfdSRui Paulo error = 0;
361132176cfdSRui Paulo switch (ireq->i_type) {
361232176cfdSRui Paulo case IEEE80211_IOC_MESH_ID:
361332176cfdSRui Paulo if (ireq->i_val != 0 || ireq->i_len > IEEE80211_MESHID_LEN)
361432176cfdSRui Paulo return EINVAL;
361532176cfdSRui Paulo error = copyin(ireq->i_data, tmpmeshid, ireq->i_len);
361632176cfdSRui Paulo if (error != 0)
361732176cfdSRui Paulo break;
361832176cfdSRui Paulo memset(ms->ms_id, 0, IEEE80211_NWID_LEN);
361932176cfdSRui Paulo ms->ms_idlen = ireq->i_len;
362032176cfdSRui Paulo memcpy(ms->ms_id, tmpmeshid, ireq->i_len);
362132176cfdSRui Paulo error = ENETRESET;
362232176cfdSRui Paulo break;
362332176cfdSRui Paulo case IEEE80211_IOC_MESH_AP:
362432176cfdSRui Paulo if (ireq->i_val)
362532176cfdSRui Paulo ms->ms_flags |= IEEE80211_MESHFLAGS_AP;
362632176cfdSRui Paulo else
362732176cfdSRui Paulo ms->ms_flags &= ~IEEE80211_MESHFLAGS_AP;
362832176cfdSRui Paulo error = ENETRESET;
362932176cfdSRui Paulo break;
363032176cfdSRui Paulo case IEEE80211_IOC_MESH_FWRD:
363132176cfdSRui Paulo if (ireq->i_val)
363232176cfdSRui Paulo ms->ms_flags |= IEEE80211_MESHFLAGS_FWD;
363332176cfdSRui Paulo else
363432176cfdSRui Paulo ms->ms_flags &= ~IEEE80211_MESHFLAGS_FWD;
3635085ff963SMatthew Dillon mesh_gatemode_setup(vap);
3636085ff963SMatthew Dillon break;
3637085ff963SMatthew Dillon case IEEE80211_IOC_MESH_GATE:
3638085ff963SMatthew Dillon if (ireq->i_val)
3639085ff963SMatthew Dillon ms->ms_flags |= IEEE80211_MESHFLAGS_GATE;
3640085ff963SMatthew Dillon else
3641085ff963SMatthew Dillon ms->ms_flags &= ~IEEE80211_MESHFLAGS_GATE;
364232176cfdSRui Paulo break;
364332176cfdSRui Paulo case IEEE80211_IOC_MESH_TTL:
364432176cfdSRui Paulo ms->ms_ttl = (uint8_t) ireq->i_val;
364532176cfdSRui Paulo break;
364632176cfdSRui Paulo case IEEE80211_IOC_MESH_RTCMD:
364732176cfdSRui Paulo switch (ireq->i_val) {
364832176cfdSRui Paulo case IEEE80211_MESH_RTCMD_LIST:
364932176cfdSRui Paulo return EINVAL;
365032176cfdSRui Paulo case IEEE80211_MESH_RTCMD_FLUSH:
365132176cfdSRui Paulo ieee80211_mesh_rt_flush(vap);
365232176cfdSRui Paulo break;
365332176cfdSRui Paulo case IEEE80211_MESH_RTCMD_ADD:
365432176cfdSRui Paulo if (IEEE80211_ADDR_EQ(vap->iv_myaddr, ireq->i_data) ||
365532176cfdSRui Paulo IEEE80211_ADDR_EQ(broadcastaddr, ireq->i_data))
365632176cfdSRui Paulo return EINVAL;
365732176cfdSRui Paulo error = copyin(ireq->i_data, &tmpaddr,
365832176cfdSRui Paulo IEEE80211_ADDR_LEN);
365932176cfdSRui Paulo if (error == 0)
366032176cfdSRui Paulo ieee80211_mesh_discover(vap, tmpaddr, NULL);
366132176cfdSRui Paulo break;
366232176cfdSRui Paulo case IEEE80211_MESH_RTCMD_DELETE:
366332176cfdSRui Paulo ieee80211_mesh_rt_del(vap, ireq->i_data);
366432176cfdSRui Paulo break;
366532176cfdSRui Paulo default:
366632176cfdSRui Paulo return ENOSYS;
366732176cfdSRui Paulo }
366832176cfdSRui Paulo break;
366932176cfdSRui Paulo case IEEE80211_IOC_MESH_PR_METRIC:
367032176cfdSRui Paulo error = copyin(ireq->i_data, tmpproto, sizeof(tmpproto));
367132176cfdSRui Paulo if (error == 0) {
367232176cfdSRui Paulo error = mesh_select_proto_metric(vap, tmpproto);
367332176cfdSRui Paulo if (error == 0)
367432176cfdSRui Paulo error = ENETRESET;
367532176cfdSRui Paulo }
367632176cfdSRui Paulo break;
367732176cfdSRui Paulo case IEEE80211_IOC_MESH_PR_PATH:
367832176cfdSRui Paulo error = copyin(ireq->i_data, tmpproto, sizeof(tmpproto));
367932176cfdSRui Paulo if (error == 0) {
368032176cfdSRui Paulo error = mesh_select_proto_path(vap, tmpproto);
368132176cfdSRui Paulo if (error == 0)
368232176cfdSRui Paulo error = ENETRESET;
368332176cfdSRui Paulo }
368432176cfdSRui Paulo break;
368532176cfdSRui Paulo default:
368632176cfdSRui Paulo return ENOSYS;
368732176cfdSRui Paulo }
368832176cfdSRui Paulo return error;
368932176cfdSRui Paulo }
369032176cfdSRui Paulo IEEE80211_IOCTL_SET(mesh, mesh_ioctl_set80211);
3691