summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sbin/ifconfig/ifconfig.887
-rw-r--r--sbin/ifconfig/ifieee80211.c427
-rw-r--r--sbin/ifconfig/ifmedia.c3
-rw-r--r--share/man/man4/ath.411
-rw-r--r--share/man/man4/mwl.411
-rw-r--r--share/man/man4/ral.45
-rw-r--r--share/man/man4/wlan.414
-rw-r--r--sys/amd64/conf/GENERIC1
-rw-r--r--sys/conf/NOTES2
-rw-r--r--sys/conf/files3
-rw-r--r--sys/conf/options1
-rw-r--r--sys/dev/ath/if_ath.c39
-rw-r--r--sys/dev/ath/if_athvar.h1
-rw-r--r--sys/dev/mwl/if_mwl.c17
-rw-r--r--sys/dev/ral/rt2560.c12
-rw-r--r--sys/dev/ral/rt2661.c9
-rw-r--r--sys/i386/conf/GENERIC1
-rw-r--r--sys/net/if_media.h2
-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
-rw-r--r--sys/pc98/conf/GENERIC1
-rw-r--r--sys/sparc64/conf/GENERIC1
-rw-r--r--tools/tools/nanobsd/gateworks/G23481
-rw-r--r--tools/tools/nanobsd/gateworks/G23581
-rw-r--r--tools/tools/net80211/scripts/config3
-rw-r--r--tools/tools/net80211/scripts/mesh/common13
-rw-r--r--tools/tools/net80211/scripts/mesh/config.mesh17
-rw-r--r--tools/tools/net80211/scripts/mesh/setup.simple13
-rw-r--r--tools/tools/net80211/scripts/mesh/topology.line38
-rw-r--r--tools/tools/net80211/scripts/mesh/topology.ring40
-rw-r--r--tools/tools/net80211/scripts/mesh/topology.star38
-rw-r--r--tools/tools/net80211/scripts/mesh/topology.tree47
-rw-r--r--tools/tools/net80211/wlanstats/wlanstats.c42
52 files changed, 6012 insertions, 203 deletions
diff --git a/sbin/ifconfig/ifconfig.8 b/sbin/ifconfig/ifconfig.8
index 5f571f8..1d2f096 100644
--- a/sbin/ifconfig/ifconfig.8
+++ b/sbin/ifconfig/ifconfig.8
@@ -28,7 +28,7 @@
.\" From: @(#)ifconfig.8 8.3 (Berkeley) 1/5/94
.\" $FreeBSD$
.\"
-.Dd June 24, 2009
+.Dd July 8, 2009
.Dt IFCONFIG 8
.Os
.Sh NAME
@@ -623,6 +623,7 @@ is one of
.Cm hostap ),
.Cm wds ,
.Cm tdma ,
+.Cm mesh ,
and
.Cm monitor .
The operating mode of a cloned interface cannot be changed.
@@ -1195,6 +1196,9 @@ indicates the address is denied access,
.Ql *
indicates the address is present but the current policy open
(so the ACL is not consulted).
+.It Cm list mesh
+Displays the mesh routing table, used for forwarding packets on a mesh
+network.
.It Cm list regdomain
Display the current regulatory settings including the available channels
and transmit power caps.
@@ -1278,6 +1282,8 @@ When operating as an access point display the stations that are
currently associated.
When operating in ad-hoc mode display stations identified as
neighbors in the IBSS.
+When operating in mesh mode display stations identified as
+neighbors in the MBSS.
When operating in station mode display the access point.
Capabilities advertised by the stations are described under
the
@@ -1813,6 +1819,85 @@ as it handles the RADIUS processing
(and marks stations as authorized).
.El
.Pp
+The following parameters are related to a wireless interface operating in mesh
+mode:
+.Bl -tag -width indent
+.It Cm meshid Ar meshid
+Set the desired Mesh Identifier.
+The Mesh ID is a string up to 32 characters in length.
+A mesh interface must have a Mesh Identifier specified
+to reach an operational state.
+.It Cm meshttl Ar ttl
+Set the desired ``time to live'' for mesh forwarded packets;
+this is the number of hops a packet may be forwarded before
+it is discarded.
+The default setting for
+.Cm meshttl
+is 31.
+.It Cm meshpeering
+Enable or disable peering with neighbor mesh stations.
+Stations must peer before any data packets can be exchanged.
+By default
+.Cm meshpeering
+is enabled.
+.It Cm meshforward
+Enable or disable forwarding packets by a mesh interface.
+By default
+.Cm meshforward
+is enabled.
+.It Cm meshmetric Ar protocol
+Set the specified
+.Ar protocol
+as the link metric protocol used on a mesh network.
+The default protocol is called
+.Ar AIRTIME .
+The mesh interface will restart after changing this setting.
+.It Cm meshpath Ar protocol
+Set the specified
+.Ar protocol
+as the path selection protocol used on a mesh network.
+The only available protocol at the moment is called
+.Ar HWMP
+(Hybrid Wireless Mesh Protocol).
+The mesh interface will restart after changing this setting.
+.It Cm hwmprootmode Ar mode
+Stations on a mesh network can operate as ``root nodes.''
+Root nodes try to find paths to all mesh nodes and advertise themselves
+regularly.
+When there is a root mesh node on a network, other mesh nodes can setup
+paths between themselves faster because they can use the root node
+to find the destination.
+This path may not be the best, but on-demand
+routing will eventually find the best path.
+The following modes are recognized:
+.Pp
+.Bl -tag -width ".Cm PROACTIVE" -compact
+.It Cm DISABLED
+Disable root mode.
+.It Cm NORMAL
+Send broadcast path requests every two seconds.
+Nodes on the mesh without a path to this root mesh station with try to
+discover a path to us.
+.It Cm PROACTIVE
+Send broadcast path requests every two seconds and every node must reply with
+with a path reply even if it already has a path to this root mesh station,
+.It Cm RANN
+Send broadcast root annoucement (RANN) frames.
+Nodes on the mesh without a path to this root mesh station with try to
+discover a path to us.
+.El
+By default
+.Cm hwmprootmode
+is set to
+.Ar DISABLED .
+.It Cm hwmpmaxhops Ar cnt
+Set the maximum number of hops allowed in an HMWP path to
+.Ar cnt .
+The default setting for
+.Cm hwmpmaxhops
+is 31.
+.El
+.Pp
The following parameters are for compatibility with other systems:
.Bl -tag -width indent
.It Cm nwid Ar ssid
diff --git a/sbin/ifconfig/ifieee80211.c b/sbin/ifconfig/ifieee80211.c
index dbbc195..2b196a6 100644
--- a/sbin/ifconfig/ifieee80211.c
+++ b/sbin/ifconfig/ifieee80211.c
@@ -81,6 +81,7 @@
#include <net80211/ieee80211_freebsd.h>
#include <net80211/ieee80211_superg.h>
#include <net80211/ieee80211_tdma.h>
+#include <net80211/ieee80211_mesh.h>
#include <assert.h>
#include <ctype.h>
@@ -162,6 +163,7 @@ static void print_channels(int, const struct ieee80211req_chaninfo *,
int allchans, int verbose);
static void regdomain_makechannels(struct ieee80211_regdomain_req *,
const struct ieee80211_devcaps_req *);
+static const char *mesh_linkstate_string(uint8_t state);
static struct ieee80211req_chaninfo *chaninfo;
static struct ieee80211_regdomain regdomain;
@@ -575,6 +577,20 @@ set80211ssid(const char *val, int d, int s, const struct afswtch *rafp)
}
static void
+set80211meshid(const char *val, int d, int s, const struct afswtch *rafp)
+{
+ int len;
+ u_int8_t data[IEEE80211_NWID_LEN];
+
+ memset(data, 0, sizeof(data));
+ len = sizeof(data);
+ if (get_string(val, NULL, data, &len) == NULL)
+ exit(1);
+
+ set80211(s, IEEE80211_IOC_MESH_ID, 0, len, data);
+}
+
+static void
set80211stationname(const char *val, int d, int s, const struct afswtch *rafp)
{
int len;
@@ -1262,6 +1278,66 @@ DECL_CMD_FUNC(set80211maccmd, val, d)
}
static void
+set80211meshrtmac(int s, int req, const char *val)
+{
+ char *temp;
+ struct sockaddr_dl sdl;
+
+ temp = malloc(strlen(val) + 2); /* ':' and '\0' */
+ if (temp == NULL)
+ errx(1, "malloc failed");
+ temp[0] = ':';
+ strcpy(temp + 1, val);
+ sdl.sdl_len = sizeof(sdl);
+ link_addr(temp, &sdl);
+ free(temp);
+ if (sdl.sdl_alen != IEEE80211_ADDR_LEN)
+ errx(1, "malformed link-level address");
+ set80211(s, IEEE80211_IOC_MESH_RTCMD, req,
+ IEEE80211_ADDR_LEN, LLADDR(&sdl));
+}
+
+static
+DECL_CMD_FUNC(set80211addmeshrt, val, d)
+{
+ set80211meshrtmac(s, IEEE80211_MESH_RTCMD_ADD, val);
+}
+
+static
+DECL_CMD_FUNC(set80211delmeshrt, val, d)
+{
+ set80211meshrtmac(s, IEEE80211_MESH_RTCMD_DELETE, val);
+}
+
+static
+DECL_CMD_FUNC(set80211meshrtcmd, val, d)
+{
+ set80211(s, IEEE80211_IOC_MESH_RTCMD, d, 0, NULL);
+}
+
+static
+DECL_CMD_FUNC(set80211hwmprootmode, val, d)
+{
+ int mode;
+
+ if (strcasecmp(val, "normal") == 0)
+ mode = IEEE80211_HWMP_ROOTMODE_NORMAL;
+ else if (strcasecmp(val, "proactive") == 0)
+ mode = IEEE80211_HWMP_ROOTMODE_PROACTIVE;
+ else if (strcasecmp(val, "rann") == 0)
+ mode = IEEE80211_HWMP_ROOTMODE_RANN;
+ else
+ mode = IEEE80211_HWMP_ROOTMODE_DISABLED;
+ set80211(s, IEEE80211_IOC_HWMP_ROOTMODE, mode, 0, NULL);
+}
+
+static
+DECL_CMD_FUNC(set80211hwmpmaxhops, val, d)
+{
+ set80211(s, IEEE80211_IOC_HWMP_MAXHOPS, atoi(val), 0, NULL);
+}
+
+static void
set80211pureg(const char *val, int d, int s, const struct afswtch *rafp)
{
set80211(s, IEEE80211_IOC_PUREG, d, 0, NULL);
@@ -1771,6 +1847,42 @@ DECL_CMD_FUNC(set80211tdmabintval, val, d)
set80211(s, IEEE80211_IOC_TDMA_BINTERVAL, atoi(val), 0, NULL);
}
+static
+DECL_CMD_FUNC(set80211meshttl, val, d)
+{
+ set80211(s, IEEE80211_IOC_MESH_TTL, atoi(val), 0, NULL);
+}
+
+static
+DECL_CMD_FUNC(set80211meshforward, val, d)
+{
+ set80211(s, IEEE80211_IOC_MESH_FWRD, atoi(val), 0, NULL);
+}
+
+static
+DECL_CMD_FUNC(set80211meshpeering, val, d)
+{
+ set80211(s, IEEE80211_IOC_MESH_AP, atoi(val), 0, NULL);
+}
+
+static
+DECL_CMD_FUNC(set80211meshmetric, val, d)
+{
+ char v[12];
+
+ memcpy(v, val, sizeof(v));
+ set80211(s, IEEE80211_IOC_MESH_PR_METRIC, 0, 0, v);
+}
+
+static
+DECL_CMD_FUNC(set80211meshpath, val, d)
+{
+ char v[12];
+
+ memcpy(v, val, sizeof(v));
+ set80211(s, IEEE80211_IOC_MESH_PR_PATH, 0, 0, v);
+}
+
static int
regdomain_sort(const void *a, const void *b)
{
@@ -2498,6 +2610,45 @@ printathie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
}
}
+
+static void
+printmeshconf(const char *tag, const uint8_t *ie, size_t ielen, int maxlen)
+{
+#define MATCHOUI(field, oui, string) \
+do { \
+ if (memcmp(field, oui, 4) == 0) \
+ printf("%s", string); \
+} while (0)
+
+ printf("%s", tag);
+ if (verbose) {
+ const struct ieee80211_meshconf_ie *mconf =
+ (const struct ieee80211_meshconf_ie *)ie;
+ const uint8_t null[4] = IEEE80211_MESHCONF_NULL;
+ const uint8_t hwmp[4] = IEEE80211_MESHCONF_HWMP;
+ const uint8_t airtime[4] = IEEE80211_MESHCONF_AIRTIME;
+ const uint8_t ccsig[4] = IEEE80211_MESHCONF_CCSIG;
+ const uint8_t sae[4] = IEEE80211_MESHCONF_SAE;
+ const uint8_t neighoff[4] = IEEE80211_MESHCONF_SAE;
+ printf("<v%d PATH:", mconf->conf_ver);
+ MATCHOUI(mconf->conf_pselid, hwmp, "HWMP");
+ printf(" LINK:");
+ MATCHOUI(mconf->conf_pmetid, airtime, "AIRTIME");
+ printf(" CONGESTION:");
+ MATCHOUI(mconf->conf_ccid, ccsig, "SIG");
+ MATCHOUI(mconf->conf_ccid, null, "NULL");
+ printf(" SYNC:");
+ MATCHOUI(mconf->conf_syncid, neighoff, "NEIGHOFF");
+ MATCHOUI(mconf->conf_syncid, null, "NULL");
+ printf(" AUTH:");
+ MATCHOUI(mconf->conf_authid, sae, "SAE");
+ MATCHOUI(mconf->conf_authid, null, "NULL");
+ printf(" FORM:0x%x CAPS:0x%x>", mconf->conf_form,
+ mconf->conf_cap);
+ }
+#undef MATCHOUI
+}
+
static const char *
wpa_cipher(const u_int8_t *sel)
{
@@ -2968,6 +3119,13 @@ printies(const u_int8_t *vp, int ielen, int maxcols)
if (verbose)
printhtinfo(" HTINFO", vp, 2+vp[1], maxcols);
break;
+ case IEEE80211_ELEMID_MESHID:
+ if (verbose)
+ printssid(" MESHID", vp, 2+vp[1], maxcols);
+ break;
+ case IEEE80211_ELEMID_MESHCONF:
+ printmeshconf(" MESHCONF", vp, 2+vp[1], maxcols);
+ break;
default:
if (verbose)
printie(iename(vp[0]), vp, 2+vp[1], maxcols);
@@ -2996,7 +3154,7 @@ list_scan(int s)
uint8_t buf[24*1024];
char ssid[IEEE80211_NWID_LEN+1];
const uint8_t *cp;
- int len, ssidmax;
+ int len, ssidmax, idlen;
if (get80211len(s, IEEE80211_IOC_SCAN_RESULTS, buf, sizeof(buf), &len) < 0)
errx(1, "unable to get scan results");
@@ -3005,9 +3163,9 @@ list_scan(int s)
getchaninfo(s);
- ssidmax = verbose ? IEEE80211_NWID_LEN : 14;
+ ssidmax = verbose ? IEEE80211_NWID_LEN - 1 : 14;
printf("%-*.*s %-17.17s %4s %4s %-7s %3s %4s\n"
- , ssidmax, ssidmax, "SSID"
+ , ssidmax, ssidmax, "SSID/MESH ID"
, "BSSID"
, "CHAN"
, "RATE"
@@ -3018,13 +3176,20 @@ list_scan(int s)
cp = buf;
do {
const struct ieee80211req_scan_result *sr;
- const uint8_t *vp;
+ const uint8_t *vp, *idp;
sr = (const struct ieee80211req_scan_result *) cp;
vp = cp + sr->isr_ie_off;
+ if (sr->isr_meshid_len) {
+ idp = vp + sr->isr_ssid_len;
+ idlen = sr->isr_meshid_len;
+ } else {
+ idp = vp;
+ idlen = sr->isr_ssid_len;
+ }
printf("%-*.*s %s %3d %3dM %3d:%-3d %3d %-4.4s"
, ssidmax
- , copy_essid(ssid, ssidmax, vp, sr->isr_ssid_len)
+ , copy_essid(ssid, ssidmax, idp, idlen)
, ssid
, ether_ntoa((const struct ether_addr *) sr->isr_bssid)
, ieee80211_mhz2ieee(sr->isr_freq, sr->isr_flags)
@@ -3033,7 +3198,8 @@ list_scan(int s)
, sr->isr_intval
, getcaps(sr->isr_capinfo)
);
- printies(vp + sr->isr_ssid_len, sr->isr_ie_len, 24);
+ printies(vp + sr->isr_ssid_len + sr->isr_meshid_len,
+ sr->isr_ie_len, 24);
printf("\n");
cp += sr->isr_len, len -= sr->isr_len;
} while (len >= sizeof(struct ieee80211req_scan_result));
@@ -3151,18 +3317,32 @@ list_stations(int s)
getchaninfo(s);
- printf("%-17.17s %4s %4s %4s %4s %4s %6s %6s %4s %-7s\n"
- , "ADDR"
- , "AID"
- , "CHAN"
- , "RATE"
- , "RSSI"
- , "IDLE"
- , "TXSEQ"
- , "RXSEQ"
- , "CAPS"
- , "FLAG"
- );
+ if (opmode == IEEE80211_M_MBSS)
+ printf("%-17.17s %4s %5s %5s %7s %4s %4s %4s %6s %6s\n"
+ , "ADDR"
+ , "CHAN"
+ , "LOCAL"
+ , "PEER"
+ , "STATE"
+ , "RATE"
+ , "RSSI"
+ , "IDLE"
+ , "TXSEQ"
+ , "RXSEQ"
+ );
+ else
+ printf("%-17.17s %4s %4s %4s %4s %4s %6s %6s %4s %-7s\n"
+ , "ADDR"
+ , "AID"
+ , "CHAN"
+ , "RATE"
+ , "RSSI"
+ , "IDLE"
+ , "TXSEQ"
+ , "RXSEQ"
+ , "CAPS"
+ , "FLAG"
+ );
cp = (const uint8_t *) u.req.info;
do {
const struct ieee80211req_sta_info *si;
@@ -3170,18 +3350,36 @@ list_stations(int s)
si = (const struct ieee80211req_sta_info *) cp;
if (si->isi_len < sizeof(*si))
break;
- printf("%s %4u %4d %3dM %3.1f %4d %6d %6d %-4.4s %-7.7s"
- , ether_ntoa((const struct ether_addr*) si->isi_macaddr)
- , IEEE80211_AID(si->isi_associd)
- , ieee80211_mhz2ieee(si->isi_freq, si->isi_flags)
- , si->isi_txmbps/2
- , si->isi_rssi/2.
- , si->isi_inact
- , gettxseq(si)
- , getrxseq(si)
- , getcaps(si->isi_capinfo)
- , getflags(si->isi_state)
- );
+ if (opmode == IEEE80211_M_MBSS)
+ printf("%s %4d %5x %5x %7.7s %3dM %4.1f %4d %6d %6d"
+ , ether_ntoa((const struct ether_addr*)
+ si->isi_macaddr)
+ , ieee80211_mhz2ieee(si->isi_freq,
+ si->isi_flags)
+ , si->isi_localid
+ , si->isi_peerid
+ , mesh_linkstate_string(si->isi_peerstate)
+ , si->isi_txmbps/2
+ , si->isi_rssi/2.
+ , si->isi_inact
+ , gettxseq(si)
+ , getrxseq(si)
+ );
+ else
+ printf("%s %4u %4d %3dM %4.1f %4d %6d %6d %-4.4s %-7.7s"
+ , ether_ntoa((const struct ether_addr*)
+ si->isi_macaddr)
+ , IEEE80211_AID(si->isi_associd)
+ , ieee80211_mhz2ieee(si->isi_freq,
+ si->isi_flags)
+ , si->isi_txmbps/2
+ , si->isi_rssi/2.
+ , si->isi_inact
+ , gettxseq(si)
+ , getrxseq(si)
+ , getcaps(si->isi_capinfo)
+ , getflags(si->isi_state)
+ );
printies(cp + si->isi_ie_off, si->isi_ie_len, 24);
printmimo(&si->isi_mimo);
printf("\n");
@@ -3190,6 +3388,28 @@ list_stations(int s)
}
static const char *
+mesh_linkstate_string(uint8_t state)
+{
+#define N(a) (sizeof(a) / sizeof(a[0]))
+ static const char *state_names[] = {
+ [0] = "IDLE",
+ [1] = "OPEN-TX",
+ [2] = "OPEN-RX",
+ [3] = "CONF-RX",
+ [4] = "ESTAB",
+ [5] = "HOLDING",
+ };
+
+ if (state >= N(state_names)) {
+ static char buf[10];
+ snprintf(buf, sizeof(buf), "#%u", state);
+ return buf;
+ } else
+ return state_names[state];
+#undef N
+}
+
+static const char *
get_chaninfo(const struct ieee80211_channel *c, int precise,
char buf[], size_t bsize)
{
@@ -3409,9 +3629,9 @@ list_keys(int s)
}
#define IEEE80211_C_BITS \
- "\20\1STA\7FF\10TURBOP\11IBSS\12PMGT" \
+ "\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"
static void
@@ -3729,6 +3949,40 @@ list_regdomain(int s, int channelsalso)
print_regdomain(&regdomain, verbose);
}
+static void
+list_mesh(int s)
+{
+ int i;
+ struct ieee80211req ireq;
+ struct ieee80211req_mesh_route routes[128];
+
+ (void) memset(&ireq, 0, sizeof(ireq));
+ (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
+ ireq.i_type = IEEE80211_IOC_MESH_RTCMD;
+ ireq.i_val = IEEE80211_MESH_RTCMD_LIST;
+ ireq.i_data = &routes;
+ ireq.i_len = sizeof(routes);
+ if (ioctl(s, SIOCG80211, &ireq) < 0)
+ err(1, "unable to get the Mesh routing table");
+
+ printf("%-17.17s %-17.17s %4s %4s %4s\n"
+ , "DEST"
+ , "NEXT HOP"
+ , "HOPS"
+ , "METRIC"
+ , "LIFETIME");
+
+ for (i = 0; i < ireq.i_len / sizeof(*routes); i++) {
+ printf("%s ",
+ ether_ntoa((const struct ether_addr *)routes[i].imr_dest));
+ printf("%s %4u %4d %6d\n",
+ ether_ntoa((const struct ether_addr *)
+ routes[i].imr_nexthop),
+ routes[i].imr_nhops, routes[i].imr_metric,
+ routes[i].imr_lifetime);
+ }
+}
+
static
DECL_CMD_FUNC(set80211list, arg, d)
{
@@ -3762,6 +4016,8 @@ DECL_CMD_FUNC(set80211list, arg, d)
list_regdomain(s, 1);
else if (iseq(arg, "countries"))
list_countries();
+ else if (iseq(arg, "mesh"))
+ list_mesh(s);
else
errx(1, "Don't know how to list %s for %s", arg, name);
LINE_BREAK();
@@ -3787,6 +4043,8 @@ get80211opmode(int s)
return IEEE80211_M_HOSTAP;
if (ifmr.ifm_current & IFM_IEEE80211_MONITOR)
return IEEE80211_M_MONITOR;
+ if (ifmr.ifm_current & IFM_IEEE80211_MBSS)
+ return IEEE80211_M_MBSS;
}
return IEEE80211_M_STA;
}
@@ -3911,13 +4169,13 @@ printrate(const char *tag, int v, int defrate, int defmcs)
}
static int
-getssid(int s, int ix, void *data, size_t len, int *plen)
+getid(int s, int ix, void *data, size_t len, int *plen, int mesh)
{
struct ieee80211req ireq;
(void) memset(&ireq, 0, sizeof(ireq));
(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
- ireq.i_type = IEEE80211_IOC_SSID;
+ ireq.i_type = (!mesh) ? IEEE80211_IOC_SSID : IEEE80211_IOC_MESH_ID;
ireq.i_val = ix;
ireq.i_data = data;
ireq.i_len = len;
@@ -3938,7 +4196,7 @@ ieee80211_status(int s)
const struct ieee80211_roamparam *rp;
const struct ieee80211_txparam *tp;
- if (getssid(s, -1, data, sizeof(data), &len) < 0) {
+ if (getid(s, -1, data, sizeof(data), &len, 0) < 0) {
/* If we can't get the SSID, this isn't an 802.11 device. */
return;
}
@@ -3953,19 +4211,25 @@ ieee80211_status(int s)
gothtconf = 0;
gotregdomain = 0;
- if (get80211val(s, IEEE80211_IOC_NUMSSIDS, &num) < 0)
- num = 0;
- printf("\tssid ");
- if (num > 1) {
- for (i = 0; i < num; i++) {
- if (getssid(s, i, data, sizeof(data), &len) >= 0 && len > 0) {
- printf(" %d:", i + 1);
- print_string(data, len);
- }
- }
- } else
+ printf("\t");
+ if (opmode == IEEE80211_M_MBSS) {
+ printf("meshid ");
+ getid(s, 0, data, sizeof(data), &len, 1);
print_string(data, len);
-
+ } else {
+ if (get80211val(s, IEEE80211_IOC_NUMSSIDS, &num) < 0)
+ num = 0;
+ printf("ssid ");
+ if (num > 1) {
+ for (i = 0; i < num; i++) {
+ if (getid(s, i, data, sizeof(data), &len, 0) >= 0 && len > 0) {
+ printf(" %d:", i + 1);
+ print_string(data, len);
+ }
+ }
+ } else
+ print_string(data, len);
+ }
c = getcurchan(s);
if (c->ic_freq != IEEE80211_CHAN_ANY) {
char buf[14];
@@ -4515,6 +4779,57 @@ end:
LINE_BREAK();
list_wme(s);
}
+
+ if (opmode == IEEE80211_M_MBSS) {
+ if (get80211val(s, IEEE80211_IOC_MESH_TTL, &val) != -1) {
+ LINE_CHECK("meshttl %u", val);
+ }
+ if (get80211val(s, IEEE80211_IOC_MESH_AP, &val) != -1) {
+ if (val)
+ LINE_CHECK("meshpeering");
+ else
+ LINE_CHECK("-meshpeering");
+ }
+ if (get80211val(s, IEEE80211_IOC_MESH_FWRD, &val) != -1) {
+ if (val)
+ LINE_CHECK("meshforward");
+ else
+ LINE_CHECK("-meshforward");
+ }
+ if (get80211len(s, IEEE80211_IOC_MESH_PR_METRIC, data, 12,
+ &len) != -1) {
+ data[len] = '\0';
+ LINE_CHECK("meshmetric %s", data);
+ }
+ if (get80211len(s, IEEE80211_IOC_MESH_PR_PATH, data, 12,
+ &len) != -1) {
+ data[len] = '\0';
+ LINE_CHECK("meshpath %s", data);
+ }
+ if (get80211val(s, IEEE80211_IOC_HWMP_ROOTMODE, &val) != -1) {
+ switch (val) {
+ case IEEE80211_HWMP_ROOTMODE_DISABLED:
+ LINE_CHECK("hwmprootmode DISABLED");
+ break;
+ case IEEE80211_HWMP_ROOTMODE_NORMAL:
+ LINE_CHECK("hwmprootmode NORMAL");
+ break;
+ case IEEE80211_HWMP_ROOTMODE_PROACTIVE:
+ LINE_CHECK("hwmprootmode PROACTIVE");
+ break;
+ case IEEE80211_HWMP_ROOTMODE_RANN:
+ LINE_CHECK("hwmprootmode RANN");
+ break;
+ default:
+ LINE_CHECK("hwmprootmode UNKNOWN(%d)", val);
+ break;
+ }
+ }
+ if (get80211val(s, IEEE80211_IOC_HWMP_MAXHOPS, &val) != -1) {
+ LINE_CHECK("hwmpmaxhops %u", val);
+ }
+ }
+
LINE_BREAK();
}
@@ -4731,7 +5046,9 @@ DECL_CMD_FUNC(set80211clone_wlanmode, arg, d)
else if (iseq(arg, "tdma")) {
params.icp_opmode = IEEE80211_M_AHDEMO;
params.icp_flags |= IEEE80211_CLONE_TDMA;
- } else
+ } else if (iseq(arg, "mesh") || iseq(arg, "mp")) /* mesh point */
+ params.icp_opmode = IEEE80211_M_MBSS;
+ else
errx(1, "Don't know to create %s for %s", arg, name);
#undef iseq
}
@@ -4767,6 +5084,7 @@ set80211clone_wdslegacy(const char *val, int d, int s, const struct afswtch *raf
static struct cmd ieee80211_cmds[] = {
DEF_CMD_ARG("ssid", set80211ssid),
DEF_CMD_ARG("nwid", set80211ssid),
+ DEF_CMD_ARG("meshid", set80211meshid),
DEF_CMD_ARG("stationname", set80211stationname),
DEF_CMD_ARG("station", set80211stationname), /* BSD/OS */
DEF_CMD_ARG("channel", set80211channel),
@@ -4908,6 +5226,19 @@ static struct cmd ieee80211_cmds[] = {
DEF_CMD_ARG("tdmaslotlen", set80211tdmaslotlen),
DEF_CMD_ARG("tdmabintval", set80211tdmabintval),
+ DEF_CMD_ARG("meshttl", set80211meshttl),
+ DEF_CMD("meshforward", 1, set80211meshforward),
+ DEF_CMD("-meshforward", 0, set80211meshforward),
+ DEF_CMD("meshpeering", 1, set80211meshpeering),
+ DEF_CMD("-meshpeering", 0, set80211meshpeering),
+ DEF_CMD_ARG("meshmetric", set80211meshmetric),
+ DEF_CMD_ARG("meshpath", set80211meshpath),
+ DEF_CMD("meshrt:flush", IEEE80211_MESH_RTCMD_FLUSH, set80211meshrtcmd),
+ DEF_CMD_ARG("meshrt:add", set80211addmeshrt),
+ DEF_CMD_ARG("meshrt:del", set80211delmeshrt),
+ DEF_CMD_ARG("hwmprootmode", set80211hwmprootmode),
+ DEF_CMD_ARG("hwmpmaxhops", set80211hwmpmaxhops),
+
/* vap cloning support */
DEF_CLONE_CMD_ARG("wlanaddr", set80211clone_wlanaddr),
DEF_CLONE_CMD_ARG("wlanbssid", set80211clone_wlanbssid),
diff --git a/sbin/ifconfig/ifmedia.c b/sbin/ifconfig/ifmedia.c
index d89f3eb..bb95043 100644
--- a/sbin/ifconfig/ifmedia.c
+++ b/sbin/ifconfig/ifmedia.c
@@ -104,7 +104,8 @@ static struct ifmedia_description *get_subtype_desc(int,
#define IFM_OPMODE(x) \
((x) & (IFM_IEEE80211_ADHOC | IFM_IEEE80211_HOSTAP | \
- IFM_IEEE80211_IBSS | IFM_IEEE80211_WDS | IFM_IEEE80211_MONITOR))
+ IFM_IEEE80211_IBSS | IFM_IEEE80211_WDS | IFM_IEEE80211_MONITOR | \
+ IFM_IEEE80211_MBSS))
#define IFM_IEEE80211_STA 0
static void
diff --git a/share/man/man4/ath.4 b/share/man/man4/ath.4
index 9d15a7b..cfc256c 100644
--- a/share/man/man4/ath.4
+++ b/share/man/man4/ath.4
@@ -28,7 +28,7 @@
.\"
.\" $FreeBSD$
.\"/
-.Dd March 25, 2009
+.Dd July 8, 2009
.Dt ATH 4
.Os
.Sh NAME
@@ -61,7 +61,7 @@ These APIs are used by a wide variety of chips; most all chips with
a PCI and/or CardBus interface are supported.
.Pp
Supported features include 802.11 and 802.3 frames, power management, BSS,
-IBSS, TDMA, and host-based access point operation modes.
+IBSS, MBSS, TDMA, and host-based access point operation modes.
All host/device interaction is via DMA.
.Pp
The
@@ -102,6 +102,7 @@ The driver supports
.Cm adhoc ,
.Cm adhoc-demo ,
.Cm hostap ,
+.Cm mesh ,
.Cm wds ,
and
.Cm monitor
@@ -175,6 +176,12 @@ ifconfig wlan0 inet 192.168.0.10 netmask 0xffffff00 ssid my_ap \e
mode 11g
.Ed
.Pp
+Create an 802.11a mesh station:
+.Bd -literal -offset indent
+ifconfig wlan0 create wlandev ath0 wlanmode mesh
+ifconfig wlan0 meshid my_mesh mode 11a inet 192.168.0.10/24
+.Ed
+.Pp
Create two virtual 802.11a host-based access points, one with
with WEP enabled and one with no security, and bridge them to
the fxp0 (wired) device:
diff --git a/share/man/man4/mwl.4 b/share/man/man4/mwl.4
index b31191b..32138c8 100644
--- a/share/man/man4/mwl.4
+++ b/share/man/man4/mwl.4
@@ -28,7 +28,7 @@
.\"
.\" $FreeBSD$
.\"/
-.Dd June 9, 2009
+.Dd July 8, 2009
.Dt MWL 4
.Os
.Sh NAME
@@ -64,7 +64,7 @@ module to work.
Normally this module is loaded on demand by the driver but it may
also be compiled into the kernel.
.Pp
-Supported features include 802.11n, power management, BSS,
+Supported features include 802.11n, power management, BSS, MBSS,
and host-based access point operation modes.
All host/device interaction is via DMA.
.Pp
@@ -83,6 +83,7 @@ AES-CCM, TKIP, and Michael cryptographic operations.
The driver supports
.Cm station ,
.Cm hostap ,
+.Cm mesh ,
and
.Cm wds
mode operation.
@@ -139,6 +140,12 @@ ifconfig wlan0 inet 192.168.0.10 netmask 0xffffff00 ssid my_ap \e
mode 11g
.Ed
.Pp
+Create an 802.11a mesh station:
+.Bd -literal -offset indent
+ifconfig wlan0 create wlandev mwl0 wlanmode mesh
+ifconfig wlan0 meshid my_mesh mode 11a inet 192.168.0.10/24
+.Ed
+.Pp
Create two virtual 802.11a host-based access points, one with
with WEP enabled and one with no security, and bridge them to
the fxp0 (wired) device:
diff --git a/share/man/man4/ral.4 b/share/man/man4/ral.4
index 7f52c52..ef8f8b2 100644
--- a/share/man/man4/ral.4
+++ b/share/man/man4/ral.4
@@ -15,7 +15,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd April 13, 2008
+.Dd July 8, 2009
.Os
.Dt RAL 4
.Sh NAME
@@ -72,12 +72,15 @@ supports
.Cm station ,
.Cm adhoc ,
.Cm hostap ,
+.Cm mesh ,
.Cm wds ,
and
.Cm monitor
mode operation.
Only one
.Cm hostap
+or
+.Cm mesh
virtual interface may be configured at a time.
Any number of
.Cm wds
diff --git a/share/man/man4/wlan.4 b/share/man/man4/wlan.4
index dffefab..c3a2b16 100644
--- a/share/man/man4/wlan.4
+++ b/share/man/man4/wlan.4
@@ -25,7 +25,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd May 15, 2009
+.Dd July 8, 2009
.Dt WLAN 4
.Os
.Sh NAME
@@ -48,7 +48,7 @@ support.
.Nm
supports multi-mode devices capable of
operating in both 2.4GHz and 5GHz bands and supports numerous
-802.11 standards: 802.11a, 802.11b, 802.11g, and 802.11n.
+802.11 standards: 802.11a, 802.11b, 802.11g, 802.11n, and 802.11s (Draft 3.0).
The WPA, 802.11i, and 802.1x security protocols are supported
through a combination of in-kernel code and user-mode applications.
The WME/WMM multi-media protocols are supported entirely within
@@ -83,6 +83,8 @@ A client station in an infrastructure bss
(i.e. one that associates to an access point).
.It Cm hostap
An access point in an infrastructure bss.
+.It Cm mesh
+A mesh station in an MBSS network.
.It Cm adhoc
A station in an IBSS network.
.It Cm ahdemo
@@ -115,8 +117,6 @@ interface that does not send beacon frames before
interfaces may be created.
.El
.Pp
-More types are planned to support
-802.11s mesh nodes (station and ap).
Note that an interface's type cannot be changed once it is created.
.Pp
.Nm
@@ -161,6 +161,12 @@ The module name of
.Nm
was used to be compatible with
.Nx .
+.Pp
+Mesh stations follow the 802.11s Draft 3.0 specification which is
+not ratified and subject to change.
+Beware that this specification is incompatible with earlier drafts;
+and stations implementing earlier drafts (e.g. Linux)
+may not interoperate.
.Sh SEE ALSO
.Xr an 4 ,
.Xr ath 4 ,
diff --git a/sys/amd64/conf/GENERIC b/sys/amd64/conf/GENERIC
index 43b3960..73a4fb6 100644
--- a/sys/amd64/conf/GENERIC
+++ b/sys/amd64/conf/GENERIC
@@ -258,6 +258,7 @@ device xe # Xircom pccard Ethernet
device wlan # 802.11 support
options IEEE80211_DEBUG # enable debug msgs
options IEEE80211_AMPDU_AGE # age frames in AMPDU reorder q's
+options IEEE80211_SUPPORT_MESH # enable 802.11s D3.0 support
device wlan_wep # 802.11 WEP support
device wlan_ccmp # 802.11 CCMP support
device wlan_tkip # 802.11 TKIP support
diff --git a/sys/conf/NOTES b/sys/conf/NOTES
index e191a23..e7e21a1 100644
--- a/sys/conf/NOTES
+++ b/sys/conf/NOTES
@@ -738,6 +738,8 @@ device vlan
device wlan
options IEEE80211_DEBUG #enable debugging msgs
options IEEE80211_AMPDU_AGE #age frames in AMPDU reorder q's
+options IEEE80211_SUPPORT_MESH #enable 802.11s D3.0 support
+options IEEE80211_SUPPORT_TDMA #enable TDMA support
# The `wlan_wep', `wlan_tkip', and `wlan_ccmp' devices provide
# support for WEP, TKIP, and AES-CCMP crypto protocols optionally
diff --git a/sys/conf/files b/sys/conf/files
index 4d3094b..b28f5b3 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -2239,6 +2239,7 @@ net80211/ieee80211_acl.c optional wlan wlan_acl
net80211/ieee80211_action.c optional wlan
net80211/ieee80211_ageq.c optional wlan
net80211/ieee80211_adhoc.c optional wlan
+net80211/ieee80211_ageq.c optional wlan
net80211/ieee80211_amrr.c optional wlan wlan_amrr
net80211/ieee80211_crypto.c optional wlan
net80211/ieee80211_crypto_ccmp.c optional wlan wlan_ccmp
@@ -2250,8 +2251,10 @@ net80211/ieee80211_dfs.c optional wlan
net80211/ieee80211_freebsd.c optional wlan
net80211/ieee80211_hostap.c optional wlan
net80211/ieee80211_ht.c optional wlan
+net80211/ieee80211_hwmp.c optional wlan ieee80211_support_mesh
net80211/ieee80211_input.c optional wlan
net80211/ieee80211_ioctl.c optional wlan
+net80211/ieee80211_mesh.c optional wlan ieee80211_support_mesh
net80211/ieee80211_monitor.c optional wlan
net80211/ieee80211_node.c optional wlan
net80211/ieee80211_output.c optional wlan
diff --git a/sys/conf/options b/sys/conf/options
index be59f53..f905d6f 100644
--- a/sys/conf/options
+++ b/sys/conf/options
@@ -804,6 +804,7 @@ INTR_FILTER
IEEE80211_DEBUG opt_wlan.h
IEEE80211_DEBUG_REFCNT opt_wlan.h
IEEE80211_AMPDU_AGE opt_wlan.h
+IEEE80211_SUPPORT_MESH opt_wlan.h
IEEE80211_SUPPORT_SUPERG opt_wlan.h
IEEE80211_SUPPORT_TDMA opt_wlan.h
diff --git a/sys/dev/ath/if_ath.c b/sys/dev/ath/if_ath.c
index 7d0eb5e..c201265 100644
--- a/sys/dev/ath/if_ath.c
+++ b/sys/dev/ath/if_ath.c
@@ -578,6 +578,7 @@ ath_attach(u_int16_t devid, struct ath_softc *sc)
| IEEE80211_C_MONITOR /* monitor mode */
| IEEE80211_C_AHDEMO /* adhoc demo mode */
| IEEE80211_C_WDS /* 4-address traffic works */
+ | IEEE80211_C_MBSS /* mesh point link mode */
| IEEE80211_C_SHPREAMBLE /* short preamble supported */
| IEEE80211_C_SHSLOT /* short slot time supported */
| IEEE80211_C_WPA /* capable of WPA1+WPA2 */
@@ -655,6 +656,7 @@ ath_attach(u_int16_t devid, struct ath_softc *sc)
if (ath_hal_hasbursting(ah))
ic->ic_caps |= IEEE80211_C_BURST;
sc->sc_hasbmask = ath_hal_hasbssidmask(ah);
+ sc->sc_hasbmatch = ath_hal_hasbssidmatch(ah);
sc->sc_hastsfadd = ath_hal_hastsfadjust(ah);
if (ath_hal_hasfastframes(ah))
ic->ic_caps |= IEEE80211_C_FF;
@@ -918,6 +920,7 @@ ath_vap_create(struct ieee80211com *ic,
}
break;
case IEEE80211_M_HOSTAP:
+ case IEEE80211_M_MBSS:
needbeacon = 1;
break;
case IEEE80211_M_WDS:
@@ -936,7 +939,6 @@ ath_vap_create(struct ieee80211com *ic,
ic_opmode = IEEE80211_M_HOSTAP;
else
ic_opmode = ic->ic_opmode;
- break;
default:
device_printf(sc->sc_dev, "unknown opmode %d\n", opmode);
goto bad;
@@ -950,7 +952,7 @@ ath_vap_create(struct ieee80211com *ic,
}
/* STA, AHDEMO? */
- if (opmode == IEEE80211_M_HOSTAP) {
+ if (opmode == IEEE80211_M_HOSTAP || opmode == IEEE80211_M_MBSS) {
assign_address(sc, mac, flags & IEEE80211_CLONE_BSSID);
ath_hal_setbssidmask(sc->sc_ah, sc->sc_hwbssidmask);
}
@@ -1042,6 +1044,7 @@ ath_vap_create(struct ieee80211com *ic,
/* fall thru... */
#endif
case IEEE80211_M_HOSTAP:
+ case IEEE80211_M_MBSS:
sc->sc_opmode = HAL_M_HOSTAP;
break;
case IEEE80211_M_MONITOR:
@@ -1129,7 +1132,8 @@ ath_vap_delete(struct ieee80211vap *vap)
sc->sc_nstavaps--;
if (sc->sc_nstavaps == 0 && sc->sc_swbmiss)
sc->sc_swbmiss = 0;
- } else if (vap->iv_opmode == IEEE80211_M_HOSTAP) {
+ } else if (vap->iv_opmode == IEEE80211_M_HOSTAP ||
+ vap->iv_opmode == IEEE80211_M_MBSS) {
reclaim_address(sc, vap->iv_myaddr);
ath_hal_setbssidmask(ah, sc->sc_hwbssidmask);
}
@@ -2330,7 +2334,7 @@ ath_key_update_end(struct ieee80211vap *vap)
* NB: older hal's add rx filter bits out of sight and we need to
* blindly preserve them
* o probe request frames are accepted only when operating in
- * hostap, adhoc, or monitor modes
+ * hostap, adhoc, mesh, or monitor modes
* o enable promiscuous mode
* - when in monitor mode
* - if interface marked PROMISC (assumes bridge setting is filtered)
@@ -2343,6 +2347,7 @@ ath_key_update_end(struct ieee80211vap *vap)
* - when doing s/w beacon miss (e.g. for ap+sta)
* - when operating in ap mode in 11g to detect overlapping bss that
* require protection
+ * - when operating in mesh mode to detect neighbors
* o accept control frames:
* - when in monitor mode
* XXX BAR frames for 11n
@@ -2375,6 +2380,13 @@ ath_calcrxfilter(struct ath_softc *sc)
if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
IEEE80211_IS_CHAN_ANYG(ic->ic_curchan))
rfilt |= HAL_RX_FILTER_BEACON;
+ if (ic->ic_opmode == IEEE80211_M_MBSS) {
+ rfilt |= HAL_RX_FILTER_BEACON;
+ if (sc->sc_hasbmatch)
+ rfilt |= HAL_RX_FILTER_BSSID;
+ else
+ rfilt |= HAL_RX_FILTER_PROM;
+ }
if (ic->ic_opmode == IEEE80211_M_MONITOR)
rfilt |= HAL_RX_FILTER_CONTROL;
DPRINTF(sc, ATH_DEBUG_MODE, "%s: RX filter 0x%x, %s if_flags 0x%x\n",
@@ -2500,7 +2512,8 @@ ath_updateslot(struct ifnet *ifp)
* immediately. For other operation we defer the change
* until beacon updates have propagated to the stations.
*/
- if (ic->ic_opmode == IEEE80211_M_HOSTAP)
+ if (ic->ic_opmode == IEEE80211_M_HOSTAP ||
+ ic->ic_opmode == IEEE80211_M_MBSS)
sc->sc_updateslot = UPDATE;
else
ath_setslottime(sc);
@@ -2535,7 +2548,8 @@ ath_beaconq_config(struct ath_softc *sc)
HAL_TXQ_INFO qi;
ath_hal_gettxqueueprops(ah, sc->sc_bhalq, &qi);
- if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+ if (ic->ic_opmode == IEEE80211_M_HOSTAP ||
+ ic->ic_opmode == IEEE80211_M_MBSS) {
/*
* Always burst out beacon and CAB traffic.
*/
@@ -3087,9 +3101,10 @@ ath_beacon_config(struct ath_softc *sc, struct ieee80211vap *vap)
/* extract tstamp from last beacon and convert to TU */
nexttbtt = TSF_TO_TU(LE_READ_4(ni->ni_tstamp.data + 4),
LE_READ_4(ni->ni_tstamp.data));
- if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+ if (ic->ic_opmode == IEEE80211_M_HOSTAP ||
+ ic->ic_opmode == IEEE80211_M_MBSS) {
/*
- * For multi-bss ap support beacons are either staggered
+ * For multi-bss ap/mesh support beacons are either staggered
* evenly over N slots or burst together. For the former
* arrange for the SWBA to be delivered for each slot.
* Slots that are not occupied will generate nothing.
@@ -3230,10 +3245,11 @@ ath_beacon_config(struct ath_softc *sc, struct ieee80211vap *vap)
} while (nexttbtt < tsftu);
}
ath_beaconq_config(sc);
- } else if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+ } else if (ic->ic_opmode == IEEE80211_M_HOSTAP ||
+ ic->ic_opmode == IEEE80211_M_MBSS) {
/*
- * In AP mode we enable the beacon timers and
- * SWBA interrupts to prepare beacon frames.
+ * In AP/mesh mode we enable the beacon timers
+ * and SWBA interrupts to prepare beacon frames.
*/
intval |= HAL_BEACON_ENA;
sc->sc_imask |= HAL_INT_SWBA; /* beacon prepare */
@@ -5602,6 +5618,7 @@ ath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
#endif
case IEEE80211_M_HOSTAP:
case IEEE80211_M_IBSS:
+ case IEEE80211_M_MBSS:
/*
* Allocate and setup the beacon frame.
*
diff --git a/sys/dev/ath/if_athvar.h b/sys/dev/ath/if_athvar.h
index 6a1ece2..0bd3ea2 100644
--- a/sys/dev/ath/if_athvar.h
+++ b/sys/dev/ath/if_athvar.h
@@ -235,6 +235,7 @@ struct ath_softc {
sc_outdoor : 1,/* outdoor operation */
sc_dturbo : 1,/* dynamic turbo in use */
sc_hasbmask : 1,/* bssid mask support */
+ sc_hasbmatch: 1,/* bssid match disable support*/
sc_hastsfadd: 1,/* tsf adjust support */
sc_beacons : 1,/* beacons running */
sc_swbmiss : 1,/* sta mode using sw bmiss */
diff --git a/sys/dev/mwl/if_mwl.c b/sys/dev/mwl/if_mwl.c
index dbbdca7..c320cdb 100644
--- a/sys/dev/mwl/if_mwl.c
+++ b/sys/dev/mwl/if_mwl.c
@@ -420,6 +420,7 @@ mwl_attach(uint16_t devid, struct mwl_softc *sc)
| IEEE80211_C_IBSS /* ibss, nee adhoc, mode */
| IEEE80211_C_AHDEMO /* adhoc demo mode */
#endif
+ | IEEE80211_C_MBSS /* mesh point link mode */
| IEEE80211_C_WDS /* WDS supported */
| IEEE80211_C_SHPREAMBLE /* short preamble supported */
| IEEE80211_C_SHSLOT /* short slot time supported */
@@ -615,6 +616,7 @@ mwl_vap_create(struct ieee80211com *ic,
IEEE80211_ADDR_COPY(mac, mac0);
switch (opmode) {
case IEEE80211_M_HOSTAP:
+ case IEEE80211_M_MBSS:
if ((flags & IEEE80211_CLONE_MACADDR) == 0)
assign_address(sc, mac, flags & IEEE80211_CLONE_BSSID);
hvap = mwl_hal_newvap(mh, MWL_HAL_AP, mac);
@@ -686,7 +688,7 @@ mwl_vap_create(struct ieee80211com *ic,
vap->iv_key_delete = mwl_key_delete;
vap->iv_key_set = mwl_key_set;
#ifdef MWL_HOST_PS_SUPPORT
- if (opmode == IEEE80211_M_HOSTAP) {
+ if (opmode == IEEE80211_M_HOSTAP || opmode == IEEE80211_M_MBSS) {
vap->iv_update_ps = mwl_update_ps;
mvp->mv_set_tim = vap->iv_set_tim;
vap->iv_set_tim = mwl_set_tim;
@@ -706,12 +708,14 @@ mwl_vap_create(struct ieee80211com *ic,
switch (vap->iv_opmode) {
case IEEE80211_M_HOSTAP:
+ case IEEE80211_M_MBSS:
case IEEE80211_M_STA:
/*
* Setup sta db entry for local address.
*/
mwl_localstadb(vap);
- if (vap->iv_opmode == IEEE80211_M_HOSTAP)
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP ||
+ vap->iv_opmode == IEEE80211_M_MBSS)
sc->sc_napvaps++;
else
sc->sc_nstavaps++;
@@ -753,11 +757,12 @@ mwl_vap_delete(struct ieee80211vap *vap)
ieee80211_vap_detach(vap);
switch (opmode) {
case IEEE80211_M_HOSTAP:
+ case IEEE80211_M_MBSS:
case IEEE80211_M_STA:
KASSERT(hvap != NULL, ("no hal vap handle"));
(void) mwl_hal_delstation(hvap, vap->iv_myaddr);
mwl_hal_delvap(hvap);
- if (opmode == IEEE80211_M_HOSTAP)
+ if (opmode == IEEE80211_M_HOSTAP || opmode == IEEE80211_M_MBSS)
sc->sc_napvaps--;
else
sc->sc_nstavaps--;
@@ -1281,6 +1286,7 @@ mwl_reset_vap(struct ieee80211vap *vap, int state)
/* re-setup beacons */
if (state == IEEE80211_S_RUN &&
(vap->iv_opmode == IEEE80211_M_HOSTAP ||
+ vap->iv_opmode == IEEE80211_M_MBSS ||
vap->iv_opmode == IEEE80211_M_IBSS)) {
mwl_setapmode(vap, vap->iv_bss->ni_chan);
mwl_hal_setnprotmode(hvap,
@@ -4184,6 +4190,7 @@ mwl_localstadb(struct ieee80211vap *vap)
mwl_setglobalkeys(vap);
break;
case IEEE80211_M_HOSTAP:
+ case IEEE80211_M_MBSS:
error = mwl_hal_newstation(hvap, vap->iv_myaddr,
0, 0, NULL, vap->iv_flags & IEEE80211_F_WME, 0);
if (error == 0)
@@ -4248,7 +4255,8 @@ mwl_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
(void) mwl_peerstadb(ni, 0, 0, NULL);
} else if (nstate == IEEE80211_S_CSA) {
/* XXX move to below? */
- if (vap->iv_opmode == IEEE80211_M_HOSTAP)
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP ||
+ vap->iv_opmode == IEEE80211_M_MBSS)
mwl_startcsa(vap);
} else if (nstate == IEEE80211_S_CAC) {
/* XXX move to below? */
@@ -4282,6 +4290,7 @@ mwl_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
mwl_localstadb(vap);
switch (vap->iv_opmode) {
case IEEE80211_M_HOSTAP:
+ case IEEE80211_M_MBSS:
if (ostate == IEEE80211_S_CAC) {
/* enable in-service radar detection */
mwl_hal_setradardetection(mh,
diff --git a/sys/dev/ral/rt2560.c b/sys/dev/ral/rt2560.c
index 7bb785c..455b5ad 100644
--- a/sys/dev/ral/rt2560.c
+++ b/sys/dev/ral/rt2560.c
@@ -285,6 +285,7 @@ rt2560_attach(device_t dev, int id)
| IEEE80211_C_MONITOR /* monitor mode */
| IEEE80211_C_AHDEMO /* adhoc demo mode */
| IEEE80211_C_WDS /* 4-address traffic works */
+ | IEEE80211_C_MBSS /* mesh point link mode */
| IEEE80211_C_SHPREAMBLE /* short preamble supported */
| IEEE80211_C_SHSLOT /* short slot time supported */
| IEEE80211_C_WPA /* capable of WPA1+WPA2 */
@@ -391,6 +392,8 @@ rt2560_vap_create(struct ieee80211com *ic,
case IEEE80211_M_AHDEMO:
case IEEE80211_M_MONITOR:
case IEEE80211_M_HOSTAP:
+ case IEEE80211_M_MBSS:
+ /* XXXRP: TBD */
if (!TAILQ_EMPTY(&ic->ic_vaps)) {
if_printf(ifp, "only 1 vap supported\n");
return NULL;
@@ -811,7 +814,8 @@ rt2560_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
}
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) {
m = ieee80211_beacon_alloc(ni, &rvp->ral_bo);
if (m == NULL) {
if_printf(ifp, "could not allocate beacon\n");
@@ -1343,7 +1347,8 @@ rt2560_beacon_expire(struct rt2560_softc *sc)
struct rt2560_tx_data *data;
if (ic->ic_opmode != IEEE80211_M_IBSS &&
- ic->ic_opmode != IEEE80211_M_HOSTAP)
+ ic->ic_opmode != IEEE80211_M_HOSTAP &&
+ ic->ic_opmode != IEEE80211_M_MBSS)
return;
data = &sc->bcnq.data[sc->bcnq.next];
@@ -2690,7 +2695,8 @@ rt2560_init_locked(struct rt2560_softc *sc)
tmp = RT2560_DROP_PHY_ERROR | RT2560_DROP_CRC_ERROR;
if (ic->ic_opmode != IEEE80211_M_MONITOR) {
tmp |= RT2560_DROP_CTL | RT2560_DROP_VERSION_ERROR;
- if (ic->ic_opmode != IEEE80211_M_HOSTAP)
+ if (ic->ic_opmode != IEEE80211_M_HOSTAP &&
+ ic->ic_opmode != IEEE80211_M_MBSS)
tmp |= RT2560_DROP_TODS;
if (!(ifp->if_flags & IFF_PROMISC))
tmp |= RT2560_DROP_NOT_TO_ME;
diff --git a/sys/dev/ral/rt2661.c b/sys/dev/ral/rt2661.c
index 8b30c9b..9e6693f 100644
--- a/sys/dev/ral/rt2661.c
+++ b/sys/dev/ral/rt2661.c
@@ -287,6 +287,7 @@ rt2661_attach(device_t dev, int id)
| IEEE80211_C_MONITOR /* monitor mode */
| IEEE80211_C_AHDEMO /* adhoc demo mode */
| IEEE80211_C_WDS /* 4-address traffic works */
+ | IEEE80211_C_MBSS /* mesh point link mode */
| IEEE80211_C_SHPREAMBLE /* short preamble supported */
| IEEE80211_C_SHSLOT /* short slot time supported */
| IEEE80211_C_WPA /* capable of WPA1+WPA2 */
@@ -387,6 +388,8 @@ rt2661_vap_create(struct ieee80211com *ic,
case IEEE80211_M_AHDEMO:
case IEEE80211_M_MONITOR:
case IEEE80211_M_HOSTAP:
+ case IEEE80211_M_MBSS:
+ /* XXXRP: TBD */
if (!TAILQ_EMPTY(&ic->ic_vaps)) {
if_printf(ifp, "only 1 vap supported\n");
return NULL;
@@ -818,7 +821,8 @@ rt2661_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
}
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) {
error = rt2661_prepare_beacon(sc, vap);
if (error != 0)
return error;
@@ -2428,7 +2432,8 @@ rt2661_init_locked(struct rt2661_softc *sc)
if (ic->ic_opmode != IEEE80211_M_MONITOR) {
tmp |= RT2661_DROP_CTL | RT2661_DROP_VER_ERROR |
RT2661_DROP_ACKCTS;
- if (ic->ic_opmode != IEEE80211_M_HOSTAP)
+ if (ic->ic_opmode != IEEE80211_M_HOSTAP &&
+ ic->ic_opmode != IEEE80211_M_MBSS)
tmp |= RT2661_DROP_TODS;
if (!(ifp->if_flags & IFF_PROMISC))
tmp |= RT2661_DROP_NOT_TO_ME;
diff --git a/sys/i386/conf/GENERIC b/sys/i386/conf/GENERIC
index 1baa581..02f5a36 100644
--- a/sys/i386/conf/GENERIC
+++ b/sys/i386/conf/GENERIC
@@ -270,6 +270,7 @@ device xe # Xircom pccard Ethernet
device wlan # 802.11 support
options IEEE80211_DEBUG # enable debug msgs
options IEEE80211_AMPDU_AGE # age frames in AMPDU reorder q's
+options IEEE80211_SUPPORT_MESH # enable 802.11s D3.0 support
device wlan_wep # 802.11 WEP support
device wlan_ccmp # 802.11 CCMP support
device wlan_tkip # 802.11 TKIP support
diff --git a/sys/net/if_media.h b/sys/net/if_media.h
index 616d0cb..1db2766 100644
--- a/sys/net/if_media.h
+++ b/sys/net/if_media.h
@@ -217,6 +217,7 @@ uint64_t ifmedia_baudrate(int);
#define IFM_IEEE80211_WDS 0x00000800 /* Operate in WDS mode */
#define IFM_IEEE80211_TURBO 0x00001000 /* Operate in turbo mode */
#define IFM_IEEE80211_MONITOR 0x00002000 /* Operate in monitor mode */
+#define IFM_IEEE80211_MBSS 0x00004000 /* Operate in MBSS mode */
/* operating mode for multi-mode devices */
#define IFM_IEEE80211_11A 0x00010000 /* 5Ghz, OFDM mode */
@@ -509,6 +510,7 @@ struct ifmedia_description {
{ IFM_IEEE80211_WDS, "wds" }, \
{ IFM_IEEE80211_TURBO, "turbo" }, \
{ IFM_IEEE80211_MONITOR, "monitor" }, \
+ { IFM_IEEE80211_MBSS, "mesh" }, \
{ 0, NULL }, \
}
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"
diff --git a/sys/pc98/conf/GENERIC b/sys/pc98/conf/GENERIC
index 34d6763..ef75694 100644
--- a/sys/pc98/conf/GENERIC
+++ b/sys/pc98/conf/GENERIC
@@ -223,6 +223,7 @@ device xe # Xircom pccard Ethernet
#device wlan # 802.11 support
#options IEEE80211_DEBUG # enable debug msgs
#options IEEE80211_AMPDU_AGE # age frames in AMPDU reorder q's
+#options IEEE80211_SUPPORT_MESH # enable 802.11s D3.0 support
#device wlan_wep # 802.11 WEP support
#device wlan_ccmp # 802.11 CCMP support
#device wlan_tkip # 802.11 TKIP support
diff --git a/sys/sparc64/conf/GENERIC b/sys/sparc64/conf/GENERIC
index 733f3a0..95be09a 100644
--- a/sys/sparc64/conf/GENERIC
+++ b/sys/sparc64/conf/GENERIC
@@ -201,6 +201,7 @@ device xl # 3Com 3c90x (``Boomerang'', ``Cyclone'')
device wlan # 802.11 support
options IEEE80211_DEBUG # enable debug msgs
options IEEE80211_AMPDU_AGE # age frames in AMPDU reorder q's
+options IEEE80211_SUPPORT_MESH # enable 802.11s D3.0 support
device wlan_wep # 802.11 WEP support
device wlan_ccmp # 802.11 CCMP support
device wlan_tkip # 802.11 TKIP support
diff --git a/tools/tools/nanobsd/gateworks/G2348 b/tools/tools/nanobsd/gateworks/G2348
index b33d3e5..cc691ad 100644
--- a/tools/tools/nanobsd/gateworks/G2348
+++ b/tools/tools/nanobsd/gateworks/G2348
@@ -93,6 +93,7 @@ device random # Entropy device
device wlan # 802.11 support
options IEEE80211_DEBUG # enable debugging msgs
options IEEE80211_AMPDU_AGE # age frames in AMPDU reorder q's
+options IEEE80211_SUPPORT_MESH
options IEEE80211_SUPPORT_TDMA
device wlan_wep # 802.11 WEP support
device wlan_ccmp # 802.11 CCMP support
diff --git a/tools/tools/nanobsd/gateworks/G2358 b/tools/tools/nanobsd/gateworks/G2358
index c7a9431..487b268 100644
--- a/tools/tools/nanobsd/gateworks/G2358
+++ b/tools/tools/nanobsd/gateworks/G2358
@@ -94,6 +94,7 @@ device random # Entropy device
device wlan # 802.11 support
options IEEE80211_DEBUG # enable debugging msgs
options IEEE80211_AMPDU_AGE # age frames in AMPDU reorder q's
+options IEEE80211_SUPPORT_MESH
options IEEE80211_SUPPORT_TDMA
device wlan_wep # 802.11 WEP support
device wlan_ccmp # 802.11 CCMP support
diff --git a/tools/tools/net80211/scripts/config b/tools/tools/net80211/scripts/config
index e501bc0..aa502e1 100644
--- a/tools/tools/net80211/scripts/config
+++ b/tools/tools/net80211/scripts/config
@@ -49,6 +49,9 @@ test -z "$WPA_PASSPHRASE" && WPA_PASSPHRASE='I am not a geek'
# default ssid for ap vaps
test -z "$SSID" && SSID=freebsd-ap
+# default meshid for mesh vaps
+test -z "$MESHID" && MESHID=freebsd-mesh
+
# directory to create files like hostapd.conf
test -z "$TMPDIR" && TMPDIR=.
diff --git a/tools/tools/net80211/scripts/mesh/common b/tools/tools/net80211/scripts/mesh/common
new file mode 100644
index 0000000..a8816a2
--- /dev/null
+++ b/tools/tools/net80211/scripts/mesh/common
@@ -0,0 +1,13 @@
+#! /bin/sh
+#
+# Common setup.
+#
+# $FreeBSD$
+#
+PATH=../:.:$PATH
+. config
+. config.mesh
+
+WLAN=`ifconfig wlan create wlanmode mesh wlandev $WIRELESS`
+ifconfig $WLAN meshid $MESHID channel $CHANNEL mtu 1500
+wlandebug -i $WLAN mesh+hwmp+state+scan+assoc
diff --git a/tools/tools/net80211/scripts/mesh/config.mesh b/tools/tools/net80211/scripts/mesh/config.mesh
new file mode 100644
index 0000000..c144218
--- /dev/null
+++ b/tools/tools/net80211/scripts/mesh/config.mesh
@@ -0,0 +1,17 @@
+#
+# Common configuration settings for mesh test scripts.
+#
+# $FreeBSD$
+#
+
+NODE1_IP=10.0.1.100
+NODE2_IP=10.0.1.101
+NODE3_IP=10.0.1.102
+NODE4_IP=10.0.1.103
+NODE5_IP=10.0.1.104
+
+NODE1_MAC=00:0b:6b:2d:dc:d8
+NODE2_MAC=00:0b:6b:2d:db:ac
+NODE3_MAC=00:0b:6b:2d:dd:17
+NODE4_MAC=00:0b:6b:87:1c:f0
+NODE5_MAC=00:14:a5:33:33:a7
diff --git a/tools/tools/net80211/scripts/mesh/setup.simple b/tools/tools/net80211/scripts/mesh/setup.simple
new file mode 100644
index 0000000..ee7b56e
--- /dev/null
+++ b/tools/tools/net80211/scripts/mesh/setup.simple
@@ -0,0 +1,13 @@
+#! /bin/sh
+#
+# Script for creating a simple Mesh Point.
+# Topology is fully connected if every node is a neighbor of each other.
+#
+# $FreeBSD$
+#
+PATH=../:.:$PATH
+. config
+. config.mesh
+
+. common
+ifconfig $WLAN up
diff --git a/tools/tools/net80211/scripts/mesh/topology.line b/tools/tools/net80211/scripts/mesh/topology.line
new file mode 100644
index 0000000..695f8bd
--- /dev/null
+++ b/tools/tools/net80211/scripts/mesh/topology.line
@@ -0,0 +1,38 @@
+#! /bin/sh
+#
+# Script for creating a mesh in line topology.
+#
+# node1 <-> node2 <-> node3 <-> node4 <-> node5
+#
+# $FreeBSD$
+#
+PATH=../:.:$PATH
+. config
+. config.mesh
+
+. common
+ifconfig $WLAN mac:allow
+MAC=`ifconfig $WLAN | grep ether | awk '{ print $2 }'`
+case $MAC in
+ $NODE1_MAC)
+ ifconfig $WLAN inet $NODE1_IP
+ ifconfig $WLAN mac:add $NODE2_MAC
+ ;;
+ $NODE2_MAC)
+ ifconfig $WLAN inet $NODE2_IP
+ ifconfig $WLAN mac:add $NODE1_MAC mac:add $NODE3_MAC
+ ;;
+ $NODE3_MAC)
+ ifconfig $WLAN inet $NODE3_IP
+ ifconfig $WLAN mac:add $NODE2_MAC mac:add $NODE4_MAC
+ ;;
+ $NODE4_MAC)
+ ifconfig $WLAN inet $NODE4_IP
+ ifconfig $WLAN mac:add $NODE3_MAC mac:add $NODE5_MAC
+ ;;
+ $NODE5_MAC)
+ ifconfig $WLAN inet $NODE5_IP
+ ifconfig $WLAN mac:add $NODE4_MAC
+ ;;
+esac
+ifconfig $WLAN up
diff --git a/tools/tools/net80211/scripts/mesh/topology.ring b/tools/tools/net80211/scripts/mesh/topology.ring
new file mode 100644
index 0000000..1810f3c
--- /dev/null
+++ b/tools/tools/net80211/scripts/mesh/topology.ring
@@ -0,0 +1,40 @@
+#! /bin/sh
+#
+# Script for creating a mesh in ring topology.
+#
+# node1 - node2 - node3 - node4 - node5
+# ^ |
+# \-------------------------------
+#
+# $FreeBSD$
+#
+PATH=../:.:$PATH
+. config
+. config.mesh
+
+. common
+ifconfig $WLAN mac:allow
+MAC=`ifconfig $WLAN | grep ether | awk '{ print $2 }'`
+case $MAC in
+ $NODE1_MAC)
+ ifconfig $WLAN inet $NODE1_IP
+ ifconfig $WLAN mac:add $NODE2_MAC mac:add $NODE5_MAC
+ ;;
+ $NODE2_MAC)
+ ifconfig $WLAN inet $NODE2_IP
+ ifconfig $WLAN mac:add $NODE1_MAC mac:add $NODE3_MAC
+ ;;
+ $NODE3_MAC)
+ ifconfig $WLAN inet $NODE3_IP
+ ifconfig $WLAN mac:add $NODE2_MAC mac:add $NODE4_MAC
+ ;;
+ $NODE4_MAC)
+ ifconfig $WLAN inet $NODE4_IP
+ ifconfig $WLAN mac:add $NODE3_MAC mac:add $NODE5_MAC
+ ;;
+ $NODE5_MAC)
+ ifconfig $WLAN inet $NODE5_IP
+ ifconfig $WLAN mac:add $NODE1_MAC mac:add $NODE4_MAC
+ ;;
+esac
+ifconfig $WLAN up
diff --git a/tools/tools/net80211/scripts/mesh/topology.star b/tools/tools/net80211/scripts/mesh/topology.star
new file mode 100644
index 0000000..a7396a0
--- /dev/null
+++ b/tools/tools/net80211/scripts/mesh/topology.star
@@ -0,0 +1,38 @@
+#! /bin/sh
+#
+# Script for creating a mesh in star topology.
+# Node 3 will be the center.
+#
+# $FreeBSD$
+#
+PATH=../:.:$PATH
+. config
+. config.mesh
+
+. common
+ifconfig $WLAN mac:allow
+MAC=`ifconfig $WLAN | grep ether | awk '{ print $2 }'`
+case $MAC in
+ $NODE1_MAC)
+ ifconfig $WLAN inet $NODE1_IP
+ ifconfig $WLAN mac:add $NODE3_MAC
+ ;;
+ $NODE2_MAC)
+ ifconfig $WLAN inet $NODE2_IP
+ ifconfig $WLAN mac:add $NODE3_MAC
+ ;;
+ $NODE3_MAC)
+ ifconfig $WLAN inet $NODE3_IP
+ ifconfig $WLAN mac:add $NODE1_MAC mac:add $NODE2_MAC mac:add \
+ $NODE4_MAC mac:add $NODE5_MAC
+ ;;
+ $NODE4_MAC)
+ ifconfig $WLAN inet $NODE4_IP
+ ifconfig $WLAN mac:add $NODE3_MAC
+ ;;
+ $NODE5_MAC)
+ ifconfig $WLAN inet $NODE5_IP
+ ifconfig $WLAN mac:add $NODE3_MAC
+ ;;
+esac
+ifconfig $WLAN up
diff --git a/tools/tools/net80211/scripts/mesh/topology.tree b/tools/tools/net80211/scripts/mesh/topology.tree
new file mode 100644
index 0000000..9a2ed32
--- /dev/null
+++ b/tools/tools/net80211/scripts/mesh/topology.tree
@@ -0,0 +1,47 @@
+#! /bin/sh
+#
+# Script for creating a mesh in tree (hirearchical) topology.
+#
+# node1
+# ^
+# |
+# / \
+# v v
+# node2 node3
+# ^ ^
+# | |
+# v v
+# node4 node5
+#
+# $FreeBSD$
+#
+PATH=../:.:$PATH
+. config
+. config.mesh
+
+. common
+ifconfig $WLAN mac:allow
+MAC=`ifconfig $WLAN | grep ether | awk '{ print $2 }'`
+case $MAC in
+ $NODE1_MAC)
+ ifconfig $WLAN inet $NODE1_IP
+ ifconfig $WLAN mac:add $NODE2_MAC mac:add $NODE3_MAC
+ ;;
+ $NODE2_MAC)
+ ifconfig $WLAN inet $NODE2_IP
+ ifconfig $WLAN mac:add $NODE1_MAC mac:add $NODE4_MAC
+ ;;
+ $NODE3_MAC)
+ ifconfig $WLAN inet $NODE3_IP
+ ifconfig $WLAN mac:add $NODE1_MAC mac:add $NODE5_MAC
+ ;;
+ $NODE4_MAC)
+ ifconfig $WLAN inet $NODE4_IP
+ ifconfig $WLAN mac:add $NODE2_MAC
+ ;;
+ $NODE5_MAC)
+ ifconfig $WLAN inet $NODE5_IP
+ ifconfig $WLAN mac:add $NODE3_MAC
+ ;;
+esac
+ifconfig $WLAN up
diff --git a/tools/tools/net80211/wlanstats/wlanstats.c b/tools/tools/net80211/wlanstats/wlanstats.c
index dd8e106..1d4ef33 100644
--- a/tools/tools/net80211/wlanstats/wlanstats.c
+++ b/tools/tools/net80211/wlanstats/wlanstats.c
@@ -322,7 +322,27 @@ static const struct fmt wlanstats[] = {
{ 4, "ht_downgrade", "ht_downgrade", "HT station downgraded to legacy operation" },
#define S_HT_ASSOC_NORATE AFTER(S_HT_ASSOC_DOWNGRADE)
{ 4, "ht_norate", "ht_norate", "HT station rejected because of HT rate set" },
-#define S_INPUT AFTER(S_HT_ASSOC_NORATE)
+#define S_MESH_WRONGMESH AFTER(S_HT_ASSOC_NORATE)
+ { 4, "mesh_wrong", "mesh_wrong", "frame discarded because sender not a mesh sta" },
+#define S_MESH_NOLINK AFTER(S_MESH_WRONGMESH)
+ { 4, "mesh_nolink", "mesh_nolink", "frame discarded because link not established" },
+#define S_MESH_FWD_TTL AFTER(S_MESH_NOLINK)
+ { 4, "mesh_fwd_ttl", "mesh_fwd_ttl", "frame not forwarded because TTL zero" },
+#define S_MESH_FWD_NOBUF AFTER(S_MESH_FWD_TTL)
+ { 4, "mesh_fwd_nobuf", "mesh_fwd_nobuf", "frame not forwarded because mbuf could not be allocated" },
+#define S_MESH_FWD_TOOSHORT AFTER(S_MESH_FWD_NOBUF)
+ { 4, "mesh_fwd_tooshort", "mesh_fwd_tooshort", "frame not forwarded because too short to have 802.11 header" },
+#define S_MESH_FWD_DISABLED AFTER(S_MESH_FWD_TOOSHORT)
+ { 4, "mesh_fwd_disabled", "mesh_fwd_disabled", "frame not forwarded because administratively disabled" },
+#define S_MESH_FWD_NOPATH AFTER(S_MESH_FWD_DISABLED)
+ { 4, "mesh_fwd_nopath", "mesh_fwd_nopath", "frame not forwarded because no path found to destination" },
+#define S_HWMP_WRONGSEQ AFTER(S_MESH_FWD_NOPATH)
+ { 4, "hwmp_wrongseq", "hwmp_wrongseq", "frame discarded because mesh sequence number is invalid" },
+#define S_HWMP_ROOTREQS AFTER(S_HWMP_WRONGSEQ)
+ { 4, "hwmp_rootreqs", "hwmp_rootreqs", "root PREQ frames sent" },
+#define S_HWMP_ROOTANN AFTER(S_HWMP_ROOTREQS)
+ { 4, "hwmp_rootann", "hwmp_rootann", "root RANN frames received" },
+#define S_INPUT AFTER(S_HWMP_ROOTANN)
{ 8, "input", "input", "total data frames received" },
#define S_RX_UCAST AFTER(S_INPUT)
{ 8, "rx_ucast", "rx_ucast", "unicast data frames received" },
@@ -766,6 +786,16 @@ wlan_get_curstat(struct statfoo *sf, int s, char b[], size_t bs)
case S_HT_ASSOC_NOHTCAP:STAT(ht_assoc_nohtcap);
case S_HT_ASSOC_DOWNGRADE:STAT(ht_assoc_downgrade);
case S_HT_ASSOC_NORATE: STAT(ht_assoc_norate);
+ case S_MESH_WRONGMESH: STAT(mesh_wrongmesh);
+ case S_MESH_NOLINK: STAT(mesh_nolink);
+ case S_MESH_FWD_TTL: STAT(mesh_fwd_ttl);
+ case S_MESH_FWD_NOBUF: STAT(mesh_fwd_nobuf);
+ case S_MESH_FWD_TOOSHORT: STAT(mesh_fwd_tooshort);
+ case S_MESH_FWD_DISABLED: STAT(mesh_fwd_disabled);
+ case S_MESH_FWD_NOPATH: STAT(mesh_fwd_nopath);
+ case S_HWMP_WRONGSEQ: STAT(hwmp_wrongseq);
+ case S_HWMP_ROOTREQS: STAT(hwmp_rootreqs);
+ case S_HWMP_ROOTANN: STAT(hwmp_rootrann);
case S_INPUT: NSTAT(rx_data);
case S_OUTPUT: NSTAT(tx_data);
case S_RX_UCAST: NSTAT(rx_ucast);
@@ -910,6 +940,16 @@ wlan_get_totstat(struct statfoo *sf, int s, char b[], size_t bs)
case S_HT_ASSOC_NOHTCAP:STAT(ht_assoc_nohtcap);
case S_HT_ASSOC_DOWNGRADE:STAT(ht_assoc_downgrade);
case S_HT_ASSOC_NORATE: STAT(ht_assoc_norate);
+ case S_MESH_WRONGMESH: STAT(mesh_wrongmesh);
+ case S_MESH_NOLINK: STAT(mesh_nolink);
+ case S_MESH_FWD_TTL: STAT(mesh_fwd_ttl);
+ case S_MESH_FWD_NOBUF: STAT(mesh_fwd_nobuf);
+ case S_MESH_FWD_TOOSHORT: STAT(mesh_fwd_tooshort);
+ case S_MESH_FWD_DISABLED: STAT(mesh_fwd_disabled);
+ case S_MESH_FWD_NOPATH: STAT(mesh_fwd_nopath);
+ case S_HWMP_WRONGSEQ: STAT(hwmp_wrongseq);
+ case S_HWMP_ROOTREQS: STAT(hwmp_rootreqs);
+ case S_HWMP_ROOTANN: STAT(hwmp_rootrann);
case S_INPUT: NSTAT(rx_data);
case S_OUTPUT: NSTAT(tx_data);
case S_RX_UCAST: NSTAT(rx_ucast);
OpenPOWER on IntegriCloud