summaryrefslogtreecommitdiffstats
path: root/sys/net80211
diff options
context:
space:
mode:
authorrpaulo <rpaulo@FreeBSD.org>2009-07-11 15:02:45 +0000
committerrpaulo <rpaulo@FreeBSD.org>2009-07-11 15:02:45 +0000
commit8424d740209fc6cee8a8bc4deba2a40cdc77d1fd (patch)
tree7dd4e6a8c026ec13b70ca0a34e625684d79ec055 /sys/net80211
parentba5583d31888cb78136b114790fce49c792c14d0 (diff)
downloadFreeBSD-src-8424d740209fc6cee8a8bc4deba2a40cdc77d1fd.zip
FreeBSD-src-8424d740209fc6cee8a8bc4deba2a40cdc77d1fd.tar.gz
Implementation of the upcoming Wireless Mesh standard, 802.11s, on the
net80211 wireless stack. This work is based on the March 2009 D3.0 draft standard. This standard is expected to become final next year. This includes two main net80211 modules, ieee80211_mesh.c which deals with peer link management, link metric calculation, routing table control and mesh configuration and ieee80211_hwmp.c which deals with the actually routing process on the mesh network. HWMP is the mandatory routing protocol on by the mesh standard, but others, such as RA-OLSR, can be implemented. Authentication and encryption are not implemented. There are several scripts under tools/tools/net80211/scripts that can be used to test different mesh network topologies and they also teach you how to setup a mesh vap (for the impatient: ifconfig wlan0 create wlandev ... wlanmode mesh). A new build option is available: IEEE80211_SUPPORT_MESH and it's enabled by default on GENERIC kernels for i386, amd64, sparc64 and pc98. Drivers that support mesh networks right now are: ath, ral and mwl. More information at: http://wiki.freebsd.org/WifiMesh Please note that this work is experimental. Also, please note that bridging a mesh vap with another network interface is not yet supported. Many thanks to the FreeBSD Foundation for sponsoring this project and to Sam Leffler for his support. Also, I would like to thank Gateworks Corporation for sending me a Cambria board which was used during the development of this project. Reviewed by: sam Approved by: re (kensmith) Obtained from: projects/mesh11s
Diffstat (limited to 'sys/net80211')
-rw-r--r--sys/net80211/_ieee80211.h3
-rw-r--r--sys/net80211/ieee80211.c8
-rw-r--r--sys/net80211/ieee80211.h36
-rw-r--r--sys/net80211/ieee80211_action.c14
-rw-r--r--sys/net80211/ieee80211_ddb.c73
-rw-r--r--sys/net80211/ieee80211_freebsd.h13
-rw-r--r--sys/net80211/ieee80211_hwmp.c1389
-rw-r--r--sys/net80211/ieee80211_input.c25
-rw-r--r--sys/net80211/ieee80211_ioctl.c19
-rw-r--r--sys/net80211/ieee80211_ioctl.h58
-rw-r--r--sys/net80211/ieee80211_mesh.c2538
-rw-r--r--sys/net80211/ieee80211_mesh.h505
-rw-r--r--sys/net80211/ieee80211_node.c73
-rw-r--r--sys/net80211/ieee80211_node.h31
-rw-r--r--sys/net80211/ieee80211_output.c303
-rw-r--r--sys/net80211/ieee80211_proto.c15
-rw-r--r--sys/net80211/ieee80211_proto.h8
-rw-r--r--sys/net80211/ieee80211_scan.c1
-rw-r--r--sys/net80211/ieee80211_scan.h3
-rw-r--r--sys/net80211/ieee80211_scan_sta.c184
-rw-r--r--sys/net80211/ieee80211_var.h15
21 files changed, 5191 insertions, 123 deletions
diff --git a/sys/net80211/_ieee80211.h b/sys/net80211/_ieee80211.h
index 163e225..c488c00 100644
--- a/sys/net80211/_ieee80211.h
+++ b/sys/net80211/_ieee80211.h
@@ -83,8 +83,9 @@ enum ieee80211_opmode {
IEEE80211_M_AHDEMO = 3, /* Old lucent compatible adhoc demo */
IEEE80211_M_HOSTAP = 4, /* Software Access Point */
IEEE80211_M_MONITOR = 5, /* Monitor mode */
+ IEEE80211_M_MBSS = 6, /* MBSS (Mesh Point) link */
};
-#define IEEE80211_OPMODE_MAX (IEEE80211_M_MONITOR+1)
+#define IEEE80211_OPMODE_MAX (IEEE80211_M_MBSS+1)
/*
* 802.11g/802.11n protection mode.
diff --git a/sys/net80211/ieee80211.c b/sys/net80211/ieee80211.c
index a887997..1cc4964 100644
--- a/sys/net80211/ieee80211.c
+++ b/sys/net80211/ieee80211.c
@@ -74,6 +74,9 @@ const int ieee80211_opcap[IEEE80211_OPMODE_MAX] = {
[IEEE80211_M_AHDEMO] = IEEE80211_C_AHDEMO,
[IEEE80211_M_HOSTAP] = IEEE80211_C_HOSTAP,
[IEEE80211_M_MONITOR] = IEEE80211_C_MONITOR,
+#ifdef IEEE80211_SUPPORT_MESH
+ [IEEE80211_M_MBSS] = IEEE80211_C_MBSS,
+#endif
};
static const uint8_t ieee80211broadcastaddr[IEEE80211_ADDR_LEN] =
@@ -960,6 +963,8 @@ addmedia(struct ifmedia *media, int caps, int addsta, int mode, int mword)
ADD(media, mword, mopt | IFM_IEEE80211_MONITOR);
if (caps & IEEE80211_C_WDS)
ADD(media, mword, mopt | IFM_IEEE80211_WDS);
+ if (caps & IEEE80211_C_MBSS)
+ ADD(media, mword, mopt | IFM_IEEE80211_MBSS);
#undef ADD
}
@@ -1264,6 +1269,9 @@ media_status(enum ieee80211_opmode opmode, const struct ieee80211_channel *chan)
case IEEE80211_M_WDS:
status |= IFM_IEEE80211_WDS;
break;
+ case IEEE80211_M_MBSS:
+ status |= IFM_IEEE80211_MBSS;
+ break;
}
if (IEEE80211_IS_CHAN_HTA(chan)) {
status |= IFM_IEEE80211_11NA;
diff --git a/sys/net80211/ieee80211.h b/sys/net80211/ieee80211.h
index d1c7658..5ddca24 100644
--- a/sys/net80211/ieee80211.h
+++ b/sys/net80211/ieee80211.h
@@ -185,6 +185,7 @@ struct ieee80211_qosframe_addr4 {
(IEEE80211_SEQ_SUB(b, a+1) < IEEE80211_SEQ_BA_RANGE-1)
#define IEEE80211_NWID_LEN 32
+#define IEEE80211_MESHID_LEN 32
#define IEEE80211_QOS_TXOP 0x00ff
/* bit 8 is reserved */
@@ -705,6 +706,30 @@ enum {
IEEE80211_ELEMID_TPC = 150,
IEEE80211_ELEMID_CCKM = 156,
IEEE80211_ELEMID_VENDOR = 221, /* vendor private */
+
+ /*
+ * 802.11s IEs based on D3.0 spec and were not assigned by
+ * ANA. Beware changing them because some of them are being
+ * kept compatible with Linux.
+ */
+ IEEE80211_ELEMID_MESHCONF = 51,
+ IEEE80211_ELEMID_MESHID = 52,
+ IEEE80211_ELEMID_MESHLINK = 35,
+ IEEE80211_ELEMID_MESHCNGST = 36,
+ IEEE80211_ELEMID_MESHPEER = 55,
+ IEEE80211_ELEMID_MESHCSA = 38,
+ IEEE80211_ELEMID_MESHTIM = 39,
+ IEEE80211_ELEMID_MESHAWAKEW = 40,
+ IEEE80211_ELEMID_MESHBEACONT = 41,
+ IEEE80211_ELEMID_MESHPANN = 48,
+ IEEE80211_ELEMID_MESHRANN = 49,
+ IEEE80211_ELEMID_MESHPREQ = 68,
+ IEEE80211_ELEMID_MESHPREP = 69,
+ IEEE80211_ELEMID_MESHPERR = 70,
+ IEEE80211_ELEMID_MESHPU = 53,
+ IEEE80211_ELEMID_MESHPUC = 54,
+ IEEE80211_ELEMID_MESHAH = 60, /* Abbreviated Handshake */
+ IEEE80211_ELEMID_MESHPEERVER = 80, /* Peering Protocol Version */
};
struct ieee80211_tim_ie {
@@ -890,6 +915,17 @@ enum {
IEEE80211_REASON_SETUP_NEEDED = 38, /* 11e */
IEEE80211_REASON_TIMEOUT = 39, /* 11e */
+ /* values not yet allocated by ANA */
+ IEEE80211_REASON_PEER_LINK_CANCELED = 2, /* 11s */
+ IEEE80211_REASON_MESH_MAX_PEERS = 3, /* 11s */
+ IEEE80211_REASON_MESH_CPVIOLATION = 4, /* 11s */
+ IEEE80211_REASON_MESH_CLOSE_RCVD = 5, /* 11s */
+ IEEE80211_REASON_MESH_MAX_RETRIES = 6, /* 11s */
+ IEEE80211_REASON_MESH_CONFIRM_TIMEOUT = 7, /* 11s */
+ IEEE80211_REASON_MESH_INVALID_GTK = 8, /* 11s */
+ IEEE80211_REASON_MESH_INCONS_PARAMS = 9, /* 11s */
+ IEEE80211_REASON_MESH_INVALID_SECURITY = 10, /* 11s */
+
IEEE80211_STATUS_SUCCESS = 0,
IEEE80211_STATUS_UNSPECIFIED = 1,
IEEE80211_STATUS_CAPINFO = 10,
diff --git a/sys/net80211/ieee80211_action.c b/sys/net80211/ieee80211_action.c
index a694d25..5371f6e 100644
--- a/sys/net80211/ieee80211_action.c
+++ b/sys/net80211/ieee80211_action.c
@@ -47,6 +47,7 @@ __FBSDID("$FreeBSD$");
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_action.h>
+#include <net80211/ieee80211_mesh.h>
static int
send_inval(struct ieee80211_node *ni, int cat, int act, void *sa)
@@ -62,8 +63,6 @@ static ieee80211_send_action_func *ht_send_action[8] = {
send_inval, send_inval, send_inval, send_inval,
send_inval, send_inval, send_inval, send_inval,
};
-/* NB: temporary until 802.11s support is added */
-#ifdef IEEE80211_ACTION_CAT_MESHPEERING
static ieee80211_send_action_func *meshpl_send_action[8] = {
send_inval, send_inval, send_inval, send_inval,
send_inval, send_inval, send_inval, send_inval,
@@ -75,7 +74,6 @@ static ieee80211_send_action_func *hwmp_send_action[8] = {
send_inval, send_inval, send_inval, send_inval,
send_inval, send_inval, send_inval, send_inval,
};
-#endif
static ieee80211_send_action_func *vendor_send_action[8] = {
send_inval, send_inval, send_inval, send_inval,
send_inval, send_inval, send_inval, send_inval,
@@ -96,7 +94,6 @@ ieee80211_send_action_register(int cat, int act, ieee80211_send_action_func *f)
break;
ht_send_action[act] = f;
return 0;
-#ifdef IEEE80211_ACTION_CAT_MESHPEERING
case IEEE80211_ACTION_CAT_MESHPEERING:
if (act >= N(meshpl_send_action))
break;
@@ -112,7 +109,6 @@ ieee80211_send_action_register(int cat, int act, ieee80211_send_action_func *f)
break;
hwmp_send_action[act] = f;
return 0;
-#endif
case IEEE80211_ACTION_CAT_VENDOR:
if (act >= N(vendor_send_action))
break;
@@ -144,7 +140,6 @@ ieee80211_send_action(struct ieee80211_node *ni, int cat, int act, void *sa)
if (act < N(ht_send_action))
f = ht_send_action[act];
break;
-#ifdef IEEE80211_ACTION_CAT_MESHPEERING
case IEEE80211_ACTION_CAT_MESHPEERING:
if (act < N(meshpl_send_action))
f = meshpl_send_action[act];
@@ -157,7 +152,6 @@ ieee80211_send_action(struct ieee80211_node *ni, int cat, int act, void *sa)
if (act < N(hwmp_send_action))
f = hwmp_send_action[act];
break;
-#endif
case IEEE80211_ACTION_CAT_VENDOR:
if (act < N(vendor_send_action))
f = vendor_send_action[act];
@@ -182,7 +176,6 @@ static ieee80211_recv_action_func *ht_recv_action[8] = {
recv_inval, recv_inval, recv_inval, recv_inval,
recv_inval, recv_inval, recv_inval, recv_inval,
};
-#ifdef IEEE80211_ACTION_CAT_MESHPEERING
static ieee80211_recv_action_func *meshpl_recv_action[8] = {
recv_inval, recv_inval, recv_inval, recv_inval,
recv_inval, recv_inval, recv_inval, recv_inval,
@@ -194,7 +187,6 @@ static ieee80211_recv_action_func *hwmp_recv_action[8] = {
recv_inval, recv_inval, recv_inval, recv_inval,
recv_inval, recv_inval, recv_inval, recv_inval,
};
-#endif
static ieee80211_recv_action_func *vendor_recv_action[8] = {
recv_inval, recv_inval, recv_inval, recv_inval,
recv_inval, recv_inval, recv_inval, recv_inval,
@@ -215,7 +207,6 @@ ieee80211_recv_action_register(int cat, int act, ieee80211_recv_action_func *f)
break;
ht_recv_action[act] = f;
return 0;
-#ifdef IEEE80211_ACTION_CAT_MESHPEERING
case IEEE80211_ACTION_CAT_MESHPEERING:
if (act >= N(meshpl_recv_action))
break;
@@ -231,7 +222,6 @@ ieee80211_recv_action_register(int cat, int act, ieee80211_recv_action_func *f)
break;
hwmp_recv_action[act] = f;
return 0;
-#endif
case IEEE80211_ACTION_CAT_VENDOR:
if (act >= N(vendor_recv_action))
break;
@@ -267,7 +257,6 @@ ieee80211_recv_action(struct ieee80211_node *ni,
if (ia->ia_action < N(ht_recv_action))
f = ht_recv_action[ia->ia_action];
break;
-#ifdef IEEE80211_ACTION_CAT_MESHPEERING
case IEEE80211_ACTION_CAT_MESHPEERING:
if (ia->ia_action < N(meshpl_recv_action))
f = meshpl_recv_action[ia->ia_action];
@@ -280,7 +269,6 @@ ieee80211_recv_action(struct ieee80211_node *ni,
if (ia->ia_action < N(hwmp_recv_action))
f = hwmp_recv_action[ia->ia_action];
break;
-#endif
case IEEE80211_ACTION_CAT_VENDOR:
if (ia->ia_action < N(vendor_recv_action))
f = vendor_recv_action[ia->ia_action];
diff --git a/sys/net80211/ieee80211_ddb.c b/sys/net80211/ieee80211_ddb.c
index 6d70930..57447d5 100644
--- a/sys/net80211/ieee80211_ddb.c
+++ b/sys/net80211/ieee80211_ddb.c
@@ -50,6 +50,9 @@ __FBSDID("$FreeBSD$");
#ifdef IEEE80211_SUPPORT_TDMA
#include <net80211/ieee80211_tdma.h>
#endif
+#ifdef IEEE80211_SUPPORT_MESH
+#include <net80211/ieee80211_mesh.h>
+#endif
#include <ddb/ddb.h>
#include <ddb/db_sym.h>
@@ -75,7 +78,11 @@ static void _db_show_roamparams(const char *tag, const void *arg,
const struct ieee80211_roamparam *rp);
static void _db_show_txparams(const char *tag, const void *arg,
const struct ieee80211_txparam *tp);
+static void _db_show_ageq(const char *tag, const struct ieee80211_ageq *q);
static void _db_show_stats(const struct ieee80211_stats *);
+#ifdef IEEE80211_SUPPORT_MESH
+static void _db_show_mesh(const struct ieee80211_mesh_state *);
+#endif
DB_SHOW_COMMAND(sta, db_show_sta)
{
@@ -178,6 +185,20 @@ DB_SHOW_ALL_COMMAND(vaps, db_show_all_vaps)
}
}
+#ifdef IEEE80211_SUPPORT_MESH
+DB_SHOW_ALL_COMMAND(mesh, db_show_mesh)
+{
+ const struct ieee80211_mesh_state *ms;
+
+ if (!have_addr) {
+ db_printf("usage: show mesh <addr>\n");
+ return;
+ }
+ ms = (const struct ieee80211_mesh_state *) addr;
+ _db_show_mesh(ms);
+}
+#endif /* IEEE80211_SUPPORT_MESH */
+
static void
_db_show_txampdu(const char *sep, int ix, const struct ieee80211_tx_ampdu *tap)
{
@@ -283,7 +304,12 @@ _db_show_sta(const struct ieee80211_node *ni)
db_printf("\tinact %u inact_reload %u txrate %u\n",
ni->ni_inact, ni->ni_inact_reload, ni->ni_txrate);
- /* XXX wdsq */
+#ifdef IEEE80211_SUPPORT_MESH
+ _db_show_ssid("\tmeshid ", 0, ni->ni_meshidlen, ni->ni_meshid);
+ db_printf(" mlstate %b mllid 0x%x mlpid 0x%x mlrcnt %u mltval %u\n",
+ ni->ni_mlstate, IEEE80211_MESH_MLSTATE_BITS,
+ ni->ni_mllid, ni->ni_mlpid, ni->ni_mlrcnt, ni->ni_mltval);
+#endif
}
#ifdef IEEE80211_SUPPORT_TDMA
@@ -563,10 +589,13 @@ _db_show_com(const struct ieee80211com *ic, int showvaps, int showsta, int showp
db_printf("\n");
db_printf("\tmax_keyix %d", ic->ic_max_keyix);
+ db_printf(" hash_key 0x%x", ic->ic_hash_key);
db_printf(" wme %p", &ic->ic_wme);
if (!showsta)
db_printf(" sta %p", &ic->ic_sta);
db_printf("\n");
+ db_printf("\tstageq@%p:\n", &ic->ic_stageq);
+ _db_show_ageq("\t", &ic->ic_stageq);
if (showsta)
_db_show_node_table("\t", &ic->ic_sta);
@@ -643,16 +672,16 @@ _db_show_node_table(const char *tag, const struct ieee80211_node_table *nt)
int i;
db_printf("%s%s@%p:\n", tag, nt->nt_name, nt);
- db_printf("%s nodelock %p", tag, &nt->nt_nodelock);
+ db_printf("%s nodelock %p", tag, &nt->nt_nodelock);
db_printf(" inact_init %d", nt->nt_inact_init);
db_printf(" scanlock %p", &nt->nt_scanlock);
db_printf(" scangen %u\n", nt->nt_scangen);
- db_printf("%s keyixmax %d keyixmap %p\n",
+ db_printf("%s keyixmax %d keyixmap %p\n",
tag, nt->nt_keyixmax, nt->nt_keyixmap);
for (i = 0; i < nt->nt_keyixmax; i++) {
const struct ieee80211_node *ni = nt->nt_keyixmap[i];
if (ni != NULL)
- db_printf("%s [%3u] %p %s\n", tag, i, ni,
+ db_printf("%s [%3u] %p %s\n", tag, i, ni,
ether_sprintf(ni->ni_macaddr));
}
}
@@ -810,7 +839,43 @@ _db_show_txparams(const char *tag, const void *arg,
}
static void
+_db_show_ageq(const char *tag, const struct ieee80211_ageq *q)
+{
+ const struct mbuf *m;
+
+ db_printf("%s lock %p len %d maxlen %d drops %d head %p tail %p\n",
+ tag, &q->aq_lock, q->aq_len, q->aq_maxlen, q->aq_drops,
+ q->aq_head, q->aq_tail);
+ for (m = q->aq_head; m != NULL; m = m->m_nextpkt)
+ db_printf("%s %p (len %d, %b)\n", tag, m, m->m_len,
+ /* XXX could be either TX or RX but is mostly TX */
+ m->m_flags, IEEE80211_MBUF_TX_FLAG_BITS);
+}
+
+static void
_db_show_stats(const struct ieee80211_stats *is)
{
}
+
+#ifdef IEEE80211_SUPPORT_MESH
+static void
+_db_show_mesh(const struct ieee80211_mesh_state *ms)
+{
+ struct ieee80211_mesh_route *rt;
+ int i;
+
+ _db_show_ssid(" meshid ", 0, ms->ms_idlen, ms->ms_id);
+ db_printf("nextseq %u ttl %u flags 0x%x\n", ms->ms_seq,
+ ms->ms_ttl, ms->ms_flags);
+ db_printf("routing table:\n");
+ i = 0;
+ TAILQ_FOREACH(rt, &ms->ms_routes, rt_next) {
+ db_printf("entry %d:\tdest: %6D nexthop: %6D metric: %u", i,
+ rt->rt_dest, ":", rt->rt_nexthop, ":", rt->rt_metric);
+ db_printf("\tlifetime: %u lastseq: %u priv: %p\n",
+ rt->rt_lifetime, rt->rt_lastmseq, rt->rt_priv);
+ i++;
+ }
+}
+#endif /* IEEE80211_SUPPORT_MESH */
#endif /* DDB */
diff --git a/sys/net80211/ieee80211_freebsd.h b/sys/net80211/ieee80211_freebsd.h
index f3a224c..6a8875a 100644
--- a/sys/net80211/ieee80211_freebsd.h
+++ b/sys/net80211/ieee80211_freebsd.h
@@ -209,6 +209,19 @@ struct mbuf *ieee80211_getmgtframe(uint8_t **frm, int headroom, int pktlen);
#define M_AMPDU_MPDU M_PROTO8 /* A-MPDU re-order done */
#endif
#define M_80211_RX (M_AMPDU|M_WEP|M_AMPDU_MPDU)
+
+#define IEEE80211_MBUF_TX_FLAG_BITS \
+ "\20\1M_EXT\2M_PKTHDR\3M_EOR\4M_RDONLY\5M_ENCAP\6M_WEP\7M_EAPOL" \
+ "\10M_PWR_SAV\11M_MORE_DATA\12M_BCAST\13M_MCAST\14M_FRAG\15M_FIRSTFRAG" \
+ "\16M_LASTFRAG\17M_SKIP_FIREWALL\20M_FREELIST\21M_VLANTAG\22M_PROMISC" \
+ "\23M_NOFREE\24M_FF\25M_TXCB\26M_AMPDU_MPDU\27M_FLOWID"
+
+#define IEEE80211_MBUF_RX_FLAG_BITS \
+ "\20\1M_EXT\2M_PKTHDR\3M_EOR\4M_RDONLY\5M_AMPDU\6M_WEP\7M_PROTO3" \
+ "\10M_PROTO4\11M_PROTO5\12M_BCAST\13M_MCAST\14M_FRAG\15M_FIRSTFRAG" \
+ "\16M_LASTFRAG\17M_SKIP_FIREWALL\20M_FREELIST\21M_VLANTAG\22M_PROMISC" \
+ "\23M_NOFREE\24M_PROTO6\25M_PROTO7\26M_AMPDU_MPDU\27M_FLOWID"
+
/*
* Store WME access control bits in the vlan tag.
* This is safe since it's done after the packet is classified
diff --git a/sys/net80211/ieee80211_hwmp.c b/sys/net80211/ieee80211_hwmp.c
new file mode 100644
index 0000000..5a36959
--- /dev/null
+++ b/sys/net80211/ieee80211_hwmp.c
@@ -0,0 +1,1389 @@
+/*-
+ * Copyright (c) 2009 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Rui Paulo under sponsorship from the
+ * FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <sys/cdefs.h>
+#ifdef __FreeBSD__
+__FBSDID("$FreeBSD$");
+#endif
+
+/*
+ * IEEE 802.11s Hybrid Wireless Mesh Protocol, HWMP.
+ *
+ * Based on March 2009, D3.0 802.11s draft spec.
+ */
+#include "opt_inet.h"
+#include "opt_wlan.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/endian.h>
+#include <sys/errno.h>
+#include <sys/proc.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/if_llc.h>
+#include <net/ethernet.h>
+
+#include <net/bpf.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_action.h>
+#include <net80211/ieee80211_input.h>
+#include <net80211/ieee80211_mesh.h>
+
+static void hwmp_vattach(struct ieee80211vap *);
+static void hwmp_vdetach(struct ieee80211vap *);
+static int hwmp_newstate(struct ieee80211vap *,
+ enum ieee80211_state, int);
+static int hwmp_send_action(struct ieee80211_node *,
+ const uint8_t [IEEE80211_ADDR_LEN],
+ const uint8_t [IEEE80211_ADDR_LEN],
+ uint8_t *, size_t);
+static uint8_t * hwmp_add_meshpreq(uint8_t *,
+ const struct ieee80211_meshpreq_ie *);
+static uint8_t * hwmp_add_meshprep(uint8_t *,
+ const struct ieee80211_meshprep_ie *);
+static uint8_t * hwmp_add_meshperr(uint8_t *,
+ const struct ieee80211_meshperr_ie *);
+static uint8_t * hwmp_add_meshrann(uint8_t *,
+ const struct ieee80211_meshrann_ie *);
+static void hwmp_rootmode_setup(struct ieee80211vap *);
+static void hwmp_rootmode_cb(void *);
+static void hwmp_rootmode_rann_cb(void *);
+static void hwmp_recv_preq(struct ieee80211vap *, struct ieee80211_node *,
+ const struct ieee80211_frame *,
+ const struct ieee80211_meshpreq_ie *);
+static int hwmp_send_preq(struct ieee80211_node *,
+ const uint8_t [IEEE80211_ADDR_LEN],
+ const uint8_t [IEEE80211_ADDR_LEN],
+ struct ieee80211_meshpreq_ie *);
+static void hwmp_recv_prep(struct ieee80211vap *, struct ieee80211_node *,
+ const struct ieee80211_frame *,
+ const struct ieee80211_meshprep_ie *);
+static int hwmp_send_prep(struct ieee80211_node *,
+ const uint8_t [IEEE80211_ADDR_LEN],
+ const uint8_t [IEEE80211_ADDR_LEN],
+ struct ieee80211_meshprep_ie *);
+static void hwmp_recv_perr(struct ieee80211vap *, struct ieee80211_node *,
+ const struct ieee80211_frame *,
+ const struct ieee80211_meshperr_ie *);
+static int hwmp_send_perr(struct ieee80211_node *,
+ const uint8_t [IEEE80211_ADDR_LEN],
+ const uint8_t [IEEE80211_ADDR_LEN],
+ struct ieee80211_meshperr_ie *);
+static void hwmp_recv_rann(struct ieee80211vap *, struct ieee80211_node *,
+ const struct ieee80211_frame *,
+ const struct ieee80211_meshrann_ie *);
+static int hwmp_send_rann(struct ieee80211_node *,
+ const uint8_t [IEEE80211_ADDR_LEN],
+ const uint8_t [IEEE80211_ADDR_LEN],
+ struct ieee80211_meshrann_ie *);
+static struct ieee80211_node *
+ hwmp_discover(struct ieee80211vap *,
+ const uint8_t [IEEE80211_ADDR_LEN], struct mbuf *);
+static void hwmp_peerdown(struct ieee80211_node *);
+
+static int ieee80211_hwmp_targetonly = 0;
+static int ieee80211_hwmp_replyforward = 1;
+static const int ieee80211_hwmp_maxprepretries = 3;
+static const struct timeval ieee80211_hwmp_maxhopstime = { 0, 500000 };
+static const struct timeval ieee80211_hwmp_preqminint = { 0, 100000 };
+static const struct timeval ieee80211_hwmp_prepminint = { 0, 100000 };
+static const struct timeval ieee80211_hwmp_perrminint = { 0, 100000 };
+static const struct timeval ieee80211_hwmp_roottimeout = { 5, 0 };
+static const struct timeval ieee80211_hwmp_pathtimeout = { 5, 0 };
+static const struct timeval ieee80211_hwmp_pathtoroottimeout = { 5, 0 };
+static const struct timeval ieee80211_hwmp_rootint = { 2, 0 };
+static const struct timeval ieee80211_hwmp_rannint = { 1, 0 };
+static const struct timeval ieee80211_hwmp_pathmaintenanceint = { 2, 0 };
+static const struct timeval ieee80211_hwmp_confirmint = { 2, 0 };
+
+#define timeval2msecs(tv) (tv.tv_sec * 1000 + tv.tv_usec / 1000)
+
+#define HWMP_ROOTMODEINT msecs_to_ticks(timeval2msecs(ieee80211_hwmp_rootint))
+#define HWMP_RANNMODEINT msecs_to_ticks(timeval2msecs(ieee80211_hwmp_rannint))
+
+/* unalligned little endian access */
+#define LE_WRITE_2(p, v) do { \
+ ((uint8_t *)(p))[0] = (v) & 0xff; \
+ ((uint8_t *)(p))[1] = ((v) >> 8) & 0xff; \
+} while (0)
+#define LE_WRITE_4(p, v) do { \
+ ((uint8_t *)(p))[0] = (v) & 0xff; \
+ ((uint8_t *)(p))[1] = ((v) >> 8) & 0xff; \
+ ((uint8_t *)(p))[2] = ((v) >> 16) & 0xff; \
+ ((uint8_t *)(p))[3] = ((v) >> 24) & 0xff; \
+} while (0)
+
+
+/* NB: the Target Address set in a Proactive PREQ is the broadcast address. */
+static const uint8_t broadcastaddr[IEEE80211_ADDR_LEN] =
+ { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+static const uint8_t invalidaddr[IEEE80211_ADDR_LEN] =
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+typedef uint32_t ieee80211_hwmp_seq;
+#define IEEE80211_HWMP_SEQ_LEQ(a, b) ((int32_t)((a)-(b)) <= 0)
+#define IEEE80211_HWMP_SEQ_GEQ(a, b) ((int32_t)((a)-(b)) >= 0)
+
+/*
+ * Private extension of ieee80211_mesh_route.
+ */
+struct ieee80211_hwmp_route {
+ ieee80211_hwmp_seq hr_seq; /* HWMP sequence number */
+ ieee80211_hwmp_seq hr_preqid; /* Last PREQ ID seen */
+ int hr_preqretries;
+};
+struct ieee80211_hwmp_state {
+ ieee80211_hwmp_seq hs_seq; /* next seq to be used */
+ ieee80211_hwmp_seq hs_preqid; /* next PREQ ID to be used */
+ struct timeval hs_lastpreq; /* last time we sent a PREQ */
+ struct timeval hs_lastprep; /* last time we sent a PREP */
+ struct timeval hs_lastperr; /* last time we sent a PERR */
+ int hs_rootmode; /* proactive HWMP */
+ struct callout hs_roottimer;
+ uint8_t hs_maxhops; /* max hop count */
+};
+
+SYSCTL_NODE(_net_wlan, OID_AUTO, hwmp, CTLFLAG_RD, 0,
+ "IEEE 802.11s HWMP parameters");
+SYSCTL_INT(_net_wlan_hwmp, OID_AUTO, targetonly, CTLTYPE_INT | CTLFLAG_RW,
+ &ieee80211_hwmp_targetonly, 0, "Set TO bit on generated PREQs");
+SYSCTL_INT(_net_wlan_hwmp, OID_AUTO, replyforward, CTLTYPE_INT | CTLFLAG_RW,
+ &ieee80211_hwmp_replyforward, 0, "Set RF bit on generated PREQs");
+
+#define IEEE80211_HWMP_DEFAULT_MAXHOPS 31
+
+static ieee80211_recv_action_func hwmp_recv_action_meshpath_preq;
+static ieee80211_recv_action_func hwmp_recv_action_meshpath_prep;
+static ieee80211_recv_action_func hwmp_recv_action_meshpath_perr;
+static ieee80211_recv_action_func hwmp_recv_action_meshpath_rann;
+
+static const struct ieee80211_mesh_proto_path mesh_proto_hwmp = {
+ .mpp_descr = "HWMP",
+ .mpp_ie = IEEE80211_MESHCONF_HWMP,
+ .mpp_discover = hwmp_discover,
+ .mpp_peerdown = hwmp_peerdown,
+ .mpp_vattach = hwmp_vattach,
+ .mpp_vdetach = hwmp_vdetach,
+ .mpp_newstate = hwmp_newstate,
+ .mpp_privlen = sizeof(struct ieee80211_hwmp_route),
+};
+
+
+static void
+ieee80211_hwmp_init(void)
+{
+ /*
+ * Register action frame handlers.
+ */
+ ieee80211_recv_action_register(IEEE80211_ACTION_CAT_MESHPATH,
+ IEEE80211_ACTION_MESHPATH_REQ, hwmp_recv_action_meshpath_preq);
+ ieee80211_recv_action_register(IEEE80211_ACTION_CAT_MESHPATH,
+ IEEE80211_ACTION_MESHPATH_REP, hwmp_recv_action_meshpath_prep);
+ ieee80211_recv_action_register(IEEE80211_ACTION_CAT_MESHPATH,
+ IEEE80211_ACTION_MESHPATH_ERR, hwmp_recv_action_meshpath_perr);
+ ieee80211_recv_action_register(IEEE80211_ACTION_CAT_MESHPATH,
+ IEEE80211_ACTION_MESHPATH_RANN, hwmp_recv_action_meshpath_rann);
+
+ /*
+ * Register HWMP.
+ */
+ ieee80211_mesh_register_proto_path(&mesh_proto_hwmp);
+}
+SYSINIT(wlan_hwmp, SI_SUB_DRIVERS, SI_ORDER_SECOND, ieee80211_hwmp_init, NULL);
+
+void
+hwmp_vattach(struct ieee80211vap *vap)
+{
+ struct ieee80211_hwmp_state *hs;
+
+ KASSERT(vap->iv_opmode == IEEE80211_M_MBSS,
+ ("not a mesh vap, opmode %d", vap->iv_opmode));
+
+ hs = malloc(sizeof(struct ieee80211_hwmp_state), M_80211_VAP,
+ M_NOWAIT | M_ZERO);
+ if (hs == NULL) {
+ printf("%s: couldn't alloc HWMP state\n", __func__);
+ return;
+ }
+ hs->hs_maxhops = IEEE80211_HWMP_DEFAULT_MAXHOPS;
+ callout_init(&hs->hs_roottimer, CALLOUT_MPSAFE);
+ vap->iv_hwmp = hs;
+}
+
+void
+hwmp_vdetach(struct ieee80211vap *vap)
+{
+ struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
+
+ if (callout_active(&hs->hs_roottimer))
+ callout_drain(&hs->hs_roottimer);
+ free(vap->iv_hwmp, M_80211_VAP);
+ vap->iv_hwmp = NULL;
+}
+
+int
+hwmp_newstate(struct ieee80211vap *vap, enum ieee80211_state ostate, int arg)
+{
+ enum ieee80211_state nstate = vap->iv_state;
+ struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
+
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n",
+ __func__, ieee80211_state_name[ostate],
+ ieee80211_state_name[nstate], arg);
+
+ /* Flush the table on RUN -> !RUN, e.g. interface down & up */
+ if (nstate != IEEE80211_S_RUN && ostate == IEEE80211_S_RUN)
+ callout_drain(&hs->hs_roottimer);
+ return 0;
+}
+
+static int
+hwmp_recv_action_meshpath_preq(struct ieee80211_node *ni,
+ const struct ieee80211_frame *wh,
+ const uint8_t *frm, const uint8_t *efrm)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211_meshpreq_ie preq;
+ const uint8_t *iefrm = frm + 2; /* action + code */
+
+ while (efrm - iefrm > 1) {
+ IEEE80211_VERIFY_LENGTH(efrm - iefrm, iefrm[1] + 2, return 0);
+ if (*iefrm == IEEE80211_ELEMID_MESHPREQ) {
+ const struct ieee80211_meshpreq_ie *mpreq =
+ (const struct ieee80211_meshpreq_ie *) iefrm;
+ /* XXX > 1 target */
+ if (mpreq->preq_len !=
+ sizeof(struct ieee80211_meshpreq_ie) - 2) {
+ IEEE80211_DISCARD(vap,
+ IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
+ wh, NULL, "%s", "PREQ with wrong len");
+ vap->iv_stats.is_rx_mgtdiscard++;
+ return 1;
+ }
+ memcpy(&preq, mpreq, sizeof(preq));
+ preq.preq_id = LE_READ_4(&mpreq->preq_id);
+ preq.preq_origseq = LE_READ_4(&mpreq->preq_origseq);
+ preq.preq_lifetime = LE_READ_4(&mpreq->preq_lifetime);
+ preq.preq_metric = LE_READ_4(&mpreq->preq_metric);
+ preq.preq_targets[0].target_seq =
+ LE_READ_4(&mpreq->preq_targets[0].target_seq);
+ hwmp_recv_preq(vap, ni, wh, &preq);
+ return 0;
+ }
+ iefrm += iefrm[1] + 2;
+ }
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
+ wh, NULL, "%s", "PREQ without IE");
+ vap->iv_stats.is_rx_mgtdiscard++;
+ return 0;
+}
+
+static int
+hwmp_recv_action_meshpath_prep(struct ieee80211_node *ni,
+ const struct ieee80211_frame *wh,
+ const uint8_t *frm, const uint8_t *efrm)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211_meshprep_ie prep;
+ const uint8_t *iefrm = frm + 2; /* action + code */
+
+ while (efrm - iefrm > 1) {
+ IEEE80211_VERIFY_LENGTH(efrm - iefrm, iefrm[1] + 2, return 0);
+ if (*iefrm == IEEE80211_ELEMID_MESHPREP) {
+ const struct ieee80211_meshprep_ie *mprep =
+ (const struct ieee80211_meshprep_ie *) iefrm;
+ if (mprep->prep_len !=
+ sizeof(struct ieee80211_meshprep_ie) - 2) {
+ IEEE80211_DISCARD(vap,
+ IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
+ wh, NULL, "%s", "PREP with wrong len");
+ vap->iv_stats.is_rx_mgtdiscard++;
+ return 1;
+ }
+ memcpy(&prep, mprep, sizeof(prep));
+ prep.prep_targetseq = LE_READ_4(&mprep->prep_targetseq);
+ prep.prep_lifetime = LE_READ_4(&mprep->prep_lifetime);
+ prep.prep_metric = LE_READ_4(&mprep->prep_metric);
+ prep.prep_origseq = LE_READ_4(&mprep->prep_origseq);
+ hwmp_recv_prep(vap, ni, wh, &prep);
+ return 0;
+ }
+ iefrm += iefrm[1] + 2;
+ }
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
+ wh, NULL, "%s", "PREP without IE");
+ vap->iv_stats.is_rx_mgtdiscard++;
+ return 0;
+}
+
+static int
+hwmp_recv_action_meshpath_perr(struct ieee80211_node *ni,
+ const struct ieee80211_frame *wh,
+ const uint8_t *frm, const uint8_t *efrm)
+{
+ struct ieee80211_meshperr_ie perr;
+ struct ieee80211vap *vap = ni->ni_vap;
+ const uint8_t *iefrm = frm + 2; /* action + code */
+
+ while (efrm - iefrm > 1) {
+ IEEE80211_VERIFY_LENGTH(efrm - iefrm, iefrm[1] + 2, return 0);
+ if (*iefrm == IEEE80211_ELEMID_MESHPERR) {
+ const struct ieee80211_meshperr_ie *mperr =
+ (const struct ieee80211_meshperr_ie *) iefrm;
+ /* XXX > 1 target */
+ if (mperr->perr_len !=
+ sizeof(struct ieee80211_meshperr_ie) - 2) {
+ IEEE80211_DISCARD(vap,
+ IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
+ wh, NULL, "%s", "PERR with wrong len");
+ vap->iv_stats.is_rx_mgtdiscard++;
+ return 1;
+ }
+ memcpy(&perr, mperr, sizeof(perr));
+ perr.perr_dests[0].dest_seq =
+ LE_READ_4(&mperr->perr_dests[0].dest_seq);
+ hwmp_recv_perr(vap, ni, wh, &perr);
+ return 0;
+ }
+ iefrm += iefrm[1] + 2;
+ }
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
+ wh, NULL, "%s", "PERR without IE");
+ vap->iv_stats.is_rx_mgtdiscard++;
+ return 0;
+}
+
+static int
+hwmp_recv_action_meshpath_rann(struct ieee80211_node *ni,
+ const struct ieee80211_frame *wh,
+ const uint8_t *frm, const uint8_t *efrm)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211_meshrann_ie rann;
+ const uint8_t *iefrm = frm + 2; /* action + code */
+
+ while (efrm - iefrm > 1) {
+ IEEE80211_VERIFY_LENGTH(efrm - iefrm, iefrm[1] + 2, return 0);
+ if (*iefrm == IEEE80211_ELEMID_MESHRANN) {
+ const struct ieee80211_meshrann_ie *mrann =
+ (const struct ieee80211_meshrann_ie *) iefrm;
+ if (mrann->rann_len !=
+ sizeof(struct ieee80211_meshrann_ie) - 2) {
+ IEEE80211_DISCARD(vap,
+ IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
+ wh, NULL, "%s", "RAN with wrong len");
+ vap->iv_stats.is_rx_mgtdiscard++;
+ return 1;
+ }
+ memcpy(&rann, mrann, sizeof(rann));
+ rann.rann_seq = LE_READ_4(&mrann->rann_seq);
+ rann.rann_metric = LE_READ_4(&mrann->rann_metric);
+ hwmp_recv_rann(vap, ni, wh, &rann);
+ return 0;
+ }
+ iefrm += iefrm[1] + 2;
+ }
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
+ wh, NULL, "%s", "RANN without IE");
+ vap->iv_stats.is_rx_mgtdiscard++;
+ return 0;
+}
+
+static int
+hwmp_send_action(struct ieee80211_node *ni,
+ const uint8_t sa[IEEE80211_ADDR_LEN],
+ const uint8_t da[IEEE80211_ADDR_LEN],
+ uint8_t *ie, size_t len)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211_bpf_params params;
+ struct mbuf *m;
+ uint8_t *frm;
+
+ if (vap->iv_state == IEEE80211_S_CAC) {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, ni,
+ "block %s frame in CAC state", "HWMP action");
+ vap->iv_stats.is_tx_badstate++;
+ return EIO; /* XXX */
+ }
+
+ KASSERT(ni != NULL, ("null node"));
+ /*
+ * Hold a reference on the node so it doesn't go away until after
+ * the xmit is complete all the way in the driver. On error we
+ * will remove our reference.
+ */
+#ifdef IEEE80211_DEBUG_REFCNT
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
+ "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n",
+ __func__, __LINE__,
+ ni, ether_sprintf(ni->ni_macaddr),
+ ieee80211_node_refcnt(ni)+1);
+#endif
+ ieee80211_ref_node(ni);
+
+ m = ieee80211_getmgtframe(&frm,
+ ic->ic_headroom + sizeof(struct ieee80211_frame),
+ sizeof(struct ieee80211_action) + len
+ );
+ if (m == NULL) {
+ ieee80211_free_node(ni);
+ vap->iv_stats.is_tx_nobuf++;
+ return ENOMEM;
+ }
+ *frm++ = IEEE80211_ACTION_CAT_MESHPATH;
+ switch (*ie) {
+ case IEEE80211_ELEMID_MESHPREQ:
+ *frm++ = IEEE80211_ACTION_MESHPATH_REQ;
+ frm = hwmp_add_meshpreq(frm,
+ (struct ieee80211_meshpreq_ie *)ie);
+ break;
+ case IEEE80211_ELEMID_MESHPREP:
+ *frm++ = IEEE80211_ACTION_MESHPATH_REP;
+ frm = hwmp_add_meshprep(frm,
+ (struct ieee80211_meshprep_ie *)ie);
+ break;
+ case IEEE80211_ELEMID_MESHPERR:
+ *frm++ = IEEE80211_ACTION_MESHPATH_ERR;
+ frm = hwmp_add_meshperr(frm,
+ (struct ieee80211_meshperr_ie *)ie);
+ break;
+ case IEEE80211_ELEMID_MESHRANN:
+ *frm++ = IEEE80211_ACTION_MESHPATH_RANN;
+ frm = hwmp_add_meshrann(frm,
+ (struct ieee80211_meshrann_ie *)ie);
+ break;
+ }
+
+ m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
+ M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT);
+ if (m == NULL) {
+ ieee80211_free_node(ni);
+ vap->iv_stats.is_tx_nobuf++;
+ return ENOMEM;
+ }
+ ieee80211_send_setup(ni, m,
+ IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_ACTION,
+ IEEE80211_NONQOS_TID, sa, da, sa);
+
+ m->m_flags |= M_ENCAP; /* mark encapsulated */
+ IEEE80211_NODE_STAT(ni, tx_mgmt);
+
+ memset(&params, 0, sizeof(params));
+ params.ibp_pri = WME_AC_VO;
+ params.ibp_rate0 = ni->ni_txparms->mgmtrate;
+ if (IEEE80211_IS_MULTICAST(da))
+ params.ibp_try0 = 1;
+ else
+ params.ibp_try0 = ni->ni_txparms->maxretry;
+ params.ibp_power = ni->ni_txpower;
+ return ic->ic_raw_xmit(ni, m, &params);
+}
+
+#define ADDWORD(frm, v) do { \
+ LE_WRITE_4(frm, v); \
+ frm += 4; \
+} while (0)
+/*
+ * Add a Mesh Path Request IE to a frame.
+ */
+static uint8_t *
+hwmp_add_meshpreq(uint8_t *frm, const struct ieee80211_meshpreq_ie *preq)
+{
+ int i;
+
+ *frm++ = IEEE80211_ELEMID_MESHPREQ;
+ *frm++ = sizeof(struct ieee80211_meshpreq_ie) - 2 +
+ (preq->preq_tcount - 1) * sizeof(*preq->preq_targets);
+ *frm++ = preq->preq_flags;
+ *frm++ = preq->preq_hopcount;
+ *frm++ = preq->preq_ttl;
+ ADDWORD(frm, preq->preq_id);
+ IEEE80211_ADDR_COPY(frm, preq->preq_origaddr); frm += 6;
+ ADDWORD(frm, preq->preq_origseq);
+ ADDWORD(frm, preq->preq_lifetime);
+ ADDWORD(frm, preq->preq_metric);
+ *frm++ = preq->preq_tcount;
+ for (i = 0; i < preq->preq_tcount; i++) {
+ *frm++ = preq->preq_targets[i].target_flags;
+ IEEE80211_ADDR_COPY(frm, preq->preq_targets[i].target_addr);
+ frm += 6;
+ ADDWORD(frm, preq->preq_targets[i].target_seq);
+ }
+ return frm;
+}
+
+/*
+ * Add a Mesh Path Reply IE to a frame.
+ */
+static uint8_t *
+hwmp_add_meshprep(uint8_t *frm, const struct ieee80211_meshprep_ie *prep)
+{
+ *frm++ = IEEE80211_ELEMID_MESHPREP;
+ *frm++ = sizeof(struct ieee80211_meshprep_ie) - 2;
+ *frm++ = prep->prep_flags;
+ *frm++ = prep->prep_hopcount;
+ *frm++ = prep->prep_ttl;
+ IEEE80211_ADDR_COPY(frm, prep->prep_targetaddr); frm += 6;
+ ADDWORD(frm, prep->prep_targetseq);
+ ADDWORD(frm, prep->prep_lifetime);
+ ADDWORD(frm, prep->prep_metric);
+ IEEE80211_ADDR_COPY(frm, prep->prep_origaddr); frm += 6;
+ ADDWORD(frm, prep->prep_origseq);
+ return frm;
+}
+
+/*
+ * Add a Mesh Path Error IE to a frame.
+ */
+static uint8_t *
+hwmp_add_meshperr(uint8_t *frm, const struct ieee80211_meshperr_ie *perr)
+{
+ int i;
+
+ *frm++ = IEEE80211_ELEMID_MESHPERR;
+ *frm++ = sizeof(struct ieee80211_meshperr_ie) - 2 +
+ (perr->perr_ndests - 1) * sizeof(*perr->perr_dests);
+ *frm++ = perr->perr_mode;
+ *frm++ = perr->perr_ndests;
+ for (i = 0; i < perr->perr_ndests; i++) {
+ IEEE80211_ADDR_COPY(frm, perr->perr_dests[i].dest_addr);
+ frm += 6;
+ ADDWORD(frm, perr->perr_dests[i].dest_seq);
+ }
+ return frm;
+}
+
+/*
+ * Add a Root Annoucement IE to a frame.
+ */
+static uint8_t *
+hwmp_add_meshrann(uint8_t *frm, const struct ieee80211_meshrann_ie *rann)
+{
+ *frm++ = IEEE80211_ELEMID_MESHRANN;
+ *frm++ = sizeof(struct ieee80211_meshrann_ie) - 2;
+ *frm++ = rann->rann_flags;
+ *frm++ = rann->rann_hopcount;
+ *frm++ = rann->rann_ttl;
+ IEEE80211_ADDR_COPY(frm, rann->rann_addr); frm += 6;
+ ADDWORD(frm, rann->rann_seq);
+ ADDWORD(frm, rann->rann_metric);
+ return frm;
+}
+
+static void
+hwmp_rootmode_setup(struct ieee80211vap *vap)
+{
+ struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
+
+ switch (hs->hs_rootmode) {
+ case IEEE80211_HWMP_ROOTMODE_DISABLED:
+ callout_drain(&hs->hs_roottimer);
+ break;
+ case IEEE80211_HWMP_ROOTMODE_NORMAL:
+ case IEEE80211_HWMP_ROOTMODE_PROACTIVE:
+ callout_reset(&hs->hs_roottimer, HWMP_ROOTMODEINT,
+ hwmp_rootmode_cb, vap);
+ break;
+ case IEEE80211_HWMP_ROOTMODE_RANN:
+ callout_reset(&hs->hs_roottimer, HWMP_RANNMODEINT,
+ hwmp_rootmode_rann_cb, vap);
+ break;
+ }
+}
+
+/*
+ * Send a broadcast Path Request to find all nodes on the mesh. We are
+ * called when the vap is configured as a HWMP root node.
+ */
+#define PREQ_TFLAGS(n) preq.preq_targets[n].target_flags
+#define PREQ_TADDR(n) preq.preq_targets[n].target_addr
+#define PREQ_TSEQ(n) preq.preq_targets[n].target_seq
+static void
+hwmp_rootmode_cb(void *arg)
+{
+ struct ieee80211vap *vap = (struct ieee80211vap *)arg;
+ struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
+ struct ieee80211_mesh_state *ms = vap->iv_mesh;
+ struct ieee80211_meshpreq_ie preq;
+
+ IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, vap->iv_bss,
+ "%s", "sending broadcast PREQ");
+
+ /* XXX check portal role */
+ preq.preq_flags = IEEE80211_MESHPREQ_FLAGS_AM;
+ if (hs->hs_rootmode == IEEE80211_HWMP_ROOTMODE_PROACTIVE)
+ preq.preq_flags |= IEEE80211_MESHPREQ_FLAGS_PP;
+ preq.preq_hopcount = 0;
+ preq.preq_ttl = ms->ms_ttl;
+ preq.preq_id = ++hs->hs_preqid;
+ IEEE80211_ADDR_COPY(preq.preq_origaddr, vap->iv_myaddr);
+ preq.preq_origseq = ++hs->hs_seq;
+ preq.preq_lifetime = timeval2msecs(ieee80211_hwmp_roottimeout);
+ preq.preq_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
+ preq.preq_tcount = 1;
+ IEEE80211_ADDR_COPY(PREQ_TADDR(0), broadcastaddr);
+ PREQ_TFLAGS(0) = IEEE80211_MESHPREQ_TFLAGS_TO |
+ IEEE80211_MESHPREQ_TFLAGS_RF;
+ PREQ_TSEQ(0) = 0;
+ vap->iv_stats.is_hwmp_rootreqs++;
+ hwmp_send_preq(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &preq);
+ hwmp_rootmode_setup(vap);
+}
+#undef PREQ_TFLAGS
+#undef PREQ_TADDR
+#undef PREQ_TSEQ
+
+/*
+ * Send a Root Annoucement (RANN) to find all the nodes on the mesh. We are
+ * called when the vap is configured as a HWMP RANN root node.
+ */
+static void
+hwmp_rootmode_rann_cb(void *arg)
+{
+ struct ieee80211vap *vap = (struct ieee80211vap *)arg;
+ struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
+ struct ieee80211_mesh_state *ms = vap->iv_mesh;
+ struct ieee80211_meshrann_ie rann;
+
+ IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, vap->iv_bss,
+ "%s", "sending broadcast RANN");
+
+ /* XXX check portal role */
+ rann.rann_flags = 0;
+ rann.rann_hopcount = 0;
+ rann.rann_ttl = ms->ms_ttl;
+ IEEE80211_ADDR_COPY(rann.rann_addr, vap->iv_myaddr);
+ rann.rann_seq = ++hs->hs_seq;
+ rann.rann_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
+
+ vap->iv_stats.is_hwmp_rootrann++;
+ hwmp_send_rann(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &rann);
+ hwmp_rootmode_setup(vap);
+}
+
+#define PREQ_TFLAGS(n) preq->preq_targets[n].target_flags
+#define PREQ_TADDR(n) preq->preq_targets[n].target_addr
+#define PREQ_TSEQ(n) preq->preq_targets[n].target_seq
+static void
+hwmp_recv_preq(struct ieee80211vap *vap, struct ieee80211_node *ni,
+ const struct ieee80211_frame *wh, const struct ieee80211_meshpreq_ie *preq)
+{
+ struct ieee80211_mesh_state *ms = vap->iv_mesh;
+ struct ieee80211_mesh_route *rt = NULL;
+ struct ieee80211_hwmp_route *hr;
+ struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
+ struct ieee80211_meshprep_ie prep;
+
+ if (ni == vap->iv_bss ||
+ ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED)
+ return;
+ /*
+ * Ignore PREQs from us. Could happen because someone forward it
+ * back to us.
+ */
+ if (IEEE80211_ADDR_EQ(vap->iv_myaddr, preq->preq_origaddr))
+ return;
+
+ IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+ "received PREQ, source %s", ether_sprintf(preq->preq_origaddr));
+
+ /*
+ * Acceptance criteria: if the PREQ is not for us and
+ * forwarding is disabled, discard this PREQ.
+ */
+ if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0)) &&
+ !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_HWMP,
+ preq->preq_origaddr, NULL, "%s", "not accepting PREQ");
+ return;
+ }
+ /*
+ * Check if the PREQ is addressed to us.
+ * XXX: check if this is part of a proxy address.
+ */
+ if (IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0))) {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+ "replying to %s", ether_sprintf(preq->preq_origaddr));
+ /*
+ * Build and send a PREP frame.
+ */
+ prep.prep_flags = 0;
+ prep.prep_hopcount = 0;
+ prep.prep_ttl = ms->ms_ttl;
+ IEEE80211_ADDR_COPY(prep.prep_targetaddr, preq->preq_origaddr);
+ prep.prep_targetseq = preq->preq_origseq;
+ prep.prep_lifetime = preq->preq_lifetime;
+ prep.prep_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
+ IEEE80211_ADDR_COPY(prep.prep_origaddr, vap->iv_myaddr);
+ prep.prep_origseq = ++hs->hs_seq;
+ hwmp_send_prep(ni, vap->iv_myaddr, wh->i_addr2, &prep);
+ /*
+ * Build the reverse path, if we don't have it already.
+ */
+ rt = ieee80211_mesh_rt_find(vap, preq->preq_origaddr);
+ if (rt == NULL)
+ hwmp_discover(vap, preq->preq_origaddr, NULL);
+ else if (IEEE80211_ADDR_EQ(rt->rt_nexthop, invalidaddr))
+ hwmp_discover(vap, rt->rt_dest, NULL);
+ return;
+ }
+ /*
+ * Proactive PREQ: reply with a proactive PREP to the
+ * root STA if requested.
+ */
+ if (IEEE80211_ADDR_EQ(PREQ_TADDR(0), broadcastaddr) &&
+ (PREQ_TFLAGS(0) &
+ ((IEEE80211_MESHPREQ_TFLAGS_TO|IEEE80211_MESHPREQ_TFLAGS_RF) ==
+ (IEEE80211_MESHPREQ_TFLAGS_TO|IEEE80211_MESHPREQ_TFLAGS_RF)))) {
+ uint8_t rootmac[IEEE80211_ADDR_LEN];
+
+ IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+ "root mesh station @ %s",
+ ether_sprintf(preq->preq_origaddr));
+ IEEE80211_ADDR_COPY(rootmac, preq->preq_origaddr);
+ rt = ieee80211_mesh_rt_find(vap, rootmac);
+ if (rt == NULL)
+ rt = ieee80211_mesh_rt_add(vap, rootmac);
+ /*
+ * Reply with a PREP if we don't have a path to the root
+ * or if the root sent us a proactive PREQ.
+ */
+ if (IEEE80211_ADDR_EQ(rt->rt_nexthop, invalidaddr) ||
+ (preq->preq_flags & IEEE80211_MESHPREQ_FLAGS_PP)) {
+ prep.prep_flags = 0;
+ prep.prep_hopcount = 0;
+ prep.prep_ttl = ms->ms_ttl;
+ IEEE80211_ADDR_COPY(prep.prep_origaddr,
+ vap->iv_myaddr);
+ prep.prep_origseq = preq->preq_origseq;
+ prep.prep_targetseq = ++hs->hs_seq;
+ prep.prep_lifetime = preq->preq_lifetime;
+ prep.prep_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
+ IEEE80211_ADDR_COPY(prep.prep_targetaddr,
+ rootmac);
+ prep.prep_targetseq = PREQ_TSEQ(0);
+ hwmp_send_prep(vap->iv_bss, vap->iv_myaddr,
+ broadcastaddr, &prep);
+ }
+ hwmp_discover(vap, rootmac, NULL);
+ return;
+ }
+ rt = ieee80211_mesh_rt_find(vap, PREQ_TADDR(0));
+
+ /* XXX missing. Check for AE bit and update proxy information */
+
+ /*
+ * Forwarding and Intermediate reply for PREQs with 1 target.
+ */
+ if (preq->preq_tcount == 1) {
+ struct ieee80211_meshpreq_ie ppreq; /* propagated PREQ */
+
+ memcpy(&ppreq, preq, sizeof(ppreq));
+ /*
+ * We have a valid route to this node.
+ */
+ if (rt != NULL &&
+ !IEEE80211_ADDR_EQ(rt->rt_nexthop, invalidaddr)) {
+
+ hr = IEEE80211_MESH_ROUTE_PRIV(rt,
+ struct ieee80211_hwmp_route);
+ hr->hr_preqid = preq->preq_id;
+ hr->hr_seq = preq->preq_origseq;
+ if (preq->preq_ttl > 1 &&
+ preq->preq_hopcount < hs->hs_maxhops) {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+ "forwarding PREQ from %s",
+ ether_sprintf(preq->preq_origaddr));
+ /*
+ * Propagate the original PREQ.
+ */
+ ppreq.preq_hopcount += 1;
+ ppreq.preq_ttl -= 1;
+ ppreq.preq_metric +=
+ ms->ms_pmetric->mpm_metric(ni);
+ /*
+ * Set TO and unset RF bits because we are going
+ * to send a PREP next.
+ */
+ ppreq.preq_targets[0].target_flags |=
+ IEEE80211_MESHPREQ_TFLAGS_TO;
+ ppreq.preq_targets[0].target_flags &=
+ ~IEEE80211_MESHPREQ_TFLAGS_RF;
+ hwmp_send_preq(ni, vap->iv_myaddr,
+ broadcastaddr, &ppreq);
+ }
+ /*
+ * Check if we can send an intermediate Path Reply,
+ * i.e., Target Only bit is not set.
+ */
+ if (!(PREQ_TFLAGS(0) & IEEE80211_MESHPREQ_TFLAGS_TO)) {
+ struct ieee80211_meshprep_ie prep;
+
+ IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+ "intermediate reply for PREQ from %s",
+ ether_sprintf(preq->preq_origaddr));
+ prep.prep_flags = 0;
+ prep.prep_hopcount = rt->rt_nhops + 1;
+ prep.prep_ttl = ms->ms_ttl;
+ IEEE80211_ADDR_COPY(&prep.prep_targetaddr,
+ preq->preq_origaddr);
+ prep.prep_targetseq = hr->hr_seq;
+ prep.prep_lifetime = preq->preq_lifetime;
+ prep.prep_metric = rt->rt_metric +
+ ms->ms_pmetric->mpm_metric(ni);
+ IEEE80211_ADDR_COPY(&prep.prep_origaddr,
+ PREQ_TADDR(0));
+ prep.prep_origseq = hs->hs_seq++;
+ hwmp_send_prep(ni, vap->iv_myaddr,
+ broadcastaddr, &prep);
+ }
+ /*
+ * We have no information about this path,
+ * propagate the PREQ.
+ */
+ } else if (preq->preq_ttl > 1 &&
+ preq->preq_hopcount < hs->hs_maxhops) {
+ if (rt == NULL)
+ rt = ieee80211_mesh_rt_add(vap, PREQ_TADDR(0));
+ hr = IEEE80211_MESH_ROUTE_PRIV(rt,
+ struct ieee80211_hwmp_route);
+ rt->rt_metric = preq->preq_metric;
+ rt->rt_lifetime = preq->preq_lifetime;
+ hr->hr_seq = preq->preq_origseq;
+ hr->hr_preqid = preq->preq_id;
+
+ IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+ "forwarding PREQ from %s",
+ ether_sprintf(preq->preq_origaddr));
+ ppreq.preq_hopcount += 1;
+ ppreq.preq_ttl -= 1;
+ ppreq.preq_metric += ms->ms_pmetric->mpm_metric(ni);
+ hwmp_send_preq(ni, vap->iv_myaddr, broadcastaddr,
+ &ppreq);
+ }
+ }
+
+}
+#undef PREQ_TFLAGS
+#undef PREQ_TADDR
+#undef PREQ_TSEQ
+
+static int
+hwmp_send_preq(struct ieee80211_node *ni,
+ const uint8_t sa[IEEE80211_ADDR_LEN],
+ const uint8_t da[IEEE80211_ADDR_LEN],
+ struct ieee80211_meshpreq_ie *preq)
+{
+ struct ieee80211_hwmp_state *hs = ni->ni_vap->iv_hwmp;
+
+ /*
+ * Enforce PREQ interval.
+ */
+ if (ratecheck(&hs->hs_lastpreq, &ieee80211_hwmp_preqminint) == 0)
+ return EALREADY;
+ getmicrouptime(&hs->hs_lastpreq);
+
+ /*
+ * mesh preq action frame format
+ * [6] da
+ * [6] sa
+ * [6] addr3 = sa
+ * [1] action
+ * [1] category
+ * [tlv] mesh path request
+ */
+ preq->preq_ie = IEEE80211_ELEMID_MESHPREQ;
+ return hwmp_send_action(ni, sa, da, (uint8_t *)preq,
+ sizeof(struct ieee80211_meshpreq_ie));
+}
+
+static void
+hwmp_recv_prep(struct ieee80211vap *vap, struct ieee80211_node *ni,
+ const struct ieee80211_frame *wh, const struct ieee80211_meshprep_ie *prep)
+{
+ struct ieee80211_mesh_state *ms = vap->iv_mesh;
+ struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
+ struct ieee80211_mesh_route *rt = NULL;
+ struct ieee80211_hwmp_route *hr;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ifnet *ifp = vap->iv_ifp;
+ struct mbuf *m, *next;
+
+ /*
+ * Acceptance criteria: if the corresponding PREQ was not generated
+ * by us and forwarding is disabled, discard this PREP.
+ */
+ if (ni == vap->iv_bss ||
+ ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED)
+ return;
+ if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, prep->prep_origaddr) &&
+ !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD))
+ return;
+
+ IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+ "received PREP from %s", ether_sprintf(prep->prep_origaddr));
+
+ /*
+ * If it's NOT for us, propagate the PREP.
+ */
+ if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, prep->prep_targetaddr) &&
+ prep->prep_ttl > 1 && prep->prep_hopcount < hs->hs_maxhops) {
+ struct ieee80211_meshprep_ie pprep; /* propagated PREP */
+
+ IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+ "propagating PREP from %s",
+ ether_sprintf(prep->prep_origaddr));
+
+ memcpy(&pprep, prep, sizeof(pprep));
+ pprep.prep_hopcount += 1;
+ pprep.prep_ttl -= 1;
+ pprep.prep_metric += ms->ms_pmetric->mpm_metric(ni);
+ IEEE80211_ADDR_COPY(pprep.prep_origaddr, vap->iv_myaddr);
+ hwmp_send_prep(ni, vap->iv_myaddr, broadcastaddr, &pprep);
+ }
+
+ rt = ieee80211_mesh_rt_find(vap, prep->prep_origaddr);
+ if (rt == NULL) {
+ /*
+ * If we have no entry this could be a reply to a root PREQ.
+ */
+ if (hs->hs_rootmode != IEEE80211_HWMP_ROOTMODE_DISABLED) {
+ rt = ieee80211_mesh_rt_add(vap, prep->prep_origaddr);
+ IEEE80211_ADDR_COPY(rt->rt_nexthop, wh->i_addr2);
+ rt->rt_nhops = prep->prep_hopcount;
+ rt->rt_lifetime = prep->prep_lifetime;
+ rt->rt_metric = prep->prep_metric;
+ return;
+ }
+ return;
+ }
+ hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
+ if (prep->prep_targetseq == hr->hr_seq) {
+ int useprep = 0;
+ /*
+ * Check if we already have a path to this node.
+ * If we do, check if this path reply contains a
+ * better route.
+ */
+ if (IEEE80211_ADDR_EQ(rt->rt_nexthop, invalidaddr))
+ useprep = 1;
+ else if (prep->prep_hopcount < rt->rt_nhops ||
+ prep->prep_metric < rt->rt_metric)
+ useprep = 1;
+ if (useprep) {
+ IEEE80211_ADDR_COPY(rt->rt_nexthop, wh->i_addr2);
+ rt->rt_nhops = prep->prep_hopcount;
+ rt->rt_lifetime = prep->prep_lifetime;
+ rt->rt_metric = prep->prep_metric;
+ }
+ } else {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+ "discard PREP from %s, wrong seqno %u != %u",
+ ether_sprintf(prep->prep_origaddr), prep->prep_targetseq,
+ hr->hr_seq);
+ vap->iv_stats.is_hwmp_wrongseq++;
+ }
+
+ /*
+ * XXX: If it's for us and the AE bit is set, update the
+ * proxy information table.
+ */
+
+ /*
+ * XXX: If it's NOT for us and the AE bit is set,
+ * update the proxy information table.
+ */
+
+ /*
+ * Check for frames queued awaiting path discovery.
+ * XXX probably can tell exactly and avoid remove call
+ * NB: hash may have false matches, if so they will get
+ * stuck back on the stageq because there won't be
+ * a path.
+ */
+ m = ieee80211_ageq_remove(&ic->ic_stageq,
+ (struct ieee80211_node *)(uintptr_t)
+ ieee80211_mac_hash(ic, rt->rt_dest));
+ for (; m != NULL; m = next) {
+ next = m->m_nextpkt;
+ m->m_nextpkt = NULL;
+ ifp->if_transmit(ifp, m);
+ }
+}
+
+static int
+hwmp_send_prep(struct ieee80211_node *ni,
+ const uint8_t sa[IEEE80211_ADDR_LEN],
+ const uint8_t da[IEEE80211_ADDR_LEN],
+ struct ieee80211_meshprep_ie *prep)
+{
+ struct ieee80211_hwmp_state *hs = ni->ni_vap->iv_hwmp;
+
+ /*
+ * Enforce PREP interval.
+ */
+ if (ratecheck(&hs->hs_lastprep, &ieee80211_hwmp_prepminint) == 0)
+ return EALREADY;
+ getmicrouptime(&hs->hs_lastprep);
+
+ /*
+ * mesh prep action frame format
+ * [6] da
+ * [6] sa
+ * [6] addr3 = sa
+ * [1] action
+ * [1] category
+ * [tlv] mesh path reply
+ */
+ prep->prep_ie = IEEE80211_ELEMID_MESHPREP;
+ return hwmp_send_action(ni, sa, da, (uint8_t *)prep,
+ sizeof(struct ieee80211_meshprep_ie));
+}
+
+#define PERR_DADDR(n) perr.perr_dests[n].dest_addr
+#define PERR_DSEQ(n) perr.perr_dests[n].dest_seq
+static void
+hwmp_peerdown(struct ieee80211_node *ni)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211_meshperr_ie perr;
+ struct ieee80211_mesh_route *rt;
+ struct ieee80211_hwmp_route *hr;
+
+ rt = ieee80211_mesh_rt_find(vap, ni->ni_macaddr);
+ if (rt == NULL)
+ return;
+ hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
+ IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+ "%s", "deleting route entry");
+ perr.perr_mode = 0;
+ perr.perr_ndests = 1;
+ IEEE80211_ADDR_COPY(PERR_DADDR(0), rt->rt_dest);
+ PERR_DSEQ(0) = hr->hr_seq;
+ ieee80211_mesh_rt_del(vap, ni->ni_macaddr);
+ hwmp_send_perr(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &perr);
+}
+#undef PERR_DADDR
+#undef PERR_DSEQ
+
+#define PERR_DADDR(n) perr->perr_dests[n].dest_addr
+#define PERR_DSEQ(n) perr->perr_dests[n].dest_seq
+static void
+hwmp_recv_perr(struct ieee80211vap *vap, struct ieee80211_node *ni,
+ const struct ieee80211_frame *wh, const struct ieee80211_meshperr_ie *perr)
+{
+ struct ieee80211_mesh_state *ms = vap->iv_mesh;
+ struct ieee80211_mesh_route *rt = NULL;
+ struct ieee80211_hwmp_route *hr;
+ struct ieee80211_meshperr_ie pperr;
+ int i, forward = 0;
+
+ /*
+ * Acceptance criteria: check if we received a PERR from a
+ * neighbor and forwarding is enabled.
+ */
+ if (ni == vap->iv_bss ||
+ ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED ||
+ !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD))
+ return;
+ /*
+ * Find all routing entries that match and delete them.
+ */
+ for (i = 0; i < perr->perr_ndests; i++) {
+ rt = ieee80211_mesh_rt_find(vap, PERR_DADDR(i));
+ if (rt == NULL)
+ continue;
+ hr = IEEE80211_MESH_ROUTE_PRIV(rt,
+ struct ieee80211_hwmp_route);
+ if (PERR_DSEQ(i) >= hr->hr_seq) {
+ ieee80211_mesh_rt_del(vap, rt->rt_dest);
+ rt = NULL;
+ forward = 1;
+ }
+ }
+ /*
+ * Propagate the PERR if we previously found it on our routing table.
+ * XXX handle ndest > 1
+ */
+ if (forward) {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+ "propagating PERR from %s", ether_sprintf(wh->i_addr2));
+ memcpy(&pperr, perr, sizeof(*perr));
+ hwmp_send_perr(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &pperr);
+ }
+}
+#undef PEER_DADDR
+#undef PERR_DSEQ
+
+static int
+hwmp_send_perr(struct ieee80211_node *ni,
+ const uint8_t sa[IEEE80211_ADDR_LEN],
+ const uint8_t da[IEEE80211_ADDR_LEN],
+ struct ieee80211_meshperr_ie *perr)
+{
+ struct ieee80211_hwmp_state *hs = ni->ni_vap->iv_hwmp;
+
+ /*
+ * Enforce PERR interval.
+ */
+ if (ratecheck(&hs->hs_lastperr, &ieee80211_hwmp_perrminint) == 0)
+ return EALREADY;
+ getmicrouptime(&hs->hs_lastperr);
+
+ /*
+ * mesh perr action frame format
+ * [6] da
+ * [6] sa
+ * [6] addr3 = sa
+ * [1] action
+ * [1] category
+ * [tlv] mesh path error
+ */
+ perr->perr_ie = IEEE80211_ELEMID_MESHPERR;
+ return hwmp_send_action(ni, sa, da, (uint8_t *)perr,
+ sizeof(struct ieee80211_meshperr_ie));
+}
+
+static void
+hwmp_recv_rann(struct ieee80211vap *vap, struct ieee80211_node *ni,
+ const struct ieee80211_frame *wh, const struct ieee80211_meshrann_ie *rann)
+{
+ struct ieee80211_mesh_state *ms = vap->iv_mesh;
+ struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
+ struct ieee80211_mesh_route *rt = NULL;
+ struct ieee80211_hwmp_route *hr;
+ struct ieee80211_meshrann_ie prann;
+
+ if (ni == vap->iv_bss ||
+ ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED)
+ return;
+
+ rt = ieee80211_mesh_rt_find(vap, rann->rann_addr);
+ /*
+ * Discover the path to the root mesh STA.
+ * If we already know it, propagate the RANN element.
+ */
+ if (rt == NULL) {
+ hwmp_discover(vap, rann->rann_addr, NULL);
+ return;
+ }
+ hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
+ if (rann->rann_seq > hr->hr_seq && rann->rann_ttl > 1 &&
+ rann->rann_hopcount < hs->hs_maxhops &&
+ (ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) {
+ memcpy(&prann, rann, sizeof(prann));
+ prann.rann_hopcount += 1;
+ prann.rann_ttl -= 1;
+ prann.rann_metric += ms->ms_pmetric->mpm_metric(ni);
+ hwmp_send_rann(vap->iv_bss, vap->iv_myaddr, broadcastaddr,
+ &prann);
+ }
+}
+
+static int
+hwmp_send_rann(struct ieee80211_node *ni,
+ const uint8_t sa[IEEE80211_ADDR_LEN],
+ const uint8_t da[IEEE80211_ADDR_LEN],
+ struct ieee80211_meshrann_ie *rann)
+{
+ /*
+ * mesh rann action frame format
+ * [6] da
+ * [6] sa
+ * [6] addr3 = sa
+ * [1] action
+ * [1] category
+ * [tlv] root annoucement
+ */
+ rann->rann_ie = IEEE80211_ELEMID_MESHRANN;
+ return hwmp_send_action(ni, sa, da, (uint8_t *)rann,
+ sizeof(struct ieee80211_meshrann_ie));
+}
+
+#define PREQ_TFLAGS(n) preq.preq_targets[n].target_flags
+#define PREQ_TADDR(n) preq.preq_targets[n].target_addr
+#define PREQ_TSEQ(n) preq.preq_targets[n].target_seq
+static struct ieee80211_node *
+hwmp_discover(struct ieee80211vap *vap,
+ const uint8_t dest[IEEE80211_ADDR_LEN], struct mbuf *m)
+{
+ struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
+ struct ieee80211_mesh_state *ms = vap->iv_mesh;
+ struct ieee80211_mesh_route *rt = NULL;
+ struct ieee80211_hwmp_route *hr;
+ struct ieee80211_meshpreq_ie preq;
+ struct ieee80211_node *ni;
+ int sendpreq = 0;
+
+ KASSERT(vap->iv_opmode == IEEE80211_M_MBSS,
+ ("not a mesh vap, opmode %d", vap->iv_opmode));
+
+ KASSERT(!IEEE80211_ADDR_EQ(vap->iv_myaddr, dest),
+ ("%s: discovering self!", __func__));
+
+ ni = NULL;
+ if (!IEEE80211_IS_MULTICAST(dest)) {
+ rt = ieee80211_mesh_rt_find(vap, dest);
+ if (rt == NULL) {
+ rt = ieee80211_mesh_rt_add(vap, dest);
+ if (rt == NULL) {
+ /* XXX stat+msg */
+ goto done;
+ }
+ }
+ hr = IEEE80211_MESH_ROUTE_PRIV(rt,
+ struct ieee80211_hwmp_route);
+ if (IEEE80211_ADDR_EQ(rt->rt_nexthop, invalidaddr)) {
+ if (hr->hr_preqid == 0) {
+ hr->hr_seq = ++hs->hs_seq;
+ hr->hr_preqid = ++hs->hs_preqid;
+ }
+ rt->rt_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
+ rt->rt_lifetime =
+ timeval2msecs(ieee80211_hwmp_pathtimeout);
+ /* XXX check preq retries */
+ sendpreq = 1;
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP, dest,
+ "%s", "initiating path discovery");
+ /*
+ * Try to discover the path for this node.
+ */
+ preq.preq_flags = 0;
+ preq.preq_hopcount = 0;
+ preq.preq_ttl = ms->ms_ttl;
+ preq.preq_id = hr->hr_preqid;
+ IEEE80211_ADDR_COPY(preq.preq_origaddr, vap->iv_myaddr);
+ preq.preq_origseq = hr->hr_seq;
+ preq.preq_lifetime = rt->rt_lifetime;
+ preq.preq_metric = rt->rt_metric;
+ preq.preq_tcount = 1;
+ IEEE80211_ADDR_COPY(PREQ_TADDR(0), dest);
+ PREQ_TFLAGS(0) = 0;
+ if (ieee80211_hwmp_targetonly)
+ PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_TO;
+ if (ieee80211_hwmp_replyforward)
+ PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_RF;
+ PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_USN;
+ PREQ_TSEQ(0) = 0;
+ /* XXX check return value */
+ hwmp_send_preq(vap->iv_bss, vap->iv_myaddr,
+ broadcastaddr, &preq);
+ }
+ if (!IEEE80211_ADDR_EQ(rt->rt_nexthop, invalidaddr))
+ ni = ieee80211_find_txnode(vap, rt->rt_nexthop);
+ } else {
+ ni = ieee80211_find_txnode(vap, dest);
+ return ni;
+ }
+done:
+ if (ni == NULL && m != NULL) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_HWMP,
+ dest, NULL, "%s", "no valid path to this node");
+ if (sendpreq) {
+ struct ieee80211com *ic = vap->iv_ic;
+ /*
+ * Queue packet for transmit when path discovery
+ * completes. If discovery never completes the
+ * frame will be flushed by way of the aging timer.
+ */
+ m->m_pkthdr.rcvif = (void *)(uintptr_t)
+ ieee80211_mac_hash(ic, dest);
+ /* XXX age chosen randomly */
+ ieee80211_ageq_append(&ic->ic_stageq, m,
+ IEEE80211_INACT_WAIT);
+ } else
+ m_freem(m);
+ }
+ return ni;
+}
+#undef PREQ_TFLAGS
+#undef PREQ_TADDR
+#undef PREQ_TSEQ
+
+static int
+hwmp_ioctl_get80211(struct ieee80211vap *vap, struct ieee80211req *ireq)
+{
+ struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
+ int error;
+
+ if (vap->iv_opmode != IEEE80211_M_MBSS)
+ return ENOSYS;
+ error = 0;
+ switch (ireq->i_type) {
+ case IEEE80211_IOC_HWMP_ROOTMODE:
+ ireq->i_val = hs->hs_rootmode;
+ break;
+ case IEEE80211_IOC_HWMP_MAXHOPS:
+ ireq->i_val = hs->hs_maxhops;
+ break;
+ default:
+ return ENOSYS;
+ }
+ return error;
+}
+IEEE80211_IOCTL_GET(hwmp, hwmp_ioctl_get80211);
+
+static int
+hwmp_ioctl_set80211(struct ieee80211vap *vap, struct ieee80211req *ireq)
+{
+ struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
+ int error;
+
+ if (vap->iv_opmode != IEEE80211_M_MBSS)
+ return ENOSYS;
+ error = 0;
+ switch (ireq->i_type) {
+ case IEEE80211_IOC_HWMP_ROOTMODE:
+ if (ireq->i_val < 0 || ireq->i_val > 3)
+ return EINVAL;
+ hs->hs_rootmode = ireq->i_val;
+ hwmp_rootmode_setup(vap);
+ break;
+ case IEEE80211_IOC_HWMP_MAXHOPS:
+ if (ireq->i_val <= 0 || ireq->i_val > 255)
+ return EINVAL;
+ hs->hs_maxhops = ireq->i_val;
+ break;
+ default:
+ return ENOSYS;
+ }
+ return error;
+}
+IEEE80211_IOCTL_SET(hwmp, hwmp_ioctl_set80211);
diff --git a/sys/net80211/ieee80211_input.c b/sys/net80211/ieee80211_input.c
index 5e50f10..8209d2e 100644
--- a/sys/net80211/ieee80211_input.c
+++ b/sys/net80211/ieee80211_input.c
@@ -46,6 +46,9 @@ __FBSDID("$FreeBSD$");
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_input.h>
+#ifdef IEEE80211_SUPPORT_MESH
+#include <net80211/ieee80211_mesh.h>
+#endif
#include <net/bpf.h>
@@ -230,7 +233,16 @@ ieee80211_deliver_data(struct ieee80211vap *vap,
struct mbuf *
ieee80211_decap(struct ieee80211vap *vap, struct mbuf *m, int hdrlen)
{
- struct ieee80211_qosframe_addr4 wh; /* Max size address frames */
+#ifdef IEEE80211_SUPPORT_MESH
+ union {
+ struct ieee80211_qosframe_addr4 wh4;
+ uint8_t b[sizeof(struct ieee80211_qosframe_addr4) +
+ sizeof(struct ieee80211_meshcntl_ae11)];
+ } whu;
+#define wh whu.wh4
+#else
+ struct ieee80211_qosframe_addr4 wh;
+#endif
struct ether_header *eh;
struct llc *llc;
@@ -327,6 +339,7 @@ ieee80211_decap(struct ieee80211vap *vap, struct mbuf *m, int hdrlen)
eh->ether_type = htons(m->m_pkthdr.len - sizeof(*eh));
}
return m;
+#undef wh
}
/*
@@ -484,6 +497,8 @@ ieee80211_parse_beacon(struct ieee80211_node *ni, struct mbuf *m,
* [tlv] HT capabilities
* [tlv] HT information
* [tlv] Atheros capabilities
+ * [tlv] Mesh ID
+ * [tlv] Mesh Configuration
*/
IEEE80211_VERIFY_LENGTH(efrm - frm, 12,
return (scan->status = IEEE80211_BPARSE_BADIELEN));
@@ -559,6 +574,14 @@ ieee80211_parse_beacon(struct ieee80211_node *ni, struct mbuf *m,
case IEEE80211_ELEMID_HTINFO:
scan->htinfo = frm;
break;
+#ifdef IEEE80211_SUPPORT_TDMA
+ case IEEE80211_ELEMID_MESHID:
+ scan->meshid = frm;
+ break;
+ case IEEE80211_ELEMID_MESHCONF:
+ scan->meshconf = frm;
+ break;
+#endif
case IEEE80211_ELEMID_VENDOR:
if (iswpaoui(frm))
scan->wpa = frm;
diff --git a/sys/net80211/ieee80211_ioctl.c b/sys/net80211/ieee80211_ioctl.c
index 7712b04..35c7981 100644
--- a/sys/net80211/ieee80211_ioctl.c
+++ b/sys/net80211/ieee80211_ioctl.c
@@ -241,7 +241,8 @@ scan_space(const struct ieee80211_scan_entry *se, int *ielen)
* packet is <3Kbytes so we are sure this doesn't overflow
* 16-bits; if this is a concern we can drop the ie's.
*/
- len = sizeof(struct ieee80211req_scan_result) + se->se_ssid[1] + *ielen;
+ len = sizeof(struct ieee80211req_scan_result) + se->se_ssid[1] +
+ se->se_meshid[1] + *ielen;
return roundup(len, sizeof(uint32_t));
}
@@ -286,14 +287,19 @@ get_scan_result(void *arg, const struct ieee80211_scan_entry *se)
memcpy(sr->isr_rates+nr, se->se_xrates+2, nxr);
sr->isr_nrates = nr + nxr;
+ /* copy SSID */
sr->isr_ssid_len = se->se_ssid[1];
cp = ((uint8_t *)sr) + sr->isr_ie_off;
memcpy(cp, se->se_ssid+2, sr->isr_ssid_len);
- if (ielen) {
- cp += sr->isr_ssid_len;
+ /* copy mesh id */
+ cp += sr->isr_ssid_len;
+ sr->isr_meshid_len = se->se_meshid[1];
+ memcpy(cp, se->se_meshid+2, sr->isr_meshid_len);
+ cp += sr->isr_meshid_len;
+
+ if (ielen)
memcpy(cp, se->se_ies.data, ielen);
- }
req->space -= len;
req->sr = (struct ieee80211req_scan_result *)(((uint8_t *)sr) + len);
@@ -435,6 +441,9 @@ get_sta_info(void *arg, struct ieee80211_node *ni)
else
si->isi_inact = vap->iv_inact_init;
si->isi_inact = (si->isi_inact - ni->ni_inact) * IEEE80211_INACT_WAIT;
+ si->isi_localid = ni->ni_mllid;
+ si->isi_peerid = ni->ni_mlpid;
+ si->isi_peerstate = ni->ni_mlstate;
if (ielen) {
cp = ((uint8_t *)si) + si->isi_ie_off;
@@ -2847,6 +2856,7 @@ ieee80211_ioctl_set80211(struct ieee80211vap *vap, u_long cmd, struct ieee80211r
break;
case IEEE80211_IOC_DTIM_PERIOD:
if (vap->iv_opmode != IEEE80211_M_HOSTAP &&
+ vap->iv_opmode != IEEE80211_M_MBSS &&
vap->iv_opmode != IEEE80211_M_IBSS)
return EINVAL;
if (IEEE80211_DTIM_MIN <= ireq->i_val &&
@@ -2858,6 +2868,7 @@ ieee80211_ioctl_set80211(struct ieee80211vap *vap, u_long cmd, struct ieee80211r
break;
case IEEE80211_IOC_BEACON_INTERVAL:
if (vap->iv_opmode != IEEE80211_M_HOSTAP &&
+ vap->iv_opmode != IEEE80211_M_MBSS &&
vap->iv_opmode != IEEE80211_M_IBSS)
return EINVAL;
if (IEEE80211_BINTVAL_MIN <= ireq->i_val &&
diff --git a/sys/net80211/ieee80211_ioctl.h b/sys/net80211/ieee80211_ioctl.h
index db43778..723d3da 100644
--- a/sys/net80211/ieee80211_ioctl.h
+++ b/sys/net80211/ieee80211_ioctl.h
@@ -223,6 +223,19 @@ struct ieee80211_stats {
uint32_t is_tx_ctl; /* tx ctrl frames */
uint32_t is_ampdu_rexmt; /* A-MPDU frames rexmt ok */
uint32_t is_ampdu_rexmt_fail; /* A-MPDU frames rexmt fail */
+
+ uint32_t is_mesh_wrongmesh; /* dropped 'cuz not mesh sta*/
+ uint32_t is_mesh_nolink; /* dropped 'cuz link not estab*/
+ uint32_t is_mesh_fwd_ttl; /* mesh not fwd'd 'cuz ttl 0 */
+ uint32_t is_mesh_fwd_nobuf; /* mesh not fwd'd 'cuz no mbuf*/
+ uint32_t is_mesh_fwd_tooshort; /* mesh not fwd'd 'cuz no hdr */
+ uint32_t is_mesh_fwd_disabled; /* mesh not fwd'd 'cuz disabled */
+ uint32_t is_mesh_fwd_nopath; /* mesh not fwd'd 'cuz path unknown */
+
+ uint32_t is_hwmp_wrongseq; /* wrong hwmp seq no. */
+ uint32_t is_hwmp_rootreqs; /* root PREQs sent */
+ uint32_t is_hwmp_rootrann; /* root RANNs sent */
+
uint32_t is_spare[16];
};
@@ -305,6 +318,27 @@ struct ieee80211req_maclist {
} __packed;
/*
+ * Mesh Routing Table Operations.
+ */
+enum {
+ IEEE80211_MESH_RTCMD_LIST = 0, /* list HWMP routing table */
+ IEEE80211_MESH_RTCMD_FLUSH = 1, /* flush HWMP routing table */
+ IEEE80211_MESH_RTCMD_ADD = 2, /* add entry to the table */
+ IEEE80211_MESH_RTCMD_DELETE = 3, /* delete an entry from the table */
+};
+
+/*
+ * HWMP root modes
+ */
+enum {
+ IEEE80211_HWMP_ROOTMODE_DISABLED = 0, /* disabled */
+ IEEE80211_HWMP_ROOTMODE_NORMAL = 1, /* normal PREPs */
+ IEEE80211_HWMP_ROOTMODE_PROACTIVE = 2, /* proactive PREPS */
+ IEEE80211_HWMP_ROOTMODE_RANN = 3, /* use RANN elemid */
+};
+
+
+/*
* Set the active channel list by IEEE channel #: each channel
* to be marked active is set in a bit vector. Note this list is
* intersected with the available channel list in calculating
@@ -384,6 +418,10 @@ struct ieee80211req_sta_info {
uint16_t isi_pad;
uint32_t isi_jointime; /* time of assoc/join */
struct ieee80211_mimo_info isi_mimo; /* MIMO info for 11n sta's */
+ /* 11s info */
+ uint16_t isi_peerid;
+ uint16_t isi_localid;
+ uint8_t isi_peerstate;
/* XXX frag state? */
/* variable length IE data */
};
@@ -637,6 +675,22 @@ struct ieee80211req {
#define IEEE80211_IOC_GREENFIELD 112 /* Greenfield (on, off) */
#define IEEE80211_IOC_STBC 113 /* STBC Tx/RX (on, off) */
+#define IEEE80211_IOC_MESH_ID 170 /* mesh identifier */
+#define IEEE80211_IOC_MESH_AP 171 /* accepting peerings */
+#define IEEE80211_IOC_MESH_FWRD 172 /* forward frames */
+#define IEEE80211_IOC_MESH_PROTO 173 /* mesh protocols */
+#define IEEE80211_IOC_MESH_TTL 174 /* mesh TTL */
+#define IEEE80211_IOC_MESH_RTCMD 175 /* mesh routing table commands*/
+#define IEEE80211_IOC_MESH_PR_METRIC 176 /* mesh metric protocol */
+#define IEEE80211_IOC_MESH_PR_PATH 177 /* mesh path protocol */
+#define IEEE80211_IOC_MESH_PR_SIG 178 /* mesh sig protocol */
+#define IEEE80211_IOC_MESH_PR_CC 179 /* mesh congestion protocol */
+#define IEEE80211_IOC_MESH_PR_AUTH 180 /* mesh auth protocol */
+
+#define IEEE80211_IOC_HWMP_ROOTMODE 190 /* HWMP root mode */
+#define IEEE80211_IOC_HWMP_MAXHOPS 191 /* number of hops before drop */
+#define IEEE80211_IOC_HWMP_TTL 192 /* HWMP TTL */
+
#define IEEE80211_IOC_TDMA_SLOT 201 /* TDMA: assigned slot */
#define IEEE80211_IOC_TDMA_SLOTCNT 202 /* TDMA: slots in bss */
#define IEEE80211_IOC_TDMA_SLOTLEN 203 /* TDMA: slot length (usecs) */
@@ -724,7 +778,9 @@ struct ieee80211req_scan_result {
uint8_t isr_nrates;
uint8_t isr_rates[IEEE80211_RATE_MAXSIZE];
uint8_t isr_ssid_len; /* SSID length */
- /* variable length SSID followed by IE data */
+ uint8_t isr_meshid_len; /* MESH ID length */
+ /* variable length SSID, followed by variable length MESH ID,
+ followed by IE data */
};
/*
diff --git a/sys/net80211/ieee80211_mesh.c b/sys/net80211/ieee80211_mesh.c
new file mode 100644
index 0000000..6e7fa8d
--- /dev/null
+++ b/sys/net80211/ieee80211_mesh.c
@@ -0,0 +1,2538 @@
+/*-
+ * Copyright (c) 2009 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Rui Paulo under sponsorship from the
+ * FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <sys/cdefs.h>
+#ifdef __FreeBSD__
+__FBSDID("$FreeBSD$");
+#endif
+
+/*
+ * IEEE 802.11s Mesh Point (MBSS) support.
+ *
+ * Based on March 2009, D3.0 802.11s draft spec.
+ */
+#include "opt_inet.h"
+#include "opt_wlan.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/endian.h>
+#include <sys/errno.h>
+#include <sys/proc.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/if_llc.h>
+#include <net/ethernet.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_action.h>
+#include <net80211/ieee80211_input.h>
+#include <net80211/ieee80211_mesh.h>
+
+static int mesh_select_proto_path(struct ieee80211vap *, const char *);
+static int mesh_select_proto_metric(struct ieee80211vap *, const char *);
+static void mesh_vattach(struct ieee80211vap *);
+static int mesh_newstate(struct ieee80211vap *, enum ieee80211_state, int);
+static __inline void
+ mesh_linkchange(struct ieee80211_node *,
+ enum ieee80211_mesh_mlstate);
+static void mesh_checkid(void *, struct ieee80211_node *);
+static uint32_t mesh_generateid(struct ieee80211vap *);
+static int mesh_checkpseq(struct ieee80211vap *,
+ const uint8_t [IEEE80211_ADDR_LEN], uint32_t);
+static struct ieee80211_node *
+ mesh_find_txnode(struct ieee80211vap *,
+ const uint8_t [IEEE80211_ADDR_LEN]);
+static void mesh_forward(struct ieee80211vap *, struct mbuf *,
+ const struct ieee80211_meshcntl *);
+static int mesh_input(struct ieee80211_node *, struct mbuf *, int, int);
+static void mesh_recv_mgmt(struct ieee80211_node *, struct mbuf *, int,
+ int, int);
+static void mesh_peer_timeout_setup(struct ieee80211_node *);
+static void mesh_peer_timeout_backoff(struct ieee80211_node *);
+static void mesh_peer_timeout_cb(void *);
+static __inline void
+ mesh_peer_timeout_stop(struct ieee80211_node *);
+static int mesh_verify_meshpeerver(struct ieee80211vap *, const uint8_t *);
+static int mesh_verify_meshid(struct ieee80211vap *, const uint8_t *);
+static int mesh_verify_meshconf(struct ieee80211vap *, const uint8_t *);
+static int mesh_verify_meshpeer(struct ieee80211vap *, const uint8_t *);
+uint32_t mesh_airtime_calc(struct ieee80211_node *);
+
+/*
+ * Timeout values come from the specification and are in milliseconds.
+ */
+SYSCTL_NODE(_net_wlan, OID_AUTO, mesh, CTLFLAG_RD, 0,
+ "IEEE 802.11s parameters");
+static int ieee80211_mesh_retrytimeout = -1;
+SYSCTL_PROC(_net_wlan_mesh, OID_AUTO, retrytimeout, CTLTYPE_INT | CTLFLAG_RW,
+ &ieee80211_mesh_retrytimeout, 0, ieee80211_sysctl_msecs_ticks, "I",
+ "Retry timeout (msec)");
+static int ieee80211_mesh_holdingtimeout = -1;
+SYSCTL_PROC(_net_wlan_mesh, OID_AUTO, holdingtimeout, CTLTYPE_INT | CTLFLAG_RW,
+ &ieee80211_mesh_holdingtimeout, 0, ieee80211_sysctl_msecs_ticks, "I",
+ "Holding state timeout (msec)");
+static int ieee80211_mesh_confirmtimeout = -1;
+SYSCTL_PROC(_net_wlan_mesh, OID_AUTO, confirmtimeout, CTLTYPE_INT | CTLFLAG_RW,
+ &ieee80211_mesh_confirmtimeout, 0, ieee80211_sysctl_msecs_ticks, "I",
+ "Confirm state timeout (msec)");
+static int ieee80211_mesh_maxretries = 2;
+SYSCTL_INT(_net_wlan_mesh, OID_AUTO, maxretries, CTLTYPE_INT | CTLFLAG_RW,
+ &ieee80211_mesh_maxretries, 0,
+ "Maximum retries during peer link establishment");
+
+static const uint8_t broadcastaddr[IEEE80211_ADDR_LEN] =
+ { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+static ieee80211_recv_action_func mesh_recv_action_meshpeering_open;
+static ieee80211_recv_action_func mesh_recv_action_meshpeering_confirm;
+static ieee80211_recv_action_func mesh_recv_action_meshpeering_close;
+static ieee80211_recv_action_func mesh_recv_action_meshlmetric_req;
+static ieee80211_recv_action_func mesh_recv_action_meshlmetric_rep;
+
+static ieee80211_send_action_func mesh_send_action_meshpeering_open;
+static ieee80211_send_action_func mesh_send_action_meshpeering_confirm;
+static ieee80211_send_action_func mesh_send_action_meshpeering_close;
+static ieee80211_send_action_func mesh_send_action_meshlink_request;
+static ieee80211_send_action_func mesh_send_action_meshlink_reply;
+
+static const struct ieee80211_mesh_proto_metric mesh_metric_airtime = {
+ .mpm_descr = "AIRTIME",
+ .mpm_ie = IEEE80211_MESHCONF_AIRTIME,
+ .mpm_metric = mesh_airtime_calc,
+};
+
+static struct ieee80211_mesh_proto_path mesh_proto_paths[4];
+static struct ieee80211_mesh_proto_metric mesh_proto_metrics[4];
+
+#define MESH_RT_LOCK(ms) mtx_lock(&(ms)->ms_rt_lock)
+#define MESH_RT_UNLOCK(ms) mtx_unlock(&(ms)->ms_rt_lock)
+
+MALLOC_DEFINE(M_80211_MESH_RT, "80211mesh", "802.11s routing table");
+
+/*
+ * Helper functions to manipulate the Mesh routing table.
+ */
+struct ieee80211_mesh_route *
+ieee80211_mesh_rt_find(struct ieee80211vap *vap,
+ const uint8_t dest[IEEE80211_ADDR_LEN])
+{
+ struct ieee80211_mesh_state *ms = vap->iv_mesh;
+ struct ieee80211_mesh_route *rt;
+
+ MESH_RT_LOCK(ms);
+ TAILQ_FOREACH(rt, &ms->ms_routes, rt_next) {
+ if (IEEE80211_ADDR_EQ(dest, rt->rt_dest)) {
+ MESH_RT_UNLOCK(ms);
+ return rt;
+ }
+ }
+ MESH_RT_UNLOCK(ms);
+ return NULL;
+}
+
+struct ieee80211_mesh_route *
+ieee80211_mesh_rt_add(struct ieee80211vap *vap,
+ const uint8_t dest[IEEE80211_ADDR_LEN])
+{
+ struct ieee80211_mesh_state *ms = vap->iv_mesh;
+ struct ieee80211_mesh_route *rt;
+
+ KASSERT(ieee80211_mesh_rt_find(vap, dest) == NULL,
+ ("%s: duplicate entry in the routing table", __func__));
+ KASSERT(!IEEE80211_ADDR_EQ(vap->iv_myaddr, dest),
+ ("%s: adding self to the routing table", __func__));
+ KASSERT(!IEEE80211_ADDR_EQ(broadcastaddr, dest),
+ ("%s: adding broadcast to the routing table", __func__));
+
+ rt = malloc(sizeof(struct ieee80211_mesh_route), M_80211_MESH_RT,
+ M_NOWAIT | M_ZERO);
+ IEEE80211_ADDR_COPY(rt->rt_dest, dest);
+ rt->rt_priv = malloc(ms->ms_ppath->mpp_privlen, M_80211_MESH_RT,
+ M_NOWAIT | M_ZERO);
+ MESH_RT_LOCK(ms);
+ TAILQ_INSERT_TAIL(&ms->ms_routes, rt, rt_next);
+ MESH_RT_UNLOCK(ms);
+ return rt;
+}
+
+void
+ieee80211_mesh_rt_del(struct ieee80211vap *vap,
+ const uint8_t dest[IEEE80211_ADDR_LEN])
+{
+ struct ieee80211_mesh_state *ms = vap->iv_mesh;
+ struct ieee80211_mesh_route *rt, *next;
+
+ MESH_RT_LOCK(ms);
+ TAILQ_FOREACH_SAFE(rt, &ms->ms_routes, rt_next, next) {
+ if (IEEE80211_ADDR_EQ(rt->rt_dest, dest)) {
+ TAILQ_REMOVE(&ms->ms_routes, rt, rt_next);
+ free(rt->rt_priv, M_80211_MESH_RT);
+ free(rt, M_80211_MESH_RT);
+ MESH_RT_UNLOCK(ms);
+ return;
+ }
+ }
+ MESH_RT_UNLOCK(ms);
+}
+
+void
+ieee80211_mesh_rt_flush(struct ieee80211vap *vap)
+{
+ struct ieee80211_mesh_state *ms = vap->iv_mesh;
+ struct ieee80211_mesh_route *rt, *next;
+
+ if (ms == NULL)
+ return;
+ MESH_RT_LOCK(ms);
+ TAILQ_FOREACH_SAFE(rt, &ms->ms_routes, rt_next, next) {
+ TAILQ_REMOVE(&ms->ms_routes, rt, rt_next);
+ free(rt->rt_priv, M_80211_MESH_RT);
+ free(rt, M_80211_MESH_RT);
+ }
+ MESH_RT_UNLOCK(ms);
+}
+
+#define N(a) (sizeof(a) / sizeof(a[0]))
+int
+ieee80211_mesh_register_proto_path(const struct ieee80211_mesh_proto_path *mpp)
+{
+ int i, firstempty = -1;
+ static const uint8_t emptyie[4] = { 0, 0, 0, 0 };
+
+ for (i = 0; i < N(mesh_proto_paths); i++) {
+ if (memcmp(mpp->mpp_ie, mesh_proto_paths[i].mpp_ie, 4) == 0)
+ return EEXIST;
+ if (memcmp(mesh_proto_paths[i].mpp_ie, emptyie, 4) == 0 &&
+ firstempty == -1)
+ firstempty = i;
+ }
+ if (firstempty < 0)
+ return ENOSPC;
+ memcpy(&mesh_proto_paths[firstempty], mpp, sizeof(*mpp));
+ return 0;
+}
+
+int
+ieee80211_mesh_register_proto_metric(const struct
+ ieee80211_mesh_proto_metric *mpm)
+{
+ int i, firstempty = -1;
+ static const uint8_t emptyie[4] = { 0, 0, 0, 0 };
+
+ for (i = 0; i < N(mesh_proto_metrics); i++) {
+ if (memcmp(mpm->mpm_ie, mesh_proto_metrics[i].mpm_ie, 4) == 0)
+ return EEXIST;
+ if (memcmp(mesh_proto_metrics[i].mpm_ie, emptyie, 4) == 0 &&
+ firstempty == -1)
+ firstempty = i;
+ }
+ if (firstempty < 0)
+ return ENOSPC;
+ memcpy(&mesh_proto_metrics[firstempty], mpm, sizeof(*mpm));
+ return 0;
+}
+
+static int
+mesh_select_proto_path(struct ieee80211vap *vap, const char *name)
+{
+ struct ieee80211_mesh_state *ms = vap->iv_mesh;
+ int i;
+
+ for (i = 0; i < N(mesh_proto_paths); i++) {
+ if (strcasecmp(mesh_proto_paths[i].mpp_descr, name) == 0) {
+ ms->ms_ppath = &mesh_proto_paths[i];
+ if (vap->iv_state == IEEE80211_S_RUN)
+ vap->iv_newstate(vap, IEEE80211_S_INIT, 0);
+ return 0;
+ }
+ }
+ return ENOENT;
+}
+
+static int
+mesh_select_proto_metric(struct ieee80211vap *vap, const char *name)
+{
+ struct ieee80211_mesh_state *ms = vap->iv_mesh;
+ int i;
+
+ for (i = 0; i < N(mesh_proto_metrics); i++) {
+ if (strcasecmp(mesh_proto_metrics[i].mpm_descr, name) == 0) {
+ ms->ms_pmetric = &mesh_proto_metrics[i];
+ if (vap->iv_state == IEEE80211_S_RUN)
+ vap->iv_newstate(vap, IEEE80211_S_INIT, 0);
+ return 0;
+ }
+ }
+ return ENOENT;
+}
+#undef N
+
+static void
+ieee80211_mesh_init(void)
+{
+
+ memset(mesh_proto_paths, 0, sizeof(mesh_proto_paths));
+ memset(mesh_proto_metrics, 0, sizeof(mesh_proto_metrics));
+
+ /*
+ * Setup mesh parameters that depends on the clock frequency.
+ */
+ ieee80211_mesh_retrytimeout = msecs_to_ticks(40);
+ ieee80211_mesh_holdingtimeout = msecs_to_ticks(40);
+ ieee80211_mesh_confirmtimeout = msecs_to_ticks(40);
+
+ /*
+ * Register action frame handlers.
+ */
+ ieee80211_recv_action_register(IEEE80211_ACTION_CAT_MESHPEERING,
+ IEEE80211_ACTION_MESHPEERING_OPEN,
+ mesh_recv_action_meshpeering_open);
+ ieee80211_recv_action_register(IEEE80211_ACTION_CAT_MESHPEERING,
+ IEEE80211_ACTION_MESHPEERING_CONFIRM,
+ mesh_recv_action_meshpeering_confirm);
+ ieee80211_recv_action_register(IEEE80211_ACTION_CAT_MESHPEERING,
+ IEEE80211_ACTION_MESHPEERING_CLOSE,
+ mesh_recv_action_meshpeering_close);
+ ieee80211_recv_action_register(IEEE80211_ACTION_CAT_MESHLMETRIC,
+ IEEE80211_ACTION_MESHLMETRIC_REQ, mesh_recv_action_meshlmetric_req);
+ ieee80211_recv_action_register(IEEE80211_ACTION_CAT_MESHLMETRIC,
+ IEEE80211_ACTION_MESHLMETRIC_REP, mesh_recv_action_meshlmetric_rep);
+
+ ieee80211_send_action_register(IEEE80211_ACTION_CAT_MESHPEERING,
+ IEEE80211_ACTION_MESHPEERING_OPEN,
+ mesh_send_action_meshpeering_open);
+ ieee80211_send_action_register(IEEE80211_ACTION_CAT_MESHPEERING,
+ IEEE80211_ACTION_MESHPEERING_CONFIRM,
+ mesh_send_action_meshpeering_confirm);
+ ieee80211_send_action_register(IEEE80211_ACTION_CAT_MESHPEERING,
+ IEEE80211_ACTION_MESHPEERING_CLOSE,
+ mesh_send_action_meshpeering_close);
+ ieee80211_send_action_register(IEEE80211_ACTION_CAT_MESHLMETRIC,
+ IEEE80211_ACTION_MESHLMETRIC_REQ,
+ mesh_send_action_meshlink_request);
+ ieee80211_send_action_register(IEEE80211_ACTION_CAT_MESHLMETRIC,
+ IEEE80211_ACTION_MESHLMETRIC_REP,
+ mesh_send_action_meshlink_reply);
+
+ /*
+ * Register Airtime Link Metric.
+ */
+ ieee80211_mesh_register_proto_metric(&mesh_metric_airtime);
+
+}
+SYSINIT(wlan_mesh, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_mesh_init, NULL);
+
+void
+ieee80211_mesh_attach(struct ieee80211com *ic)
+{
+ ic->ic_vattach[IEEE80211_M_MBSS] = mesh_vattach;
+}
+
+void
+ieee80211_mesh_detach(struct ieee80211com *ic)
+{
+}
+
+static void
+mesh_vdetach_peers(void *arg, struct ieee80211_node *ni)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ uint16_t args[3];
+
+ if (ni->ni_mlstate == IEEE80211_NODE_MESH_ESTABLISHED) {
+ args[0] = ni->ni_mlpid;
+ args[1] = ni->ni_mllid;
+ args[2] = IEEE80211_REASON_PEER_LINK_CANCELED;
+ ieee80211_send_action(ni,
+ IEEE80211_ACTION_CAT_MESHPEERING,
+ IEEE80211_ACTION_MESHPEERING_CLOSE,
+ args);
+ }
+ callout_stop(&ni->ni_mltimer);
+ /* XXX belongs in hwmp */
+ ieee80211_ageq_drain_node(&ic->ic_stageq,
+ (void *)(uintptr_t) ieee80211_mac_hash(ic, ni->ni_macaddr));
+}
+
+static void
+mesh_vdetach(struct ieee80211vap *vap)
+{
+ struct ieee80211_mesh_state *ms = vap->iv_mesh;
+
+ ieee80211_iterate_nodes(&vap->iv_ic->ic_sta, mesh_vdetach_peers,
+ NULL);
+ ieee80211_mesh_rt_flush(vap);
+ mtx_destroy(&ms->ms_rt_lock);
+ ms->ms_ppath->mpp_vdetach(vap);
+ free(vap->iv_mesh, M_80211_VAP);
+ vap->iv_mesh = NULL;
+}
+
+static void
+mesh_vattach(struct ieee80211vap *vap)
+{
+ struct ieee80211_mesh_state *ms;
+ vap->iv_newstate = mesh_newstate;
+ vap->iv_input = mesh_input;
+ vap->iv_opdetach = mesh_vdetach;
+ vap->iv_recv_mgmt = mesh_recv_mgmt;
+ ms = malloc(sizeof(struct ieee80211_mesh_state), M_80211_VAP,
+ M_NOWAIT | M_ZERO);
+ if (ms == NULL) {
+ printf("%s: couldn't alloc MBSS state\n", __func__);
+ return;
+ }
+ vap->iv_mesh = ms;
+ ms->ms_seq = 0;
+ ms->ms_flags = (IEEE80211_MESHFLAGS_AP | IEEE80211_MESHFLAGS_FWD);
+ ms->ms_ttl = IEEE80211_MESH_DEFAULT_TTL;
+ TAILQ_INIT(&ms->ms_routes);
+ mtx_init(&ms->ms_rt_lock, "MBSS", "802.11s routing table", MTX_DEF);
+ mesh_select_proto_metric(vap, "AIRTIME");
+ KASSERT(ms->ms_pmetric, ("ms_pmetric == NULL"));
+ mesh_select_proto_path(vap, "HWMP");
+ KASSERT(ms->ms_ppath, ("ms_ppath == NULL"));
+ ms->ms_ppath->mpp_vattach(vap);
+}
+
+/*
+ * IEEE80211_M_MBSS vap state machine handler.
+ */
+static int
+mesh_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+ struct ieee80211_mesh_state *ms = vap->iv_mesh;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_node *ni;
+ enum ieee80211_state ostate;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ ostate = vap->iv_state;
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n",
+ __func__, ieee80211_state_name[ostate],
+ ieee80211_state_name[nstate], arg);
+ vap->iv_state = nstate; /* state transition */
+ if (ostate != IEEE80211_S_SCAN)
+ ieee80211_cancel_scan(vap); /* background scan */
+ ni = vap->iv_bss; /* NB: no reference held */
+ /* Flush the routing table */
+ if (nstate != IEEE80211_S_INIT && ostate == IEEE80211_S_INIT)
+ ieee80211_mesh_rt_flush(vap);
+ switch (nstate) {
+ case IEEE80211_S_INIT:
+ switch (ostate) {
+ case IEEE80211_S_SCAN:
+ ieee80211_cancel_scan(vap);
+ break;
+ case IEEE80211_S_CAC:
+ ieee80211_dfs_cac_stop(vap);
+ break;
+ case IEEE80211_S_RUN:
+ ieee80211_iterate_nodes(&ic->ic_sta,
+ mesh_vdetach_peers, NULL);
+ break;
+ default:
+ break;
+ }
+ if (ostate != IEEE80211_S_INIT) {
+ /* NB: optimize INIT -> INIT case */
+ ieee80211_reset_bss(vap);
+ }
+ break;
+ case IEEE80211_S_SCAN:
+ switch (ostate) {
+ case IEEE80211_S_INIT:
+ if (vap->iv_des_chan != IEEE80211_CHAN_ANYC &&
+ !IEEE80211_IS_CHAN_RADAR(vap->iv_des_chan) &&
+ ms->ms_idlen != 0) {
+ /*
+ * Already have a channel and a mesh ID; bypass
+ * the scan and startup immediately.
+ */
+ ieee80211_create_ibss(vap, vap->iv_des_chan);
+ break;
+ }
+ /*
+ * Initiate a scan. We can come here as a result
+ * of an IEEE80211_IOC_SCAN_REQ too in which case
+ * the vap will be marked with IEEE80211_FEXT_SCANREQ
+ * and the scan request parameters will be present
+ * in iv_scanreq. Otherwise we do the default.
+ */
+ if (vap->iv_flags_ext & IEEE80211_FEXT_SCANREQ) {
+ ieee80211_check_scan(vap,
+ vap->iv_scanreq_flags,
+ vap->iv_scanreq_duration,
+ vap->iv_scanreq_mindwell,
+ vap->iv_scanreq_maxdwell,
+ vap->iv_scanreq_nssid, vap->iv_scanreq_ssid);
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANREQ;
+ } else
+ ieee80211_check_scan_current(vap);
+ break;
+ default:
+ break;
+ }
+ break;
+ case IEEE80211_S_CAC:
+ /*
+ * Start CAC on a DFS channel. We come here when starting
+ * a bss on a DFS channel (see ieee80211_create_ibss).
+ */
+ ieee80211_dfs_cac_start(vap);
+ break;
+ case IEEE80211_S_RUN:
+ switch (ostate) {
+ case IEEE80211_S_INIT:
+ /*
+ * Already have a channel; bypass the
+ * scan and startup immediately.
+ * Note that ieee80211_create_ibss will call
+ * back to do a RUN->RUN state change.
+ */
+ ieee80211_create_ibss(vap,
+ ieee80211_ht_adjust_channel(ic,
+ ic->ic_curchan, vap->iv_flags_ht));
+ /* NB: iv_bss is changed on return */
+ break;
+ case IEEE80211_S_CAC:
+ /*
+ * NB: This is the normal state change when CAC
+ * expires and no radar was detected; no need to
+ * clear the CAC timer as it's already expired.
+ */
+ /* fall thru... */
+ case IEEE80211_S_CSA:
+#if 0
+ /*
+ * Shorten inactivity timer of associated stations
+ * to weed out sta's that don't follow a CSA.
+ */
+ ieee80211_iterate_nodes(&ic->ic_sta, sta_csa, vap);
+#endif
+ /*
+ * Update bss node channel to reflect where
+ * we landed after CSA.
+ */
+ ieee80211_node_set_chan(vap->iv_bss,
+ ieee80211_ht_adjust_channel(ic, ic->ic_curchan,
+ ieee80211_htchanflags(vap->iv_bss->ni_chan)));
+ /* XXX bypass debug msgs */
+ break;
+ case IEEE80211_S_SCAN:
+ case IEEE80211_S_RUN:
+#ifdef IEEE80211_DEBUG
+ if (ieee80211_msg_debug(vap)) {
+ struct ieee80211_node *ni = vap->iv_bss;
+ ieee80211_note(vap,
+ "synchronized with %s meshid ",
+ ether_sprintf(ni->ni_meshid));
+ ieee80211_print_essid(ni->ni_meshid,
+ ni->ni_meshidlen);
+ /* XXX MCS/HT */
+ printf(" channel %d\n",
+ ieee80211_chan2ieee(ic, ic->ic_curchan));
+ }
+#endif
+ break;
+ default:
+ break;
+ }
+ ieee80211_node_authorize(vap->iv_bss);
+ break;
+ default:
+ break;
+ }
+
+ /* NB: ostate not nstate */
+ ms->ms_ppath->mpp_newstate(vap, ostate, arg);
+
+ return 0;
+}
+
+/*
+ * Helper function to note the Mesh Peer Link FSM change.
+ */
+static void
+mesh_linkchange(struct ieee80211_node *ni, enum ieee80211_mesh_mlstate state)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211_mesh_state *ms = vap->iv_mesh;
+#ifdef IEEE80211_DEBUG
+ static const char *meshlinkstates[] = {
+ [IEEE80211_NODE_MESH_IDLE] = "IDLE",
+ [IEEE80211_NODE_MESH_OPENSNT] = "OPEN SENT",
+ [IEEE80211_NODE_MESH_OPENRCV] = "OPEN RECEIVED",
+ [IEEE80211_NODE_MESH_CONFIRMRCV] = "CONFIRM RECEIVED",
+ [IEEE80211_NODE_MESH_ESTABLISHED] = "ESTABLISHED",
+ [IEEE80211_NODE_MESH_HOLDING] = "HOLDING"
+ };
+#endif
+ IEEE80211_NOTE(vap, IEEE80211_MSG_MESH,
+ ni, "peer link: %s -> %s",
+ meshlinkstates[ni->ni_mlstate], meshlinkstates[state]);
+
+ /* track neighbor count */
+ if (state == IEEE80211_NODE_MESH_ESTABLISHED &&
+ ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED) {
+ KASSERT(ms->ms_neighbors < 65535, ("neighbor count overflow"));
+ ms->ms_neighbors++;
+ } else if (ni->ni_mlstate == IEEE80211_NODE_MESH_ESTABLISHED &&
+ state != IEEE80211_NODE_MESH_ESTABLISHED) {
+ KASSERT(ms->ms_neighbors > 0, ("neighbor count 0"));
+ ms->ms_neighbors--;
+ }
+ ni->ni_mlstate = state;
+ if (state == IEEE80211_NODE_MESH_HOLDING)
+ ms->ms_ppath->mpp_peerdown(ni);
+}
+
+/*
+ * Helper function to generate a unique local ID required for mesh
+ * peer establishment.
+ */
+static void
+mesh_checkid(void *arg, struct ieee80211_node *ni)
+{
+ uint16_t *r = arg;
+
+ if (*r == ni->ni_mllid)
+ *(uint16_t *)arg = 0;
+}
+
+static uint32_t
+mesh_generateid(struct ieee80211vap *vap)
+{
+ int maxiter = 4;
+ uint16_t r;
+
+ do {
+ get_random_bytes(&r, 2);
+ ieee80211_iterate_nodes(&vap->iv_ic->ic_sta, mesh_checkid, &r);
+ maxiter--;
+ } while (r == 0 && maxiter > 0);
+ return r;
+}
+
+/*
+ * Verifies if we already received this packet by checking its
+ * sequence number.
+ */
+static int
+mesh_checkpseq(struct ieee80211vap *vap,
+ const uint8_t source[IEEE80211_ADDR_LEN], uint32_t seq)
+{
+ struct ieee80211_mesh_route *rt;
+
+ rt = ieee80211_mesh_rt_find(vap, source);
+ if (rt == NULL) {
+ rt = ieee80211_mesh_rt_add(vap, source);
+ rt->rt_lastmseq = seq;
+ return 0;
+ }
+ if (IEEE80211_MESH_SEQ_GEQ(rt->rt_lastmseq, seq)) {
+ return 1;
+ } else {
+ rt->rt_lastmseq = seq;
+ return 0;
+ }
+}
+
+/*
+ * Iterate the routing table and locate the next hop.
+ */
+static struct ieee80211_node *
+mesh_find_txnode(struct ieee80211vap *vap,
+ const uint8_t dest[IEEE80211_ADDR_LEN])
+{
+ struct ieee80211_mesh_route *rt;
+
+ rt = ieee80211_mesh_rt_find(vap, dest);
+ if (rt == NULL)
+ return NULL;
+ return ieee80211_find_txnode(vap, rt->rt_nexthop);
+}
+
+/*
+ * Forward the specified frame.
+ * Decrement the TTL and set TA to our MAC address.
+ */
+static void
+mesh_forward(struct ieee80211vap *vap, struct mbuf *m,
+ const struct ieee80211_meshcntl *mc)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_mesh_state *ms = vap->iv_mesh;
+ struct ifnet *ifp = vap->iv_ifp;
+ struct ifnet *parent = ic->ic_ifp;
+ const struct ieee80211_frame *wh =
+ mtod(m, const struct ieee80211_frame *);
+ struct mbuf *mcopy;
+ struct ieee80211_meshcntl *mccopy;
+ struct ieee80211_frame *whcopy;
+ struct ieee80211_node *ni;
+ int err;
+
+ if (mc->mc_ttl == 0) {
+ IEEE80211_NOTE_FRAME(vap, IEEE80211_MSG_MESH, wh,
+ "%s", "frame not fwd'd, ttl 0");
+ vap->iv_stats.is_mesh_fwd_ttl++;
+ return;
+ }
+ if (!(ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) {
+ IEEE80211_NOTE_FRAME(vap, IEEE80211_MSG_MESH, wh,
+ "%s", "frame not fwd'd, fwding disabled");
+ vap->iv_stats.is_mesh_fwd_disabled++;
+ return;
+ }
+ mcopy = m_dup(m, M_DONTWAIT);
+ if (mcopy == NULL) {
+ IEEE80211_NOTE_FRAME(vap, IEEE80211_MSG_MESH, wh,
+ "%s", "frame not fwd'd, cannot dup");
+ vap->iv_stats.is_mesh_fwd_nobuf++;
+ ifp->if_oerrors++;
+ return;
+ }
+ mcopy = m_pullup(mcopy, ieee80211_hdrspace(ic, wh) +
+ sizeof(struct ieee80211_meshcntl));
+ if (mcopy == NULL) {
+ IEEE80211_NOTE_FRAME(vap, IEEE80211_MSG_MESH, wh,
+ "%s", "frame not fwd'd, too short");
+ vap->iv_stats.is_mesh_fwd_tooshort++;
+ ifp->if_oerrors++;
+ m_freem(mcopy);
+ return;
+ }
+ whcopy = mtod(mcopy, struct ieee80211_frame *);
+ mccopy = (struct ieee80211_meshcntl *)
+ (mtod(mcopy, uint8_t *) + ieee80211_hdrspace(ic, wh));
+ /* XXX clear other bits? */
+ whcopy->i_fc[1] &= ~IEEE80211_FC1_RETRY;
+ IEEE80211_ADDR_COPY(whcopy->i_addr2, vap->iv_myaddr);
+ if (IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ ni = ieee80211_ref_node(vap->iv_bss);
+ mcopy->m_flags |= M_MCAST;
+ } else {
+ ni = mesh_find_txnode(vap, whcopy->i_addr3);
+ if (ni == NULL) {
+ IEEE80211_NOTE_FRAME(vap, IEEE80211_MSG_MESH, wh,
+ "%s", "frame not fwd'd, no path");
+ vap->iv_stats.is_mesh_fwd_nopath++;
+ m_freem(mcopy);
+ return;
+ }
+ IEEE80211_ADDR_COPY(whcopy->i_addr1, ni->ni_macaddr);
+ }
+ IEEE80211_NOTE(vap, IEEE80211_MSG_MESH, ni,
+ "fwd %s frame from %s ttl %d",
+ IEEE80211_IS_MULTICAST(wh->i_addr1) ? "mcast" : "ucast",
+ ether_sprintf(wh->i_addr3), mccopy->mc_ttl);
+
+ KASSERT(mccopy->mc_ttl > 0, ("%s called with wrong ttl", __func__));
+ mccopy->mc_ttl--;
+
+ /* XXX calculate priority so drivers can find the tx queue */
+ M_WME_SETAC(mcopy, WME_AC_BE);
+
+ /* XXX do we know m_nextpkt is NULL? */
+ mcopy->m_pkthdr.rcvif = (void *) ni;
+ err = parent->if_transmit(parent, mcopy);
+ if (err != 0) {
+ /* NB: IFQ_HANDOFF reclaims mbuf */
+ ieee80211_free_node(ni);
+ } else {
+ ifp->if_opackets++;
+ }
+}
+
+static int
+mesh_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
+{
+#define SEQ_LEQ(a,b) ((int)((a)-(b)) <= 0)
+#define HAS_SEQ(type) ((type & 0x4) == 0)
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ifnet *ifp = vap->iv_ifp;
+ struct ieee80211_frame *wh;
+ const struct ieee80211_meshcntl *mc;
+ int hdrspace, need_tap;
+ uint8_t dir, type, subtype, qos;
+ uint32_t seq;
+ uint8_t *addr;
+ ieee80211_seq rxseq;
+
+ KASSERT(ni != NULL, ("null node"));
+ ni->ni_inact = ni->ni_inact_reload;
+
+ need_tap = 1; /* mbuf need to be tapped. */
+ type = -1; /* undefined */
+
+ if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, NULL,
+ "too short (1): len %u", m->m_pkthdr.len);
+ vap->iv_stats.is_rx_tooshort++;
+ goto out;
+ }
+ /*
+ * Bit of a cheat here, we use a pointer for a 3-address
+ * frame format but don't reference fields past outside
+ * ieee80211_frame_min w/o first validating the data is
+ * present.
+ */
+ wh = mtod(m, struct ieee80211_frame *);
+
+ if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
+ IEEE80211_FC0_VERSION_0) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, NULL, "wrong version %x", wh->i_fc[0]);
+ vap->iv_stats.is_rx_badversion++;
+ goto err;
+ }
+ dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
+ type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
+ subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
+ if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
+ IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi);
+ ni->ni_noise = nf;
+ if (HAS_SEQ(type)) {
+ uint8_t tid = ieee80211_gettid(wh);
+
+ if (IEEE80211_QOS_HAS_SEQ(wh) &&
+ TID_TO_WME_AC(tid) >= WME_AC_VI)
+ ic->ic_wme.wme_hipri_traffic++;
+ rxseq = le16toh(*(uint16_t *)wh->i_seq);
+ if ((ni->ni_flags & IEEE80211_NODE_HT) == 0 &&
+ (wh->i_fc[1] & IEEE80211_FC1_RETRY) &&
+ SEQ_LEQ(rxseq, ni->ni_rxseqs[tid])) {
+ /* duplicate, discard */
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ wh->i_addr1, "duplicate",
+ "seqno <%u,%u> fragno <%u,%u> tid %u",
+ rxseq >> IEEE80211_SEQ_SEQ_SHIFT,
+ ni->ni_rxseqs[tid] >>
+ IEEE80211_SEQ_SEQ_SHIFT,
+ rxseq & IEEE80211_SEQ_FRAG_MASK,
+ ni->ni_rxseqs[tid] &
+ IEEE80211_SEQ_FRAG_MASK,
+ tid);
+ vap->iv_stats.is_rx_dup++;
+ IEEE80211_NODE_STAT(ni, rx_dup);
+ goto out;
+ }
+ ni->ni_rxseqs[tid] = rxseq;
+ }
+ }
+#ifdef IEEE80211_DEBUG
+ /*
+ * It's easier, but too expensive, to simulate different mesh
+ * topologies by consulting the ACL policy very early, so do this
+ * only under DEBUG.
+ *
+ * NB: this check is also done upon peering link initiation.
+ */
+ if (vap->iv_acl != NULL && !vap->iv_acl->iac_check(vap, wh->i_addr2)) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ACL,
+ wh, NULL, "%s", "disallowed by ACL");
+ vap->iv_stats.is_rx_acl++;
+ goto out;
+ }
+#endif
+ switch (type) {
+ case IEEE80211_FC0_TYPE_DATA:
+ if (ni == vap->iv_bss)
+ goto out;
+ if (ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_MESH,
+ ni->ni_macaddr, NULL,
+ "peer link not yet established (%d)",
+ ni->ni_mlstate);
+ vap->iv_stats.is_mesh_nolink++;
+ goto out;
+ }
+ if (dir != IEEE80211_FC1_DIR_FROMDS &&
+ dir != IEEE80211_FC1_DIR_DSTODS) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, "data", "incorrect dir 0x%x", dir);
+ vap->iv_stats.is_rx_wrongdir++;
+ goto err;
+ }
+ /* pull up enough to get to the mesh control */
+ hdrspace = ieee80211_hdrspace(ic, wh);
+ if (m->m_len < hdrspace + sizeof(struct ieee80211_meshcntl) &&
+ (m = m_pullup(m, hdrspace +
+ sizeof(struct ieee80211_meshcntl))) == NULL) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, NULL,
+ "data too short: expecting %u", hdrspace);
+ vap->iv_stats.is_rx_tooshort++;
+ goto out; /* XXX */
+ }
+ /*
+ * Now calculate the full extent of the headers. Note
+ * ieee80211_decap will pull up anything we didn't get
+ * above when it strips the 802.11 headers.
+ */
+ mc = (const struct ieee80211_meshcntl *)
+ (mtod(m, const uint8_t *) + hdrspace);
+ hdrspace += sizeof(struct ieee80211_meshcntl) +
+ (mc->mc_flags & 3) * IEEE80211_ADDR_LEN;
+ seq = LE_READ_4(mc->mc_seq);
+ if (IEEE80211_IS_MULTICAST(wh->i_addr1))
+ addr = wh->i_addr3;
+ else
+ addr = ((struct ieee80211_qosframe_addr4 *)wh)->i_addr4;
+ if (IEEE80211_ADDR_EQ(vap->iv_myaddr, addr)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ addr, "data", "%s", "not to me");
+ vap->iv_stats.is_rx_wrongbss++; /* XXX kinda */
+ goto out;
+ }
+ if (mesh_checkpseq(vap, addr, seq) != 0) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_MESH,
+ addr, "data", "duplicate mesh seqno %u ttl %u",
+ seq, mc->mc_ttl);
+ vap->iv_stats.is_rx_dup++;
+ goto out;
+ }
+
+ /*
+ * Potentially forward packet. See table s36 (p140)
+ * for the rules. XXX tap fwd'd packets not for us?
+ */
+ if (dir == IEEE80211_FC1_DIR_FROMDS ||
+ !IEEE80211_ADDR_EQ(wh->i_addr3, vap->iv_myaddr)) {
+ mesh_forward(vap, m, mc);
+ if (dir == IEEE80211_FC1_DIR_DSTODS)
+ goto out;
+ /* NB: fall thru to deliver mcast frames locally */
+ }
+
+ /*
+ * Save QoS bits for use below--before we strip the header.
+ */
+ if (subtype == IEEE80211_FC0_SUBTYPE_QOS) {
+ qos = (dir == IEEE80211_FC1_DIR_DSTODS) ?
+ ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0] :
+ ((struct ieee80211_qosframe *)wh)->i_qos[0];
+ } else
+ qos = 0;
+ /*
+ * Next up, any fragmentation.
+ */
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ m = ieee80211_defrag(ni, m, hdrspace);
+ if (m == NULL) {
+ /* Fragment dropped or frame not complete yet */
+ goto out;
+ }
+ }
+ wh = NULL; /* no longer valid, catch any uses */
+
+ if (ieee80211_radiotap_active_vap(vap))
+ ieee80211_radiotap_rx(vap, m);
+ need_tap = 0;
+
+ /*
+ * Finally, strip the 802.11 header.
+ */
+ m = ieee80211_decap(vap, m, hdrspace);
+ if (m == NULL) {
+ /* XXX mask bit to check for both */
+ /* don't count Null data frames as errors */
+ if (subtype == IEEE80211_FC0_SUBTYPE_NODATA ||
+ subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL)
+ goto out;
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ ni->ni_macaddr, "data", "%s", "decap error");
+ vap->iv_stats.is_rx_decap++;
+ IEEE80211_NODE_STAT(ni, rx_decap);
+ goto err;
+ }
+ if (qos & IEEE80211_QOS_AMSDU) {
+ m = ieee80211_decap_amsdu(ni, m);
+ if (m == NULL)
+ return IEEE80211_FC0_TYPE_DATA;
+ } else {
+#ifdef IEEE80211_SUPPORT_SUPERG
+ m = ieee80211_decap_fastframe(vap, ni, m);
+ if (m == NULL)
+ return IEEE80211_FC0_TYPE_DATA;
+#endif
+ }
+ ieee80211_deliver_data(vap, ni, m);
+ return type;
+ case IEEE80211_FC0_TYPE_MGT:
+ vap->iv_stats.is_rx_mgmt++;
+ IEEE80211_NODE_STAT(ni, rx_mgmt);
+ if (dir != IEEE80211_FC1_DIR_NODS) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, "mgt", "incorrect dir 0x%x", dir);
+ vap->iv_stats.is_rx_wrongdir++;
+ goto err;
+ }
+ if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, "mgt", "too short: len %u",
+ m->m_pkthdr.len);
+ vap->iv_stats.is_rx_tooshort++;
+ goto out;
+ }
+#ifdef IEEE80211_DEBUG
+ if ((ieee80211_msg_debug(vap) &&
+ (vap->iv_ic->ic_flags & IEEE80211_F_SCAN)) ||
+ ieee80211_msg_dumppkts(vap)) {
+ if_printf(ifp, "received %s from %s rssi %d\n",
+ ieee80211_mgt_subtype_name[subtype >>
+ IEEE80211_FC0_SUBTYPE_SHIFT],
+ ether_sprintf(wh->i_addr2), rssi);
+ }
+#endif
+ if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, NULL, "%s", "WEP set but not permitted");
+ vap->iv_stats.is_rx_mgtdiscard++; /* XXX */
+ goto out;
+ }
+ vap->iv_recv_mgmt(ni, m, subtype, rssi, nf);
+ goto out;
+ case IEEE80211_FC0_TYPE_CTL:
+ vap->iv_stats.is_rx_ctl++;
+ IEEE80211_NODE_STAT(ni, rx_ctrl);
+ goto out;
+ default:
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
+ wh, "bad", "frame type 0x%x", type);
+ /* should not come here */
+ break;
+ }
+err:
+ ifp->if_ierrors++;
+out:
+ if (m != NULL) {
+ if (need_tap && ieee80211_radiotap_active_vap(vap))
+ ieee80211_radiotap_rx(vap, m);
+ m_freem(m);
+ }
+ return type;
+}
+
+static void
+mesh_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, int subtype,
+ int rssi, int nf)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211_mesh_state *ms = vap->iv_mesh;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211_frame *wh;
+ uint8_t *frm, *efrm;
+
+ wh = mtod(m0, struct ieee80211_frame *);
+ frm = (uint8_t *)&wh[1];
+ efrm = mtod(m0, uint8_t *) + m0->m_len;
+ switch (subtype) {
+ case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
+ case IEEE80211_FC0_SUBTYPE_BEACON:
+ {
+ struct ieee80211_scanparams scan;
+ /*
+ * We process beacon/probe response
+ * frames to discover neighbors.
+ */
+ if (ieee80211_parse_beacon(ni, m0, &scan) != 0)
+ return;
+ /*
+ * Count frame now that we know it's to be processed.
+ */
+ if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) {
+ vap->iv_stats.is_rx_beacon++; /* XXX remove */
+ IEEE80211_NODE_STAT(ni, rx_beacons);
+ } else
+ IEEE80211_NODE_STAT(ni, rx_proberesp);
+ /*
+ * If scanning, just pass information to the scan module.
+ */
+ if (ic->ic_flags & IEEE80211_F_SCAN) {
+ if (ic->ic_flags_ext & IEEE80211_FEXT_PROBECHAN) {
+ /*
+ * Actively scanning a channel marked passive;
+ * send a probe request now that we know there
+ * is 802.11 traffic present.
+ *
+ * XXX check if the beacon we recv'd gives
+ * us what we need and suppress the probe req
+ */
+ ieee80211_probe_curchan(vap, 1);
+ ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN;
+ }
+ ieee80211_add_scan(vap, &scan, wh,
+ subtype, rssi, nf);
+ return;
+ }
+
+ /* The rest of this code assumes we are running */
+ if (vap->iv_state != IEEE80211_S_RUN)
+ return;
+ /*
+ * Ignore non-mesh STAs.
+ */
+ if ((scan.capinfo &
+ (IEEE80211_CAPINFO_ESS|IEEE80211_CAPINFO_IBSS)) ||
+ scan.meshid == NULL || scan.meshconf == NULL) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, "beacon", "%s", "not a mesh sta");
+ vap->iv_stats.is_mesh_wrongmesh++;
+ return;
+ }
+ /*
+ * Ignore STAs for other mesh networks.
+ */
+ if (memcmp(scan.meshid+2, ms->ms_id, ms->ms_idlen) != 0 ||
+ mesh_verify_meshconf(vap, scan.meshconf)) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, "beacon", "%s", "not for our mesh");
+ vap->iv_stats.is_mesh_wrongmesh++;
+ return;
+ }
+ /*
+ * Peer only based on the current ACL policy.
+ */
+ if (vap->iv_acl != NULL &&
+ !vap->iv_acl->iac_check(vap, wh->i_addr2)) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ACL,
+ wh, NULL, "%s", "disallowed by ACL");
+ vap->iv_stats.is_rx_acl++;
+ return;
+ }
+ /*
+ * Do neighbor discovery.
+ */
+ if (!IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) {
+ /*
+ * Create a new entry in the neighbor table.
+ */
+ ni = ieee80211_add_neighbor(vap, wh, &scan);
+ }
+ /*
+ * Automatically peer with discovered nodes if possible.
+ * XXX backoff on repeated failure
+ */
+ if (ni != vap->iv_bss &&
+ (ms->ms_flags & IEEE80211_MESHFLAGS_AP) &&
+ ni->ni_mlstate == IEEE80211_NODE_MESH_IDLE) {
+ uint16_t args[1];
+
+ ni->ni_mlpid = mesh_generateid(vap);
+ if (ni->ni_mlpid == 0)
+ return;
+ mesh_linkchange(ni, IEEE80211_NODE_MESH_OPENSNT);
+ args[0] = ni->ni_mlpid;
+ ieee80211_send_action(ni,
+ IEEE80211_ACTION_CAT_MESHPEERING,
+ IEEE80211_ACTION_MESHPEERING_OPEN, args);
+ ni->ni_mlrcnt = 0;
+ mesh_peer_timeout_setup(ni);
+ }
+ break;
+ }
+ case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
+ {
+ uint8_t *ssid, *meshid, *rates, *xrates;
+ uint8_t *sfrm;
+
+ if (vap->iv_state != IEEE80211_S_RUN) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, NULL, "wrong state %s",
+ ieee80211_state_name[vap->iv_state]);
+ vap->iv_stats.is_rx_mgtdiscard++;
+ return;
+ }
+ if (IEEE80211_IS_MULTICAST(wh->i_addr2)) {
+ /* frame must be directed */
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, NULL, "%s", "not unicast");
+ vap->iv_stats.is_rx_mgtdiscard++; /* XXX stat */
+ return;
+ }
+ /*
+ * prreq frame format
+ * [tlv] ssid
+ * [tlv] supported rates
+ * [tlv] extended supported rates
+ * [tlv] mesh id
+ */
+ ssid = meshid = rates = xrates = NULL;
+ sfrm = frm;
+ while (efrm - frm > 1) {
+ IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return);
+ switch (*frm) {
+ case IEEE80211_ELEMID_SSID:
+ ssid = frm;
+ break;
+ case IEEE80211_ELEMID_RATES:
+ rates = frm;
+ break;
+ case IEEE80211_ELEMID_XRATES:
+ xrates = frm;
+ break;
+ case IEEE80211_ELEMID_MESHID:
+ meshid = frm;
+ break;
+ }
+ frm += frm[2] + 2;
+ }
+ IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN, return);
+ IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE, return);
+ if (xrates != NULL)
+ IEEE80211_VERIFY_ELEMENT(xrates,
+ IEEE80211_RATE_MAXSIZE - rates[1], return);
+ if (meshid != NULL)
+ IEEE80211_VERIFY_ELEMENT(meshid,
+ IEEE80211_MESHID_LEN, return);
+ /* NB: meshid, not ssid */
+ IEEE80211_VERIFY_SSID(vap->iv_bss, meshid, return);
+
+ /* XXX find a better class or define it's own */
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_INPUT, wh->i_addr2,
+ "%s", "recv probe req");
+ /*
+ * Some legacy 11b clients cannot hack a complete
+ * probe response frame. When the request includes
+ * only a bare-bones rate set, communicate this to
+ * the transmit side.
+ */
+ ieee80211_send_proberesp(vap, wh->i_addr2, 0);
+ break;
+ }
+ case IEEE80211_FC0_SUBTYPE_ACTION:
+ if (vap->iv_state != IEEE80211_S_RUN) {
+ vap->iv_stats.is_rx_mgtdiscard++;
+ break;
+ }
+ /*
+ * We received an action for an unknown neighbor.
+ * XXX: wait for it to beacon or create ieee80211_node?
+ */
+ if (ni == vap->iv_bss) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_MESH,
+ wh, NULL, "%s", "unknown node");
+ vap->iv_stats.is_rx_mgtdiscard++;
+ break;
+ }
+ /*
+ * Discard if not for us.
+ */
+ if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, wh->i_addr1) &&
+ !IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_MESH,
+ wh, NULL, "%s", "not for me");
+ vap->iv_stats.is_rx_mgtdiscard++;
+ break;
+ }
+ /* XXX parse_action is a bit useless now */
+ if (ieee80211_parse_action(ni, m0) == 0)
+ ic->ic_recv_action(ni, wh, frm, efrm);
+ break;
+ case IEEE80211_FC0_SUBTYPE_AUTH:
+ case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
+ case IEEE80211_FC0_SUBTYPE_REASSOC_REQ:
+ case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
+ case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
+ case IEEE80211_FC0_SUBTYPE_DEAUTH:
+ case IEEE80211_FC0_SUBTYPE_DISASSOC:
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, NULL, "%s", "not handled");
+ vap->iv_stats.is_rx_mgtdiscard++;
+ return;
+ default:
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
+ wh, "mgt", "subtype 0x%x not handled", subtype);
+ vap->iv_stats.is_rx_badsubtype++;
+ break;
+ }
+}
+
+/*
+ * Parse meshpeering action ie's for open+confirm frames; the
+ * important bits are returned in the supplied structure.
+ */
+static const struct ieee80211_meshpeer_ie *
+mesh_parse_meshpeering_action(struct ieee80211_node *ni,
+ const struct ieee80211_frame *wh, /* XXX for VERIFY_LENGTH */
+ const uint8_t *frm, const uint8_t *efrm,
+ struct ieee80211_meshpeer_ie *mp)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ const struct ieee80211_meshpeer_ie *mpie;
+ const uint8_t *meshid, *meshconf, *meshpeerver, *meshpeer;
+
+ meshid = meshconf = meshpeerver = meshpeer = NULL;
+ while (efrm - frm > 1) {
+ IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return NULL);
+ switch (*frm) {
+ case IEEE80211_ELEMID_MESHPEERVER:
+ meshpeerver = frm;
+ break;
+ case IEEE80211_ELEMID_MESHID:
+ meshid = frm;
+ break;
+ case IEEE80211_ELEMID_MESHCONF:
+ meshconf = frm;
+ break;
+ case IEEE80211_ELEMID_MESHPEER:
+ meshpeer = frm;
+ mpie = (const struct ieee80211_meshpeer_ie *) frm;
+ memset(mp, 0, sizeof(*mp));
+ mp->peer_subtype = mpie->peer_subtype;
+ mp->peer_llinkid = LE_READ_2(&mpie->peer_llinkid);
+ /* NB: peer link ID is optional on these frames */
+ if (mpie->peer_subtype ==
+ IEEE80211_MESH_PEER_LINK_CLOSE &&
+ mpie->peer_len == 5) {
+ mp->peer_linkid = 0;
+ mp->peer_rcode = LE_READ_2(&mpie->peer_linkid);
+ } else {
+ mp->peer_linkid = LE_READ_2(&mpie->peer_linkid);
+ mp->peer_rcode = LE_READ_2(&mpie->peer_rcode);
+ }
+ break;
+ }
+ frm += frm[1] + 2;
+ }
+
+ /*
+ * Verify the contents of the frame. Action frames with
+ * close subtype don't have a Mesh Configuration IE.
+ * If if fails validation, close the peer link.
+ */
+ KASSERT(meshpeer != NULL && mp->peer_subtype !=
+ IEEE80211_ACTION_MESHPEERING_CLOSE, ("parsing close action"));
+
+ if (mesh_verify_meshpeerver(vap, meshpeerver) ||
+ mesh_verify_meshid(vap, meshid) ||
+ mesh_verify_meshpeer(vap, meshpeer) ||
+ mesh_verify_meshconf(vap, meshconf)) {
+ uint16_t args[3];
+
+ IEEE80211_DISCARD(vap,
+ IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH,
+ wh, NULL, "%s", "not for our mesh");
+ vap->iv_stats.is_rx_mgtdiscard++;
+ switch (ni->ni_mlstate) {
+ case IEEE80211_NODE_MESH_IDLE:
+ case IEEE80211_NODE_MESH_ESTABLISHED:
+ case IEEE80211_NODE_MESH_HOLDING:
+ /* ignore */
+ break;
+ case IEEE80211_NODE_MESH_OPENSNT:
+ case IEEE80211_NODE_MESH_OPENRCV:
+ case IEEE80211_NODE_MESH_CONFIRMRCV:
+ args[0] = ni->ni_mlpid;
+ args[1] = ni->ni_mllid;
+ args[2] = IEEE80211_REASON_PEER_LINK_CANCELED;
+ ieee80211_send_action(ni,
+ IEEE80211_ACTION_CAT_MESHPEERING,
+ IEEE80211_ACTION_MESHPEERING_CLOSE,
+ args);
+ mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING);
+ mesh_peer_timeout_setup(ni);
+ break;
+ }
+ return NULL;
+ }
+ return (const struct ieee80211_meshpeer_ie *) mp;
+}
+
+static int
+mesh_recv_action_meshpeering_open(struct ieee80211_node *ni,
+ const struct ieee80211_frame *wh,
+ const uint8_t *frm, const uint8_t *efrm)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211_meshpeer_ie ie;
+ const struct ieee80211_meshpeer_ie *meshpeer;
+ uint16_t args[3];
+
+ /* +2+2 for action + code + capabilites */
+ meshpeer = mesh_parse_meshpeering_action(ni, wh, frm+2+2, efrm, &ie);
+ if (meshpeer == NULL) {
+ return 0;
+ }
+
+ /* XXX move up */
+ IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, ni,
+ "recv PEER OPEN, lid 0x%x", meshpeer->peer_llinkid);
+
+ switch (ni->ni_mlstate) {
+ case IEEE80211_NODE_MESH_IDLE:
+ mesh_linkchange(ni, IEEE80211_NODE_MESH_OPENRCV);
+ ni->ni_mllid = meshpeer->peer_llinkid;
+ ni->ni_mlpid = mesh_generateid(vap);
+ if (ni->ni_mlpid == 0)
+ return 0; /* XXX */
+ args[0] = ni->ni_mlpid;
+ /* Announce we're open too... */
+ ieee80211_send_action(ni,
+ IEEE80211_ACTION_CAT_MESHPEERING,
+ IEEE80211_ACTION_MESHPEERING_OPEN, args);
+ /* ...and confirm the link. */
+ args[0] = ni->ni_mlpid;
+ args[1] = ni->ni_mllid;
+ ieee80211_send_action(ni,
+ IEEE80211_ACTION_CAT_MESHPEERING,
+ IEEE80211_ACTION_MESHPEERING_CONFIRM,
+ args);
+ mesh_peer_timeout_setup(ni);
+ break;
+ case IEEE80211_NODE_MESH_OPENRCV:
+ /* Wrong Link ID */
+ if (ni->ni_mllid != meshpeer->peer_llinkid) {
+ args[0] = ni->ni_mllid;
+ args[1] = ni->ni_mlpid;
+ args[2] = IEEE80211_REASON_PEER_LINK_CANCELED;
+ ieee80211_send_action(ni,
+ IEEE80211_ACTION_CAT_MESHPEERING,
+ IEEE80211_ACTION_MESHPEERING_CLOSE,
+ args);
+ mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING);
+ mesh_peer_timeout_setup(ni);
+ break;
+ }
+ /* Duplicate open, confirm again. */
+ args[0] = ni->ni_mlpid;
+ args[1] = ni->ni_mllid;
+ ieee80211_send_action(ni,
+ IEEE80211_ACTION_CAT_MESHPEERING,
+ IEEE80211_ACTION_MESHPEERING_CONFIRM,
+ args);
+ break;
+ case IEEE80211_NODE_MESH_OPENSNT:
+ ni->ni_mllid = meshpeer->peer_llinkid;
+ mesh_linkchange(ni, IEEE80211_NODE_MESH_OPENRCV);
+ args[0] = ni->ni_mlpid;
+ args[1] = ni->ni_mllid;
+ ieee80211_send_action(ni,
+ IEEE80211_ACTION_CAT_MESHPEERING,
+ IEEE80211_ACTION_MESHPEERING_CONFIRM,
+ args);
+ /* NB: don't setup/clear any timeout */
+ break;
+ case IEEE80211_NODE_MESH_CONFIRMRCV:
+ if (ni->ni_mlpid != meshpeer->peer_linkid ||
+ ni->ni_mllid != meshpeer->peer_llinkid) {
+ args[0] = ni->ni_mlpid;
+ args[1] = ni->ni_mllid;
+ args[2] = IEEE80211_REASON_PEER_LINK_CANCELED;
+ ieee80211_send_action(ni,
+ IEEE80211_ACTION_CAT_MESHPEERING,
+ IEEE80211_ACTION_MESHPEERING_CLOSE,
+ args);
+ mesh_linkchange(ni,
+ IEEE80211_NODE_MESH_HOLDING);
+ mesh_peer_timeout_setup(ni);
+ break;
+ }
+ mesh_linkchange(ni, IEEE80211_NODE_MESH_ESTABLISHED);
+ ni->ni_mllid = meshpeer->peer_llinkid;
+ args[0] = ni->ni_mlpid;
+ args[1] = ni->ni_mllid;
+ ieee80211_send_action(ni,
+ IEEE80211_ACTION_CAT_MESHPEERING,
+ IEEE80211_ACTION_MESHPEERING_CONFIRM,
+ args);
+ mesh_peer_timeout_stop(ni);
+ break;
+ case IEEE80211_NODE_MESH_ESTABLISHED:
+ if (ni->ni_mllid != meshpeer->peer_llinkid) {
+ args[0] = ni->ni_mllid;
+ args[1] = ni->ni_mlpid;
+ args[2] = IEEE80211_REASON_PEER_LINK_CANCELED;
+ ieee80211_send_action(ni,
+ IEEE80211_ACTION_CAT_MESHPEERING,
+ IEEE80211_ACTION_MESHPEERING_CLOSE,
+ args);
+ mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING);
+ mesh_peer_timeout_setup(ni);
+ break;
+ }
+ args[0] = ni->ni_mlpid;
+ args[1] = ni->ni_mllid;
+ ieee80211_send_action(ni,
+ IEEE80211_ACTION_CAT_MESHPEERING,
+ IEEE80211_ACTION_MESHPEERING_CONFIRM,
+ args);
+ break;
+ case IEEE80211_NODE_MESH_HOLDING:
+ args[0] = ni->ni_mlpid;
+ args[1] = meshpeer->peer_llinkid;
+ args[2] = IEEE80211_REASON_MESH_MAX_RETRIES;
+ ieee80211_send_action(ni,
+ IEEE80211_ACTION_CAT_MESHPEERING,
+ IEEE80211_ACTION_MESHPEERING_CLOSE,
+ args);
+ break;
+ }
+ return 0;
+}
+
+static int
+mesh_recv_action_meshpeering_confirm(struct ieee80211_node *ni,
+ const struct ieee80211_frame *wh,
+ const uint8_t *frm, const uint8_t *efrm)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211_meshpeer_ie ie;
+ const struct ieee80211_meshpeer_ie *meshpeer;
+ uint16_t args[3];
+
+ /* +2+2+2+2 for action + code + capabilites + status code + AID */
+ meshpeer = mesh_parse_meshpeering_action(ni, wh, frm+2+2+2+2, efrm, &ie);
+ if (meshpeer == NULL) {
+ return 0;
+ }
+
+ IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, ni,
+ "recv PEER CONFIRM, local id 0x%x, peer id 0x%x",
+ meshpeer->peer_llinkid, meshpeer->peer_linkid);
+
+ switch (ni->ni_mlstate) {
+ case IEEE80211_NODE_MESH_OPENRCV:
+ mesh_linkchange(ni, IEEE80211_NODE_MESH_ESTABLISHED);
+ mesh_peer_timeout_stop(ni);
+ break;
+ case IEEE80211_NODE_MESH_OPENSNT:
+ mesh_linkchange(ni, IEEE80211_NODE_MESH_CONFIRMRCV);
+ break;
+ case IEEE80211_NODE_MESH_HOLDING:
+ args[0] = ni->ni_mlpid;
+ args[1] = meshpeer->peer_llinkid;
+ args[2] = IEEE80211_REASON_MESH_MAX_RETRIES;
+ ieee80211_send_action(ni,
+ IEEE80211_ACTION_CAT_MESHPEERING,
+ IEEE80211_ACTION_MESHPEERING_CLOSE,
+ args);
+ break;
+ case IEEE80211_NODE_MESH_CONFIRMRCV:
+ if (ni->ni_mllid != meshpeer->peer_llinkid) {
+ args[0] = ni->ni_mlpid;
+ args[1] = ni->ni_mllid;
+ args[2] = IEEE80211_REASON_PEER_LINK_CANCELED;
+ ieee80211_send_action(ni,
+ IEEE80211_ACTION_CAT_MESHPEERING,
+ IEEE80211_ACTION_MESHPEERING_CLOSE,
+ args);
+ mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING);
+ mesh_peer_timeout_setup(ni);
+ }
+ break;
+ default:
+ IEEE80211_DISCARD(vap,
+ IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH,
+ wh, NULL, "received confirm in invalid state %d",
+ ni->ni_mlstate);
+ vap->iv_stats.is_rx_mgtdiscard++;
+ break;
+ }
+ return 0;
+}
+
+static int
+mesh_recv_action_meshpeering_close(struct ieee80211_node *ni,
+ const struct ieee80211_frame *wh,
+ const uint8_t *frm, const uint8_t *efrm)
+{
+ uint16_t args[3];
+
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH,
+ ni, "%s", "recv PEER CLOSE");
+
+ switch (ni->ni_mlstate) {
+ case IEEE80211_NODE_MESH_IDLE:
+ /* ignore */
+ break;
+ case IEEE80211_NODE_MESH_OPENRCV:
+ case IEEE80211_NODE_MESH_OPENSNT:
+ case IEEE80211_NODE_MESH_CONFIRMRCV:
+ case IEEE80211_NODE_MESH_ESTABLISHED:
+ args[0] = ni->ni_mlpid;
+ args[1] = ni->ni_mllid;
+ args[2] = IEEE80211_REASON_MESH_CLOSE_RCVD;
+ ieee80211_send_action(ni,
+ IEEE80211_ACTION_CAT_MESHPEERING,
+ IEEE80211_ACTION_MESHPEERING_CLOSE,
+ args);
+ mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING);
+ mesh_peer_timeout_setup(ni);
+ break;
+ case IEEE80211_NODE_MESH_HOLDING:
+ mesh_linkchange(ni, IEEE80211_NODE_MESH_IDLE);
+ mesh_peer_timeout_setup(ni);
+ break;
+ }
+ return 0;
+}
+
+/*
+ * Link Metric handling.
+ */
+static int
+mesh_recv_action_meshlmetric_req(struct ieee80211_node *ni,
+ const struct ieee80211_frame *wh,
+ const uint8_t *frm, const uint8_t *efrm)
+{
+ uint32_t metric;
+
+ metric = mesh_airtime_calc(ni);
+ ieee80211_send_action(ni,
+ IEEE80211_ACTION_CAT_MESHLMETRIC,
+ IEEE80211_ACTION_MESHLMETRIC_REP,
+ &metric);
+ return 0;
+}
+
+static int
+mesh_recv_action_meshlmetric_rep(struct ieee80211_node *ni,
+ const struct ieee80211_frame *wh,
+ const uint8_t *frm, const uint8_t *efrm)
+{
+ return 0;
+}
+
+static int
+mesh_send_action(struct ieee80211_node *ni, struct mbuf *m)
+{
+ struct ieee80211_bpf_params params;
+
+ memset(&params, 0, sizeof(params));
+ params.ibp_pri = WME_AC_VO;
+ params.ibp_rate0 = ni->ni_txparms->mgmtrate;
+ /* XXX ucast/mcast */
+ params.ibp_try0 = ni->ni_txparms->maxretry;
+ params.ibp_power = ni->ni_txpower;
+ return ieee80211_mgmt_output(ni, m, IEEE80211_FC0_SUBTYPE_ACTION,
+ &params);
+}
+
+#define ADDSHORT(frm, v) do { \
+ frm[0] = (v) & 0xff; \
+ frm[1] = (v) >> 8; \
+ frm += 2; \
+} while (0)
+#define ADDWORD(frm, v) do { \
+ frm[0] = (v) & 0xff; \
+ frm[1] = ((v) >> 8) & 0xff; \
+ frm[2] = ((v) >> 16) & 0xff; \
+ frm[3] = ((v) >> 24) & 0xff; \
+ frm += 4; \
+} while (0)
+
+static int
+mesh_send_action_meshpeering_open(struct ieee80211_node *ni,
+ int category, int action, void *args0)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ uint16_t *args = args0;
+ const struct ieee80211_rateset *rs;
+ struct mbuf *m;
+ uint8_t *frm;
+
+ IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, ni,
+ "send PEER OPEN action: localid 0x%x", args[0]);
+
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
+ "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__,
+ ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1);
+ ieee80211_ref_node(ni);
+
+ m = ieee80211_getmgtframe(&frm,
+ ic->ic_headroom + sizeof(struct ieee80211_frame),
+ sizeof(uint16_t) /* action+category */
+ + sizeof(uint16_t) /* capabilites */
+ + sizeof(struct ieee80211_meshpeerver_ie)
+ + 2 + IEEE80211_RATE_SIZE
+ + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
+ + 2 + IEEE80211_MESHID_LEN
+ + sizeof(struct ieee80211_meshconf_ie)
+ + sizeof(struct ieee80211_meshpeer_ie)
+ );
+ if (m != NULL) {
+ /*
+ * mesh peer open action frame format:
+ * [1] category
+ * [1] action
+ * [2] capabilities
+ * [tlv] mesh peer protocol version
+ * [tlv] rates
+ * [tlv] xrates
+ * [tlv] mesh id
+ * [tlv] mesh conf
+ * [tlv] mesh peer link mgmt
+ */
+ *frm++ = category;
+ *frm++ = action;
+ ADDSHORT(frm, ieee80211_getcapinfo(vap, ni->ni_chan));
+ frm = ieee80211_add_meshpeerver(frm, vap);
+ rs = ieee80211_get_suprates(ic, ic->ic_curchan);
+ frm = ieee80211_add_rates(frm, rs);
+ frm = ieee80211_add_xrates(frm, rs);
+ frm = ieee80211_add_meshid(frm, vap);
+ frm = ieee80211_add_meshconf(frm, vap);
+ frm = ieee80211_add_meshpeer(frm, IEEE80211_MESH_PEER_LINK_OPEN,
+ args[0], 0, 0);
+ m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
+ return mesh_send_action(ni, m);
+ } else {
+ vap->iv_stats.is_tx_nobuf++;
+ ieee80211_free_node(ni);
+ return ENOMEM;
+ }
+}
+
+static int
+mesh_send_action_meshpeering_confirm(struct ieee80211_node *ni,
+ int category, int action, void *args0)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ uint16_t *args = args0;
+ const struct ieee80211_rateset *rs;
+ struct mbuf *m;
+ uint8_t *frm;
+
+ IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, ni,
+ "send PEER CONFIRM action: localid 0x%x, peerid 0x%x",
+ args[0], args[1]);
+
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
+ "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__,
+ ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1);
+ ieee80211_ref_node(ni);
+
+ m = ieee80211_getmgtframe(&frm,
+ ic->ic_headroom + sizeof(struct ieee80211_frame),
+ sizeof(uint16_t) /* action+category */
+ + sizeof(uint16_t) /* capabilites */
+ + sizeof(uint16_t) /* status code */
+ + sizeof(uint16_t) /* AID */
+ + sizeof(struct ieee80211_meshpeerver_ie)
+ + 2 + IEEE80211_RATE_SIZE
+ + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
+ + 2 + IEEE80211_MESHID_LEN
+ + sizeof(struct ieee80211_meshconf_ie)
+ + sizeof(struct ieee80211_meshpeer_ie)
+ );
+ if (m != NULL) {
+ /*
+ * mesh peer confirm action frame format:
+ * [1] category
+ * [1] action
+ * [2] capabilities
+ * [2] status code
+ * [2] association id (peer ID)
+ * [tlv] mesh peer protocol version
+ * [tlv] rates
+ * [tlv] xrates
+ * [tlv] mesh id
+ * [tlv] mesh conf
+ * [tlv] mesh peer link mgmt
+ */
+ *frm++ = category;
+ *frm++ = action;
+ ADDSHORT(frm, ieee80211_getcapinfo(vap, ni->ni_chan));
+ ADDSHORT(frm, 0); /* status code */
+ ADDSHORT(frm, args[1]); /* AID */
+ frm = ieee80211_add_meshpeerver(frm, vap);
+ rs = ieee80211_get_suprates(ic, ic->ic_curchan);
+ frm = ieee80211_add_rates(frm, rs);
+ frm = ieee80211_add_xrates(frm, rs);
+ frm = ieee80211_add_meshid(frm, vap);
+ frm = ieee80211_add_meshconf(frm, vap);
+ frm = ieee80211_add_meshpeer(frm,
+ IEEE80211_MESH_PEER_LINK_CONFIRM,
+ args[0], args[1], 0);
+ m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
+ return mesh_send_action(ni, m);
+ } else {
+ vap->iv_stats.is_tx_nobuf++;
+ ieee80211_free_node(ni);
+ return ENOMEM;
+ }
+}
+
+static int
+mesh_send_action_meshpeering_close(struct ieee80211_node *ni,
+ int category, int action, void *args0)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ uint16_t *args = args0;
+ struct mbuf *m;
+ uint8_t *frm;
+
+ IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, ni,
+ "send PEER CLOSE action: localid 0x%x, peerid 0x%x reason %d",
+ args[0], args[1], args[2]);
+
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
+ "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__,
+ ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1);
+ ieee80211_ref_node(ni);
+
+ m = ieee80211_getmgtframe(&frm,
+ ic->ic_headroom + sizeof(struct ieee80211_frame),
+ sizeof(uint16_t) /* action+category */
+ + sizeof(uint16_t) /* reason code */
+ + sizeof(struct ieee80211_meshpeerver_ie)
+ + 2 + IEEE80211_MESHID_LEN
+ + sizeof(struct ieee80211_meshpeer_ie)
+ );
+ if (m != NULL) {
+ /*
+ * mesh peer close action frame format:
+ * [1] category
+ * [1] action
+ * [2] reason code
+ * [tlv] mesh peer protocol version
+ * [tlv] mesh id
+ * [tlv] mesh peer link mgmt
+ */
+ *frm++ = category;
+ *frm++ = action;
+ ADDSHORT(frm, args[2]); /* reason code */
+ frm = ieee80211_add_meshpeerver(frm, vap);
+ frm = ieee80211_add_meshid(frm, vap);
+ frm = ieee80211_add_meshpeer(frm,
+ IEEE80211_MESH_PEER_LINK_CLOSE,
+ args[0], args[1], args[2]);
+ m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
+ return mesh_send_action(ni, m);
+ } else {
+ vap->iv_stats.is_tx_nobuf++;
+ ieee80211_free_node(ni);
+ return ENOMEM;
+ }
+}
+
+static int
+mesh_send_action_meshlink_request(struct ieee80211_node *ni,
+ int category, int action, void *arg0)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct mbuf *m;
+ uint8_t *frm;
+
+ IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, ni,
+ "%s", "send LINK METRIC REQUEST action");
+
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
+ "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__,
+ ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1);
+ ieee80211_ref_node(ni);
+
+ m = ieee80211_getmgtframe(&frm,
+ ic->ic_headroom + sizeof(struct ieee80211_frame),
+ sizeof(uint16_t) /* action+category */
+ );
+ if (m != NULL) {
+ /*
+ * mesh link metric request
+ * [1] category
+ * [1] action
+ */
+ *frm++ = category;
+ *frm++ = action;
+ m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
+ return mesh_send_action(ni, m);
+ } else {
+ vap->iv_stats.is_tx_nobuf++;
+ ieee80211_free_node(ni);
+ return ENOMEM;
+ }
+}
+
+static int
+mesh_send_action_meshlink_reply(struct ieee80211_node *ni,
+ int category, int action, void *args0)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ uint32_t *metric = args0;
+ struct mbuf *m;
+ uint8_t *frm;
+
+ IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, ni,
+ "send LINK METRIC REPLY action: metric 0x%x", *metric);
+
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
+ "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__,
+ ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1);
+ ieee80211_ref_node(ni);
+
+ m = ieee80211_getmgtframe(&frm,
+ ic->ic_headroom + sizeof(struct ieee80211_frame),
+ sizeof(uint16_t) /* action+category */
+ + sizeof(struct ieee80211_meshlmetric_ie)
+ );
+ if (m != NULL) {
+ /*
+ * mesh link metric reply
+ * [1] category
+ * [1] action
+ * [tlv] mesh link metric
+ */
+ *frm++ = category;
+ *frm++ = action;
+ frm = ieee80211_add_meshlmetric(frm, *metric);
+ m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
+ return mesh_send_action(ni, m);
+ } else {
+ vap->iv_stats.is_tx_nobuf++;
+ ieee80211_free_node(ni);
+ return ENOMEM;
+ }
+}
+
+static void
+mesh_peer_timeout_setup(struct ieee80211_node *ni)
+{
+ switch (ni->ni_mlstate) {
+ case IEEE80211_NODE_MESH_HOLDING:
+ ni->ni_mltval = ieee80211_mesh_holdingtimeout;
+ break;
+ case IEEE80211_NODE_MESH_CONFIRMRCV:
+ ni->ni_mltval = ieee80211_mesh_confirmtimeout;
+ break;
+ case IEEE80211_NODE_MESH_IDLE:
+ ni->ni_mltval = 0;
+ break;
+ default:
+ ni->ni_mltval = ieee80211_mesh_retrytimeout;
+ break;
+ }
+ if (ni->ni_mltval)
+ callout_reset(&ni->ni_mltimer, ni->ni_mltval,
+ mesh_peer_timeout_cb, ni);
+}
+
+/*
+ * Same as above but backoffs timer statisically 50%.
+ */
+static void
+mesh_peer_timeout_backoff(struct ieee80211_node *ni)
+{
+ uint32_t r;
+
+ r = arc4random();
+ ni->ni_mltval += r % ni->ni_mltval;
+ callout_reset(&ni->ni_mltimer, ni->ni_mltval, mesh_peer_timeout_cb,
+ ni);
+}
+
+static __inline void
+mesh_peer_timeout_stop(struct ieee80211_node *ni)
+{
+ callout_stop(&ni->ni_mltimer);
+}
+
+/*
+ * Mesh Peer Link Management FSM timeout handling.
+ */
+static void
+mesh_peer_timeout_cb(void *arg)
+{
+ struct ieee80211_node *ni = (struct ieee80211_node *)arg;
+ uint16_t args[3];
+
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_MESH,
+ ni, "mesh link timeout, state %d, retry counter %d",
+ ni->ni_mlstate, ni->ni_mlrcnt);
+
+ switch (ni->ni_mlstate) {
+ case IEEE80211_NODE_MESH_IDLE:
+ case IEEE80211_NODE_MESH_ESTABLISHED:
+ break;
+ case IEEE80211_NODE_MESH_OPENSNT:
+ case IEEE80211_NODE_MESH_OPENRCV:
+ if (ni->ni_mlrcnt == ieee80211_mesh_maxretries) {
+ args[0] = ni->ni_mlpid;
+ args[2] = IEEE80211_REASON_MESH_MAX_RETRIES;
+ ieee80211_send_action(ni,
+ IEEE80211_ACTION_CAT_MESHPEERING,
+ IEEE80211_ACTION_MESHPEERING_CLOSE, args);
+ ni->ni_mlrcnt = 0;
+ mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING);
+ mesh_peer_timeout_setup(ni);
+ } else {
+ args[0] = ni->ni_mlpid;
+ ieee80211_send_action(ni,
+ IEEE80211_ACTION_CAT_MESHPEERING,
+ IEEE80211_ACTION_MESHPEERING_OPEN, args);
+ ni->ni_mlrcnt++;
+ mesh_peer_timeout_backoff(ni);
+ }
+ break;
+ case IEEE80211_NODE_MESH_CONFIRMRCV:
+ if (ni->ni_mlrcnt == ieee80211_mesh_maxretries) {
+ args[0] = ni->ni_mlpid;
+ args[2] = IEEE80211_REASON_MESH_CONFIRM_TIMEOUT;
+ ieee80211_send_action(ni,
+ IEEE80211_ACTION_CAT_MESHPEERING,
+ IEEE80211_ACTION_MESHPEERING_CLOSE, args);
+ ni->ni_mlrcnt = 0;
+ mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING);
+ mesh_peer_timeout_setup(ni);
+ } else {
+ ni->ni_mlrcnt++;
+ mesh_peer_timeout_setup(ni);
+ }
+ break;
+ case IEEE80211_NODE_MESH_HOLDING:
+ mesh_linkchange(ni, IEEE80211_NODE_MESH_IDLE);
+ break;
+ }
+}
+
+static __inline int
+mesh_verify_meshpeerver(struct ieee80211vap *vap, const uint8_t *ie)
+{
+ static const uint8_t peer[4] = IEEE80211_MESHPEERVER_PEER;
+ const struct ieee80211_meshpeerver_ie *meshpeerver =
+ (const struct ieee80211_meshpeerver_ie *) ie;
+
+ if (meshpeerver->peerver_len !=
+ sizeof(struct ieee80211_meshpeerver_ie) - 2)
+ return 1;
+ return memcmp(meshpeerver->peerver_proto, peer, 4);
+}
+
+static __inline int
+mesh_verify_meshid(struct ieee80211vap *vap, const uint8_t *ie)
+{
+ struct ieee80211_mesh_state *ms = vap->iv_mesh;
+
+ if (ie == NULL || ie[1] != ms->ms_idlen)
+ return 1;
+ return memcmp(ms->ms_id, ie + 2, ms->ms_idlen);
+}
+
+/*
+ * Check if we are using the same algorithms for this mesh.
+ */
+static int
+mesh_verify_meshconf(struct ieee80211vap *vap, const uint8_t *ie)
+{
+ static const uint8_t null[4] = IEEE80211_MESHCONF_NULL;
+ const struct ieee80211_meshconf_ie *meshconf =
+ (const struct ieee80211_meshconf_ie *) ie;
+ const struct ieee80211_mesh_state *ms = vap->iv_mesh;
+
+ if (meshconf == NULL)
+ return 1;
+ if (meshconf->conf_ver != IEEE80211_MESHCONF_VERSION) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_MESH,
+ "wrong mesh conf version: %d\n", meshconf->conf_ver);
+ return 1;
+ }
+ if (memcmp(meshconf->conf_pselid, ms->ms_ppath->mpp_ie, 4) != 0) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_MESH,
+ "unknown path selection algorithm: 0x%x%x%x%x\n",
+ meshconf->conf_pselid[0], meshconf->conf_pselid[1],
+ meshconf->conf_pselid[2], meshconf->conf_pselid[3]);
+ return 1;
+ }
+ if (memcmp(meshconf->conf_pmetid, ms->ms_pmetric->mpm_ie, 4) != 0) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_MESH,
+ "unknown path metric algorithm: 0x%x%x%x%x\n",
+ meshconf->conf_pmetid[0], meshconf->conf_pmetid[1],
+ meshconf->conf_pmetid[2], meshconf->conf_pmetid[3]);
+ return 1;
+ }
+ if (memcmp(meshconf->conf_ccid, null, 4) != 0) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_MESH,
+ "unknown congestion sig algorithm: 0x%x%x%x%x\n",
+ meshconf->conf_ccid[0], meshconf->conf_ccid[1],
+ meshconf->conf_ccid[2], meshconf->conf_ccid[3]);
+ return 1;
+ }
+ if (memcmp(meshconf->conf_syncid, null, 4) != 0) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_MESH,
+ "unknown sync algorithm: 0x%x%x%x%x\n",
+ meshconf->conf_syncid[0], meshconf->conf_syncid[1],
+ meshconf->conf_syncid[2], meshconf->conf_syncid[3]);
+ return 1;
+ }
+ if (memcmp(meshconf->conf_authid, null, 4) != 0) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_MESH,
+ "unknown auth auth algorithm: 0x%x%x%x%x\n",
+ meshconf->conf_pselid[0], meshconf->conf_pselid[1],
+ meshconf->conf_pselid[2], meshconf->conf_pselid[3]);
+ return 1;
+ }
+ /* Not accepting peers */
+ if (!(meshconf->conf_cap & IEEE80211_MESHCONF_CAP_AP)) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_MESH,
+ "not accepting peers: 0x%x\n", meshconf->conf_cap);
+ return 1;
+ }
+ return 0;
+}
+
+static int
+mesh_verify_meshpeer(struct ieee80211vap *vap, const uint8_t *ie)
+{
+ const struct ieee80211_meshpeer_ie *meshpeer =
+ (const struct ieee80211_meshpeer_ie *) ie;
+
+ if (meshpeer == NULL)
+ return 1;
+ switch (meshpeer->peer_subtype) {
+ case IEEE80211_MESH_PEER_LINK_OPEN:
+ if (meshpeer->peer_len != 3)
+ return 1;
+ break;
+ case IEEE80211_MESH_PEER_LINK_CONFIRM:
+ if (meshpeer->peer_len != 5)
+ return 1;
+ break;
+ case IEEE80211_MESH_PEER_LINK_CLOSE:
+ if (meshpeer->peer_len < 5)
+ return 1;
+ if (meshpeer->peer_len == 5 && meshpeer->peer_linkid != 0)
+ return 1;
+ if (meshpeer->peer_rcode == 0)
+ return 1;
+ break;
+ }
+ return 0;
+}
+
+/*
+ * Add a Mesh ID IE to a frame.
+ */
+uint8_t *
+ieee80211_add_meshid(uint8_t *frm, struct ieee80211vap *vap)
+{
+ struct ieee80211_mesh_state *ms = vap->iv_mesh;
+
+ KASSERT(vap->iv_opmode == IEEE80211_M_MBSS, ("not a mbss vap"));
+
+ *frm++ = IEEE80211_ELEMID_MESHID;
+ *frm++ = ms->ms_idlen;
+ memcpy(frm, ms->ms_id, ms->ms_idlen);
+ return frm + ms->ms_idlen;
+}
+
+/*
+ * Add a Mesh Configuration IE to a frame.
+ * For now just use HWMP routing, Airtime link metric, Null Congestion
+ * Signaling, Null Sync Protocol and Null Authentication.
+ */
+uint8_t *
+ieee80211_add_meshconf(uint8_t *frm, struct ieee80211vap *vap)
+{
+ const struct ieee80211_mesh_state *ms = vap->iv_mesh;
+ static const uint8_t null[4] = IEEE80211_MESHCONF_NULL;
+
+ KASSERT(vap->iv_opmode == IEEE80211_M_MBSS, ("not a MBSS vap"));
+
+ *frm++ = IEEE80211_ELEMID_MESHCONF;
+ *frm++ = sizeof(struct ieee80211_meshconf_ie) - 2;
+ *frm++ = IEEE80211_MESHCONF_VERSION;
+ memcpy(frm, ms->ms_ppath->mpp_ie, 4); /* path selection */
+ frm += 4;
+ memcpy(frm, ms->ms_pmetric->mpm_ie, 4); /* link metric */
+ frm += 4;
+ /* XXX null for now */
+ memcpy(frm, null, 4); /* congestion control */
+ frm += 4;
+ memcpy(frm, null, 4); /* sync */
+ frm += 4;
+ memcpy(frm, null, 4); /* auth */
+ frm += 4;
+ /* NB: set the number of neighbors before the rest */
+ *frm = (ms->ms_neighbors > 15 ? 15 : ms->ms_neighbors) << 1;
+ if (ms->ms_flags & IEEE80211_MESHFLAGS_PORTAL)
+ *frm |= IEEE80211_MESHCONF_FORM_MP;
+ frm += 1;
+ if (ms->ms_flags & IEEE80211_MESHFLAGS_AP)
+ *frm |= IEEE80211_MESHCONF_CAP_AP;
+ if (ms->ms_flags & IEEE80211_MESHFLAGS_FWD)
+ *frm |= IEEE80211_MESHCONF_CAP_FWRD;
+ frm += 1;
+ return frm;
+}
+
+/*
+ * Add a Mesh Peer Protocol IE to a frame.
+ * XXX: needs to grow support for Abbreviated Handshake
+ */
+uint8_t *
+ieee80211_add_meshpeerver(uint8_t *frm, struct ieee80211vap *vap)
+{
+ static struct ieee80211_meshpeerver_ie ie = {
+ .peerver_ie = IEEE80211_ELEMID_MESHPEERVER,
+ .peerver_len = 4,
+ .peerver_proto = IEEE80211_MESHPEERVER_PEER,
+ };
+
+ KASSERT(vap->iv_opmode == IEEE80211_M_MBSS, ("not a MBSS vap"));
+
+ memcpy(frm, &ie, sizeof(ie));
+ return frm + sizeof(ie);
+}
+
+/*
+ * Add a Mesh Peer Management IE to a frame.
+ */
+uint8_t *
+ieee80211_add_meshpeer(uint8_t *frm, uint8_t subtype, uint16_t localid,
+ uint16_t peerid, uint16_t reason)
+{
+ KASSERT(localid != 0, ("localid == 0"));
+
+ *frm++ = IEEE80211_ELEMID_MESHPEER;
+ switch (subtype) {
+ case IEEE80211_MESH_PEER_LINK_OPEN:
+ *frm++ = 3; /* length */
+ *frm++ = subtype;
+ ADDSHORT(frm, localid); /* local ID */
+ break;
+ case IEEE80211_MESH_PEER_LINK_CONFIRM:
+ KASSERT(peerid != 0, ("sending peer confirm without peer id"));
+ *frm++ = 5; /* length */
+ *frm++ = subtype;
+ ADDSHORT(frm, localid); /* local ID */
+ ADDSHORT(frm, peerid); /* peer ID */
+ break;
+ case IEEE80211_MESH_PEER_LINK_CLOSE:
+ if (peerid)
+ *frm++ = 7; /* length */
+ else
+ *frm++ = 5; /* length */
+ *frm++ = subtype;
+ ADDSHORT(frm, localid); /* local ID */
+ if (peerid)
+ ADDSHORT(frm, peerid); /* peer ID */
+ ADDSHORT(frm, reason);
+ break;
+ }
+ return frm;
+}
+
+/*
+ * Compute an Airtime Link Metric for the link with this node.
+ *
+ * Based on Draft 3.0 spec (11B.10, p.149).
+ */
+/*
+ * Max 802.11s overhead.
+ */
+#define IEEE80211_MESH_MAXOVERHEAD \
+ (sizeof(struct ieee80211_qosframe_addr4) \
+ + sizeof(struct ieee80211_meshcntl_ae11) \
+ + sizeof(struct llc) \
+ + IEEE80211_ADDR_LEN \
+ + IEEE80211_WEP_IVLEN \
+ + IEEE80211_WEP_KIDLEN \
+ + IEEE80211_WEP_CRCLEN \
+ + IEEE80211_WEP_MICLEN \
+ + IEEE80211_CRC_LEN)
+uint32_t
+mesh_airtime_calc(struct ieee80211_node *ni)
+{
+#define M_BITS 8
+#define S_FACTOR (2 * M_BITS)
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ifnet *ifp = ni->ni_vap->iv_ifp;
+ const static int nbits = 8192 << M_BITS;
+ uint32_t overhead, rate, errrate;
+ uint64_t res;
+
+ /* Time to transmit a frame */
+ rate = ni->ni_txrate;
+ overhead = ieee80211_compute_duration(ic->ic_rt,
+ ifp->if_mtu + IEEE80211_MESH_MAXOVERHEAD, rate, 0) << M_BITS;
+ /* Error rate in percentage */
+ /* XXX assuming small failures are ok */
+ errrate = (((ifp->if_oerrors +
+ ifp->if_ierrors) / 100) << M_BITS) / 100;
+ res = (overhead + (nbits / rate)) *
+ ((1 << S_FACTOR) / ((1 << M_BITS) - errrate));
+
+ return (uint32_t)(res >> S_FACTOR);
+#undef M_BITS
+#undef S_FACTOR
+}
+
+/*
+ * Add a Mesh Link Metric report IE to a frame.
+ */
+uint8_t *
+ieee80211_add_meshlmetric(uint8_t *frm, uint32_t metric)
+{
+ *frm++ = IEEE80211_ELEMID_MESHLINK;
+ *frm++ = 4;
+ ADDWORD(frm, metric);
+ return frm;
+}
+#undef ADDSHORT
+#undef ADDWORD
+
+/*
+ * Initialize any mesh-specific node state.
+ */
+void
+ieee80211_mesh_node_init(struct ieee80211vap *vap, struct ieee80211_node *ni)
+{
+ ni->ni_flags |= IEEE80211_NODE_QOS;
+ callout_init(&ni->ni_mltimer, CALLOUT_MPSAFE);
+}
+
+/*
+ * Cleanup any mesh-specific node state.
+ */
+void
+ieee80211_mesh_node_cleanup(struct ieee80211_node *ni)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211_mesh_state *ms = vap->iv_mesh;
+
+ callout_drain(&ni->ni_mltimer);
+ /* NB: short-circuit callbacks after mesh_vdetach */
+ if (vap->iv_mesh != NULL)
+ ms->ms_ppath->mpp_peerdown(ni);
+}
+
+void
+ieee80211_parse_meshid(struct ieee80211_node *ni, const uint8_t *ie)
+{
+ ni->ni_meshidlen = ie[1];
+ memcpy(ni->ni_meshid, ie + 2, ie[1]);
+}
+
+/*
+ * Setup mesh-specific node state on neighbor discovery.
+ */
+void
+ieee80211_mesh_init_neighbor(struct ieee80211_node *ni,
+ const struct ieee80211_frame *wh,
+ const struct ieee80211_scanparams *sp)
+{
+ ieee80211_parse_meshid(ni, sp->meshid);
+}
+
+static int
+mesh_ioctl_get80211(struct ieee80211vap *vap, struct ieee80211req *ireq)
+{
+ struct ieee80211_mesh_state *ms = vap->iv_mesh;
+ uint8_t tmpmeshid[IEEE80211_NWID_LEN];
+ struct ieee80211_mesh_route *rt;
+ struct ieee80211req_mesh_route *imr;
+ size_t len, off;
+ uint8_t *p;
+ int error;
+
+ if (vap->iv_opmode != IEEE80211_M_MBSS)
+ return ENOSYS;
+
+ error = 0;
+ switch (ireq->i_type) {
+ case IEEE80211_IOC_MESH_ID:
+ ireq->i_len = ms->ms_idlen;
+ memcpy(tmpmeshid, ms->ms_id, ireq->i_len);
+ error = copyout(tmpmeshid, ireq->i_data, ireq->i_len);
+ break;
+ case IEEE80211_IOC_MESH_AP:
+ ireq->i_val = (ms->ms_flags & IEEE80211_MESHFLAGS_AP) != 0;
+ break;
+ case IEEE80211_IOC_MESH_FWRD:
+ ireq->i_val = (ms->ms_flags & IEEE80211_MESHFLAGS_FWD) != 0;
+ break;
+ case IEEE80211_IOC_MESH_TTL:
+ ireq->i_val = ms->ms_ttl;
+ break;
+ case IEEE80211_IOC_MESH_RTCMD:
+ switch (ireq->i_val) {
+ case IEEE80211_MESH_RTCMD_LIST:
+ len = 0;
+ MESH_RT_LOCK(ms);
+ TAILQ_FOREACH(rt, &ms->ms_routes, rt_next) {
+ len += sizeof(*imr);
+ }
+ MESH_RT_UNLOCK(ms);
+ if (len > ireq->i_len || ireq->i_len < sizeof(*imr)) {
+ ireq->i_len = len;
+ return ENOMEM;
+ }
+ ireq->i_len = len;
+ p = malloc(len, M_TEMP, M_NOWAIT | M_ZERO);
+ if (p == NULL)
+ return ENOMEM;
+ off = 0;
+ MESH_RT_LOCK(ms);
+ TAILQ_FOREACH(rt, &ms->ms_routes, rt_next) {
+ if (off >= len)
+ break;
+ imr = (struct ieee80211req_mesh_route *)
+ (p + off);
+ IEEE80211_ADDR_COPY(imr->imr_dest,
+ rt->rt_dest);
+ IEEE80211_ADDR_COPY(imr->imr_nexthop,
+ rt->rt_nexthop);
+ imr->imr_metric = rt->rt_metric;
+ imr->imr_nhops = rt->rt_nhops;
+ imr->imr_lifetime = rt->rt_lifetime;
+ off += sizeof(*imr);
+ }
+ MESH_RT_UNLOCK(ms);
+ error = copyout(p, (uint8_t *)ireq->i_data,
+ ireq->i_len);
+ free(p, M_TEMP);
+ break;
+ case IEEE80211_MESH_RTCMD_FLUSH:
+ case IEEE80211_MESH_RTCMD_ADD:
+ case IEEE80211_MESH_RTCMD_DELETE:
+ return EINVAL;
+ default:
+ return ENOSYS;
+ }
+ break;
+ case IEEE80211_IOC_MESH_PR_METRIC:
+ len = strlen(ms->ms_pmetric->mpm_descr);
+ if (ireq->i_len < len)
+ return EINVAL;
+ ireq->i_len = len;
+ error = copyout(ms->ms_pmetric->mpm_descr,
+ (uint8_t *)ireq->i_data, len);
+ break;
+ case IEEE80211_IOC_MESH_PR_PATH:
+ len = strlen(ms->ms_ppath->mpp_descr);
+ if (ireq->i_len < len)
+ return EINVAL;
+ ireq->i_len = len;
+ error = copyout(ms->ms_ppath->mpp_descr,
+ (uint8_t *)ireq->i_data, len);
+ break;
+ default:
+ return ENOSYS;
+ }
+
+ return error;
+}
+IEEE80211_IOCTL_GET(mesh, mesh_ioctl_get80211);
+
+static int
+mesh_ioctl_set80211(struct ieee80211vap *vap, struct ieee80211req *ireq)
+{
+ struct ieee80211_mesh_state *ms = vap->iv_mesh;
+ uint8_t tmpmeshid[IEEE80211_NWID_LEN];
+ uint8_t tmpaddr[IEEE80211_ADDR_LEN];
+ char tmpproto[IEEE80211_MESH_PROTO_DSZ];
+ int error;
+
+ if (vap->iv_opmode != IEEE80211_M_MBSS)
+ return ENOSYS;
+
+ error = 0;
+ switch (ireq->i_type) {
+ case IEEE80211_IOC_MESH_ID:
+ if (ireq->i_val != 0 || ireq->i_len > IEEE80211_MESHID_LEN)
+ return EINVAL;
+ error = copyin(ireq->i_data, tmpmeshid, ireq->i_len);
+ if (error)
+ break;
+ memset(ms->ms_id, 0, IEEE80211_NWID_LEN);
+ ms->ms_idlen = ireq->i_len;
+ memcpy(ms->ms_id, tmpmeshid, ireq->i_len);
+ break;
+ case IEEE80211_IOC_MESH_AP:
+ if (ireq->i_val)
+ ms->ms_flags |= IEEE80211_MESHFLAGS_AP;
+ else
+ ms->ms_flags &= ~IEEE80211_MESHFLAGS_AP;
+ break;
+ case IEEE80211_IOC_MESH_FWRD:
+ if (ireq->i_val)
+ ms->ms_flags |= IEEE80211_MESHFLAGS_FWD;
+ else
+ ms->ms_flags &= ~IEEE80211_MESHFLAGS_FWD;
+ break;
+ case IEEE80211_IOC_MESH_TTL:
+ ms->ms_ttl = (uint8_t) ireq->i_val;
+ break;
+ case IEEE80211_IOC_MESH_RTCMD:
+ switch (ireq->i_val) {
+ case IEEE80211_MESH_RTCMD_LIST:
+ return EINVAL;
+ case IEEE80211_MESH_RTCMD_FLUSH:
+ ieee80211_mesh_rt_flush(vap);
+ break;
+ case IEEE80211_MESH_RTCMD_ADD:
+ if (IEEE80211_ADDR_EQ(vap->iv_myaddr, ireq->i_data) ||
+ IEEE80211_ADDR_EQ(broadcastaddr, ireq->i_data))
+ return EINVAL;
+ error = copyin(ireq->i_data, &tmpaddr,
+ IEEE80211_ADDR_LEN);
+ if (!error)
+ ieee80211_mesh_discover(vap, tmpaddr, NULL);
+ break;
+ case IEEE80211_MESH_RTCMD_DELETE:
+ ieee80211_mesh_rt_del(vap, ireq->i_data);
+ break;
+ default:
+ return ENOSYS;
+ }
+ break;
+ case IEEE80211_IOC_MESH_PR_METRIC:
+ error = copyin(ireq->i_data, tmpproto, sizeof(tmpproto));
+ if (!error)
+ return mesh_select_proto_metric(vap, tmpproto);
+ break;
+ case IEEE80211_IOC_MESH_PR_PATH:
+ error = copyin(ireq->i_data, tmpproto, sizeof(tmpproto));
+ if (!error)
+ return mesh_select_proto_path(vap, tmpproto);
+ break;
+ default:
+ return ENOSYS;
+ }
+ return error;
+}
+IEEE80211_IOCTL_SET(mesh, mesh_ioctl_set80211);
diff --git a/sys/net80211/ieee80211_mesh.h b/sys/net80211/ieee80211_mesh.h
new file mode 100644
index 0000000..595eece
--- /dev/null
+++ b/sys/net80211/ieee80211_mesh.h
@@ -0,0 +1,505 @@
+/*-
+ * Copyright (c) 2009 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Rui Paulo under sponsorship from the
+ * FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef _NET80211_IEEE80211_MESH_H_
+#define _NET80211_IEEE80211_MESH_H_
+
+#define IEEE80211_MESH_DEFAULT_TTL 31
+
+/*
+ * NB: all structures are__packed so sizeof works on arm, et. al.
+ */
+/*
+ * 802.11s Information Elements.
+*/
+/* Mesh Configuration */
+struct ieee80211_meshconf_ie {
+ uint8_t conf_ie; /* IEEE80211_ELEMID_MESHCONF */
+ uint8_t conf_len;
+ uint8_t conf_ver;
+ uint8_t conf_pselid[4]; /* Active Path Sel. Proto. ID */
+ uint8_t conf_pmetid[4]; /* APS Metric Identifier */
+ uint8_t conf_ccid[4]; /* Congestion Control Mode ID */
+ uint8_t conf_syncid[4]; /* Sync. Protocol ID */
+ uint8_t conf_authid[4]; /* Auth. Protocol ID */
+ uint8_t conf_form; /* Formation Information */
+ uint8_t conf_cap;
+} __packed;
+
+#define IEEE80211_MESHCONF_VERSION 1
+/* Null Protocol */
+#define IEEE80211_MESHCONF_NULL_OUI 0x00, 0x0f, 0xac
+#define IEEE80211_MESHCONF_NULL_VALUE 0xff
+#define IEEE80211_MESHCONF_NULL { IEEE80211_MESHCONF_NULL_OUI, \
+ IEEE80211_MESHCONF_NULL_VALUE }
+/* Hybrid Wireless Mesh Protocol */
+#define IEEE80211_MESHCONF_HWMP_OUI 0x00, 0x0f, 0xac
+#define IEEE80211_MESHCONF_HWMP_VALUE 0x00
+#define IEEE80211_MESHCONF_HWMP { IEEE80211_MESHCONF_HWMP_OUI, \
+ IEEE80211_MESHCONF_HWMP_VALUE }
+/* Airtime Link Metric */
+#define IEEE80211_MESHCONF_AIRTIME_OUI 0x00, 0x0f, 0xac
+#define IEEE80211_MESHCONF_AIRTIME_VALUE 0x00
+#define IEEE80211_MESHCONF_AIRTIME { IEEE80211_MESHCONF_AIRTIME_OUI, \
+ IEEE80211_MESHCONF_AIRTIME_VALUE }
+/* Congestion Control Signaling */
+#define IEEE80211_MESHCONF_CCSIG_OUI 0x00, 0x0f, 0xac
+#define IEEE80211_MESHCONF_CCSIG_VALUE 0x00
+#define IEEE80211_MESHCONF_CCSIG { IEEE80211_MESHCONF_CCSIG_OUI,\
+ IEEE80211_MESHCONF_CCSIG_VALUE }
+/* Neighbour Offset */
+#define IEEE80211_MESHCONF_NEIGHOFF_OUI 0x00, 0x0f, 0xac
+#define IEEE80211_MESHCONF_NEIGHOFF_VALUE 0x00
+#define IEEE80211_MESHCONF_NEIGHOFF { IEEE80211_MESHCONF_NEIGHOFF_OUI, \
+ IEEE80211_MESHCONF_NEIGHOFF_VALUE }
+/* Simultaneous Authenticaction of Equals */
+#define IEEE80211_MESHCONF_SAE_OUI 0x00, 0x0f, 0xac
+#define IEEE80211_MESHCONF_SAE_VALUE 0x01
+#define IEEE80211_MESHCONF_SAE { IEEE80211_MESHCONF_SAE_OUI, \
+ IEEE80211_MESHCONF_SAE_VALUE }
+#define IEEE80211_MESHCONF_FORM_MP 0x01 /* Connected to Portal */
+#define IEEE80211_MESHCONF_FORM_NNEIGH_MASK 0x04 /* Number of Neighbours */
+#define IEEE80211_MESHCONF_CAP_AP 0x01 /* Accepting Peers */
+#define IEEE80211_MESHCONF_CAP_MCCAS 0x02 /* MCCA supported */
+#define IEEE80211_MESHCONF_CAP_MCCAE 0x04 /* MCCA enabled */
+#define IEEE80211_MESHCONF_CAP_FWRD 0x08 /* forwarding enabled */
+#define IEEE80211_MESHCONF_CAP_BTR 0x10 /* Beacon Timing Report Enab */
+#define IEEE80211_MESHCONF_CAP_TBTTA 0x20 /* TBTT Adj. Enabled */
+#define IEEE80211_MESHCONF_CAP_PSL 0x40 /* Power Save Level */
+
+/* Mesh Identifier */
+struct ieee80211_meshid_ie {
+ uint8_t id_ie; /* IEEE80211_ELEMID_MESHID */
+ uint8_t id_len;
+} __packed;
+
+/* Link Metric Report */
+struct ieee80211_meshlmetric_ie {
+ uint8_t lm_ie; /* IEEE80211_ELEMID_MESHLINK */
+ uint8_t lm_len;
+ uint32_t lm_metric;
+#define IEEE80211_MESHLMETRIC_INITIALVAL 0
+} __packed;
+
+/* Congestion Notification */
+struct ieee80211_meshcngst_ie {
+ uint8_t cngst_ie; /* IEEE80211_ELEMID_MESHCNGST */
+ uint8_t cngst_len;
+ uint16_t cngst_timer[4]; /* Expiration Timers: AC_BK,
+ AC_BE, AC_VI, AC_VO */
+} __packed;
+
+/* Peer Version */
+struct ieee80211_meshpeerver_ie {
+ uint8_t peerver_ie; /* IEEE80211_ELEMID_MESHPEERVER */
+ uint8_t peerver_len;
+ uint8_t peerver_proto[4];
+} __packed;
+/* Mesh Peering Management Protocol */
+#define IEEE80211_MESHPEERVER_PEER_OUI 0x00, 0x0f, 0xac
+#define IEEE80211_MESHPEERVER_PEER_VALUE 0x2a
+#define IEEE80211_MESHPEERVER_PEER { IEEE80211_MESHPEERVER_PEER_OUI, \
+ IEEE80211_MESHPEERVER_PEER_VALUE }
+/* Abbreviated Handshake Protocol */
+#define IEEE80211_MESHPEERVER_AH_OUI 0x00, 0x0f, 0xac
+#define IEEE80211_MESHPEERVER_AH_VALUE 0x2b
+#define IEEE80211_MESHPEERVER_AH { IEEE80211_MESHPEERVER_AH_OUI, \
+ IEEE80211_MESHPEERVER_AH_VALUE }
+
+/* Peer Link Management */
+struct ieee80211_meshpeer_ie {
+ uint8_t peer_ie; /* IEEE80211_ELEMID_MESHPEER */
+ uint8_t peer_len;
+ uint8_t peer_subtype;
+ uint16_t peer_llinkid; /* Local Link ID */
+ uint16_t peer_linkid; /* Peer Link ID */
+ uint16_t peer_rcode;
+} __packed;
+
+enum {
+ IEEE80211_MESH_PEER_LINK_OPEN = 0,
+ IEEE80211_MESH_PEER_LINK_CONFIRM = 1,
+ IEEE80211_MESH_PEER_LINK_CLOSE = 2,
+ /* values 3-255 are reserved */
+};
+
+#ifdef notyet
+/* Mesh Channel Switch Annoucement */
+struct ieee80211_meshcsa_ie {
+ uint8_t csa_ie; /* IEEE80211_ELEMID_MESHCSA */
+ uint8_t csa_len;
+ uint8_t csa_mode;
+ uint8_t csa_newclass; /* New Regulatory Class */
+ uint8_t csa_newchan;
+ uint8_t csa_precvalue; /* Precedence Value */
+ uint8_t csa_count;
+} __packed;
+
+/* Mesh TIM */
+/* Equal to the non Mesh version */
+
+/* Mesh Awake Window */
+struct ieee80211_meshawakew_ie {
+ uint8_t awakew_ie; /* IEEE80211_ELEMID_MESHAWAKEW */
+ uint8_t awakew_len;
+ uint8_t awakew_windowlen; /* in TUs */
+} __packed;
+
+/* Mesh Beacon Timing */
+struct ieee80211_meshbeacont_ie {
+ uint8_t beacont_ie; /* IEEE80211_ELEMID_MESHBEACONT */
+ uint8_t beacont_len;
+ struct {
+ uint8_t mp_aid; /* Least Octet of AID */
+ uint16_t mp_btime; /* Beacon Time */
+ uint16_t mp_bint; /* Beacon Interval */
+ } __packed mp[1]; /* NB: variable size */
+} __packed;
+#endif
+
+/* Portal (MP) Annoucement */
+struct ieee80211_meshpann_ie {
+ uint8_t pann_ie; /* IEEE80211_ELEMID_MESHPANN */
+ uint8_t pann_len;
+ uint8_t pann_flags;
+ uint8_t pann_hopcount;
+ uint8_t pann_ttl;
+ uint8_t pann_addr[IEEE80211_ADDR_LEN];
+ uint8_t pann_seq; /* PANN Sequence Number */
+} __packed;
+
+/* Root (MP) Annoucement */
+struct ieee80211_meshrann_ie {
+ uint8_t rann_ie; /* IEEE80211_ELEMID_MESHRANN */
+ uint8_t rann_len;
+ uint8_t rann_flags;
+#define IEEE80211_MESHRANN_FLAGS_PR 0x01 /* Portal Role */
+ uint8_t rann_hopcount;
+ uint8_t rann_ttl;
+ uint8_t rann_addr[IEEE80211_ADDR_LEN];
+ uint32_t rann_seq; /* HWMP Sequence Number */
+ uint32_t rann_metric;
+} __packed;
+
+/* Mesh Path Request */
+struct ieee80211_meshpreq_ie {
+ uint8_t preq_ie; /* IEEE80211_ELEMID_MESHPREQ */
+ uint8_t preq_len;
+ uint8_t preq_flags;
+#define IEEE80211_MESHPREQ_FLAGS_PR 0x01 /* Portal Role */
+#define IEEE80211_MESHPREQ_FLAGS_AM 0x02 /* 0 = ucast / 1 = bcast */
+#define IEEE80211_MESHPREQ_FLAGS_PP 0x04 /* Proactive PREP */
+#define IEEE80211_MESHPREQ_FLAGS_AE 0x40 /* Address Extension */
+ uint8_t preq_hopcount;
+ uint8_t preq_ttl;
+ uint32_t preq_id;
+ uint8_t preq_origaddr[IEEE80211_ADDR_LEN];
+ uint32_t preq_origseq; /* HWMP Sequence Number */
+ /* NB: may have Originator Proxied Address */
+ uint32_t preq_lifetime;
+ uint32_t preq_metric;
+ uint8_t preq_tcount; /* target count */
+ struct {
+ uint8_t target_flags;
+#define IEEE80211_MESHPREQ_TFLAGS_TO 0x01 /* Target Only */
+#define IEEE80211_MESHPREQ_TFLAGS_RF 0x02 /* Reply and Forward */
+#define IEEE80211_MESHPREQ_TFLAGS_USN 0x04 /* Unknown HWMP seq number */
+ uint8_t target_addr[IEEE80211_ADDR_LEN];
+ uint32_t target_seq; /* HWMP Sequence Number */
+ } __packed preq_targets[1]; /* NB: variable size */
+} __packed;
+
+/* Mesh Path Reply */
+struct ieee80211_meshprep_ie {
+ uint8_t prep_ie; /* IEEE80211_ELEMID_MESHPREP */
+ uint8_t prep_len;
+ uint8_t prep_flags;
+ uint8_t prep_hopcount;
+ uint8_t prep_ttl;
+ uint8_t prep_targetaddr[IEEE80211_ADDR_LEN];
+ uint32_t prep_targetseq;
+ /* NB: May have Target Proxied Address */
+ uint32_t prep_lifetime;
+ uint32_t prep_metric;
+ uint8_t prep_origaddr[IEEE80211_ADDR_LEN];
+ uint32_t prep_origseq; /* HWMP Sequence Number */
+} __packed;
+
+/* Mesh Path Error */
+struct ieee80211_meshperr_ie {
+ uint8_t perr_ie; /* IEEE80211_ELEMID_MESHPERR */
+ uint8_t perr_len;
+ uint8_t perr_mode; /* NB: reserved */
+ uint8_t perr_ndests; /* Number of Destinations */
+ struct {
+ uint8_t dest_addr[IEEE80211_ADDR_LEN];
+ uint32_t dest_seq; /* HWMP Sequence Number */
+ } __packed perr_dests[1]; /* NB: variable size */
+} __packed;
+
+#ifdef notyet
+/* Mesh Proxy Update */
+struct ieee80211_meshpu_ie {
+ uint8_t pu_ie; /* IEEE80211_ELEMID_MESHPU */
+ uint8_t pu_len;
+ uint8_t pu_flags;
+#define IEEE80211_MESHPU_FLAGS_MASK 0x1
+#define IEEE80211_MESHPU_FLAGS_DEL 0x0
+#define IEEE80211_MESHPU_FLAGS_ADD 0x1
+ uint8_t pu_seq; /* PU Sequence Number */
+ uint8_t pu_addr[IEEE80211_ADDR_LEN];
+ uint8_t pu_naddr; /* Number of Proxied Addresses */
+ /* NB: proxied address follows */
+} __packed;
+
+/* Mesh Proxy Update Confirmation */
+struct ieee80211_meshpuc_ie {
+ uint8_t puc_ie; /* IEEE80211_ELEMID_MESHPUC */
+ uint8_t puc_len;
+ uint8_t puc_flags;
+ uint8_t puc_seq; /* PU Sequence Number */
+ uint8_t puc_daddr[IEEE80211_ADDR_LEN];
+} __packed;
+#endif
+
+/*
+ * 802.11s Action Frames
+ */
+#define IEEE80211_ACTION_CAT_MESHPEERING 30 /* XXX Linux */
+#define IEEE80211_ACTION_CAT_MESHLMETRIC 13
+#define IEEE80211_ACTION_CAT_MESHPATH 32 /* XXX Linux */
+#define IEEE80211_ACTION_CAT_INTERWORK 15
+#define IEEE80211_ACTION_CAT_RESOURCE 16
+#define IEEE80211_ACTION_CAT_PROXY 17
+
+/*
+ * Mesh Peering Action codes.
+ */
+enum {
+ IEEE80211_ACTION_MESHPEERING_OPEN = 0,
+ IEEE80211_ACTION_MESHPEERING_CONFIRM = 1,
+ IEEE80211_ACTION_MESHPEERING_CLOSE = 2,
+ /* 3-255 reserved */
+};
+
+/*
+ * Mesh Path Selection Action codes.
+ */
+enum {
+ IEEE80211_ACTION_MESHPATH_REQ = 0,
+ IEEE80211_ACTION_MESHPATH_REP = 1,
+ IEEE80211_ACTION_MESHPATH_ERR = 2,
+ IEEE80211_ACTION_MESHPATH_RANN = 3,
+ /* 4-255 reserved */
+};
+
+/*
+ * Mesh Link Metric Action codes.
+ */
+enum {
+ IEEE80211_ACTION_MESHLMETRIC_REQ = 0, /* Link Metric Request */
+ IEEE80211_ACTION_MESHLMETRIC_REP = 1, /* Link Metric Report */
+ /* 2-255 reserved */
+};
+
+/*
+ * Mesh Portal Annoucement Action codes.
+ */
+enum {
+ IEEE80211_ACTION_MESHPANN = 0,
+ /* 1-255 reserved */
+};
+
+/*
+ * Different mesh control structures based on the AE
+ * (Address Extension) bits.
+ */
+struct ieee80211_meshcntl {
+ uint8_t mc_flags; /* Address Extension 00 */
+ uint8_t mc_ttl; /* TTL */
+ uint8_t mc_seq[4]; /* Sequence No. */
+ /* NB: more addresses may follow */
+} __packed;
+
+struct ieee80211_meshcntl_ae01 {
+ uint8_t mc_flags; /* Address Extension 01 */
+ uint8_t mc_ttl; /* TTL */
+ uint8_t mc_seq[4]; /* Sequence No. */
+ uint8_t mc_addr4[IEEE80211_ADDR_LEN];
+} __packed;
+
+struct ieee80211_meshcntl_ae10 {
+ uint8_t mc_flags; /* Address Extension 10 */
+ uint8_t mc_ttl; /* TTL */
+ uint8_t mc_seq[4]; /* Sequence No. */
+ uint8_t mc_addr5[IEEE80211_ADDR_LEN];
+ uint8_t mc_addr6[IEEE80211_ADDR_LEN];
+} __packed;
+
+struct ieee80211_meshcntl_ae11 {
+ uint8_t mc_flags; /* Address Extension 11 */
+ uint8_t mc_ttl; /* TTL */
+ uint8_t mc_seq[4]; /* Sequence No. */
+ uint8_t mc_addr4[IEEE80211_ADDR_LEN];
+ uint8_t mc_addr5[IEEE80211_ADDR_LEN];
+ uint8_t mc_addr6[IEEE80211_ADDR_LEN];
+} __packed;
+
+struct ieee80211req_mesh_route {
+ uint8_t imr_dest[IEEE80211_ADDR_LEN];
+ uint8_t imr_nexthop[IEEE80211_ADDR_LEN];
+ uint32_t imr_metric;
+ uint16_t imr_nhops;
+ uint32_t imr_lifetime;
+};
+
+#ifdef _KERNEL
+MALLOC_DECLARE(M_80211_MESH_RT);
+struct ieee80211_mesh_route {
+ TAILQ_ENTRY(ieee80211_mesh_route) rt_next;
+ uint8_t rt_dest[IEEE80211_ADDR_LEN];
+ uint8_t rt_nexthop[IEEE80211_ADDR_LEN];
+ uint32_t rt_metric; /* path metric */
+ uint16_t rt_nhops; /* number of hops */
+ uint32_t rt_lifetime;
+ uint32_t rt_lastmseq; /* last seq# seen dest */
+ void *rt_priv; /* private data */
+};
+#define IEEE80211_MESH_ROUTE_PRIV(rt, cast) ((cast *)rt->rt_priv)
+
+#define IEEE80211_MESH_PROTO_DSZ 12 /* description size */
+/*
+ * Mesh Path Selection Protocol.
+ */
+enum ieee80211_state;
+struct ieee80211_mesh_proto_path {
+ char mpp_descr[IEEE80211_MESH_PROTO_DSZ];
+ uint8_t mpp_ie[4];
+ struct ieee80211_node *
+ (*mpp_discover)(struct ieee80211vap *,
+ const uint8_t [IEEE80211_ADDR_LEN],
+ struct mbuf *);
+ void (*mpp_peerdown)(struct ieee80211_node *);
+ void (*mpp_vattach)(struct ieee80211vap *);
+ void (*mpp_vdetach)(struct ieee80211vap *);
+ int (*mpp_newstate)(struct ieee80211vap *,
+ enum ieee80211_state, int);
+ size_t mpp_privlen; /* size required in the routing table
+ for private data */
+};
+
+/*
+ * Mesh Link Metric Report Protocol.
+ */
+struct ieee80211_mesh_proto_metric {
+ char mpm_descr[IEEE80211_MESH_PROTO_DSZ];
+ uint8_t mpm_ie[4];
+ uint32_t (*mpm_metric)(struct ieee80211_node *);
+};
+
+#ifdef notyet
+/*
+ * Mesh Authentication Protocol.
+ */
+struct ieee80211_mesh_proto_auth {
+ uint8_t mpa_ie[4];
+};
+
+struct ieee80211_mesh_proto_congestion {
+};
+
+struct ieee80211_mesh_proto_sync {
+};
+#endif
+
+typedef uint32_t ieee80211_mesh_seq;
+#define IEEE80211_MESH_SEQ_LEQ(a, b) ((int32_t)((a)-(b)) <= 0)
+#define IEEE80211_MESH_SEQ_GEQ(a, b) ((int32_t)((a)-(b)) >= 0)
+
+struct ieee80211_mesh_state {
+ int ms_idlen;
+ uint8_t ms_id[IEEE80211_MESHID_LEN];
+ ieee80211_mesh_seq ms_seq; /* seq no for meshcntl */
+ uint16_t ms_neighbors;
+ uint8_t ms_ttl; /* mesh ttl set in packets */
+#define IEEE80211_MESHFLAGS_AP 0x01 /* accept peers */
+#define IEEE80211_MESHFLAGS_PORTAL 0x02 /* mesh portal role */
+#define IEEE80211_MESHFLAGS_FWD 0x04 /* forward packets */
+ uint8_t ms_flags;
+ struct mtx ms_rt_lock;
+ TAILQ_HEAD(, ieee80211_mesh_route) ms_routes;
+ struct ieee80211_mesh_proto_metric *ms_pmetric;
+ struct ieee80211_mesh_proto_path *ms_ppath;
+};
+void ieee80211_mesh_attach(struct ieee80211com *);
+void ieee80211_mesh_detach(struct ieee80211com *);
+
+struct ieee80211_mesh_route *
+ ieee80211_mesh_rt_find(struct ieee80211vap *,
+ const uint8_t [IEEE80211_ADDR_LEN]);
+struct ieee80211_mesh_route *
+ ieee80211_mesh_rt_add(struct ieee80211vap *,
+ const uint8_t [IEEE80211_ADDR_LEN]);
+void ieee80211_mesh_rt_del(struct ieee80211vap *,
+ const uint8_t [IEEE80211_ADDR_LEN]);
+void ieee80211_mesh_rt_flush(struct ieee80211vap *);
+
+int ieee80211_mesh_register_proto_path(const
+ struct ieee80211_mesh_proto_path *);
+int ieee80211_mesh_register_proto_metric(const
+ struct ieee80211_mesh_proto_metric *);
+
+uint8_t * ieee80211_add_meshpeerver(uint8_t *, struct ieee80211vap *);
+uint8_t * ieee80211_add_meshid(uint8_t *, struct ieee80211vap *);
+uint8_t * ieee80211_add_meshconf(uint8_t *, struct ieee80211vap *);
+uint8_t * ieee80211_add_meshpeer(uint8_t *, uint8_t, uint16_t, uint16_t,
+ uint16_t);
+uint8_t * ieee80211_add_meshlmetric(uint8_t *, uint32_t);
+
+void ieee80211_mesh_node_init(struct ieee80211vap *,
+ struct ieee80211_node *);
+void ieee80211_mesh_node_cleanup(struct ieee80211_node *);
+void ieee80211_parse_meshid(struct ieee80211_node *,
+ const uint8_t *);
+struct ieee80211_scanparams;
+void ieee80211_mesh_init_neighbor(struct ieee80211_node *,
+ const struct ieee80211_frame *,
+ const struct ieee80211_scanparams *);
+
+static __inline struct ieee80211_node *
+ieee80211_mesh_discover(struct ieee80211vap *vap,
+ const uint8_t dest[IEEE80211_ADDR_LEN], struct mbuf *m)
+{
+ struct ieee80211_mesh_state *ms = vap->iv_mesh;
+ return ms->ms_ppath->mpp_discover(vap, dest, m);
+}
+
+#endif /* _KERNEL */
+#endif /* !_NET80211_IEEE80211_MESH_H_ */
diff --git a/sys/net80211/ieee80211_node.c b/sys/net80211/ieee80211_node.c
index 7fae394..30508d9 100644
--- a/sys/net80211/ieee80211_node.c
+++ b/sys/net80211/ieee80211_node.c
@@ -50,10 +50,16 @@ __FBSDID("$FreeBSD$");
#include <net80211/ieee80211_tdma.h>
#endif
#include <net80211/ieee80211_wds.h>
+#include <net80211/ieee80211_mesh.h>
#include <net/bpf.h>
/*
+ * IEEE80211_NODE_HASHSIZE must be a power of 2.
+ */
+CTASSERT((IEEE80211_NODE_HASHSIZE & (IEEE80211_NODE_HASHSIZE-1)) == 0);
+
+/*
* Association id's are managed with a bit vector.
*/
#define IEEE80211_AID_SET(_vap, b) \
@@ -322,7 +328,8 @@ ieee80211_create_ibss(struct ieee80211vap* vap, struct ieee80211_channel *chan)
struct ieee80211_node *ni;
IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
- "%s: creating ibss on channel %u\n", __func__,
+ "%s: creating %s on channel %u\n", __func__,
+ ieee80211_opmode_name[vap->iv_opmode],
ieee80211_chan2ieee(ic, chan));
ni = ieee80211_alloc_node(&ic->ic_sta, vap, vap->iv_myaddr);
@@ -360,6 +367,11 @@ ieee80211_create_ibss(struct ieee80211vap* vap, struct ieee80211_channel *chan)
if ((vap->iv_caps & IEEE80211_C_TDMA) == 0)
#endif
memset(ni->ni_bssid, 0, IEEE80211_ADDR_LEN);
+#ifdef IEEE80211_SUPPORT_MESH
+ } else if (vap->iv_opmode == IEEE80211_M_MBSS) {
+ ni->ni_meshidlen = vap->iv_mesh->ms_idlen;
+ memcpy(ni->ni_meshid, vap->iv_mesh->ms_id, ni->ni_meshidlen);
+#endif
}
/*
* Fix the channel and related attributes.
@@ -609,6 +621,7 @@ gethtadjustflags(struct ieee80211com *ic)
case IEEE80211_M_AHDEMO:
case IEEE80211_M_HOSTAP:
case IEEE80211_M_IBSS:
+ case IEEE80211_M_MBSS:
flags |= ieee80211_htchanflags(vap->iv_bss->ni_chan);
break;
default:
@@ -784,6 +797,10 @@ ieee80211_sta_join(struct ieee80211vap *vap, struct ieee80211_channel *chan,
ieee80211_parse_htcap(ni, ni->ni_ies.htcap_ie);
if (ni->ni_ies.htinfo_ie != NULL)
ieee80211_parse_htinfo(ni, ni->ni_ies.htinfo_ie);
+#ifdef IEEE80211_SUPPORT_MESH
+ if (ni->ni_ies.meshid_ie != NULL)
+ ieee80211_parse_meshid(ni, ni->ni_ies.meshid_ie);
+#endif
#ifdef IEEE80211_SUPPORT_TDMA
if (ni->ni_ies.tdma_ie != NULL)
ieee80211_parse_tdma(ni, ni->ni_ies.tdma_ie);
@@ -914,6 +931,11 @@ ieee80211_ies_expand(struct ieee80211_ies *ies)
case IEEE80211_ELEMID_HTCAP:
ies->htcap_ie = ie;
break;
+#ifdef IEEE80211_SUPPORT_MESH
+ case IEEE80211_ELEMID_MESHID:
+ ies->meshid_ie = ie;
+ break;
+#endif
}
ielen -= 2 + ie[1];
ie += 2 + ie[1];
@@ -951,6 +973,13 @@ node_cleanup(struct ieee80211_node *ni)
else if (ni->ni_ath_flags & IEEE80211_NODE_ATH)
ieee80211_ff_node_cleanup(ni);
#endif
+#ifdef IEEE80211_SUPPORT_MESH
+ /*
+ * Cleanup any mesh-related state.
+ */
+ if (vap->iv_opmode == IEEE80211_M_MBSS)
+ ieee80211_mesh_node_cleanup(ni);
+#endif
/*
* Clear any staging queue entries.
*/
@@ -1078,7 +1107,7 @@ ieee80211_alloc_node(struct ieee80211_node_table *nt,
ether_sprintf(macaddr), nt->nt_name);
IEEE80211_ADDR_COPY(ni->ni_macaddr, macaddr);
- hash = IEEE80211_NODE_HASH(macaddr);
+ hash = IEEE80211_NODE_HASH(ic, macaddr);
ieee80211_node_initref(ni); /* mark referenced */
ni->ni_chan = IEEE80211_CHAN_ANYC;
ni->ni_authmode = IEEE80211_AUTH_OPEN;
@@ -1090,7 +1119,10 @@ ieee80211_alloc_node(struct ieee80211_node_table *nt,
ni->ni_inact = ni->ni_inact_reload;
ni->ni_ath_defkeyix = 0x7fff;
ieee80211_psq_init(&ni->ni_psq, "unknown");
-
+#ifdef IEEE80211_SUPPORT_MESH
+ if (vap->iv_opmode == IEEE80211_M_MBSS)
+ ieee80211_mesh_node_init(vap, ni);
+#endif
IEEE80211_NODE_LOCK(nt);
TAILQ_INSERT_TAIL(&nt->nt_node, ni, ni_list);
LIST_INSERT_HEAD(&nt->nt_hash[hash], ni, ni_hash);
@@ -1242,7 +1274,7 @@ ieee80211_find_node_locked(struct ieee80211_node_table *nt,
IEEE80211_NODE_LOCK_ASSERT(nt);
- hash = IEEE80211_NODE_HASH(macaddr);
+ hash = IEEE80211_NODE_HASH(nt->nt_ic, macaddr);
LIST_FOREACH(ni, &nt->nt_hash[hash], ni_hash) {
if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr)) {
ieee80211_ref_node(ni); /* mark referenced */
@@ -1292,7 +1324,7 @@ ieee80211_find_vap_node_locked(struct ieee80211_node_table *nt,
IEEE80211_NODE_LOCK_ASSERT(nt);
- hash = IEEE80211_NODE_HASH(macaddr);
+ hash = IEEE80211_NODE_HASH(nt->nt_ic, macaddr);
LIST_FOREACH(ni, &nt->nt_hash[hash], ni_hash) {
if (ni->ni_vap == vap &&
IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr)) {
@@ -1391,7 +1423,10 @@ ieee80211_init_neighbor(struct ieee80211_node *ni,
ni->ni_fhindex = sp->fhindex;
ni->ni_erp = sp->erp;
ni->ni_timoff = sp->timoff;
-
+#ifdef IEEE80211_SUPPORT_MESH
+ if (ni->ni_vap->iv_opmode == IEEE80211_M_MBSS)
+ ieee80211_mesh_init_neighbor(ni, wh, sp);
+#endif
if (ieee80211_ies_init(&ni->ni_ies, sp->ies, sp->ies_len)) {
ieee80211_ies_expand(&ni->ni_ies);
if (ni->ni_ies.wme_ie != NULL)
@@ -2525,6 +2560,27 @@ get_adhoc_rssi(void *arg, struct ieee80211_node *ni)
}
}
+#ifdef IEEE80211_SUPPORT_MESH
+static void
+get_mesh_rssi(void *arg, struct ieee80211_node *ni)
+{
+ struct rssiinfo *info = arg;
+ struct ieee80211vap *vap = ni->ni_vap;
+ int8_t rssi;
+
+ if (info->vap != vap)
+ return;
+ /* only neighbors that peered successfully */
+ if (ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED)
+ return;
+ rssi = vap->iv_ic->ic_node_getrssi(ni);
+ if (rssi != 0) {
+ info->rssi_samples++;
+ info->rssi_total += rssi;
+ }
+}
+#endif /* IEEE80211_SUPPORT_MESH */
+
int8_t
ieee80211_getrssi(struct ieee80211vap *vap)
{
@@ -2543,6 +2599,11 @@ ieee80211_getrssi(struct ieee80211vap *vap)
case IEEE80211_M_HOSTAP: /* average of all associated stations */
ieee80211_iterate_nodes(&ic->ic_sta, get_hostap_rssi, &info);
break;
+#ifdef IEEE80211_SUPPORT_MESH
+ case IEEE80211_M_MBSS: /* average of all mesh neighbors */
+ ieee80211_iterate_nodes(&ic->ic_sta, get_mesh_rssi, &info);
+ break;
+#endif
case IEEE80211_M_MONITOR: /* XXX */
case IEEE80211_M_STA: /* use stats from associated ap */
default:
diff --git a/sys/net80211/ieee80211_node.h b/sys/net80211/ieee80211_node.h
index 6d26759..63b2355 100644
--- a/sys/net80211/ieee80211_node.h
+++ b/sys/net80211/ieee80211_node.h
@@ -35,7 +35,7 @@
* Each ieee80211com instance has a single timer that fires every
* IEEE80211_INACT_WAIT seconds to handle "inactivity processing".
* This is used to do node inactivity processing when operating
- * as an AP or in adhoc mode. For inactivity processing each node
+ * as an AP, adhoc or mesh mode. For inactivity processing each node
* has a timeout set in it's ni_inact field that is decremented
* on each timeout and the node is reclaimed when the counter goes
* to zero. We use different inactivity timeout values depending
@@ -56,9 +56,9 @@
/* threshold for aging overlapping non-ERP bss */
#define IEEE80211_NONERP_PRESENT_AGE msecs_to_ticks(60*1000)
-#define IEEE80211_NODE_HASHSIZE 32
+#define IEEE80211_NODE_HASHSIZE 32 /* NB: hash size must be pow2 */
/* simple hash is enough for variation of macaddr */
-#define IEEE80211_NODE_HASH(addr) \
+#define IEEE80211_NODE_HASH(ic, addr) \
(((const uint8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % \
IEEE80211_NODE_HASHSIZE)
@@ -81,6 +81,7 @@ struct ieee80211_ies {
uint8_t *htcap_ie; /* captured HTCAP ie */
uint8_t *htinfo_ie; /* captured HTINFO ie */
uint8_t *tdma_ie; /* captured TDMA ie */
+ uint8_t *meshid_ie; /* captured MESH ID ie */
uint8_t *spare[4];
/* NB: these must be the last members of this structure */
uint8_t *data; /* frame data > 802.11 header */
@@ -88,6 +89,20 @@ struct ieee80211_ies {
};
/*
+ * 802.11s (Mesh) Peer Link FSM state.
+ */
+enum ieee80211_mesh_mlstate {
+ IEEE80211_NODE_MESH_IDLE = 0,
+ IEEE80211_NODE_MESH_OPENSNT = 1, /* open frame sent */
+ IEEE80211_NODE_MESH_OPENRCV = 2, /* open frame received */
+ IEEE80211_NODE_MESH_CONFIRMRCV = 3, /* confirm frame received */
+ IEEE80211_NODE_MESH_ESTABLISHED = 4, /* link established */
+ IEEE80211_NODE_MESH_HOLDING = 5, /* link closing */
+};
+#define IEEE80211_MESH_MLSTATE_BITS \
+ "\20\1IDLE\2OPENSNT\2OPENRCV\3CONFIRMRCV\4ESTABLISHED\5HOLDING"
+
+/*
* Node specific information. Note that drivers are expected
* to derive from this structure to add device-specific per-node
* state. This is done by overriding the ic_node_* methods in
@@ -173,6 +188,16 @@ struct ieee80211_node {
uint8_t ni_dtim_period; /* DTIM period */
uint8_t ni_dtim_count; /* DTIM count for last bcn */
+ /* 11s state */
+ uint8_t ni_meshidlen;
+ uint8_t ni_meshid[IEEE80211_MESHID_LEN];
+ enum ieee80211_mesh_mlstate ni_mlstate; /* peering management state */
+ uint16_t ni_mllid; /* link local ID */
+ uint16_t ni_mlpid; /* link peer ID */
+ struct callout ni_mltimer; /* link mesh timer */
+ uint8_t ni_mlrcnt; /* link mesh retry counter */
+ uint8_t ni_mltval; /* link mesh timer value */
+
/* 11n state */
uint16_t ni_htcap; /* HT capabilities */
uint8_t ni_htparam; /* HT params */
diff --git a/sys/net80211/ieee80211_output.c b/sys/net80211/ieee80211_output.c
index ef74efc..96b2d45 100644
--- a/sys/net80211/ieee80211_output.c
+++ b/sys/net80211/ieee80211_output.c
@@ -55,6 +55,7 @@ __FBSDID("$FreeBSD$");
#include <net80211/ieee80211_tdma.h>
#endif
#include <net80211/ieee80211_wds.h>
+#include <net80211/ieee80211_mesh.h>
#ifdef INET
#include <netinet/in.h>
@@ -71,6 +72,18 @@ __FBSDID("$FreeBSD$");
#define ETHER_HEADER_COPY(dst, src) \
memcpy(dst, src, sizeof(struct ether_header))
+/* unalligned little endian access */
+#define LE_WRITE_2(p, v) do { \
+ ((uint8_t *)(p))[0] = (v) & 0xff; \
+ ((uint8_t *)(p))[1] = ((v) >> 8) & 0xff; \
+} while (0)
+#define LE_WRITE_4(p, v) do { \
+ ((uint8_t *)(p))[0] = (v) & 0xff; \
+ ((uint8_t *)(p))[1] = ((v) >> 8) & 0xff; \
+ ((uint8_t *)(p))[2] = ((v) >> 16) & 0xff; \
+ ((uint8_t *)(p))[3] = ((v) >> 24) & 0xff; \
+} while (0)
+
static int ieee80211_fragment(struct ieee80211vap *, struct mbuf *,
u_int hdrsize, u_int ciphdrsize, u_int mtu);
static void ieee80211_tx_mgt_cb(struct ieee80211_node *, void *, int);
@@ -205,25 +218,42 @@ ieee80211_start(struct ifnet *ifp)
ieee80211_dwds_mcast(vap, m);
}
}
- ni = ieee80211_find_txnode(vap, eh->ether_dhost);
- if (ni == NULL) {
- /* NB: ieee80211_find_txnode does stat+msg */
- ifp->if_oerrors++;
- m_freem(m);
- continue;
- }
- if (ni->ni_associd == 0 &&
- (ni->ni_flags & IEEE80211_NODE_ASSOCID)) {
- IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_OUTPUT,
- eh->ether_dhost, NULL,
- "sta not associated (type 0x%04x)",
- htons(eh->ether_type));
- vap->iv_stats.is_tx_notassoc++;
- ifp->if_oerrors++;
- m_freem(m);
- ieee80211_free_node(ni);
- continue;
+#ifdef IEEE80211_SUPPORT_MESH
+ if (vap->iv_opmode != IEEE80211_M_MBSS) {
+#endif
+ ni = ieee80211_find_txnode(vap, eh->ether_dhost);
+ if (ni == NULL) {
+ /* NB: ieee80211_find_txnode does stat+msg */
+ ifp->if_oerrors++;
+ m_freem(m);
+ continue;
+ }
+ if (ni->ni_associd == 0 &&
+ (ni->ni_flags & IEEE80211_NODE_ASSOCID)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_OUTPUT,
+ eh->ether_dhost, NULL,
+ "sta not associated (type 0x%04x)",
+ htons(eh->ether_type));
+ vap->iv_stats.is_tx_notassoc++;
+ ifp->if_oerrors++;
+ m_freem(m);
+ ieee80211_free_node(ni);
+ continue;
+ }
+#ifdef IEEE80211_SUPPORT_MESH
+ } else {
+ ni = ieee80211_mesh_discover(vap, eh->ether_dhost, m);
+ if (ni == NULL) {
+ /*
+ * NB: ieee80211_mesh_discover function
+ * holds/disposes frame
+ * (e.g. queueing on path discovery).
+ */
+ ifp->if_oerrors++;
+ continue;
+ }
}
+#endif
if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) &&
(m->m_flags & M_PWR_SAV) == 0) {
@@ -455,7 +485,7 @@ bad:
* frame. Note this should be called early on in constructing
* a frame as it sets i_fc[1]; other bits can then be or'd in.
*/
-static void
+void
ieee80211_send_setup(
struct ieee80211_node *ni,
struct mbuf *m,
@@ -465,13 +495,12 @@ ieee80211_send_setup(
const uint8_t bssid[IEEE80211_ADDR_LEN])
{
#define WH4(wh) ((struct ieee80211_frame_addr4 *)wh)
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *);
ieee80211_seq seqno;
wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | type;
if ((type & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_DATA) {
- struct ieee80211vap *vap = ni->ni_vap;
-
switch (vap->iv_opmode) {
case IEEE80211_M_STA:
wh->i_fc[1] = IEEE80211_FC1_DIR_TODS;
@@ -499,6 +528,25 @@ ieee80211_send_setup(
IEEE80211_ADDR_COPY(wh->i_addr3, da);
IEEE80211_ADDR_COPY(WH4(wh)->i_addr4, sa);
break;
+ case IEEE80211_M_MBSS:
+#ifdef IEEE80211_SUPPORT_MESH
+ /* XXX add support for proxied addresses */
+ if (IEEE80211_IS_MULTICAST(da)) {
+ wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS;
+ /* XXX next hop */
+ IEEE80211_ADDR_COPY(wh->i_addr1, da);
+ IEEE80211_ADDR_COPY(wh->i_addr2,
+ vap->iv_myaddr);
+ } else {
+ wh->i_fc[1] = IEEE80211_FC1_DIR_DSTODS;
+ IEEE80211_ADDR_COPY(wh->i_addr1, da);
+ IEEE80211_ADDR_COPY(wh->i_addr2,
+ vap->iv_myaddr);
+ IEEE80211_ADDR_COPY(wh->i_addr3, da);
+ IEEE80211_ADDR_COPY(WH4(wh)->i_addr4, sa);
+ }
+#endif
+ break;
case IEEE80211_M_MONITOR: /* NB: to quiet compiler */
break;
}
@@ -506,7 +554,12 @@ ieee80211_send_setup(
wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
IEEE80211_ADDR_COPY(wh->i_addr1, da);
IEEE80211_ADDR_COPY(wh->i_addr2, sa);
- IEEE80211_ADDR_COPY(wh->i_addr3, bssid);
+#ifdef IEEE80211_SUPPORT_MESH
+ if (vap->iv_opmode == IEEE80211_M_MBSS)
+ IEEE80211_ADDR_COPY(wh->i_addr3, sa);
+ else
+#endif
+ IEEE80211_ADDR_COPY(wh->i_addr3, bssid);
}
*(uint16_t *)&wh->i_dur[0] = 0;
@@ -932,12 +985,18 @@ ieee80211_encap(struct ieee80211vap *vap, struct ieee80211_node *ni,
{
#define WH4(wh) ((struct ieee80211_frame_addr4 *)(wh))
struct ieee80211com *ic = ni->ni_ic;
+#ifdef IEEE80211_SUPPORT_MESH
+ struct ieee80211_mesh_state *ms = vap->iv_mesh;
+ struct ieee80211_meshcntl_ae11 *mc;
+#endif
struct ether_header eh;
struct ieee80211_frame *wh;
struct ieee80211_key *key;
struct llc *llc;
int hdrsize, hdrspace, datalen, addqos, txfrag, is4addr;
ieee80211_seq seqno;
+ int meshhdrsize, meshae;
+ uint8_t *qos;
/*
* Copy existing Ethernet header to a safe place. The
@@ -990,17 +1049,56 @@ ieee80211_encap(struct ieee80211vap *vap, struct ieee80211_node *ni,
hdrsize = sizeof(struct ieee80211_qosframe);
else
hdrsize = sizeof(struct ieee80211_frame);
- /*
- * 4-address frames need to be generated for:
- * o packets sent through a WDS vap (IEEE80211_M_WDS)
- * o packets sent through a vap marked for relaying
- * (e.g. a station operating with dynamic WDS)
- */
- is4addr = vap->iv_opmode == IEEE80211_M_WDS ||
- ((vap->iv_flags_ext & IEEE80211_FEXT_4ADDR) &&
- !IEEE80211_ADDR_EQ(eh.ether_shost, vap->iv_myaddr));
- if (is4addr)
- hdrsize += IEEE80211_ADDR_LEN;
+#ifdef IEEE80211_SUPPORT_MESH
+ if (vap->iv_opmode == IEEE80211_M_MBSS) {
+ /*
+ * Mesh data frames are encapsulated according to the
+ * rules of Section 11B.8.5 (p.139 of D3.0 spec).
+ * o Group Addressed data (aka multicast) originating
+ * at the local sta are sent w/ 3-address format and
+ * address extension mode 00
+ * o Individually Addressed data (aka unicast) originating
+ * at the local sta are sent w/ 4-address format and
+ * address extension mode 00
+ * o Group Addressed data forwarded from a non-mesh sta are
+ * sent w/ 3-address format and address extension mode 01
+ * o Individually Address data from another sta are sent
+ * w/ 4-address format and address extension mode 10
+ */
+ is4addr = 0; /* NB: don't use, disable */
+ meshhdrsize = sizeof(struct ieee80211_meshcntl);
+ /* XXX defines for AE modes */
+ /* XXX not right, need to check if from non-mesh-sta */
+ if (IEEE80211_ADDR_EQ(eh.ether_shost, vap->iv_myaddr)) {
+ if (!IEEE80211_IS_MULTICAST(eh.ether_dhost)) {
+ hdrsize += IEEE80211_ADDR_LEN;
+ meshae = 0;
+ } else
+ meshae = 4; /* NB: pseudo */
+ } else if (IEEE80211_IS_MULTICAST(eh.ether_dhost)) {
+ meshae = 1;
+ meshhdrsize += 2*IEEE80211_ADDR_LEN;
+ } else {
+ meshae = 2;
+ meshhdrsize += 3*IEEE80211_ADDR_LEN;
+ }
+ } else {
+#endif
+ /*
+ * 4-address frames need to be generated for:
+ * o packets sent through a WDS vap (IEEE80211_M_WDS)
+ * o packets sent through a vap marked for relaying
+ * (e.g. a station operating with dynamic WDS)
+ */
+ is4addr = vap->iv_opmode == IEEE80211_M_WDS ||
+ ((vap->iv_flags_ext & IEEE80211_FEXT_4ADDR) &&
+ !IEEE80211_ADDR_EQ(eh.ether_shost, vap->iv_myaddr));
+ if (is4addr)
+ hdrsize += IEEE80211_ADDR_LEN;
+ meshhdrsize = meshae = 0;
+#ifdef IEEE80211_SUPPORT_MESH
+ }
+#endif
/*
* Honor driver DATAPAD requirement.
*/
@@ -1013,7 +1111,7 @@ ieee80211_encap(struct ieee80211vap *vap, struct ieee80211_node *ni,
/*
* Normal frame.
*/
- m = ieee80211_mbuf_adjust(vap, hdrspace, key, m);
+ m = ieee80211_mbuf_adjust(vap, hdrspace + meshhdrsize, key, m);
if (m == NULL) {
/* NB: ieee80211_mbuf_adjust handles msgs+statistics */
goto bad;
@@ -1032,14 +1130,14 @@ ieee80211_encap(struct ieee80211vap *vap, struct ieee80211_node *ni,
/*
* Aggregated frame.
*/
- m = ieee80211_ff_encap(vap, m, hdrspace, key);
+ m = ieee80211_ff_encap(vap, m, hdrspace + meshhdrsize, key);
if (m == NULL)
#endif
goto bad;
}
datalen = m->m_pkthdr.len; /* NB: w/o 802.11 header */
- M_PREPEND(m, hdrspace, M_DONTWAIT);
+ M_PREPEND(m, hdrspace + meshhdrsize, M_DONTWAIT);
if (m == NULL) {
vap->iv_stats.is_tx_nobuf++;
goto bad;
@@ -1047,6 +1145,7 @@ ieee80211_encap(struct ieee80211vap *vap, struct ieee80211_node *ni,
wh = mtod(m, struct ieee80211_frame *);
wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_DATA;
*(uint16_t *)wh->i_dur = 0;
+ qos = NULL; /* NB: quiet compiler */
if (is4addr) {
wh->i_fc[1] = IEEE80211_FC1_DIR_DSTODS;
IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr);
@@ -1077,19 +1176,73 @@ ieee80211_encap(struct ieee80211vap *vap, struct ieee80211_node *ni,
IEEE80211_ADDR_COPY(wh->i_addr2, ni->ni_bssid);
IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_shost);
break;
- case IEEE80211_M_MONITOR:
+#ifdef IEEE80211_SUPPORT_MESH
+ case IEEE80211_M_MBSS:
+ /* NB: offset by hdrspace to deal with DATAPAD */
+ mc = (struct ieee80211_meshcntl_ae11 *)
+ (mtod(m, uint8_t *) + hdrspace);
+ switch (meshae) {
+ case 0: /* ucast, no proxy */
+ wh->i_fc[1] = IEEE80211_FC1_DIR_DSTODS;
+ IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr);
+ IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr);
+ IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_dhost);
+ IEEE80211_ADDR_COPY(WH4(wh)->i_addr4, eh.ether_shost);
+ mc->mc_flags = 0;
+ qos = ((struct ieee80211_qosframe_addr4 *) wh)->i_qos;
+ break;
+ case 4: /* mcast, no proxy */
+ wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS;
+ IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost);
+ IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr);
+ IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_shost);
+ mc->mc_flags = 0; /* NB: AE is really 0 */
+ qos = ((struct ieee80211_qosframe *) wh)->i_qos;
+ break;
+ case 1: /* mcast, proxy */
+ wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS;
+ IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost);
+ IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr);
+ /* XXX not right, need MeshSA */
+ IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_shost);
+ mc->mc_flags = 1;
+ IEEE80211_ADDR_COPY(mc->mc_addr4, eh.ether_shost);
+ qos = ((struct ieee80211_qosframe *) wh)->i_qos;
+ break;
+ case 2: /* ucast, proxy */
+ wh->i_fc[1] = IEEE80211_FC1_DIR_DSTODS;
+ IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr);
+ IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr);
+ /* XXX not right, need MeshDA+MeshSA */
+ IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_dhost);
+ IEEE80211_ADDR_COPY(WH4(wh)->i_addr4, eh.ether_shost);
+ mc->mc_flags = 2;
+ IEEE80211_ADDR_COPY(mc->mc_addr5, eh.ether_shost);
+ IEEE80211_ADDR_COPY(mc->mc_addr6, eh.ether_shost);
+ qos = ((struct ieee80211_qosframe_addr4 *) wh)->i_qos;
+ break;
+ default:
+ KASSERT(0, ("meshae %d", meshae));
+ break;
+ }
+ mc->mc_ttl = ms->ms_ttl;
+ ms->ms_seq++;
+ LE_WRITE_4(mc->mc_seq, ms->ms_seq);
+ break;
+#endif
case IEEE80211_M_WDS: /* NB: is4addr should always be true */
+ default:
goto bad;
}
if (m->m_flags & M_MORE_DATA)
wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA;
if (addqos) {
- uint8_t *qos;
int ac, tid;
if (is4addr) {
qos = ((struct ieee80211_qosframe_addr4 *) wh)->i_qos;
- } else
+ /* NB: mesh case handled earlier */
+ } else if (vap->iv_opmode != IEEE80211_M_MBSS)
qos = ((struct ieee80211_qosframe *) wh)->i_qos;
ac = M_WME_GETAC(m);
/* map from access class/queue to 11e header priorty value */
@@ -1125,6 +1278,7 @@ ieee80211_encap(struct ieee80211vap *vap, struct ieee80211_node *ni,
M_SEQNO_SET(m, seqno);
}
+
/* check if xmit fragmentation is required */
txfrag = (m->m_pkthdr.len > vap->iv_fragthreshold &&
!IEEE80211_IS_MULTICAST(wh->i_addr1) &&
@@ -1271,7 +1425,7 @@ bad:
/*
* Add a supported rates element id to a frame.
*/
-static uint8_t *
+uint8_t *
ieee80211_add_rates(uint8_t *frm, const struct ieee80211_rateset *rs)
{
int nrates;
@@ -1288,7 +1442,7 @@ ieee80211_add_rates(uint8_t *frm, const struct ieee80211_rateset *rs)
/*
* Add an extended supported rates element id to a frame.
*/
-static uint8_t *
+uint8_t *
ieee80211_add_xrates(uint8_t *frm, const struct ieee80211_rateset *rs)
{
/*
@@ -1343,10 +1497,9 @@ ieee80211_add_erp(uint8_t *frm, struct ieee80211com *ic)
static uint8_t *
ieee80211_add_cfparms(uint8_t *frm, struct ieee80211com *ic)
{
-#define ADDSHORT(frm, v) do { \
- frm[0] = (v) & 0xff; \
- frm[1] = (v) >> 8; \
- frm += 2; \
+#define ADDSHORT(frm, v) do { \
+ LE_WRITE_2(frm, v); \
+ frm += 2; \
} while (0)
*frm++ = IEEE80211_ELEMID_CFPARMS;
*frm++ = 6;
@@ -1399,10 +1552,9 @@ static uint8_t *
ieee80211_add_wme_param(uint8_t *frm, struct ieee80211_wme_state *wme)
{
#define SM(_v, _f) (((_v) << _f##_S) & _f)
-#define ADDSHORT(frm, v) do { \
- frm[0] = (v) & 0xff; \
- frm[1] = (v) >> 8; \
- frm += 2; \
+#define ADDSHORT(frm, v) do { \
+ LE_WRITE_2(frm, v); \
+ frm += 2; \
} while (0)
/* NB: this works 'cuz a param has an info at the front */
static const struct ieee80211_wme_info param = {
@@ -1649,8 +1801,8 @@ ieee80211_send_probereq(struct ieee80211_node *ni,
/*
* Calculate capability information for mgt frames.
*/
-static uint16_t
-getcapinfo(struct ieee80211vap *vap, struct ieee80211_channel *chan)
+uint16_t
+ieee80211_getcapinfo(struct ieee80211vap *vap, struct ieee80211_channel *chan)
{
struct ieee80211com *ic = vap->iv_ic;
uint16_t capinfo;
@@ -1945,7 +2097,7 @@ ieee80211_send_mgmt(struct ieee80211_node *ni, int type, int arg)
if (m == NULL)
senderr(ENOMEM, is_tx_nobuf);
- capinfo = getcapinfo(vap, bss->ni_chan);
+ capinfo = ieee80211_getcapinfo(vap, bss->ni_chan);
*(uint16_t *)frm = htole16(capinfo);
frm += 2;
@@ -2060,6 +2212,8 @@ ieee80211_alloc_proberesp(struct ieee80211_node *bss, int legacy)
* [tlv] Vendor OUI HT information (optional)
* [tlv] Atheros capabilities
* [tlv] AppIE's (optional)
+ * [tlv] Mesh ID (MBSS)
+ * [tlv] Mesh Conf (MBSS)
*/
m = ieee80211_getmgtframe(&frm,
ic->ic_headroom + sizeof(struct ieee80211_frame),
@@ -2084,6 +2238,10 @@ ieee80211_alloc_proberesp(struct ieee80211_node *bss, int legacy)
#ifdef IEEE80211_SUPPORT_SUPERG
+ sizeof(struct ieee80211_ath_ie)
#endif
+#ifdef IEEE80211_SUPPORT_MESH
+ + 2 + IEEE80211_MESHID_LEN
+ + sizeof(struct ieee80211_meshconf_ie)
+#endif
+ (vap->iv_appie_proberesp != NULL ?
vap->iv_appie_proberesp->ie_len : 0)
);
@@ -2096,7 +2254,7 @@ ieee80211_alloc_proberesp(struct ieee80211_node *bss, int legacy)
frm += 8;
*(uint16_t *)frm = htole16(bss->ni_intval);
frm += 2;
- capinfo = getcapinfo(vap, bss->ni_chan);
+ capinfo = ieee80211_getcapinfo(vap, bss->ni_chan);
*(uint16_t *)frm = htole16(capinfo);
frm += 2;
@@ -2173,6 +2331,12 @@ ieee80211_alloc_proberesp(struct ieee80211_node *bss, int legacy)
#endif
if (vap->iv_appie_proberesp != NULL)
frm = add_appie(frm, vap->iv_appie_proberesp);
+#ifdef IEEE80211_SUPPORT_MESH
+ if (vap->iv_opmode == IEEE80211_M_MBSS) {
+ frm = ieee80211_add_meshid(frm, vap);
+ frm = ieee80211_add_meshconf(frm, vap);
+ }
+#endif
m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
return m;
@@ -2365,6 +2529,8 @@ ieee80211_beacon_construct(struct mbuf *m, uint8_t *frm,
* [tlv] Vendor OUI HT information (optional)
* [tlv] Atheros capabilities (optional)
* [tlv] TDMA parameters (optional)
+ * [tlv] Mesh ID (MBSS)
+ * [tlv] Mesh Conf (MBSS)
* [tlv] application data (optional)
*/
@@ -2374,7 +2540,7 @@ ieee80211_beacon_construct(struct mbuf *m, uint8_t *frm,
frm += 8;
*(uint16_t *)frm = htole16(ni->ni_intval);
frm += 2;
- capinfo = getcapinfo(vap, ni->ni_chan);
+ capinfo = ieee80211_getcapinfo(vap, ni->ni_chan);
bo->bo_caps = (uint16_t *)frm;
*(uint16_t *)frm = htole16(capinfo);
frm += 2;
@@ -2401,7 +2567,9 @@ ieee80211_beacon_construct(struct mbuf *m, uint8_t *frm,
*frm++ = 2;
*frm++ = 0; *frm++ = 0; /* TODO: ATIM window */
bo->bo_tim_len = 0;
- } else if (vap->iv_opmode == IEEE80211_M_HOSTAP) {
+ } else if (vap->iv_opmode == IEEE80211_M_HOSTAP ||
+ vap->iv_opmode == IEEE80211_M_MBSS) {
+ /* TIM IE is the same for Mesh and Hostap */
struct ieee80211_tim_ie *tie = (struct ieee80211_tim_ie *) frm;
tie->tim_ie = IEEE80211_ELEMID_TIM;
@@ -2471,6 +2639,12 @@ ieee80211_beacon_construct(struct mbuf *m, uint8_t *frm,
bo->bo_appie_len = vap->iv_appie_beacon->ie_len;
frm = add_appie(frm, vap->iv_appie_beacon);
}
+#ifdef IEEE80211_SUPPORT_MESH
+ if (vap->iv_opmode == IEEE80211_M_MBSS) {
+ frm = ieee80211_add_meshid(frm, vap);
+ frm = ieee80211_add_meshconf(frm, vap);
+ }
+#endif
bo->bo_tim_trailer_len = frm - bo->bo_tim_trailer;
bo->bo_csa_trailer_len = frm - bo->bo_csa;
m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
@@ -2515,6 +2689,8 @@ ieee80211_beacon_alloc(struct ieee80211_node *ni,
* [tlv] WPA parameters
* [tlv] WME parameters
* [tlv] TDMA parameters (optional)
+ * [tlv] Mesh ID (MBSS)
+ * [tlv] Mesh Conf (MBSS)
* [tlv] application data (optional)
* NB: we allocate the max space required for the TIM bitmap.
* XXX how big is this?
@@ -2546,6 +2722,10 @@ ieee80211_beacon_alloc(struct ieee80211_node *ni,
+ (vap->iv_caps & IEEE80211_C_TDMA ? /* TDMA */
sizeof(struct ieee80211_tdma_param) : 0)
#endif
+#ifdef IEEE80211_SUPPORT_MESH
+ + 2 + ni->ni_meshidlen
+ + sizeof(struct ieee80211_meshconf_ie)
+#endif
+ IEEE80211_MAX_APPIE
;
m = ieee80211_getmgtframe(&frm,
@@ -2567,7 +2747,13 @@ ieee80211_beacon_alloc(struct ieee80211_node *ni,
*(uint16_t *)wh->i_dur = 0;
IEEE80211_ADDR_COPY(wh->i_addr1, ifp->if_broadcastaddr);
IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr);
- IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid);
+#ifdef IEEE80211_SUPPORT_MESH
+ if (vap->iv_opmode == IEEE80211_M_MBSS) {
+ static const uint8_t zerobssid[IEEE80211_ADDR_LEN];
+ IEEE80211_ADDR_COPY(wh->i_addr3, zerobssid);
+ } else
+#endif
+ IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid);
*(uint16_t *)wh->i_seq = 0;
return m;
@@ -2616,7 +2802,7 @@ ieee80211_beacon_update(struct ieee80211_node *ni,
}
/* XXX faster to recalculate entirely or just changes? */
- capinfo = getcapinfo(vap, ni->ni_chan);
+ capinfo = ieee80211_getcapinfo(vap, ni->ni_chan);
*bo->bo_caps = htole16(capinfo);
if (vap->iv_flags & IEEE80211_F_WME) {
@@ -2672,7 +2858,8 @@ ieee80211_beacon_update(struct ieee80211_node *ni,
ieee80211_tdma_update_beacon(vap, bo);
}
#endif
- if (vap->iv_opmode == IEEE80211_M_HOSTAP) { /* NB: no IBSS support*/
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP ||
+ vap->iv_opmode == IEEE80211_M_MBSS) { /* NB: no IBSS support*/
struct ieee80211_tim_ie *tie =
(struct ieee80211_tim_ie *) bo->bo_tim;
if (isset(bo->bo_flags, IEEE80211_BEACON_TIM)) {
diff --git a/sys/net80211/ieee80211_proto.c b/sys/net80211/ieee80211_proto.c
index 9f80cdf..9aabfcf 100644
--- a/sys/net80211/ieee80211_proto.c
+++ b/sys/net80211/ieee80211_proto.c
@@ -50,6 +50,9 @@ __FBSDID("$FreeBSD$");
#include <net80211/ieee80211_sta.h>
#include <net80211/ieee80211_hostap.h>
#include <net80211/ieee80211_wds.h>
+#ifdef IEEE80211_SUPPORT_MESH
+#include <net80211/ieee80211_mesh.h>
+#endif
#include <net80211/ieee80211_monitor.h>
#include <net80211/ieee80211_input.h>
@@ -75,7 +78,8 @@ const char *ieee80211_opmode_name[IEEE80211_OPMODE_MAX] = {
"WDS", /* IEEE80211_M_WDS */
"AHDEMO", /* IEEE80211_M_AHDEMO */
"HOSTAP", /* IEEE80211_M_HOSTAP */
- "MONITOR" /* IEEE80211_M_MONITOR */
+ "MONITOR", /* IEEE80211_M_MONITOR */
+ "MBSS" /* IEEE80211_M_MBSS */
};
const char *ieee80211_state_name[IEEE80211_S_MAX] = {
"INIT", /* IEEE80211_S_INIT */
@@ -152,6 +156,9 @@ ieee80211_proto_attach(struct ieee80211com *ic)
ieee80211_sta_attach(ic);
ieee80211_wds_attach(ic);
ieee80211_hostap_attach(ic);
+#ifdef IEEE80211_SUPPORT_MESH
+ ieee80211_mesh_attach(ic);
+#endif
ieee80211_monitor_attach(ic);
}
@@ -159,6 +166,9 @@ void
ieee80211_proto_detach(struct ieee80211com *ic)
{
ieee80211_monitor_detach(ic);
+#ifdef IEEE80211_SUPPORT_MESH
+ ieee80211_mesh_detach(ic);
+#endif
ieee80211_hostap_detach(ic);
ieee80211_wds_detach(ic);
ieee80211_adhoc_detach(ic);
@@ -1456,7 +1466,8 @@ ieee80211_csa_startswitch(struct ieee80211com *ic,
ic->ic_flags |= IEEE80211_F_CSAPENDING;
TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
if (vap->iv_opmode == IEEE80211_M_HOSTAP ||
- vap->iv_opmode == IEEE80211_M_IBSS)
+ vap->iv_opmode == IEEE80211_M_IBSS ||
+ vap->iv_opmode == IEEE80211_M_MBSS)
ieee80211_beacon_notify(vap, IEEE80211_BEACON_CSA);
/* switch to CSA state to block outbound traffic */
if (vap->iv_state == IEEE80211_S_RUN)
diff --git a/sys/net80211/ieee80211_proto.h b/sys/net80211/ieee80211_proto.h
index de043c3..9bfbc61 100644
--- a/sys/net80211/ieee80211_proto.h
+++ b/sys/net80211/ieee80211_proto.h
@@ -71,6 +71,9 @@ int ieee80211_raw_xmit(struct ieee80211_node *, struct mbuf *,
const struct ieee80211_bpf_params *);
int ieee80211_output(struct ifnet *, struct mbuf *,
struct sockaddr *, struct route *ro);
+void ieee80211_send_setup(struct ieee80211_node *, struct mbuf *, int, int,
+ const uint8_t [IEEE80211_ADDR_LEN], const uint8_t [IEEE80211_ADDR_LEN],
+ const uint8_t [IEEE80211_ADDR_LEN]);
void ieee80211_start(struct ifnet *);
int ieee80211_send_nulldata(struct ieee80211_node *);
int ieee80211_classify(struct ieee80211_node *, struct mbuf *m);
@@ -102,6 +105,11 @@ struct mbuf *ieee80211_alloc_rts(struct ieee80211com *ic,
struct mbuf *ieee80211_alloc_cts(struct ieee80211com *,
const uint8_t [IEEE80211_ADDR_LEN], uint16_t);
+uint8_t *ieee80211_add_rates(uint8_t *, const struct ieee80211_rateset *);
+uint8_t *ieee80211_add_xrates(uint8_t *, const struct ieee80211_rateset *);
+uint16_t ieee80211_getcapinfo(struct ieee80211vap *,
+ struct ieee80211_channel *);
+
void ieee80211_reset_erp(struct ieee80211com *);
void ieee80211_set_shortslottime(struct ieee80211com *, int onoff);
int ieee80211_iserp_rateset(const struct ieee80211_rateset *);
diff --git a/sys/net80211/ieee80211_scan.c b/sys/net80211/ieee80211_scan.c
index 951e762..ab30c29 100644
--- a/sys/net80211/ieee80211_scan.c
+++ b/sys/net80211/ieee80211_scan.c
@@ -209,6 +209,7 @@ static const char *scan_modnames[IEEE80211_OPMODE_MAX] = {
"wlan_scan_sta", /* IEEE80211_M_AHDEMO */
"wlan_scan_ap", /* IEEE80211_M_HOSTAP */
"wlan_scan_monitor", /* IEEE80211_M_MONITOR */
+ "wlan_scan_sta", /* IEEE80211_M_MBSS */
};
static const struct ieee80211_scanner *scanners[IEEE80211_OPMODE_MAX];
diff --git a/sys/net80211/ieee80211_scan.h b/sys/net80211/ieee80211_scan.h
index ac44474..4c5e869 100644
--- a/sys/net80211/ieee80211_scan.h
+++ b/sys/net80211/ieee80211_scan.h
@@ -213,6 +213,8 @@ struct ieee80211_scanparams {
uint8_t *ath;
uint8_t *tdma;
uint8_t *csa;
+ uint8_t *meshid;
+ uint8_t *meshconf;
uint8_t *spare[3];
};
@@ -242,6 +244,7 @@ struct ieee80211_scan_entry {
int8_t se_rssi; /* avg'd recv ssi */
int8_t se_noise; /* noise floor */
uint8_t se_cc[2]; /* captured country code */
+ uint8_t se_meshid[2+IEEE80211_MESHID_LEN];
struct ieee80211_ies se_ies; /* captured ie's */
u_int se_age; /* age of entry (0 on create) */
};
diff --git a/sys/net80211/ieee80211_scan_sta.c b/sys/net80211/ieee80211_scan_sta.c
index 367ecce..a76dd3c 100644
--- a/sys/net80211/ieee80211_scan_sta.c
+++ b/sys/net80211/ieee80211_scan_sta.c
@@ -48,6 +48,9 @@ __FBSDID("$FreeBSD$");
#ifdef IEEE80211_SUPPORT_TDMA
#include <net80211/ieee80211_tdma.h>
#endif
+#ifdef IEEE80211_SUPPORT_MESH
+#include <net80211/ieee80211_mesh.h>
+#endif
#include <net/bpf.h>
@@ -112,21 +115,23 @@ static void sta_flush_table(struct sta_table *);
* contents explains why. The following flags are or'd to to this
* mask and can be used to figure out why the entry was rejected.
*/
-#define MATCH_CHANNEL 0x0001 /* channel mismatch */
-#define MATCH_CAPINFO 0x0002 /* capabilities mismatch, e.g. no ess */
-#define MATCH_PRIVACY 0x0004 /* privacy mismatch */
-#define MATCH_RATE 0x0008 /* rate set mismatch */
-#define MATCH_SSID 0x0010 /* ssid mismatch */
-#define MATCH_BSSID 0x0020 /* bssid mismatch */
-#define MATCH_FAILS 0x0040 /* too many failed auth attempts */
-#define MATCH_NOTSEEN 0x0080 /* not seen in recent scans */
-#define MATCH_RSSI 0x0100 /* rssi deemed too low to use */
-#define MATCH_CC 0x0200 /* country code mismatch */
-#define MATCH_TDMA_NOIE 0x0400 /* no TDMA ie */
-#define MATCH_TDMA_NOTMASTER 0x0800 /* not TDMA master */
-#define MATCH_TDMA_NOSLOT 0x1000 /* all TDMA slots occupied */
-#define MATCH_TDMA_LOCAL 0x2000 /* local address */
-#define MATCH_TDMA_VERSION 0x4000 /* protocol version mismatch */
+#define MATCH_CHANNEL 0x00001 /* channel mismatch */
+#define MATCH_CAPINFO 0x00002 /* capabilities mismatch, e.g. no ess */
+#define MATCH_PRIVACY 0x00004 /* privacy mismatch */
+#define MATCH_RATE 0x00008 /* rate set mismatch */
+#define MATCH_SSID 0x00010 /* ssid mismatch */
+#define MATCH_BSSID 0x00020 /* bssid mismatch */
+#define MATCH_FAILS 0x00040 /* too many failed auth attempts */
+#define MATCH_NOTSEEN 0x00080 /* not seen in recent scans */
+#define MATCH_RSSI 0x00100 /* rssi deemed too low to use */
+#define MATCH_CC 0x00200 /* country code mismatch */
+#define MATCH_TDMA_NOIE 0x00400 /* no TDMA ie */
+#define MATCH_TDMA_NOTMASTER 0x00800 /* not TDMA master */
+#define MATCH_TDMA_NOSLOT 0x01000 /* all TDMA slots occupied */
+#define MATCH_TDMA_LOCAL 0x02000 /* local address */
+#define MATCH_TDMA_VERSION 0x04000 /* protocol version mismatch */
+#define MATCH_MESH_NOID 0x10000 /* no MESHID ie */
+#define MATCH_MESHID 0x20000 /* meshid mismatch */
static int match_bss(struct ieee80211vap *,
const struct ieee80211_scan_state *, struct sta_entry *, int);
static void adhoc_age(struct ieee80211_scan_state *);
@@ -139,6 +144,10 @@ isocmp(const uint8_t cc1[], const uint8_t cc2[])
/* number of references from net80211 layer */
static int nrefs = 0;
+/*
+ * Module glue.
+ */
+IEEE80211_SCANNER_MODULE(sta, 1);
/*
* Attach prior to any scanning work.
@@ -281,6 +290,10 @@ found:
memcpy(ise->se_tstamp.data, sp->tstamp, sizeof(ise->se_tstamp));
ise->se_intval = sp->bintval;
ise->se_capinfo = sp->capinfo;
+#ifdef IEEE80211_SUPPORT_MESH
+ if (sp->meshid != NULL && sp->meshid[1] != 0)
+ memcpy(ise->se_meshid, sp->meshid, 2+sp->meshid[1]);
+#endif
/*
* Beware of overriding se_chan for frames seen
* off-channel; this can cause us to attempt an
@@ -895,6 +908,12 @@ back:
#undef RV
}
+static __inline int
+match_id(const uint8_t *ie, const uint8_t *val, int len)
+{
+ return (ie[1] == len && memcmp(ie+2, val, len) == 0);
+}
+
static int
match_ssid(const uint8_t *ie,
int nssid, const struct ieee80211_scan_ssid ssids[])
@@ -902,8 +921,7 @@ match_ssid(const uint8_t *ie,
int i;
for (i = 0; i < nssid; i++) {
- if (ie[1] == ssids[i].len &&
- memcmp(ie+2, ssids[i].ssid, ie[1]) == 0)
+ if (match_id(ie, ssids[i].ssid, ssids[i].len))
return 1;
}
return 0;
@@ -986,7 +1004,22 @@ match_bss(struct ieee80211vap *vap,
#endif
}
#endif /* IEEE80211_SUPPORT_TDMA */
+#ifdef IEEE80211_SUPPORT_MESH
+ } else if (vap->iv_opmode == IEEE80211_M_MBSS) {
+ const struct ieee80211_mesh_state *ms = vap->iv_mesh;
+ /*
+ * Mesh nodes have IBSS & ESS bits in capinfo turned off
+ * and two special ie's that must be present.
+ */
+ if (se->se_capinfo & (IEEE80211_CAPINFO_IBSS|IEEE80211_CAPINFO_ESS))
+ fail |= MATCH_CAPINFO;
+ else if (se->se_meshid == NULL)
+ fail |= MATCH_MESH_NOID;
+ else if (ms->ms_idlen != 0 &&
+ match_id(se->se_meshid, ms->ms_id, ms->ms_idlen))
+ fail |= MATCH_MESHID;
} else {
+#endif
if ((se->se_capinfo & IEEE80211_CAPINFO_ESS) == 0)
fail |= MATCH_CAPINFO;
/*
@@ -1070,6 +1103,7 @@ match_bss(struct ieee80211vap *vap,
fail & MATCH_TDMA_NOSLOT ? 'f' :
fail & MATCH_TDMA_LOCAL ? 'l' :
#endif
+ fail & MATCH_MESH_NOID ? 'm' :
fail ? '-' : '+', ether_sprintf(se->se_macaddr));
printf(" %s%c", ether_sprintf(se->se_bssid),
fail & MATCH_BSSID ? '!' : ' ');
@@ -1087,7 +1121,7 @@ match_bss(struct ieee80211vap *vap,
"wep" : "no",
fail & MATCH_PRIVACY ? '!' : ' ');
ieee80211_print_essid(se->se_ssid+2, se->se_ssid[1]);
- printf("%s\n", fail & MATCH_SSID ? "!" : "");
+ printf("%s\n", fail & (MATCH_SSID | MATCH_MESHID) ? "!" : "");
}
#endif
return fail;
@@ -1415,6 +1449,7 @@ static const struct ieee80211_scanner sta_default = {
.scan_assoc_fail = sta_assoc_fail,
.scan_assoc_success = sta_assoc_success,
};
+IEEE80211_SCANNER_ALG(sta, IEEE80211_M_STA, sta_default);
/*
* Adhoc mode-specific support.
@@ -1514,7 +1549,8 @@ adhoc_pick_bss(struct ieee80211_scan_state *ss, struct ieee80211vap *vap)
struct ieee80211_channel *chan;
KASSERT(vap->iv_opmode == IEEE80211_M_IBSS ||
- vap->iv_opmode == IEEE80211_M_AHDEMO,
+ vap->iv_opmode == IEEE80211_M_AHDEMO ||
+ vap->iv_opmode == IEEE80211_M_MBSS,
("wrong opmode %u", vap->iv_opmode));
if (st->st_newscan) {
@@ -1630,6 +1666,8 @@ static const struct ieee80211_scanner adhoc_default = {
.scan_assoc_fail = sta_assoc_fail,
.scan_assoc_success = sta_assoc_success,
};
+IEEE80211_SCANNER_ALG(ibss, IEEE80211_M_IBSS, adhoc_default);
+IEEE80211_SCANNER_ALG(ahdemo, IEEE80211_M_AHDEMO, adhoc_default);
static void
ap_force_promisc(struct ieee80211com *ic)
@@ -1781,12 +1819,108 @@ static const struct ieee80211_scanner ap_default = {
.scan_assoc_success = sta_assoc_success,
.scan_assoc_fail = sta_assoc_fail,
};
+IEEE80211_SCANNER_ALG(ap, IEEE80211_M_HOSTAP, ap_default);
+#ifdef IEEE80211_SUPPORT_MESH
/*
- * Module glue.
+ * Pick an mbss network to join or find a channel
+ * to use to start an mbss network.
*/
-IEEE80211_SCANNER_MODULE(sta, 1);
-IEEE80211_SCANNER_ALG(sta, IEEE80211_M_STA, sta_default);
-IEEE80211_SCANNER_ALG(ibss, IEEE80211_M_IBSS, adhoc_default);
-IEEE80211_SCANNER_ALG(ahdemo, IEEE80211_M_AHDEMO, adhoc_default);
-IEEE80211_SCANNER_ALG(ap, IEEE80211_M_HOSTAP, ap_default);
+static int
+mesh_pick_bss(struct ieee80211_scan_state *ss, struct ieee80211vap *vap)
+{
+ struct sta_table *st = ss->ss_priv;
+ struct ieee80211_mesh_state *ms = vap->iv_mesh;
+ struct sta_entry *selbs;
+ struct ieee80211_channel *chan;
+
+ KASSERT(vap->iv_opmode == IEEE80211_M_MBSS,
+ ("wrong opmode %u", vap->iv_opmode));
+
+ if (st->st_newscan) {
+ sta_update_notseen(st);
+ st->st_newscan = 0;
+ }
+ if (ss->ss_flags & IEEE80211_SCAN_NOPICK) {
+ /*
+ * Manual/background scan, don't select+join the
+ * bss, just return. The scanning framework will
+ * handle notification that this has completed.
+ */
+ ss->ss_flags &= ~IEEE80211_SCAN_NOPICK;
+ return 1;
+ }
+ /*
+ * Automatic sequencing; look for a candidate and
+ * if found join the network.
+ */
+ /* NB: unlocked read should be ok */
+ if (TAILQ_FIRST(&st->st_entry) == NULL) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
+ "%s: no scan candidate\n", __func__);
+ if (ss->ss_flags & IEEE80211_SCAN_NOJOIN)
+ return 0;
+notfound:
+ if (ms->ms_idlen != 0) {
+ /*
+ * No existing mbss network to join and we have
+ * a meshid; start one up. If no channel was
+ * specified, try to select a channel.
+ */
+ if (vap->iv_des_chan == IEEE80211_CHAN_ANYC ||
+ IEEE80211_IS_CHAN_RADAR(vap->iv_des_chan)) {
+ struct ieee80211com *ic = vap->iv_ic;
+
+ chan = adhoc_pick_channel(ss, 0);
+ if (chan != NULL)
+ chan = ieee80211_ht_adjust_channel(ic,
+ chan, vap->iv_flags_ht);
+ } else
+ chan = vap->iv_des_chan;
+ if (chan != NULL) {
+ ieee80211_create_ibss(vap, chan);
+ return 1;
+ }
+ }
+ /*
+ * If nothing suitable was found decrement
+ * the failure counts so entries will be
+ * reconsidered the next time around. We
+ * really want to do this only for sta's
+ * where we've previously had some success.
+ */
+ sta_dec_fails(st);
+ st->st_newscan = 1;
+ return 0; /* restart scan */
+ }
+ selbs = select_bss(ss, vap, IEEE80211_MSG_SCAN);
+ if (ss->ss_flags & IEEE80211_SCAN_NOJOIN)
+ return (selbs != NULL);
+ if (selbs == NULL)
+ goto notfound;
+ chan = selbs->base.se_chan;
+ if (selbs->se_flags & STA_DEMOTE11B)
+ chan = demote11b(vap, chan);
+ if (!ieee80211_sta_join(vap, chan, &selbs->base))
+ goto notfound;
+ return 1; /* terminate scan */
+}
+
+static const struct ieee80211_scanner mesh_default = {
+ .scan_name = "default",
+ .scan_attach = sta_attach,
+ .scan_detach = sta_detach,
+ .scan_start = adhoc_start,
+ .scan_restart = sta_restart,
+ .scan_cancel = sta_cancel,
+ .scan_end = mesh_pick_bss,
+ .scan_flush = sta_flush,
+ .scan_pickchan = adhoc_pick_channel,
+ .scan_add = sta_add,
+ .scan_age = adhoc_age,
+ .scan_iterate = sta_iterate,
+ .scan_assoc_fail = sta_assoc_fail,
+ .scan_assoc_success = sta_assoc_success,
+};
+IEEE80211_SCANNER_ALG(mesh, IEEE80211_M_MBSS, mesh_default);
+#endif /* IEEE80211_SUPPORT_MESH */
diff --git a/sys/net80211/ieee80211_var.h b/sys/net80211/ieee80211_var.h
index 833c696..3d23f47 100644
--- a/sys/net80211/ieee80211_var.h
+++ b/sys/net80211/ieee80211_var.h
@@ -318,6 +318,8 @@ struct ieee80211com {
struct ieee80211_aclator;
struct ieee80211_tdma_state;
+struct ieee80211_mesh_state;
+struct ieee80211_hwmp_state;
struct ieee80211vap {
struct ifmedia iv_media; /* interface media config */
@@ -432,6 +434,8 @@ struct ieee80211vap {
void *iv_as; /* private aclator state */
struct ieee80211_tdma_state *iv_tdma; /* tdma state */
+ struct ieee80211_mesh_state *iv_mesh; /* MBSS state */
+ struct ieee80211_hwmp_state *iv_hwmp; /* HWMP state */
/* operate-mode detach hook */
void (*iv_opdetach)(struct ieee80211vap *);
@@ -588,6 +592,7 @@ MALLOC_DECLARE(M_80211_VAP);
#define IEEE80211_C_SHPREAMBLE 0x00008000 /* CAPABILITY: short preamble */
#define IEEE80211_C_MONITOR 0x00010000 /* CAPABILITY: monitor mode */
#define IEEE80211_C_DFS 0x00020000 /* CAPABILITY: DFS/radar avail*/
+#define IEEE80211_C_MBSS 0x00040000 /* CAPABILITY: MBSS available */
/* 0x7c0000 available */
#define IEEE80211_C_WPA1 0x00800000 /* CAPABILITY: WPA1 avail */
#define IEEE80211_C_WPA2 0x01000000 /* CAPABILITY: WPA2 avail */
@@ -604,12 +609,12 @@ MALLOC_DECLARE(M_80211_VAP);
#define IEEE80211_C_OPMODE \
(IEEE80211_C_STA | IEEE80211_C_IBSS | IEEE80211_C_HOSTAP | \
IEEE80211_C_AHDEMO | IEEE80211_C_MONITOR | IEEE80211_C_WDS | \
- IEEE80211_C_TDMA)
+ IEEE80211_C_TDMA | IEEE80211_C_MBSS)
#define IEEE80211_C_BITS \
"\20\1STA\002803ENCAP\7FF\10TURBOP\11IBSS\12PMGT" \
"\13HOSTAP\14AHDEMO\15SWRETRY\16TXPMGT\17SHSLOT\20SHPREAMBLE" \
- "\21MONITOR\22DFS\30WPA1\31WPA2\32BURST\33WME\34WDS\36BGSCAN" \
+ "\21MONITOR\22DFS\23MBSS\30WPA1\31WPA2\32BURST\33WME\34WDS\36BGSCAN" \
"\37TXFRAG\40TDMA"
/*
@@ -783,7 +788,7 @@ ieee80211_htchanflags(const struct ieee80211_channel *c)
* program) and/or the msgs generated by net80211. Messages are
* broken into functional classes and can be controlled with the
* wlandebug program. Certain of these msg groups are for facilities
- * that are no longer part of net80211 (e.g. IEEE80211_MSG_DOT1X).
+ * that are no longer part of net80211 (e.g. IEEE80211_MSG_DOT1XSM).
*/
#define IEEE80211_MSG_11N 0x80000000 /* 11n mode debug */
#define IEEE80211_MSG_DEBUG 0x40000000 /* IFF_DEBUG equivalent */
@@ -799,7 +804,7 @@ ieee80211_htchanflags(const struct ieee80211_channel *c)
#define IEEE80211_MSG_OUTPUT 0x00100000 /* output handling */
#define IEEE80211_MSG_STATE 0x00080000 /* state machine */
#define IEEE80211_MSG_POWER 0x00040000 /* power save handling */
-#define IEEE80211_MSG_DOT1X 0x00020000 /* 802.1x authenticator */
+#define IEEE80211_MSG_HWMP 0x00020000 /* hybrid mesh protocol */
#define IEEE80211_MSG_DOT1XSM 0x00010000 /* 802.1x state machine */
#define IEEE80211_MSG_RADIUS 0x00008000 /* 802.1x radius client */
#define IEEE80211_MSG_RADDUMP 0x00004000 /* dump 802.1x radius packets */
@@ -821,7 +826,7 @@ ieee80211_htchanflags(const struct ieee80211_channel *c)
#define IEEE80211_MSG_BITS \
"\20\2TDMA\3IOCTL\4WDS\5ACTION\6RATECTL\7ROAM\10INACT\11DOTH\12SUPERG" \
- "\13WME\14ACL\15WPA\16RADKEYS\17RADDUMP\20RADIUS\21DOT1XSM\22DOT1X" \
+ "\13WME\14ACL\15WPA\16RADKEYS\17RADDUMP\20RADIUS\21DOT1XSM\22HWMP" \
"\23POWER\24STATE\25OUTPUT\26SCAN\27AUTH\30ASSOC\31NODE\32ELEMID" \
"\33XRATE\34INPUT\35CRYPTO\36DUPMPKTS\37DEBUG\04011N"
OpenPOWER on IntegriCloud