From 59aa14a91db84e940b6732cd1ff069f0268793bf Mon Sep 17 00:00:00 2001 From: Rui Paulo Date: Sat, 11 Jul 2009 15:02:45 +0000 Subject: [PATCH] Implementation of the upcoming Wireless Mesh standard, 802.11s, on the net80211 wireless stack. This work is based on the March 2009 D3.0 draft standard. This standard is expected to become final next year. This includes two main net80211 modules, ieee80211_mesh.c which deals with peer link management, link metric calculation, routing table control and mesh configuration and ieee80211_hwmp.c which deals with the actually routing process on the mesh network. HWMP is the mandatory routing protocol on by the mesh standard, but others, such as RA-OLSR, can be implemented. Authentication and encryption are not implemented. There are several scripts under tools/tools/net80211/scripts that can be used to test different mesh network topologies and they also teach you how to setup a mesh vap (for the impatient: ifconfig wlan0 create wlandev ... wlanmode mesh). A new build option is available: IEEE80211_SUPPORT_MESH and it's enabled by default on GENERIC kernels for i386, amd64, sparc64 and pc98. Drivers that support mesh networks right now are: ath, ral and mwl. More information at: http://wiki.freebsd.org/WifiMesh Please note that this work is experimental. Also, please note that bridging a mesh vap with another network interface is not yet supported. Many thanks to the FreeBSD Foundation for sponsoring this project and to Sam Leffler for his support. Also, I would like to thank Gateworks Corporation for sending me a Cambria board which was used during the development of this project. Reviewed by: sam Approved by: re (kensmith) Obtained from: projects/mesh11s --- sbin/ifconfig/ifconfig.8 | 87 +- sbin/ifconfig/ifieee80211.c | 427 ++- sbin/ifconfig/ifmedia.c | 3 +- share/man/man4/ath.4 | 11 +- share/man/man4/mwl.4 | 11 +- share/man/man4/ral.4 | 5 +- share/man/man4/wlan.4 | 14 +- sys/amd64/conf/GENERIC | 1 + sys/conf/NOTES | 2 + sys/conf/files | 3 + sys/conf/options | 1 + sys/dev/ath/if_ath.c | 39 +- sys/dev/ath/if_athvar.h | 1 + sys/dev/mwl/if_mwl.c | 17 +- sys/dev/ral/rt2560.c | 12 +- sys/dev/ral/rt2661.c | 9 +- sys/i386/conf/GENERIC | 1 + sys/net/if_media.h | 2 + sys/net80211/_ieee80211.h | 3 +- sys/net80211/ieee80211.c | 8 + sys/net80211/ieee80211.h | 36 + sys/net80211/ieee80211_action.c | 14 +- sys/net80211/ieee80211_ddb.c | 73 +- sys/net80211/ieee80211_freebsd.h | 13 + sys/net80211/ieee80211_hwmp.c | 1389 +++++++++ sys/net80211/ieee80211_input.c | 25 +- sys/net80211/ieee80211_ioctl.c | 19 +- sys/net80211/ieee80211_ioctl.h | 58 +- sys/net80211/ieee80211_mesh.c | 2538 +++++++++++++++++ sys/net80211/ieee80211_mesh.h | 505 ++++ sys/net80211/ieee80211_node.c | 73 +- sys/net80211/ieee80211_node.h | 31 +- sys/net80211/ieee80211_output.c | 303 +- sys/net80211/ieee80211_proto.c | 15 +- sys/net80211/ieee80211_proto.h | 8 + sys/net80211/ieee80211_scan.c | 1 + sys/net80211/ieee80211_scan.h | 3 + sys/net80211/ieee80211_scan_sta.c | 188 +- sys/net80211/ieee80211_var.h | 15 +- sys/pc98/conf/GENERIC | 1 + sys/sparc64/conf/GENERIC | 1 + tools/tools/nanobsd/gateworks/G2348 | 1 + tools/tools/nanobsd/gateworks/G2358 | 1 + tools/tools/net80211/scripts/config | 3 + tools/tools/net80211/scripts/mesh/common | 13 + tools/tools/net80211/scripts/mesh/config.mesh | 17 + .../tools/net80211/scripts/mesh/setup.simple | 13 + .../tools/net80211/scripts/mesh/topology.line | 38 + .../tools/net80211/scripts/mesh/topology.ring | 40 + .../tools/net80211/scripts/mesh/topology.star | 38 + .../tools/net80211/scripts/mesh/topology.tree | 47 + tools/tools/net80211/wlanstats/wlanstats.c | 42 +- 52 files changed, 6014 insertions(+), 205 deletions(-) create mode 100644 sys/net80211/ieee80211_hwmp.c create mode 100644 sys/net80211/ieee80211_mesh.c create mode 100644 sys/net80211/ieee80211_mesh.h create mode 100644 tools/tools/net80211/scripts/mesh/common create mode 100644 tools/tools/net80211/scripts/mesh/config.mesh create mode 100644 tools/tools/net80211/scripts/mesh/setup.simple create mode 100644 tools/tools/net80211/scripts/mesh/topology.line create mode 100644 tools/tools/net80211/scripts/mesh/topology.ring create mode 100644 tools/tools/net80211/scripts/mesh/topology.star create mode 100644 tools/tools/net80211/scripts/mesh/topology.tree diff --git a/sbin/ifconfig/ifconfig.8 b/sbin/ifconfig/ifconfig.8 index 5f571f83973f..1d2f09644480 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 dbbc195e749d..2b196a615abb 100644 --- a/sbin/ifconfig/ifieee80211.c +++ b/sbin/ifconfig/ifieee80211.c @@ -81,6 +81,7 @@ #include #include #include +#include #include #include @@ -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; @@ -574,6 +576,20 @@ set80211ssid(const char *val, int d, int s, const struct afswtch *rafp) set80211(s, IEEE80211_IOC_SSID, ssid, len, data); } +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) { @@ -1261,6 +1277,66 @@ DECL_CMD_FUNC(set80211maccmd, val, d) set80211(s, IEEE80211_IOC_MACCMD, d, 0, NULL); } +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) { @@ -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("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"); @@ -3189,6 +3387,28 @@ list_stations(int s) } while (len >= sizeof(struct ieee80211req_sta_info)); } +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(®domain, 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 @@ ieee80211_status(int s) 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 d89f3eb61155..bb95043ca694 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 9d15a7bd4918..cfc256c84d83 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 b31191b06685..32138c89d2fe 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 7f52c52f3a15..ef8f8b2f5bbc 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 dffefabc4563..c3a2b1663037 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 43b396092837..73a4fb66e4d1 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 e191a23d4c69..e7e21a119ae4 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 4d3094bea3e3..b28f5b3003ca 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 be59f53cd269..f905d6fce061 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 7d0eb5ebce43..c201265d6c05 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 6a1ece223daa..0bd3ea2f5cb2 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 dbbdca728352..c320cdb49c3c 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 7bb785c371a6..455b5ad8e723 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 8b30c9b34bbc..9e6693f43d18 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 1baa5810c90f..02f5a36ddabd 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 616d0cbe1c1a..1db27666e219 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 163e225d3220..c488c006abd3 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 a8879972d01e..1cc4964bd445 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 d1c76582d845..5ddca24e69f4 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 a694d257df25..5371f6e975d0 100644 --- a/sys/net80211/ieee80211_action.c +++ b/sys/net80211/ieee80211_action.c @@ -47,6 +47,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include 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 6d7093077b3e..57447d50f044 100644 --- a/sys/net80211/ieee80211_ddb.c +++ b/sys/net80211/ieee80211_ddb.c @@ -50,6 +50,9 @@ __FBSDID("$FreeBSD$"); #ifdef IEEE80211_SUPPORT_TDMA #include #endif +#ifdef IEEE80211_SUPPORT_MESH +#include +#endif #include #include @@ -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 \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)); } } @@ -809,8 +838,44 @@ _db_show_txparams(const char *tag, const void *arg, db_printf(" maxretry %d", tp->maxretry); } +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 f3a224c5ead7..6a8875a6c75c 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 000000000000..5a369590ea7b --- /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 +#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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +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(¶ms, 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, ¶ms); +} + +#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 5e50f102476d..8209d2e65418 100644 --- a/sys/net80211/ieee80211_input.c +++ b/sys/net80211/ieee80211_input.c @@ -46,6 +46,9 @@ __FBSDID("$FreeBSD$"); #include #include +#ifdef IEEE80211_SUPPORT_MESH +#include +#endif #include @@ -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 7712b04ad120..35c79819bdfa 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 db43778cd4e5..723d3da35717 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]; }; @@ -304,6 +317,27 @@ struct ieee80211req_maclist { uint8_t ml_macaddr[IEEE80211_ADDR_LEN]; } __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 @@ -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 000000000000..6e7fa8d277cc --- /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 +#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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +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(¶ms, 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, + ¶ms); +} + +#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 000000000000..595eece24906 --- /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 7fae394e1bcf..30508d963e7d 100644 --- a/sys/net80211/ieee80211_node.c +++ b/sys/net80211/ieee80211_node.c @@ -50,9 +50,15 @@ __FBSDID("$FreeBSD$"); #include #endif #include +#include #include +/* + * 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. */ @@ -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]; @@ -950,6 +972,13 @@ node_cleanup(struct ieee80211_node *ni) #ifdef IEEE80211_SUPPORT_SUPERG 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 6d267591a1d4..63b235528972 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,12 +81,27 @@ 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 */ int len; /* data size in bytes */ }; +/* + * 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 @@ -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 ef74efc6432e..96b2d45e2589 100644 --- a/sys/net80211/ieee80211_output.c +++ b/sys/net80211/ieee80211_output.c @@ -55,6 +55,7 @@ __FBSDID("$FreeBSD$"); #include #endif #include +#include #ifdef INET #include @@ -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 @@ ieee80211_output(struct ifnet *ifp, struct mbuf *m, * 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 @@ ieee80211_fragment(struct ieee80211vap *vap, struct mbuf *m0, /* * 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), @@ -2083,6 +2237,10 @@ ieee80211_alloc_proberesp(struct ieee80211_node *bss, int legacy) + 4 + sizeof(struct ieee80211_ie_htinfo) #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? @@ -2545,6 +2721,10 @@ ieee80211_beacon_alloc(struct ieee80211_node *ni, #ifdef IEEE80211_SUPPORT_TDMA + (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 ; @@ -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 9f80cdffd024..9aabfcff8c67 100644 --- a/sys/net80211/ieee80211_proto.c +++ b/sys/net80211/ieee80211_proto.c @@ -50,6 +50,9 @@ __FBSDID("$FreeBSD$"); #include #include #include +#ifdef IEEE80211_SUPPORT_MESH +#include +#endif #include #include @@ -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 de043c3869ad..9bfbc61715fa 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 951e7623aea4..ab30c290977f 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 ac4447427701..4c5e869a79c8 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 367ecce45555..a76dd3c740c2 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 #endif +#ifdef IEEE80211_SUPPORT_MESH +#include +#endif #include @@ -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 @@ sta_add(struct ieee80211_scan_state *ss, 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 @@ check_rate(struct ieee80211vap *vap, const struct ieee80211_channel *chan, #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, }; - -/* - * Module glue. - */ -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); + +#ifdef IEEE80211_SUPPORT_MESH +/* + * Pick an mbss network to join or find a channel + * to use to start an mbss network. + */ +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 833c696527c4..3d23f4780d08 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 34d67630cc32..ef75694ca50c 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 733f3a067f0e..95be09ad6234 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 b33d3e5b2a16..cc691adda4b7 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 c7a9431e93fa..487b268ffa6e 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 e501bc0bd2a1..aa502e14d32d 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 000000000000..a8816a2cf190 --- /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 000000000000..c144218c0668 --- /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 000000000000..ee7b56e06763 --- /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 000000000000..695f8bdc11d0 --- /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 000000000000..1810f3c05495 --- /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 000000000000..a7396a0b31b3 --- /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 000000000000..9a2ed326d844 --- /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 dd8e1063f8d7..1d4ef3382cb0 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);