diff options
author | sam <sam@FreeBSD.org> | 2008-04-20 20:35:46 +0000 |
---|---|---|
committer | sam <sam@FreeBSD.org> | 2008-04-20 20:35:46 +0000 |
commit | 3569e353ca63336d80ab0143dd9669b0b9e6b123 (patch) | |
tree | bc7985c57e7ecfa1ac03e48c406a25430dba634b /sbin/ifconfig | |
parent | 682b4ae9be70192e298129ada878af3486683aaf (diff) | |
download | FreeBSD-src-3569e353ca63336d80ab0143dd9669b0b9e6b123.zip FreeBSD-src-3569e353ca63336d80ab0143dd9669b0b9e6b123.tar.gz |
Multi-bss (aka vap) support for 802.11 devices.
Note this includes changes to all drivers and moves some device firmware
loading to use firmware(9) and a separate module (e.g. ral). Also there
no longer are separate wlan_scan* modules; this functionality is now
bundled into the wlan module.
Supported by: Hobnob and Marvell
Reviewed by: many
Obtained from: Atheros (some bits)
Diffstat (limited to 'sbin/ifconfig')
-rw-r--r-- | sbin/ifconfig/Makefile | 9 | ||||
-rw-r--r-- | sbin/ifconfig/ifconfig.8 | 715 | ||||
-rw-r--r-- | sbin/ifconfig/ifieee80211.c | 1333 | ||||
-rw-r--r-- | sbin/ifconfig/ifmedia.c | 16 | ||||
-rw-r--r-- | sbin/ifconfig/regdomain.c | 636 | ||||
-rw-r--r-- | sbin/ifconfig/regdomain.h | 112 |
6 files changed, 2491 insertions, 330 deletions
diff --git a/sbin/ifconfig/Makefile b/sbin/ifconfig/Makefile index 09592ad..1f5cfed 100644 --- a/sbin/ifconfig/Makefile +++ b/sbin/ifconfig/Makefile @@ -23,7 +23,10 @@ SRCS+= ifclone.c # clone device support SRCS+= ifmac.c # MAC support SRCS+= ifmedia.c # SIOC[GS]IFMEDIA support SRCS+= ifvlan.c # SIOC[GS]ETVLAN support -SRCS+= ifieee80211.c # SIOC[GS]IEEE80211 support + +SRCS+= ifieee80211.c regdomain.c # SIOC[GS]IEEE80211 support +DPADD+= ${LIBBSDXML} ${LIBSBUF} +LDADD+= -lbsdxml -lsbuf SRCS+= ifcarp.c # SIOC[GS]VH support SRCS+= ifgroup.c # ... @@ -34,8 +37,8 @@ SRCS+= iflagg.c # lagg support .if ${MK_IPX_SUPPORT} != "no" && !defined(RELEASE_CRUNCH) SRCS+= af_ipx.c # IPX support -DPADD= ${LIBIPX} -LDADD= -lipx +DPADD+= ${LIBIPX} +LDADD+= -lipx .endif MAN= ifconfig.8 diff --git a/sbin/ifconfig/ifconfig.8 b/sbin/ifconfig/ifconfig.8 index ffa3ba1..22adfaa 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 February 29, 2008 +.Dd April 11, 2008 .Dt IFCONFIG 8 .Os .Sh NAME @@ -584,8 +584,137 @@ If the interface was reset when previously marked down, the hardware will be re-initialized. .El .Pp -The following parameters are specific to IEEE 802.11 wireless interfaces: +The following parameters are specific to cloning +IEEE 802.11 wireless interfaces with the +.Cm create +request: +.Bl -tag -width indent +.It Cm wlandev Ar device +Use +.Ar device +as the parent for the cloned device. +.It Cm wlanmode Ar mode +Specify the operating mode for this cloned device. +.Ar mode +is one of +.Cm sta , +.Cm ahdemo +(or +.Cm adhoc-demo ), +.Cm ibss , +(or +.Cm adhoc ), +.Cm ap , +(or +.Cm hostap ), +.Cm wds , +and +.Cm monitor . +The operating mode of a cloned interface cannot be changed. +.It Cm wlanbssid Ar bssid +The 802.11 mac address to use for the bssid. +This must be specified at create time for a legacy +.Cm wds +device. +.It Cm wlanaddr Ar address +The local mac address. +If this is not specified then a mac address will automatically be assigned +to the cloned device. +Typically this address is the same as the address of the parent device +but if the +.Cm bssid +parameter is specified then the driver will craft a unique address for +the device (if supported). +.It Cm wdslegacy +Mark a +.Cm wds +device as operating in ``legacy mode''. +Legacy +.Cm wds +devices have a fixed peer relationship and do not, for example, roam +if their peer stops communicating. +For completeness a Dynamic WDS (DWDS) interface may marked as +.Fl wdslegacy . +.It Cm bssid +Request a unique local mac address for the cloned device. +This is only possible if the device supports multiple mac addresses. +To force use of the parent's mac address use +.Fl bssid . +.It Cm beacons +Mark the cloned interface as depending on hardware support to +track received beacons. +To have beacons tracked in software use +.Fl beacons . +For +.Cm hostap +mode +.Fl beacons +can also be used to indicate no beacons should +be transmitted; this can be useful when creating a WDS configuration but +.Cm wds +interfaces can only be created as companions to an access point. +.El +.Pp +The following parameters are specific to IEEE 802.11 wireless interfaces +cloned with a +.Cm create +operation: .Bl -tag -width indent +.It Cm ampdu +Enable sending and receiving AMPDU frames when using 802.11n (default). +The 802.11n specification states a compliant station must be capable +of receiving AMPDU frames but transmision is optional. +Use +.Fl ampdu +to disable all use of AMPDU with 802.11n. +For testing and/or to work around interoperability problems one can use +.Cm ampdutx +and +.Cm ampdurx +to control use of AMPDU in one direction. +.It Cm ampdudensity Ar density +Set the AMPDU density parameter used when operating with 802.11n. +This parameter controls the inter-packet gap for AMPDU frames. +The sending device normally controls this setting but a receiving station +may request wider gaps. +Legal values for +.Ar density +are 0, .25, .5, 1, 2, 4, 8, and 16 (microseconds). +A value of +.Cm - +is treated the same as 0. +.It Cm ampdulimit Ar limit +Set the limit on packet size for receiving AMPDU frames when operating +with 802.11n. +Legal values for +.Ar limit +are 8192, 16384, 32768, and 65536 but one can also specify +just the unique prefix: 8, 16, 32, 64. +Note the sender may limit the size of AMPDU frames to be less +than the maximum specified by the receiving station. +.It Cm amsdu +Enable sending and receiving AMSDU frames when using 802.11n. +By default AMSDU is received but not transmitted. +Use +.Fl amsdu +to disable all use of AMSDU with 802.11n. +For testing and/or to work around interoperability problems one can use +.Cm amsdutx +and +.Cm amsdurx +to control use of AMSDU in one direction. +.It Cm amsdulimit Ar limit +Set the limit on packet size for sending and receiving AMSDU frames +when operating with 802.11n. +Legal values for +.Ar limit +are 7935 and 3839 (bytes). +Note the sender may limit the size of AMSDU frames to be less +than the maximum specified by the receiving station. +Note also that devices are not required to support the 7935 limit, +only 3839 is required by the specification and the larger value +may require more memory to be dedicated to support functionality +that is rarely used. .It Cm apbridge When operating as an access point, pass packets between wireless clients directly (default). @@ -597,7 +726,7 @@ is useful when traffic is to be processed with packet filtering. .It Cm authmode Ar mode Set the desired authentication mode in infrastructure mode. -Not all adaptors support all modes. +Not all adapters support all modes. The set of valid modes is .Cm none , open , shared @@ -725,7 +854,7 @@ or .Cm - will give you the default for your adaptor. Some -adaptors ignore this setting unless you are in ad-hoc mode. +adapters ignore this setting unless you are in ad-hoc mode. Alternatively the frequency, in megahertz, may be specified instead of the channel number. .Pp @@ -770,9 +899,61 @@ a 40MHz HT channel specification may include the location of the extension channel by appending ``+'' or ``-'' for above and below, respectively; e.g. ``2437:ht/40+'' specifies 40MHz wide HT operation with the center channel at frequency 2437 and the extension channel above. +.It Cm country Ar name +Set the country code to use in calculating the regulatory constraints +for operation. +In particular the set of available channels, how the wireless device +will operation on the channels, and the maximum transmit power that +can be used on a channel are defined by this setting. +Country/Region codes are specified as a 2-character abbreviation +defined by ISO 3166 or using a longer, but possibly ambiguous, spelling; +e.g. "ES" and "Spain". +The set of country codes are taken from /etc/regdomain.xml and can also +be viewed with the ``list countries'' request. +Note that not all devices support changing the country code from a default +setting; typically stored in EEPROM. +See also +.Cm regdomain , +.Cm indoor , +.Cm outdoor , +and +.Cm anywhere . +.It Cm dfs +Enable Dynamic Frequency Selection (DFS) as specified in 802.11h. +DFS embodies several facilities including detection of overlapping +radar signals, dynamic transmit power control, and channel selection +according to a least-congested criteria. +DFS support is mandatory for some 5Ghz frequencies in certain +locales (e.g. ETSI). +By default DFS is enabled according to the regulatory definitions +specified in /etc/regdomain.xml and the curent country code, regdomain, +and channel. +Note the underlying device (and driver) must support radar detection +for full DFS support to work. +To be fully compliant with the local regulatory agency frequencies that +require DFS should not be used unless it is fully supported. +Use +.Fl dfs +to disable this functionality for testing. +.It Cm dotd +Enable support for the 802.11d specification (default). +When this support is enabled in station mode, beacon frames that advertise +a country code different than the currently configured country code will +cause an event to be dispatched to user applications. +This event can be used by the station to adopt that country code and +operate according to the associated regulatory constraints. +When operating as an access point with 802.11d enabled the beacon and +probe response frames transmitted will advertise the current regulatory +domain settings. +To disable 802.11d use +.Fl dotd . .It Cm doth -Enable inclusion of an 802.11h country information element in beacon -frames transmitted when operating as an access point. +Enable 802.11h support including spectrum management. +When 802.11h is enabled beacon and probe response frames will have +the SpectrumMgt bit set in the capabilities field and +country and power constraint information elements will be present. +802.11h support also includes handling Channel Switch Announcements (CSA) +which are a mechanism to coordinate channel changes by an access point. By default 802.11h is enabled if the device is capable. To disable 802.11h use .Fl doth . @@ -811,6 +992,48 @@ channels depending on the regulatory constraints; use the command to identify the channels where turbo mode may be used. To disable Dynamic Turbo mode use .Fl dturbo . +.It Cm dwds +Enable Dynamic WDS (DWDS) support. +DWDS is a facility by which 4-address traffic can be carried between +stations operating in infrastructure mode. +A station first associates to an access point and authenticates using +normal procedures (e.g. WPA). +Then 4-address frames are passed to carry traffic for stations +operating on either side of the wireless link. +DWDS extends the normal WDS mechanism by leveraging existing security +protocols and eliminating static binding. +.Pp +When DWDS is enabled on an access point 4-address frames received from +an authorized station will generate a ``DWDS discovery'' event to user +applications. +This event should be used to create a WDS interface that is bound +to the remote station (and usually plumbed into a bridge). +Once the WDS interface is up and running 4-address traffic then logically +flows through that interface. +.Pp +When DWDS is enabled on a station, traffic with a destination address +different from the peer station are encapsulated in a 4-address frame +and transmitted to the peer. +All 4-address traffic uses the security information of the stations +(e.g. cryptographic keys). +A station is associated using 802.11n facilities may transport +4-address traffic using these same mechanisms; this depends on available +resources and capabilities of the device. +The DWDS implementation guards against layer 2 routing loops of +multicast traffic. +.It Cm ff +Enable the use of Atheros Fast Frames when communicating with +another Fast Frames-capable station. +Fast Frames are an encapsulation technique by which two 802.3 +frames are transmitted in a single 802.11 frame. +This can noticeably improve throughput but requires that the +receiving station understand how to decapsulate the frame. +Fast frame use is negotiated using the Atheros 802.11 vendor-specific +protocol extension so enabling use is safe when communicating with +non-Atheros devices. +By default, use of fast frames is enabled if the device is capable. +To explicitly disable fast frames, use +.Fl ff . .It Cm fragthreshold Ar length Set the threshold for which transmitted frames are broken into fragments. The @@ -824,7 +1047,7 @@ to or .Cm - disables transmit fragmentation. -Not all adaptors honor the fragmentation threshold. +Not all adapters honor the fragmentation threshold. .It Cm hidessid When operating as an access point, do not broadcast the SSID in beacon frames or respond to probe request frames unless @@ -833,19 +1056,76 @@ By default, the SSID is included in beacon frames and undirected probe request frames are answered. To re-enable the broadcast of the SSID etc., use .Fl hidessid . -.It Cm ff -Enable the use of Atheros Fast Frames when communicating with -another Fast Frames-capable station. -Fast Frames are an encapsulation technique by which two 802.3 -frames are transmitted in a single 802.11 frame. -This can noticeably improve throughput but requires that the -receiving station understand how to decapsulate the frame. -Fast frame use is negotiated using the Atheros 802.11 vendor-specific -protocol extension so enabling use is safe when communicating with -non-Atheros devices. -By default, use of fast frames is enabled if the device is capable. -To explicitly disable fast frames, use -.Fl ff . +.It Cm ht +Enable use of High Throughput (HT) when using 802.11n (default). +The 802.11n specification includes mechanisms for operation +on 20MHz and 40MHz wide channels using different signalling mechanisms +than specified in 802.11b, 802.11g, and 802.11a. +Stations negotiate use of these facilities, termed HT20 and HT40, +when they associate. +To disable all use of 802.11n use +.Fl ht . +To disable use of HT20 (e.g. to force only HT40 use) use +.Fl ht20 . +To disable use of HT40 use +.Fl ht40 . +.Pp +HT configuration is used to ``auto promote'' operation +when several choices are available. +For example, if a station associates to an 11n-capable access point +it controls whether the station uses legacy operation, HT20, or HT40. +When an 11n-capable device is setup as an access point and +Auto Channel Selection is used to locate a channel to operate on, +HT configuration controls whether legacy, HT20, or HT40 operation is setup +on the selected channel. +If a fixed channel is specified for a station then HT configuration can +be given as part of the channel specification; e.g. 6:ht/20 to setup +HT20 operation on channel 6. +.It Cm htcompat +Enable use of compatibility support for pre-802.11n devices (default). +The 802.11n protocol specification went through several incompatible iterations. +Some vendors implemented 11n support to older specifications that +will not interoperate with a purely 11n-compliant station. +In particular the information elements included in management frames +for old devices are different. +When compatibility support is enabled both standard and compatible data +will be provided. +Stations that associate using the compatiblity mechanisms are flagged +in ``list sta''. +To disable compatiblity support use +.Fl htcompat . +.It Cm htprotmode Ar technique +For interfaces operating in 802.11n, use the specified +.Ar technique +for protecting HT frames in a mixed legacy/HT network. +The set of valid techniques is +.Cm off , +and +.Cm rts +(RTS/CTS, default). +Technique names are case insensitive. +.It Cm inact +Enable inactivity processing for stations associated to an +access point (default). +When operating as an access point the 802.11 layer monitors +the activity of each associated station. +When a station is inactive for 5 minutes it will send several +``probe frames'' to see if the station is still present. +If no response is received then the station is deauthenticated. +Applications that prefer to handle this work can disable this +facility by using +.Fl inact . +.It Cm indoor +Set the location to use in calculating regulatory constraints. +The location is also advertised in beacon and probe response frames +when 802.11d is enabled with +.Cm dotd . +See also +.Cm outdoor , +.Cm anywhere , +.Cm country , +and +.Cm regdomain . .It Cm list active Display the list of channels available for use taking into account any restrictions set with the @@ -883,6 +1163,9 @@ is another way of requesting this information. By default a compacted list of channels is displayed; if the .Fl v option is specified then all channels are shown. +.It Cm list countries +Display the set of country codes and regulatory domains that can be +used in regulatory configuration. .It Cm list mac Display the current MAC Access Control List state. Each address is prefixed with a character that indicates the @@ -894,18 +1177,79 @@ 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 regdomain +Display the current regulatory settings including the available channels +and transmit power caps. +.It Cm list roam +Display the parameters that govern roaming operation. +.It Cm list txparam +Display the parameters that govern transmit operation. +.It Cm list txpower +Display the transmit power caps for each channel. .It Cm list scan Display the access points and/or ad-hoc neighbors located in the vicinity. -The -.Fl v -flag may be used to display long SSIDs. -.Fl v -also causes received information elements to be displayed symbolicaly. -This information may be updated automatically by the adaptor -and/or with a +This information may be updated automatically by the adapter +with a .Cm scan request or through background scanning. +Depending on the capabilities of the stations the following +flags can be included in the output: +.Bl -tag -width 3n +.It Li A +Authorized. +Indicates that the station is permitted to send/receive data frames. +.It Li E +Extended Rate Phy (ERP). +Indicates that the station is operating in an 802.11g network +using extended transmit rates. +.It Li H +High Throughput (HT). +Indicates that the station is using HT transmit rates. +If a `+' follows immediately after then the station associated +using deprecated mechanisms supported only when +.Cm htcompat +is enabled. +.It Li P +Power Save. +Indicates that the station is operating in power save mode. +.It Li Q +Quality of Service (QoS). +Indicates that the station is using QoS encapsulation for +data frame. +QoS encapsulation is enabled only when WME mode is enabled. +.It Li T +Transitional Security Network (TSN). +Indicates that the station associated using TSN; see also +.Cm tsn +below. +.It Li W +Wi-Fi Protected Setup (WPS). +Indicates that the station associated using WPS. +.El +.Pp +By default interesting information elements captured from the neighboring +stations are displayed at the end of each row. +Possible elements include: +.Cm WME +(station supports WME), +.Cm WPA +(station supports WPA), +.Cm RSN +(station supports 802.11i/RSN), +.Cm HTCAP +(station supports 802.11n/HT communication), +.Cm ATH +(station supoprts Atheros protocol extensions), +.Cm VEN +(station supports unknown vendor-specific extensions). +If the +.Fl v +flag is used all the information elements and their +contents will be shown. +Specifying The +.Fl v +flag also enables display of long SSIDs. .Cm list ap is another way of requesting this information. .It Cm list sta @@ -930,7 +1274,11 @@ Indicates that the station is operating in an 802.11g network using extended transmit rates. .It Li H High Throughput (HT). -Indicates that the station is using MCS to send/receive frames. +Indicates that the station is using HT transmit rates. +If a `+' follows immediately after then the station associated +using deprecated mechanisms supported only when +.Cm htcompat +is enabled. .It Li P Power Save. Indicates that the station is operating in power save mode. @@ -939,6 +1287,14 @@ Quality of Service (QoS). Indicates that the station is using QoS encapsulation for data frame. QoS encapsulation is enabled only when WME mode is enabled. +.It Li T +Transitional Security Network (TSN). +Indicates that the station associated using TSN; see also +.Cm tsn +below. +.It Li W +Wi-Fi Protected Setup (WPS). +Indicates that the station associated using WPS. .El .Pp By default information elements received from associated stations @@ -953,12 +1309,30 @@ for examining parameters when WME mode is disabled. See the description of the .Cm wme directive for information on the various parameters. +.It Cm maxretry Ar count +Set the maximum number of tries to use in sending unicast frames. +The default setting is 6 but drivers may override this with a value +they choose. .It Cm mcastrate Ar rate Set the rate for transmitting multicast/broadcast frames. Rates are specified as megabits/second in decimal; e.g.\& 5.5 for 5.5 Mb/s. This rate should be valid for the current operating conditions; if an invalid rate is specified drivers are free to chose an appropriate rate. +.It Cm mgtrate Ar rate +Set the rate for transmitting management and/or control frames. +Rates are specified as megabits/second in decimal; e.g.\& 5.5 for 5.5 Mb/s. +.It Cm outdoor +Set the location to use in calculating regulatory constraints. +The location is also advertised in beacon and probe response frames +when 802.11d is enabled with +.Cm dotd . +See also +.Cm anywhere , +.Cm country , +.Cm indoor , +and +.Cm regdomain . .It Cm powersave Enable powersave operation. When operating as a client, the station will conserve power by @@ -994,6 +1368,60 @@ When operating as an access point in 802.11g mode allow only permitted to associate). To allow both 11g and 11b-only stations to associate, use .Fl pureg . +.It Cm puren +When operating as an access point in 802.11n mode allow only +HT-capable stations to associate (legacy stations are not +permitted to associate). +To allow both HT and legacy stations to associate, use +.Fl puren . +.It Cm regdomain Ar sku +Set the regulatory domain to use in calculating the regulatory constraints +for operation. +In particular the set of available channels, how the wireless device +will operation on the channels, and the maximum transmit power that +can be used on a channel are defined by this setting. +Regdomain codes (SKU's) are taken from /etc/regdomain.xml and can also +be viewed with the ``list countries'' request. +Note that not all devices support changing the regdomain from a default +setting; typically stored in EEPROM. +See also +.Cm country , +.Cm indoor , +.Cm outdoor , +and +.Cm anywhere . +.It Cm roam:rate Ar rate +Set the threshold for controlling roaming when operating in a BSS. +The +.Ar rate +parameter specifies the transmit rate in megabits +at which roaming should be considered. +If the current transmit rate drops below this setting and background scanning +is enabled, then the system will check if a more desirable access point is +available and switch over to it. +The current scan cache contents are used if they are considered +valid according to the +.Cm scanvalid +parameter; otherwise a background scan operation is triggered before +any selection occurs. +Each channel type has a separate rate threshold; the default values are: +12 Mb/s (11a), 2 Mb/s (11b), 2 Mb/s (11g), MCS 1 (11na, 11ng). +.It Cm roam:rssi Ar rssi +Set the threshold for controlling roaming when operating in a BSS. +The +.Ar rssi +parameter specifies the receive signal strength in dBm units +at which roaming should be considered. +If the current rssi drops below this setting and background scanning +is enabled, then the system will check if a more desirable access point is +available and switch over to it. +The current scan cache contents are used if they are considered +valid according to the +.Cm scanvalid +parameter; otherwise a background scan operation is triggered before +any selection occurs. +Rach channel type has a separate rssi threshold; the default values are +all 7 dBm. .It Cm roaming Ar mode When operating as a station, control how the system will behave when communication with the current access point @@ -1013,78 +1441,6 @@ attempt to reestablish communication. Manual mode is used by applications such as .Xr wpa_supplicant 8 that want to control the selection of an access point. -.It Cm roam:rssi11a Ar rssi -Set the threshold for controlling roaming when operating in an -802.11a BSS. -The -.Ar rssi -parameter specifies the receive signal strength in dBm units -at which roaming should be considered. -If the current rssi drops below this setting and background scanning -is enabled, then the system will check if a more desirable access point is -available and switch over to it. -The current scan cache contents are used if they are considered -valid according to the -.Cm scanvalid -parameter; otherwise a background scan operation is triggered before -any selection occurs. -By default -.Ar rssi -is set to 7 dBm. -.It Cm roam:rssi11b Ar rssi -Set the threshold for controlling roaming when operating in an -802.11b-only BSS. -See -.Cm roam:rssi11a -for a description of this parameter. -By default -.Ar rssi -is set to 7 dBm. -.It Cm roam:rssi11g Ar rssi -Set the threshold for controlling roaming when operating in a -(mixed) 802.11g BSS. -See -.Cm roam:rssi11a -for a description of this parameter. -By default -.Ar rssi -is set to 7 dBm. -.It Cm roam:rate11a Ar rate -Set the threshold for controlling roaming when operating in an -802.11a BSS. -The -.Ar rate -parameter specifies the transmit rate in megabits -at which roaming should be considered. -If the current transmit rate drops below this setting and background scanning -is enabled, then the system will check if a more desirable access point is -available and switch over to it. -The current scan cache contents are used if they are considered -valid according to the -.Cm scanvalid -parameter; otherwise a background scan operation is triggered before -any selection occurs. -By default -.Ar rate -is set to 12 Mb/s. -.It Cm roam:rate11b Ar rate -Set the threshold for controlling roaming when operating in an -802.11b-only BSS. -See -.Cm roam:rate11a -for a description of this parameter. -By default -.Ar rate -is set to 1 Mb/s. -.It Cm roam:rate11g Ar rate -Set the threshold for controlling roaming when operating in a -(mixed) 802.11g BSS. -See -.Cm roam:rate11a -for a description of this parameter. -By default -.Ar rate -is set to 5 Mb/s. .It Cm rtsthreshold Ar length Set the threshold for which transmitted frames are preceded by transmission of an @@ -1102,93 +1458,20 @@ to or .Cm - disables transmission of RTS frames. -Not all adaptors support setting the RTS threshold. -.It Cm ssid Ar ssid -Set the desired Service Set Identifier (aka network name). -The SSID is a string up to 32 characters -in length and may be specified as either a normal string or in -hexadecimal when preceded by -.Ql 0x . -Additionally, the SSID may be cleared by setting it to -.Ql - . +Not all adapters support setting the RTS threshold. .It Cm scan Initiate a scan of neighboring stations, wait for it to complete, and display all stations found. Only the super-user can initiate a scan. -Depending on the capabilities of the APs, the following -flags can be included in the output: -.Bl -tag -width 3n -.It Li A -Channel Agility. -Indicates that the station support channel hopping as described by the -IEEE 802.11b specification. -.It Li B -Packet Binary Convolution Code (PBCC). -A modulation alternative to the standard OFDM method. -.It Dv C -Pollreq -.It Dv c -Pollable -.It Dv D -Direct Sequence Spread Spectrum (DSSSOFDM). -Indicates the the station supports DSSS modulation. -.It Li E -Extended Service Set (ESS). -Indicates that the station is part of an infrastructure network -(in contrast to an IBSS/ad-hoc network). -.It Li I -IBSS/ad-hoc network. -Indicates that the station is part of an ad-hoc network -(in contrast to an ESS network). -.It Li P -Privacy. -Data confidentiality is required for all data frames -exchanged within the BSS. -This means that this BSS requires the station to -use cryptographic means such as WEP, TKIP or AES-CCMP to -encrypt/decrypt data frames being exchanged with others. -.It Dv R -Robust Security Network (RSN). -Indicates that the station supports the IEEE 802.11i authentication -and key management protocol. -.It Li S -Short Preamble. -Indicates that the network is using short preambles (defined -in 802.11b High Rate/DSSS PHY, short preamble utilizes a -56 bit sync field in contrast to a 128 bit field used in long -preamble mode). -.It Li s -Short slot time. -Indicates that the network is using a short slot time. -.El -.Pp -Interesting information elements captured from the neighboring -stations are displayed at the end of each row. -Possible elements are: -.Cm WME -(station supports WME), -.Cm WPA -(station supports WPA), -.Cm RSN -(station supports 802.11i/RSN), -.Cm HT -(station supports 802.11n/HT communication), -.Cm ATH -(station supoprts Atheros protocol extensions), -.Cm VEN -(station supports unknown vendor-specific extensions). -If the -.Fl v -flag is used the information element contents will be shown. -.Pp +See +.Cm list scan +for information on the display. +By default a background scan is done; otherwise a foreground +scan is done and the station may roam to a different access point. The .Cm list scan request can be used to show recent scan results without initiating a new scan. -.Pp -The -.Fl v -flag may be used to prevent the shortening of long SSIDs. .It Cm scanvalid Ar threshold Set the maximum time the scan cache contents are considered valid; i.e. will be used without first triggering a scan operation to @@ -1202,13 +1485,25 @@ is 10 seconds. One should take care setting this threshold; if it is set too low then attempts to roam to another access point may trigger unnecessary background scan operations. -.It Cm stationname Ar name -Set the name of this station. -The station name is not part of the IEEE 802.11 -protocol though some interfaces support it. -As such it only -seems to be meaningful to identical or virtually identical equipment. -Setting the station name is identical in syntax to setting the SSID. +.It Cm shortgi +Enable use of Short Guard Interval when operating in 802.11n +on an HT channel. +NB: this currently enables Short GI on both HT40 and HT20 channels. +To disable Short GI use +.Fl shortgi . +.It Cm ssid Ar ssid +Set the desired Service Set Identifier (aka network name). +The SSID is a string up to 32 characters +in length and may be specified as either a normal string or in +hexadecimal when preceded by +.Ql 0x . +Additionally, the SSID may be cleared by setting it to +.Ql - . +.It Cm tsn +When operating as an access point with WPA/802.11i allow legacy +stations to associate using static key WEP and open authentication. +To disallow legacy station use of WEP, use +.Fl tsn . .It Cm txpower Ar power Set the power used to transmit frames. The @@ -1217,10 +1512,16 @@ argument is specified in .5 dBm units. Out of range values are truncated. Typically only a few discreet power settings are available and the driver will use the setting closest to the specified value. -Not all adaptors support changing the transmit power. +Not all adapters support changing the transmit power. +.It Cm ucastrate Ar rate +Set a fixed rate for transmitting unicast frames. +Rates are specified as megabits/second in decimal; e.g.\& 5.5 for 5.5 Mb/s. +This rate should be valid for the current operating conditions; +if an invalid rate is specified drivers are free to chose an +appropriate rate. .It Cm wepmode Ar mode Set the desired WEP mode. -Not all adaptors support all modes. +Not all adapters support all modes. The set of valid modes is .Cm off , on , and @@ -1229,10 +1530,10 @@ The .Cm mixed mode explicitly tells the adaptor to allow association with access points which allow both encrypted and unencrypted traffic. -On these adaptors, +On these adapters, .Cm on means that the access point must only allow encrypted connections. -On other adaptors, +On other adapters, .Cm on is generally another name for .Cm mixed . @@ -1261,7 +1562,7 @@ drivers do this mapping differently to A key may be cleared by setting it to .Ql - . If WEP is supported then there are at least four keys. -Some adaptors support more than four keys. +Some adapters support more than four keys. If that is the case, then the first four keys (1-4) will be the standard temporary keys and any others will be adaptor specific keys such as permanent keys stored in NVRAM. @@ -1276,6 +1577,8 @@ WME is a subset of the IEEE 802.11e standard to support the efficient communication of realtime and multimedia data. To disable WME support, use .Fl wme . +Another name for this parameter is +.Cm wmm . .Pp The following parameters are meaningful only when WME support is in use. Parameters are specified per-AC (Access Category) and @@ -1362,10 +1665,15 @@ This parameter is meaningful only when operating in ap mode. Set the TxOpLimit channel access parameter to send to stations in a BSS. This parameter is meaningful only when operating in ap mode. .El +.It Cm wps +Enable Wireless Privacy Subscriber support. +Note that WPS support requires a WPS-capable supplicant. +To disable this function use +.Fl wps . .El .Pp The following parameters support an optional access control list -feature available with some adaptors when operating in ap mode; see +feature available with some adapters when operating in ap mode; see .Xr wlan_acl 4 . This facility allows an access point to accept/deny association requests based on the MAC address of the station. @@ -1392,6 +1700,14 @@ address database. Set the ACL policy to allow all stations to associate. .It Cm mac:flush Delete all entries in the database. +.It Cm mac:radius +Set the ACL policy to permit association only by +stations approved by a RADIUS server. +Note that this feature requires the +.Xr hostapd 8 +program be configured to do the right thing +as it handles the RADIUS processing +(and marks stations as authorized). .El .Pp The following parameters are for compatibility with other systems: @@ -1403,11 +1719,16 @@ parameter. Included for .Nx compatibility. -.It Cm station Ar name -Another name for the -.Cm stationname -parameter. -Included for +.It Cm stationname Ar name +Set the name of this station. +The station name is not part of the IEEE 802.11 +protocol though some interfaces support it. +As such it only +seems to be meaningful to identical or virtually identical equipment. +Setting the station name is identical in syntax to setting the SSID. +One can also use +.Cm station +for .Bsx compatibility. .It Cm wep diff --git a/sbin/ifconfig/ifieee80211.c b/sbin/ifconfig/ifieee80211.c index 97eed75..e81b6a3 100644 --- a/sbin/ifconfig/ifieee80211.c +++ b/sbin/ifconfig/ifieee80211.c @@ -77,8 +77,6 @@ #include <net/if_media.h> #include <net/route.h> -#include <net80211/ieee80211.h> -#include <net80211/ieee80211_crypto.h> #include <net80211/ieee80211_ioctl.h> #include <ctype.h> @@ -94,6 +92,27 @@ #include <stddef.h> /* NB: for offsetof */ #include "ifconfig.h" +#include "regdomain.h" + +#ifndef IEEE80211_FIXED_RATE_NONE +#define IEEE80211_FIXED_RATE_NONE 0xff +#endif + +#define REQ_ECM 0x01000000 /* enable if ECM set */ +#define REQ_OUTDOOR 0x02000000 /* enable for outdoor operation */ +#define REQ_FLAGS 0xff000000 /* private flags, don't pass to os */ + +/* XXX need these publicly defined or similar */ +#ifndef IEEE80211_NODE_AUTH +#define IEEE80211_NODE_AUTH 0x0001 /* authorized for data */ +#define IEEE80211_NODE_QOS 0x0002 /* QoS enabled */ +#define IEEE80211_NODE_ERP 0x0004 /* ERP enabled */ +#define IEEE80211_NODE_PWR_MGT 0x0010 /* power save mode enabled */ +#define IEEE80211_NODE_HT 0x0040 /* HT enabled */ +#define IEEE80211_NODE_HTCOMPAT 0x0080 /* HT setup w/ vendor OUI's */ +#define IEEE80211_NODE_WPS 0x0100 /* WPS association */ +#define IEEE80211_NODE_TSN 0x0200 /* TSN association */ +#endif #define MAXCOL 78 static int col; @@ -103,38 +122,34 @@ static void LINE_INIT(char c); static void LINE_BREAK(void); static void LINE_CHECK(const char *fmt, ...); -/* XXX need max array size */ -static const int htrates[16] = { - 13, /* IFM_IEEE80211_MCS0 */ - 26, /* IFM_IEEE80211_MCS1 */ - 39, /* IFM_IEEE80211_MCS2 */ - 52, /* IFM_IEEE80211_MCS3 */ - 78, /* IFM_IEEE80211_MCS4 */ - 104, /* IFM_IEEE80211_MCS5 */ - 117, /* IFM_IEEE80211_MCS6 */ - 130, /* IFM_IEEE80211_MCS7 */ - 26, /* IFM_IEEE80211_MCS8 */ - 52, /* IFM_IEEE80211_MCS9 */ - 78, /* IFM_IEEE80211_MCS10 */ - 104, /* IFM_IEEE80211_MCS11 */ - 156, /* IFM_IEEE80211_MCS12 */ - 208, /* IFM_IEEE80211_MCS13 */ - 234, /* IFM_IEEE80211_MCS14 */ - 260, /* IFM_IEEE80211_MCS15 */ +static const char *modename[] = { + "auto", "11a", "11b", "11g", "fh", "turboA", "turboG", + "sturbo", "11na", "11ng" }; +static void set80211(int s, int type, int val, int len, void *data); static int get80211(int s, int type, void *data, int len); static int get80211len(int s, int type, void *data, int len, int *plen); static int get80211val(int s, int type, int *val); -static void set80211(int s, int type, int val, int len, void *data); static const char *get_string(const char *val, const char *sep, u_int8_t *buf, int *lenp); static void print_string(const u_int8_t *buf, int len); +static void print_regdomain(const struct ieee80211_regdomain *, int); +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 struct ieee80211req_chaninfo chaninfo; -static struct ifmediareq *ifmr; +static struct ieee80211_regdomain regdomain; +static int gotregdomain = 0; +static struct ieee80211_roamparams_req roamparams; +static int gotroam = 0; +static struct ieee80211_txparams_req txparams; +static int gottxparams = 0; static struct ieee80211_channel curchan; static int gotcurchan = 0; +static struct ifmediareq *ifmr; static int htconf = 0; static int gothtconf = 0; @@ -159,11 +174,22 @@ getchaninfo(int s) return; if (get80211(s, IEEE80211_IOC_CHANINFO, &chaninfo, sizeof(chaninfo)) < 0) errx(1, "unable to get channel information"); - ifmr = ifmedia_getstate(s); gethtconf(s); } +static struct regdata * +getregdata(void) +{ + static struct regdata *rdp = NULL; + if (rdp == NULL) { + rdp = lib80211_alloc_regdata(); + if (rdp == NULL) + exit(-1); + } + return rdp; +} + /* * Given the channel at index i with attributes from, * check if there is a channel with attributes to in @@ -308,6 +334,159 @@ getcurchan(int s) return &curchan; } +static enum ieee80211_phymode +chan2mode(const struct ieee80211_channel *c) +{ + if (IEEE80211_IS_CHAN_HTA(c)) + return IEEE80211_MODE_11NA; + if (IEEE80211_IS_CHAN_HTG(c)) + return IEEE80211_MODE_11NG; + if (IEEE80211_IS_CHAN_108A(c)) + return IEEE80211_MODE_TURBO_A; + if (IEEE80211_IS_CHAN_108G(c)) + return IEEE80211_MODE_TURBO_G; + if (IEEE80211_IS_CHAN_ST(c)) + return IEEE80211_MODE_STURBO_A; + if (IEEE80211_IS_CHAN_FHSS(c)) + return IEEE80211_MODE_FH; + if (IEEE80211_IS_CHAN_A(c)) + return IEEE80211_MODE_11A; + if (IEEE80211_IS_CHAN_ANYG(c)) + return IEEE80211_MODE_11G; + if (IEEE80211_IS_CHAN_B(c)) + return IEEE80211_MODE_11B; + return IEEE80211_MODE_AUTO; +} + +static void +getroam(int s) +{ + if (gotroam) + return; + if (get80211(s, IEEE80211_IOC_ROAM, + &roamparams, sizeof(roamparams)) < 0) + errx(1, "unable to get roaming parameters"); + gotroam = 1; +} + +static void +setroam_cb(int s, void *arg) +{ + struct ieee80211_roamparams_req *roam = arg; + set80211(s, IEEE80211_IOC_ROAM, 0, sizeof(*roam), roam); +} + +static void +gettxparams(int s) +{ + if (gottxparams) + return; + if (get80211(s, IEEE80211_IOC_TXPARAMS, + &txparams, sizeof(txparams)) < 0) + errx(1, "unable to get transmit parameters"); + gottxparams = 1; +} + +static void +settxparams_cb(int s, void *arg) +{ + struct ieee80211_txparams_req *txp = arg; + set80211(s, IEEE80211_IOC_TXPARAMS, 0, sizeof(*txp), txp); +} + +static void +getregdomain(int s) +{ + if (gotregdomain) + return; + if (get80211(s, IEEE80211_IOC_REGDOMAIN, + ®domain, sizeof(regdomain)) < 0) + errx(1, "unable to get regulatory domain info"); + gotregdomain = 1; +} + +static void +getdevcaps(int s, struct ieee80211_devcaps_req *dc) +{ + if (get80211(s, IEEE80211_IOC_DEVCAPS, dc, sizeof(*dc)) < 0) + errx(1, "unable to get device capabilities"); +} + +static void +setregdomain_cb(int s, void *arg) +{ + struct ieee80211_regdomain_req req; + struct ieee80211_regdomain *rd = arg; + struct ieee80211_devcaps_req dc; + struct regdata *rdp = getregdata(); + + if (rd->country != 0) { + const struct country *cc; + /* + * Check current country seting to make sure it's + * compatible with the new regdomain. If not, then + * override it with any default country for this + * SKU. If we cannot arrange a match, then abort. + */ + cc = lib80211_country_findbycc(rdp, rd->country); + if (cc == NULL) + errx(1, "unknown ISO country code %d", rd->country); + if (cc->rd->sku != rd->regdomain) { + const struct regdomain *rp; + /* + * Check if country is incompatible with regdomain. + * To enable multiple regdomains for a country code + * we permit a mismatch between the regdomain and + * the country's associated regdomain when the + * regdomain is setup w/o a default country. For + * example, US is bound to the FCC regdomain but + * we allow US to be combined with FCC3 because FCC3 + * has not default country. This allows bogus + * combinations like FCC3+DK which are resolved when + * constructing the channel list by deferring to the + * regdomain to construct the channel list. + */ + rp = lib80211_regdomain_findbysku(rdp, rd->regdomain); + if (rp == NULL) + errx(1, "country %s (%s) is not usable with " + "regdomain %d", cc->isoname, cc->name, + rd->regdomain); + else if (rp->cc != 0 && rp->cc != cc) + errx(1, "country %s (%s) is not usable with " + "regdomain %s", cc->isoname, cc->name, + rp->name); + } + } + req.rd = *rd; + /* + * Fetch the device capabilities and calculate the + * full set of netbands for which we request a new + * channel list be constructed. Once that's done we + * push the regdomain info + channel list to the kernel. + */ + getdevcaps(s, &dc); +#if 0 + if (verbose) { + printf("drivercaps: 0x%x\n", dc.dc_drivercaps); + printf("cryptocaps: 0x%x\n", dc.dc_cryptocaps); + printf("htcaps : 0x%x\n", dc.dc_htcaps); + memcpy(&chaninfo, &dc.dc_chaninfo, sizeof(chaninfo)); + print_channels(s, &dc.dc_chaninfo, 1/*allchans*/, 1/*verbose*/); + } +#endif + regdomain_makechannels(&req, &dc); + if (verbose) { + LINE_INIT(':'); + print_regdomain(rd, 1/*verbose*/); + LINE_BREAK(); + memcpy(&chaninfo, &req.chaninfo, sizeof(chaninfo)); + print_channels(s, &req.chaninfo, 1/*allchans*/, 1/*verbose*/); + } + if (req.chaninfo.ic_nchans == 0) + errx(1, "no channels calculated"); + set80211(s, IEEE80211_IOC_REGDOMAIN, 0, sizeof(req), &req); +} + static int ieee80211_mhz2ieee(int freq, int flags) { @@ -332,7 +511,7 @@ set80211ssid(const char *val, int d, int s, const struct afswtch *rafp) ssid = 0; len = strlen(val); - if (len > 2 && isdigit(val[0]) && val[1] == ':') { + if (len > 2 && isdigit((int)val[0]) && val[1] == ':') { ssid = atoi(val)-1; val += 2; } @@ -503,6 +682,26 @@ set80211channel(const char *val, int d, int s, const struct afswtch *rafp) } static void +set80211chanswitch(const char *val, int d, int s, const struct afswtch *rafp) +{ + struct ieee80211_chanswitch_req csr; + int v, flags; + + memset(&csr, 0, sizeof(csr)); + getchaninfo(s); + v = atoi(val); + flags = getchannelflags(val, v); + if (v > 255) { /* treat as frequency */ + mapfreq(&csr.csa_chan, v, flags); + } else { + mapchan(&csr.csa_chan, v, flags); + } + csr.csa_mode = 1; + csr.csa_count = 5; + set80211(s, IEEE80211_IOC_CHANSWITCH, 0, sizeof(csr), &csr); +} + +static void set80211authmode(const char *val, int d, int s, const struct afswtch *rafp) { int mode; @@ -609,7 +808,7 @@ set80211wepkey(const char *val, int d, int s, const struct afswtch *rafp) int len; u_int8_t data[IEEE80211_KEYBUF_SIZE]; - if (isdigit(val[0]) && val[1] == ':') { + if (isdigit((int)val[0]) && val[1] == ':') { key = atoi(val)-1; val += 2; } @@ -635,7 +834,7 @@ set80211nwkey(const char *val, int d, int s, const struct afswtch *rafp) set80211(s, IEEE80211_IOC_WEP, IEEE80211_WEP_ON, 0, NULL); - if (isdigit(val[0]) && val[1] == ':') { + if (isdigit((int)val[0]) && val[1] == ':') { txkey = val[0]-'0'-1; val += 2; @@ -1040,47 +1239,271 @@ DECL_CMD_FUNC(set80211scanvalid, val, d) set80211(s, IEEE80211_IOC_SCANVALID, atoi(val), 0, NULL); } -static -DECL_CMD_FUNC(set80211roamrssi11a, val, d) +/* + * Parse an optional trailing specification of which netbands + * to apply a parameter to. This is basically the same syntax + * as used for channels but you can concatenate to specify + * multiple. For example: + * 14:abg apply to 11a, 11b, and 11g + * 6:ht apply to 11na and 11ng + * We don't make a big effort to catch silly things; this is + * really a convenience mechanism. + */ +static int +getmodeflags(const char *val) { - set80211(s, IEEE80211_IOC_ROAM_RSSI_11A, atoi(val), 0, NULL); + const char *cp; + int flags; + + flags = 0; + + cp = strchr(val, ':'); + if (cp != NULL) { + for (cp++; isalpha((int) *cp); cp++) { + /* accept mixed case */ + int c = *cp; + if (isupper(c)) + c = tolower(c); + switch (c) { + case 'a': /* 802.11a */ + flags |= IEEE80211_CHAN_A; + break; + case 'b': /* 802.11b */ + flags |= IEEE80211_CHAN_B; + break; + case 'g': /* 802.11g */ + flags |= IEEE80211_CHAN_G; + break; + case 'h': /* ht = 802.11n */ + case 'n': /* 802.11n */ + flags |= IEEE80211_CHAN_HT; + break; + case 'd': /* dt = Atheros Dynamic Turbo */ + flags |= IEEE80211_CHAN_TURBO; + break; + case 't': /* ht, dt, st, t */ + /* dt and unadorned t specify Dynamic Turbo */ + if ((flags & (IEEE80211_CHAN_STURBO|IEEE80211_CHAN_HT)) == 0) + flags |= IEEE80211_CHAN_TURBO; + break; + case 's': /* st = Atheros Static Turbo */ + flags |= IEEE80211_CHAN_STURBO; + break; + default: + errx(-1, "%s: Invalid mode attribute %c\n", + val, *cp); + } + } + } + return flags; } +#define IEEE80211_CHAN_HTA (IEEE80211_CHAN_HT|IEEE80211_CHAN_5GHZ) +#define IEEE80211_CHAN_HTG (IEEE80211_CHAN_HT|IEEE80211_CHAN_2GHZ) + +#define _APPLY(_flags, _base, _param, _v) do { \ + if (_flags & IEEE80211_CHAN_HT) { \ + if ((_flags & (IEEE80211_CHAN_5GHZ|IEEE80211_CHAN_2GHZ)) == 0) {\ + _base.params[IEEE80211_MODE_11NA]._param = _v; \ + _base.params[IEEE80211_MODE_11NG]._param = _v; \ + } else if (_flags & IEEE80211_CHAN_5GHZ) \ + _base.params[IEEE80211_MODE_11NA]._param = _v; \ + else \ + _base.params[IEEE80211_MODE_11NG]._param = _v; \ + } \ + if (_flags & IEEE80211_CHAN_TURBO) { \ + if ((_flags & (IEEE80211_CHAN_5GHZ|IEEE80211_CHAN_2GHZ)) == 0) {\ + _base.params[IEEE80211_MODE_TURBO_A]._param = _v; \ + _base.params[IEEE80211_MODE_TURBO_G]._param = _v; \ + } else if (_flags & IEEE80211_CHAN_5GHZ) \ + _base.params[IEEE80211_MODE_TURBO_A]._param = _v; \ + else \ + _base.params[IEEE80211_MODE_TURBO_G]._param = _v; \ + } \ + if (_flags & IEEE80211_CHAN_STURBO) \ + _base.params[IEEE80211_MODE_STURBO_A]._param = _v; \ + if ((_flags & IEEE80211_CHAN_A) == IEEE80211_CHAN_A) \ + _base.params[IEEE80211_MODE_11A]._param = _v; \ + if ((_flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G) \ + _base.params[IEEE80211_MODE_11G]._param = _v; \ + if ((_flags & IEEE80211_CHAN_B) == IEEE80211_CHAN_B) \ + _base.params[IEEE80211_MODE_11B]._param = _v; \ +} while (0) +#define _APPLY1(_flags, _base, _param, _v) do { \ + if (_flags & IEEE80211_CHAN_HT) { \ + if (_flags & IEEE80211_CHAN_5GHZ) \ + _base.params[IEEE80211_MODE_11NA]._param = _v; \ + else \ + _base.params[IEEE80211_MODE_11NG]._param = _v; \ + } else if ((_flags & IEEE80211_CHAN_108A) == IEEE80211_CHAN_108A) \ + _base.params[IEEE80211_MODE_TURBO_A]._param = _v; \ + else if ((_flags & IEEE80211_CHAN_108G) == IEEE80211_CHAN_108G) \ + _base.params[IEEE80211_MODE_TURBO_G]._param = _v; \ + else if ((_flags & IEEE80211_CHAN_ST) == IEEE80211_CHAN_ST) \ + _base.params[IEEE80211_MODE_STURBO_A]._param = _v; \ + else if ((_flags & IEEE80211_CHAN_A) == IEEE80211_CHAN_A) \ + _base.params[IEEE80211_MODE_11A]._param = _v; \ + else if ((_flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G) \ + _base.params[IEEE80211_MODE_11G]._param = _v; \ + else if ((_flags & IEEE80211_CHAN_B) == IEEE80211_CHAN_B) \ + _base.params[IEEE80211_MODE_11B]._param = _v; \ +} while (0) +#define _APPLY_RATE(_flags, _base, _param, _v) do { \ + if (_flags & IEEE80211_CHAN_HT) { \ + if ((_flags & (IEEE80211_CHAN_5GHZ|IEEE80211_CHAN_2GHZ)) == 0) {\ + _base.params[IEEE80211_MODE_11NA]._param = _v|0x80; \ + _base.params[IEEE80211_MODE_11NG]._param = _v|0x80; \ + } else if (_flags & IEEE80211_CHAN_5GHZ) \ + _base.params[IEEE80211_MODE_11NA]._param = _v|0x80; \ + else \ + _base.params[IEEE80211_MODE_11NG]._param = _v|0x80; \ + } \ + if (_flags & IEEE80211_CHAN_TURBO) { \ + if ((_flags & (IEEE80211_CHAN_5GHZ|IEEE80211_CHAN_2GHZ)) == 0) {\ + _base.params[IEEE80211_MODE_TURBO_A]._param = 2*_v; \ + _base.params[IEEE80211_MODE_TURBO_G]._param = 2*_v; \ + } else if (_flags & IEEE80211_CHAN_5GHZ) \ + _base.params[IEEE80211_MODE_TURBO_A]._param = 2*_v; \ + else \ + _base.params[IEEE80211_MODE_TURBO_G]._param = 2*_v; \ + } \ + if (_flags & IEEE80211_CHAN_STURBO) \ + _base.params[IEEE80211_MODE_STURBO_A]._param = 2*_v; \ + if ((_flags & IEEE80211_CHAN_A) == IEEE80211_CHAN_A) \ + _base.params[IEEE80211_MODE_11A]._param = 2*_v; \ + if ((_flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G) \ + _base.params[IEEE80211_MODE_11G]._param = (_v == 5 ? 11 : 2*_v);\ + if ((_flags & IEEE80211_CHAN_B) == IEEE80211_CHAN_B) \ + _base.params[IEEE80211_MODE_11B]._param = (_v == 5 ? 11 : 2*_v);\ +} while (0) +#define _APPLY_RATE1(_flags, _base, _param, _v) do { \ + if (_flags & IEEE80211_CHAN_HT) { \ + if (_flags & IEEE80211_CHAN_5GHZ) \ + _base.params[IEEE80211_MODE_11NA]._param = _v|0x80; \ + else \ + _base.params[IEEE80211_MODE_11NG]._param = _v|0x80; \ + } else if ((_flags & IEEE80211_CHAN_108A) == IEEE80211_CHAN_108A) \ + _base.params[IEEE80211_MODE_TURBO_A]._param = 2*_v; \ + else if ((_flags & IEEE80211_CHAN_108G) == IEEE80211_CHAN_108G) \ + _base.params[IEEE80211_MODE_TURBO_G]._param = 2*_v; \ + else if ((_flags & IEEE80211_CHAN_ST) == IEEE80211_CHAN_ST) \ + _base.params[IEEE80211_MODE_STURBO_A]._param = 2*_v; \ + else if ((_flags & IEEE80211_CHAN_A) == IEEE80211_CHAN_A) \ + _base.params[IEEE80211_MODE_11A]._param = 2*_v; \ + else if ((_flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G) \ + _base.params[IEEE80211_MODE_11G]._param = (_v == 5 ? 11 : 2*_v);\ + else if ((_flags & IEEE80211_CHAN_B) == IEEE80211_CHAN_B) \ + _base.params[IEEE80211_MODE_11B]._param = (_v == 5 ? 11 : 2*_v);\ +} while (0) + static -DECL_CMD_FUNC(set80211roamrssi11b, val, d) +DECL_CMD_FUNC(set80211roamrssi, val, d) { - set80211(s, IEEE80211_IOC_ROAM_RSSI_11B, atoi(val), 0, NULL); + double v = atof(val); + int rssi, flags; + + rssi = (int) (2*v); + if (rssi != 2*v) + errx(-1, "invalid rssi (must be .5 dBm units)"); + flags = getmodeflags(val); + getroam(s); + if (flags == 0) { /* NB: no flags => current channel */ + flags = getcurchan(s)->ic_flags; + _APPLY1(flags, roamparams, rssi, rssi); + } else + _APPLY(flags, roamparams, rssi, rssi); + callback_register(setroam_cb, &roamparams); } static -DECL_CMD_FUNC(set80211roamrssi11g, val, d) +DECL_CMD_FUNC(set80211roamrate, val, d) { - set80211(s, IEEE80211_IOC_ROAM_RSSI_11G, atoi(val), 0, NULL); + int v = atoi(val), flags; + + flags = getmodeflags(val); + getroam(s); + if (flags == 0) { /* NB: no flags => current channel */ + flags = getcurchan(s)->ic_flags; + _APPLY_RATE1(flags, roamparams, rate, v); + } else + _APPLY_RATE(flags, roamparams, rate, v); + callback_register(setroam_cb, &roamparams); } static -DECL_CMD_FUNC(set80211roamrate11a, val, d) +DECL_CMD_FUNC(set80211mcastrate, val, d) { - set80211(s, IEEE80211_IOC_ROAM_RATE_11A, 2*atoi(val), 0, NULL); + int v = atoi(val), flags; + + flags = getmodeflags(val); + gettxparams(s); + if (flags == 0) { /* NB: no flags => current channel */ + flags = getcurchan(s)->ic_flags; + _APPLY_RATE1(flags, txparams, mcastrate, v); + } else + _APPLY_RATE(flags, txparams, mcastrate, v); + callback_register(settxparams_cb, &txparams); } static -DECL_CMD_FUNC(set80211roamrate11b, val, d) +DECL_CMD_FUNC(set80211mgtrate, val, d) { - set80211(s, IEEE80211_IOC_ROAM_RATE_11B, 2*atoi(val), 0, NULL); + int v = atoi(val), flags; + + flags = getmodeflags(val); + gettxparams(s); + if (flags == 0) { /* NB: no flags => current channel */ + flags = getcurchan(s)->ic_flags; + _APPLY_RATE1(flags, txparams, mgmtrate, v); + } else + _APPLY_RATE(flags, txparams, mgmtrate, v); + callback_register(settxparams_cb, &txparams); } static -DECL_CMD_FUNC(set80211roamrate11g, val, d) +DECL_CMD_FUNC(set80211ucastrate, val, d) { - set80211(s, IEEE80211_IOC_ROAM_RATE_11G, 2*atoi(val), 0, NULL); + int v, flags; + + gettxparams(s); + flags = getmodeflags(val); + if (isanyarg(val)) { + if (flags == 0) { /* NB: no flags => current channel */ + flags = getcurchan(s)->ic_flags; + _APPLY1(flags, txparams, ucastrate, + IEEE80211_FIXED_RATE_NONE); + } else + _APPLY(flags, txparams, ucastrate, + IEEE80211_FIXED_RATE_NONE); + } else { + v = atoi(val); + if (flags == 0) { /* NB: no flags => current channel */ + flags = getcurchan(s)->ic_flags; + _APPLY_RATE1(flags, txparams, ucastrate, v); + } else + _APPLY_RATE(flags, txparams, ucastrate, v); + } + callback_register(settxparams_cb, &txparams); } static -DECL_CMD_FUNC(set80211mcastrate, val, d) +DECL_CMD_FUNC(set80211maxretry, val, d) { - set80211(s, IEEE80211_IOC_MCAST_RATE, 2*atoi(val), 0, NULL); + int v = atoi(val), flags; + + flags = getmodeflags(val); + gettxparams(s); + if (flags == 0) { /* NB: no flags => current channel */ + flags = getcurchan(s)->ic_flags; + _APPLY1(flags, txparams, maxretry, v); + } else + _APPLY(flags, txparams, maxretry, v); + callback_register(settxparams_cb, &txparams); } +#undef _APPLY_RATE +#undef _APPLY +#undef IEEE80211_CHAN_HTA +#undef IEEE80211_CHAN_HTG static DECL_CMD_FUNC(set80211fragthreshold, val, d) @@ -1109,6 +1532,12 @@ set80211doth(const char *val, int d, int s, const struct afswtch *rafp) } static void +set80211dfs(const char *val, int d, int s, const struct afswtch *rafp) +{ + set80211(s, IEEE80211_IOC_DFS, d, 0, NULL); +} + +static void set80211shortgi(const char *val, int d, int s, const struct afswtch *rafp) { set80211(s, IEEE80211_IOC_SHORTGI, @@ -1238,12 +1667,321 @@ set80211htconf(const char *val, int d, int s, const struct afswtch *rafp) } static void +set80211dwds(const char *val, int d, int s, const struct afswtch *rafp) +{ + set80211(s, IEEE80211_IOC_DWDS, d, 0, NULL); +} + +static void set80211inact(const char *val, int d, int s, const struct afswtch *rafp) { set80211(s, IEEE80211_IOC_INACTIVITY, d, 0, NULL); } static void +set80211tsn(const char *val, int d, int s, const struct afswtch *rafp) +{ + set80211(s, IEEE80211_IOC_TSN, d, 0, NULL); +} + +static void +set80211dotd(const char *val, int d, int s, const struct afswtch *rafp) +{ + set80211(s, IEEE80211_IOC_DOTD, d, 0, NULL); +} + +static int +regdomain_sort(const void *a, const void *b) +{ +#define CHAN_ALL \ + (IEEE80211_CHAN_ALLTURBO|IEEE80211_CHAN_HALF|IEEE80211_CHAN_QUARTER) + const struct ieee80211_channel *ca = a; + const struct ieee80211_channel *cb = b; + + return ca->ic_freq == cb->ic_freq ? + (ca->ic_flags & CHAN_ALL) - (cb->ic_flags & CHAN_ALL) : + ca->ic_freq - cb->ic_freq; +#undef CHAN_ALL +} + +static const struct ieee80211_channel * +chanlookup(const struct ieee80211_channel chans[], int nchans, + int freq, int flags) +{ + int i; + + flags &= IEEE80211_CHAN_ALLTURBO; + for (i = 0; i < nchans; i++) { + const struct ieee80211_channel *c = &chans[i]; + if (c->ic_freq == freq && + (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags) + return c; + } + return NULL; +} + +static void +regdomain_addchans(struct ieee80211req_chaninfo *ci, + const netband_head *bands, + const struct ieee80211_regdomain *reg, + uint32_t chanFlags, + const struct ieee80211req_chaninfo *avail) +{ + const struct netband *nb; + const struct freqband *b; + struct ieee80211_channel *c, *prev; + int freq, channelSep; + + channelSep = (chanFlags & IEEE80211_CHAN_2GHZ) ? 0 : 40; + LIST_FOREACH(nb, bands, next) { + b = nb->band; + if (verbose) + printf("%s: chanFlags 0x%x b %p\n", + __func__, chanFlags, b); + prev = NULL; + for (freq = b->freqStart; freq <= b->freqEnd; freq += b->chanSep) { + uint32_t flags = nb->flags | b->flags; + + /* check if device can operate on this frequency */ + if (chanlookup(avail->ic_chans, avail->ic_nchans, freq, chanFlags) == NULL) { + if (verbose) + printf("%u: skip, flags 0x%x not available\n", freq, chanFlags); + continue; + } + if ((flags & IEEE80211_CHAN_HALF) && + (chanFlags & IEEE80211_CHAN_HALF) == 0) { + if (verbose) + printf("%u: skip, device does not support half-rate channels\n", freq); + continue; + } + if ((flags & IEEE80211_CHAN_QUARTER) && + (chanFlags & IEEE80211_CHAN_QUARTER) == 0) { + if (verbose) + printf("%u: skip, device does not support quarter-rate channels\n", freq); + continue; + } + if ((flags & IEEE80211_CHAN_HT20) && + (chanFlags & IEEE80211_CHAN_HT20) == 0) { + if (verbose) + printf("%u: skip, device does not support HT20 operation\n", freq); + continue; + } + if ((flags & IEEE80211_CHAN_HT40) && + (chanFlags & IEEE80211_CHAN_HT40) == 0) { + if (verbose) + printf("%u: skip, device does not support HT40 operation\n", freq); + continue; + } + if ((flags & REQ_ECM) && !reg->ecm) { + if (verbose) + printf("%u: skip, ECM channel\n", freq); + continue; + } + if ((flags & REQ_OUTDOOR) && reg->location == 'I') { + if (verbose) + printf("%u: skip, outdoor channel\n", freq); + continue; + } + if ((flags & IEEE80211_CHAN_HT40) && + prev != NULL && (freq - prev->ic_freq) < channelSep) { + if (verbose) + printf("%u: skip, only %u channel " + "separation, need %d\n", freq, + freq - prev->ic_freq, channelSep); + continue; + } + if (ci->ic_nchans == IEEE80211_CHAN_MAX) { + if (verbose) + printf("%u: skip, channel table full\n", freq); + break; + } + c = &ci->ic_chans[ci->ic_nchans++]; + c->ic_freq = freq; + c->ic_flags = chanFlags | + (flags &~ (REQ_FLAGS | IEEE80211_CHAN_HT40)); + if (c->ic_flags & IEEE80211_CHAN_DFS) + c->ic_maxregpower = nb->maxPowerDFS; + else + c->ic_maxregpower = nb->maxPower; + if (verbose) + printf("[%3d] add freq %u flags 0x%x power %u\n", + ci->ic_nchans-1, c->ic_freq, c->ic_flags, + c->ic_maxregpower); + /* NB: kernel fills in other fields */ + prev = c; + } + } +} + +static void +regdomain_makechannels( + struct ieee80211_regdomain_req *req, + const struct ieee80211_devcaps_req *dc) +{ + struct regdata *rdp = getregdata(); + const struct country *cc; + const struct ieee80211_regdomain *reg = &req->rd; + struct ieee80211req_chaninfo *ci = &req->chaninfo; + const struct regdomain *rd; + + /* + * Locate construction table for new channel list. We treat + * the regdomain/SKU as definitive so a country can be in + * multiple with different properties (e.g. US in FCC+FCC3). + * If no regdomain is specified then we fallback on the country + * code to find the associated regdomain since countries always + * belong to at least one regdomain. + */ + if (reg->regdomain == 0) { + cc = lib80211_country_findbycc(rdp, reg->country); + if (cc == NULL) + errx(1, "internal error, country %d not found", + reg->country); + rd = cc->rd; + } else + rd = lib80211_regdomain_findbysku(rdp, reg->regdomain); + if (rd == NULL) + errx(1, "internal error, regdomain %d not found", + reg->regdomain); + if (rd->sku != SKU_DEBUG) { + memset(ci, 0, sizeof(*ci)); + if (!LIST_EMPTY(&rd->bands_11b)) + regdomain_addchans(ci, &rd->bands_11b, reg, + IEEE80211_CHAN_B, &dc->dc_chaninfo); + if (!LIST_EMPTY(&rd->bands_11g)) + regdomain_addchans(ci, &rd->bands_11g, reg, + IEEE80211_CHAN_G, &dc->dc_chaninfo); + if (!LIST_EMPTY(&rd->bands_11a)) + regdomain_addchans(ci, &rd->bands_11a, reg, + IEEE80211_CHAN_A, &dc->dc_chaninfo); + if (!LIST_EMPTY(&rd->bands_11na)) { + regdomain_addchans(ci, &rd->bands_11na, reg, + IEEE80211_CHAN_A | IEEE80211_CHAN_HT20, + &dc->dc_chaninfo); + regdomain_addchans(ci, &rd->bands_11na, reg, + IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U, + &dc->dc_chaninfo); + regdomain_addchans(ci, &rd->bands_11na, reg, + IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D, + &dc->dc_chaninfo); + } + if (!LIST_EMPTY(&rd->bands_11ng)) { + regdomain_addchans(ci, &rd->bands_11ng, reg, + IEEE80211_CHAN_G | IEEE80211_CHAN_HT20, + &dc->dc_chaninfo); + regdomain_addchans(ci, &rd->bands_11ng, reg, + IEEE80211_CHAN_G | IEEE80211_CHAN_HT40U, + &dc->dc_chaninfo); + regdomain_addchans(ci, &rd->bands_11ng, reg, + IEEE80211_CHAN_G | IEEE80211_CHAN_HT40D, + &dc->dc_chaninfo); + } + qsort(ci->ic_chans, ci->ic_nchans, sizeof(ci->ic_chans[0]), + regdomain_sort); + } else + *ci = dc->dc_chaninfo; +} + +static void +list_countries(void) +{ + struct regdata *rdp = getregdata(); + const struct country *cp; + const struct regdomain *dp; + int i; + + i = 0; + printf("\nCountry codes:\n"); + LIST_FOREACH(cp, &rdp->countries, next) { + printf("%2s %-15.15s%s", cp->isoname, + cp->name, ((i+1)%4) == 0 ? "\n" : " "); + i++; + } + i = 0; + printf("\nRegulatory domains:\n"); + LIST_FOREACH(dp, &rdp->domains, next) { + printf("%-15.15s%s", dp->name, ((i+1)%4) == 0 ? "\n" : " "); + i++; + } + printf("\n"); +} + +static void +defaultcountry(const struct regdomain *rd) +{ + struct regdata *rdp = getregdata(); + const struct country *cc; + + cc = lib80211_country_findbycc(rdp, rd->cc->code); + if (cc == NULL) + errx(1, "internal error, ISO country code %d not " + "defined for regdomain %s", rd->cc->code, rd->name); + regdomain.country = cc->code; + regdomain.isocc[0] = cc->isoname[0]; + regdomain.isocc[1] = cc->isoname[1]; +} + +static +DECL_CMD_FUNC(set80211regdomain, val, d) +{ + struct regdata *rdp = getregdata(); + const struct regdomain *rd; + + rd = lib80211_regdomain_findbyname(rdp, val); + if (rd == NULL) { + rd = lib80211_regdomain_findbysku(rdp, atoi(val)); + if (rd == NULL) + errx(1, "unknown regdomain %s", val); + } + getregdomain(s); + regdomain.regdomain = rd->sku; + if (regdomain.country == 0 && rd->cc != NULL) { + /* + * No country code setup and there's a default + * one for this regdomain fill it in. + */ + defaultcountry(rd); + } + callback_register(setregdomain_cb, ®domain); +} + +static +DECL_CMD_FUNC(set80211country, val, d) +{ + struct regdata *rdp = getregdata(); + const struct country *cc; + + cc = lib80211_country_findbyname(rdp, val); + if (cc == NULL) { + cc = lib80211_country_findbycc(rdp, atoi(val)); + if (cc == NULL) + errx(1, "unknown ISO country code %s", val); + } + getregdomain(s); + regdomain.regdomain = cc->rd->sku; + regdomain.country = cc->code; + regdomain.isocc[0] = cc->isoname[0]; + regdomain.isocc[1] = cc->isoname[1]; + callback_register(setregdomain_cb, ®domain); +} + +static void +set80211location(const char *val, int d, int s, const struct afswtch *rafp) +{ + getregdomain(s); + regdomain.location = d; + callback_register(setregdomain_cb, ®domain); +} + +static void +set80211ecm(const char *val, int d, int s, const struct afswtch *rafp) +{ + getregdomain(s); + regdomain.ecm = d; + callback_register(setregdomain_cb, ®domain); +} + +static void LINE_INIT(char c) { spacer = c; @@ -1331,16 +2069,6 @@ getcaps(int capinfo) static const char * getflags(int flags) { -/* XXX need these publicly defined or similar */ -#define IEEE80211_NODE_AUTH 0x0001 /* authorized for data */ -#define IEEE80211_NODE_QOS 0x0002 /* QoS enabled */ -#define IEEE80211_NODE_ERP 0x0004 /* ERP enabled */ -#define IEEE80211_NODE_PWR_MGT 0x0010 /* power save mode enabled */ -#define IEEE80211_NODE_HT 0x0040 /* HT enabled */ -#define IEEE80211_NODE_HTCOMPAT 0x0080 /* HT setup w/ vendor OUI's */ -#define IEEE80211_NODE_WPS 0x0100 /* WPS association */ -#define IEEE80211_NODE_TSN 0x0200 /* TSN association */ - static char flagstring[32]; char *cp = flagstring; @@ -1363,14 +2091,6 @@ getflags(int flags) *cp++ = 'T'; *cp = '\0'; return flagstring; -#undef IEEE80211_NODE_TSN -#undef IEEE80211_NODE_WPS -#undef IEEE80211_NODE_HTCOMPAT -#undef IEEE80211_NODE_HT -#undef IEEE80211_NODE_AUTH -#undef IEEE80211_NODE_QOS -#undef IEEE80211_NODE_ERP -#undef IEEE80211_NODE_PWR_MGT } static void @@ -1814,27 +2534,27 @@ printcountry(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen) (((const u_int8_t *)(p))[2] << 16) | \ (((const u_int8_t *)(p))[3] << 24))) -static int __inline +static __inline int iswpaoui(const u_int8_t *frm) { return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI); } -static int __inline +static __inline int iswmeinfo(const u_int8_t *frm) { return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) && frm[6] == WME_INFO_OUI_SUBTYPE; } -static int __inline +static __inline int iswmeparam(const u_int8_t *frm) { return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) && frm[6] == WME_PARAM_OUI_SUBTYPE; } -static int __inline +static __inline int isatherosoui(const u_int8_t *frm) { return frm[1] > 3 && LE_READ_4(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI); @@ -1925,6 +2645,18 @@ printies(const u_int8_t *vp, int ielen, int maxcols) } static void +printmimo(const struct ieee80211_mimo_info *mi) +{ + /* NB: don't muddy display unless there's something to show */ + if (mi->rssi[0] != 0 || mi->rssi[1] != 0 || mi->rssi[2] != 0) { + /* XXX ignore EVM for now */ + printf(" (rssi %d:%d:%d nf %d:%d:%d)", + mi->rssi[0], mi->rssi[1], mi->rssi[2], + mi->noise[0], mi->noise[1], mi->noise[2]); + } +} + +static void list_scan(int s) { uint8_t buf[24*1024]; @@ -1973,11 +2705,17 @@ list_scan(int s) } while (len >= sizeof(struct ieee80211req_scan_result)); } +#ifdef __FreeBSD__ #include <net80211/ieee80211_freebsd.h> +#endif +#ifdef __NetBSD__ +#include <net80211/ieee80211_netbsd.h> +#endif static void scan_and_wait(int s) { + struct ieee80211_scan_req sr; struct ieee80211req ireq; int sroute; @@ -1989,6 +2727,16 @@ scan_and_wait(int s) (void) memset(&ireq, 0, sizeof(ireq)); (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); ireq.i_type = IEEE80211_IOC_SCAN_REQ; + + memset(&sr, 0, sizeof(sr)); + sr.sr_flags = IEEE80211_IOC_SCAN_ACTIVE + | IEEE80211_IOC_SCAN_NOPICK + | IEEE80211_IOC_SCAN_ONCE; + sr.sr_duration = IEEE80211_IOC_SCAN_FOREVER; + sr.sr_nssid = 0; + + ireq.i_data = &sr; + ireq.i_len = sizeof(sr); /* NB: only root can trigger a scan so ignore errors */ if (ioctl(s, SIOCS80211, &ireq) >= 0) { char buf[2048]; @@ -2055,20 +2803,6 @@ getrxseq(const struct ieee80211req_sta_info *si) #undef IEEE80211_NODE_QOS } -static int -gettxrate(const struct ieee80211req_sta_info *si) -{ - int txrate = si->isi_txrate; - - if (txrate & 0x80) { - txrate = htrates[txrate & 0xf]; - /* NB: could bump this more based on short gi */ - return si->isi_flags & IEEE80211_CHAN_HT40 ? - txrate : txrate / 2; - } else - return (si->isi_rates[txrate] & IEEE80211_RATE_VAL) / 2; -} - static void list_stations(int s) { @@ -2119,7 +2853,7 @@ list_stations(int s) , ether_ntoa((const struct ether_addr*) si->isi_macaddr) , IEEE80211_AID(si->isi_associd) , ieee80211_mhz2ieee(si->isi_freq, si->isi_flags) - , gettxrate(si) + , si->isi_txmbps/2 , si->isi_rssi/2. , si->isi_inact , gettxseq(si) @@ -2128,6 +2862,7 @@ list_stations(int s) , getflags(si->isi_state) ); printies(cp + si->isi_ie_off, si->isi_ie_len, 24); + printmimo(&si->isi_mimo); printf("\n"); cp += si->isi_len, len -= si->isi_len; } while (len >= sizeof(struct ieee80211req_sta_info)); @@ -2326,23 +3061,33 @@ list_keys(int s) } #define IEEE80211_C_BITS \ -"\020\1WEP\2TKIP\3AES\4AES_CCM\6CKIP\7FF\10TURBOP\11IBSS\12PMGT\13HOSTAP\14AHDEMO" \ -"\15SWRETRY\16TXPMGT\17SHSLOT\20SHPREAMBLE\21MONITOR\22TKIPMIC\30WPA1" \ -"\31WPA2\32BURST\33WME\34WDS\36BGSCAN\37TXFRAG" + "\20\7FF\10TURBOP\11IBSS\12PMGT" \ + "\13HOSTAP\14AHDEMO\15SWRETRY\16TXPMGT\17SHSLOT\20SHPREAMBLE" \ + "\21MONITOR\30WPA1\31WPA2\32BURST\33WME\34WDS\36BGSCAN" \ + "\37TXFRAG" + +#define IEEE80211_CRYPTO_BITS \ + "\20\1WEP\2TKIP\3AES\4AES_CCM\5TKIPMIC\6CKIP\12PMGT" + +#define IEEE80211_HTCAP_BITS \ + "\20\1LDPC\2CHWIDTH40\5GREENFIELD\6SHORTGI20\7SHORTGI40\10TXSTBC" \ + "\21AMPDU\22AMSDU\23HT" static void list_capabilities(int s) { - struct ieee80211req ireq; - u_int32_t caps; + struct ieee80211_devcaps_req dc; - (void) memset(&ireq, 0, sizeof(ireq)); - (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); - ireq.i_type = IEEE80211_IOC_DRIVER_CAPS; - if (ioctl(s, SIOCG80211, &ireq) < 0) - errx(1, "unable to get driver capabilities"); - caps = (((u_int16_t) ireq.i_val) << 16) | ((u_int16_t) ireq.i_len); - printb(name, caps, IEEE80211_C_BITS); + getdevcaps(s, &dc); + printb("drivercaps", dc.dc_drivercaps, IEEE80211_C_BITS); + if (dc.dc_cryptocaps != 0 || verbose) { + putchar('\n'); + printb("cryptocaps", dc.dc_cryptocaps, IEEE80211_CRYPTO_BITS); + } + if (dc.dc_htcaps != 0 || verbose) { + putchar('\n'); + printb("htcaps", dc.dc_htcaps, IEEE80211_HTCAP_BITS); + } putchar('\n'); } @@ -2412,6 +3157,77 @@ again: } static void +list_roam(int s) +{ + const struct ieee80211_roamparam *rp; + int mode; + + getroam(s); + for (mode = IEEE80211_MODE_11A; mode < IEEE80211_MODE_11NA; mode++) { + rp = &roamparams.params[mode]; + if (rp->rssi == 0 && rp->rate == 0) + continue; + if (rp->rssi & 1) + LINE_CHECK("roam:%-6.6s rssi %2u.5dBm rate %2u Mb/s", + modename[mode], rp->rssi/2, rp->rate/2); + else + LINE_CHECK("roam:%-6.6s rssi %4udBm rate %2u Mb/s", + modename[mode], rp->rssi/2, rp->rate/2); + } + for (; mode < IEEE80211_MODE_MAX; mode++) { + rp = &roamparams.params[mode]; + if (rp->rssi == 0 && rp->rate == 0) + continue; + if (rp->rssi & 1) + LINE_CHECK("roam:%-6.6s rssi %2u.5dBm MCS %2u ", + modename[mode], rp->rssi/2, rp->rate &~ 0x80); + else + LINE_CHECK("roam:%-6.6s rssi %4udBm MCS %2u ", + modename[mode], rp->rssi/2, rp->rate &~ 0x80); + } +} + +static void +list_txparams(int s) +{ + const struct ieee80211_txparam *tp; + int mode; + + gettxparams(s); + for (mode = IEEE80211_MODE_11A; mode < IEEE80211_MODE_11NA; mode++) { + tp = &txparams.params[mode]; + if (tp->mgmtrate == 0 && tp->mcastrate == 0) + continue; + if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE) + LINE_CHECK("%-6.6s ucast NONE mgmt %2u Mb/s " + "mcast %2u Mb/s maxretry %u", + modename[mode], tp->mgmtrate/2, + tp->mcastrate/2, tp->maxretry); + else + LINE_CHECK("%-6.6s ucast %2u Mb/s mgmt %2u Mb/s " + "mcast %2u Mb/s maxretry %u", + modename[mode], tp->ucastrate/2, tp->mgmtrate/2, + tp->mcastrate/2, tp->maxretry); + } + for (; mode < IEEE80211_MODE_MAX; mode++) { + tp = &txparams.params[mode]; + if (tp->mgmtrate == 0 && tp->mcastrate == 0) + continue; + if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE) + LINE_CHECK("%-6.6s ucast NONE mgmt %2u MCS " + "mcast %2u MCS maxretry %u", + modename[mode], tp->mgmtrate &~ 0x80, + tp->mcastrate &~ 0x80, tp->maxretry); + else + LINE_CHECK("%-6.6s ucast %2u MCS mgmt %2u MCS " + "mcast %2u MCS maxretry %u", + modename[mode], tp->ucastrate &~ 0x80, + tp->mgmtrate &~ 0x80, + tp->mcastrate &~ 0x80, tp->maxretry); + } +} + +static void printpolicy(int policy) { switch (policy) { @@ -2424,6 +3240,9 @@ printpolicy(int policy) case IEEE80211_MACCMD_POLICY_DENY: printf("policy: deny\n"); break; + case IEEE80211_MACCMD_POLICY_RADIUS: + printf("policy: radius\n"); + break; default: printf("policy: unknown (%u)\n", policy); break; @@ -2457,6 +3276,8 @@ list_mac(int s) c = '+'; } else if (policy == IEEE80211_MACCMD_POLICY_DENY) { c = '-'; + } else if (policy == IEEE80211_MACCMD_POLICY_RADIUS) { + c = 'r'; /* NB: should never have entries */ } else { printf("policy: unknown (%u)\n", policy); c = '?'; @@ -2490,6 +3311,52 @@ list_mac(int s) free(data); } +static void +print_regdomain(const struct ieee80211_regdomain *reg, int verb) +{ + if ((reg->regdomain != 0 && + reg->regdomain != reg->country) || verb) { + const struct regdomain *rd = + lib80211_regdomain_findbysku(getregdata(), reg->regdomain); + if (rd == NULL) + LINE_CHECK("regdomain %d", reg->regdomain); + else + LINE_CHECK("regdomain %s", rd->name); + } + if (reg->country != 0 || verb) { + const struct country *cc = + lib80211_country_findbycc(getregdata(), reg->country); + if (cc == NULL) + LINE_CHECK("country %d", reg->country); + else + LINE_CHECK("country %s", cc->isoname); + } + if (reg->location == 'I') + LINE_CHECK("indoor"); + else if (reg->location == 'O') + LINE_CHECK("outdoor"); + else if (verb) + LINE_CHECK("anywhere"); + if (reg->ecm) + LINE_CHECK("ecm"); + else if (verb) + LINE_CHECK("-ecm"); +} + +static void +list_regdomain(int s, int channelsalso) +{ + getregdomain(s); + if (channelsalso) { + getchaninfo(s); + spacer = ':'; + print_regdomain(®domain, 1); + LINE_BREAK(); + print_channels(s, &chaninfo, 1/*allchans*/, 1/*verbose*/); + } else + print_regdomain(®domain, verbose); +} + static DECL_CMD_FUNC(set80211list, arg, d) { @@ -2509,14 +3376,23 @@ DECL_CMD_FUNC(set80211list, arg, d) list_keys(s); else if (iseq(arg, "caps")) list_capabilities(s); - else if (iseq(arg, "wme")) + else if (iseq(arg, "wme") || iseq(arg, "wmm")) list_wme(s); else if (iseq(arg, "mac")) list_mac(s); else if (iseq(arg, "txpow")) list_txpow(s); + else if (iseq(arg, "roam")) + list_roam(s); + else if (iseq(arg, "txparam") || iseq(arg, "txparm")) + list_txparams(s); + else if (iseq(arg, "regdomain")) + list_regdomain(s, 1); + else if (iseq(arg, "countries")) + list_countries(); else errx(1, "Don't know how to list %s for %s", arg, name); + LINE_BREAK(); #undef iseq } @@ -2674,15 +3550,6 @@ getssid(int s, int ix, void *data, size_t len, int *plen) } static void -printrssi(const char *tag, int rssi) -{ - if (rssi & 1) - LINE_CHECK("%s %u.5", tag, rssi/2); - else - LINE_CHECK("%s %u", tag, rssi/2); -} - -static void ieee80211_status(int s) { static const uint8_t zerobssid[IEEE80211_ADDR_LEN]; @@ -2690,6 +3557,8 @@ ieee80211_status(int s) int i, num, wpa, wme, bgscan, bgscaninterval, val, len, wepmode; uint8_t data[32]; const struct ieee80211_channel *c; + const struct ieee80211_roamparam *rp; + const struct ieee80211_txparam *tp; if (getssid(s, -1, data, sizeof(data), &len) < 0) { /* If we can't get the SSID, this isn't an 802.11 device. */ @@ -2701,7 +3570,10 @@ ieee80211_status(int s) * if's doesn't reuse the first interfaces' cached state. */ gotcurchan = 0; + gotroam = 0; + gottxparams = 0; gothtconf = 0; + gotregdomain = 0; if (get80211val(s, IEEE80211_IOC_NUMSSIDS, &num) < 0) num = 0; @@ -2736,6 +3608,8 @@ ieee80211_status(int s) spacer = ' '; /* force first break */ LINE_BREAK(); + list_regdomain(s, 0); + wpa = 0; if (get80211val(s, IEEE80211_IOC_AUTHMODE, &val) != -1) { switch (val) { @@ -2776,12 +3650,55 @@ ieee80211_status(int s) } if (wpa || verbose) { + if (get80211val(s, IEEE80211_IOC_WPS, &val) != -1) { + if (val) + LINE_CHECK("wps"); + else if (verbose) + LINE_CHECK("-wps"); + } + if (get80211val(s, IEEE80211_IOC_TSN, &val) != -1) { + if (val) + LINE_CHECK("tsn"); + else if (verbose) + LINE_CHECK("-tsn"); + } if (ioctl(s, IEEE80211_IOC_COUNTERMEASURES, &val) != -1) { if (val) LINE_CHECK("countermeasures"); else if (verbose) LINE_CHECK("-countermeasures"); } +#if 0 + /* XXX not interesting with WPA done in user space */ + ireq.i_type = IEEE80211_IOC_KEYMGTALGS; + if (ioctl(s, SIOCG80211, &ireq) != -1) { + } + + ireq.i_type = IEEE80211_IOC_MCASTCIPHER; + if (ioctl(s, SIOCG80211, &ireq) != -1) { + LINE_CHECK("mcastcipher "); + printcipher(s, &ireq, IEEE80211_IOC_MCASTKEYLEN); + spacer = ' '; + } + + ireq.i_type = IEEE80211_IOC_UCASTCIPHER; + if (ioctl(s, SIOCG80211, &ireq) != -1) { + LINE_CHECK("ucastcipher "); + printcipher(s, &ireq, IEEE80211_IOC_UCASTKEYLEN); + } + + if (wpa & 2) { + ireq.i_type = IEEE80211_IOC_RSNCAPS; + if (ioctl(s, SIOCG80211, &ireq) != -1) { + LINE_CHECK("RSN caps 0x%x", ireq.i_val); + spacer = ' '; + } + } + + ireq.i_type = IEEE80211_IOC_UCASTCIPHERS; + if (ioctl(s, SIOCG80211, &ireq) != -1) { + } +#endif } if (get80211val(s, IEEE80211_IOC_WEP, &wepmode) != -1 && @@ -2876,6 +3793,13 @@ end: LINE_CHECK("txpowmax %.1f", val/2.); } + if (get80211val(s, IEEE80211_IOC_DOTD, &val) != -1) { + if (val) + LINE_CHECK("dotd"); + else if (verbose) + LINE_CHECK("-dotd"); + } + if (get80211val(s, IEEE80211_IOC_RTSTHRESHOLD, &val) != -1) { if (val != IEEE80211_RTS_MAX || verbose) LINE_CHECK("rtsthreshold %d", val); @@ -2892,8 +3816,19 @@ end: } } - if (get80211val(s, IEEE80211_IOC_MCAST_RATE, &val) != -1) - printrate("mcastrate", val, 2*1, 0/*XXX*/); + if (!verbose) { + gettxparams(s); + tp = &txparams.params[chan2mode(c)]; + printrate("ucastrate", tp->ucastrate, + IEEE80211_FIXED_RATE_NONE, IEEE80211_FIXED_RATE_NONE); + printrate("mcastrate", tp->mcastrate, 2*1, 0x80|0); + printrate("mgmtrate", tp->mgmtrate, 2*1, 0x80|0); + if (tp->maxretry != 6) /* XXX */ + LINE_CHECK("maxretry %d", tp->maxretry); + } else { + LINE_BREAK(); + list_txparams(s); + } bgscaninterval = -1; (void) get80211val(s, IEEE80211_IOC_BGSCAN_INTERVAL, &bgscaninterval); @@ -2915,23 +3850,17 @@ end: LINE_CHECK("bgscanintvl %u", bgscaninterval); if (get80211val(s, IEEE80211_IOC_BGSCAN_IDLE, &val) != -1) LINE_CHECK("bgscanidle %u", val); - if (IEEE80211_IS_CHAN_A(c) || verbose) { - if (get80211val(s, IEEE80211_IOC_ROAM_RSSI_11A, &val) != -1) - printrssi("roam:rssi11a", val); - if (get80211val(s, IEEE80211_IOC_ROAM_RATE_11A, &val) != -1) - printrate("roam:rate11a", val, -1, -1); - } - if (IEEE80211_IS_CHAN_B(c) || verbose) { - if (get80211val(s, IEEE80211_IOC_ROAM_RSSI_11B, &val) != -1) - printrssi("roam:rssi11b", val); - if (get80211val(s, IEEE80211_IOC_ROAM_RATE_11B, &val) != -1) - printrate("roam:rate11b", val, -1, -1); - } - if (IEEE80211_IS_CHAN_ANYG(c) || verbose) { - if (get80211val(s, IEEE80211_IOC_ROAM_RSSI_11G, &val) != -1) - printrssi("roam:rssi11g", val); - if (get80211val(s, IEEE80211_IOC_ROAM_RATE_11G, &val) != -1) - printrate("roam:rate11g", val, -1, -1); + if (!verbose) { + getroam(s); + rp = &roamparams.params[chan2mode(c)]; + if (rp->rssi & 1) + LINE_CHECK("roam:rssi %u.5", rp->rssi/2); + else + LINE_CHECK("roam:rssi %u", rp->rssi/2); + LINE_CHECK("roam:rate %u", rp->rate/2); + } else { + LINE_BREAK(); + list_roam(s); } } @@ -3061,7 +3990,6 @@ end: } } /* XXX amsdu limit */ - /* XXX 20/40 */ if (get80211val(s, IEEE80211_IOC_SHORTGI, &val) != -1) { if (val) LINE_CHECK("shortgi"); @@ -3111,6 +4039,12 @@ end: else if (verbose) LINE_CHECK("-dturbo"); } + if (get80211val(s, IEEE80211_IOC_DWDS, &val) != -1) { + if (val) + LINE_CHECK("dwds"); + else if (verbose) + LINE_CHECK("-dwds"); + } if (opmode == IEEE80211_M_HOSTAP) { if (get80211val(s, IEEE80211_IOC_HIDESSID, &val) != -1) { @@ -3134,6 +4068,12 @@ end: else if (verbose) LINE_CHECK("doth"); } + if (get80211val(s, IEEE80211_IOC_DFS, &val) != -1) { + if (!val) + LINE_CHECK("-dfs"); + else if (verbose) + LINE_CHECK("dfs"); + } if (get80211val(s, IEEE80211_IOC_INACTIVITY, &val) != -1) { if (!val) LINE_CHECK("-inact"); @@ -3314,6 +4254,113 @@ print_string(const u_int8_t *buf, int len) } } +/* + * Virtual AP cloning support. + */ +static struct ieee80211_clone_params params = { + .icp_opmode = IEEE80211_M_STA, /* default to station mode */ +}; + +static void +wlan_create(int s, struct ifreq *ifr) +{ + static const uint8_t zerobssid[IEEE80211_ADDR_LEN]; + + if (params.icp_parent[0] == '\0') + errx(1, "must specify a parent when creating a wlan device"); + if (params.icp_opmode == IEEE80211_M_WDS && + memcmp(params.icp_bssid, zerobssid, sizeof(zerobssid)) == 0) + errx(1, "no bssid specified for WDS (use wlanbssid)"); + ifr->ifr_data = (caddr_t) ¶ms; + if (ioctl(s, SIOCIFCREATE2, ifr) < 0) + err(1, "SIOCIFCREATE2"); +} + +static +DECL_CMD_FUNC(set80211clone_wlandev, arg, d) +{ + strlcpy(params.icp_parent, arg, IFNAMSIZ); + clone_setcallback(wlan_create); +} + +static +DECL_CMD_FUNC(set80211clone_wlanbssid, arg, d) +{ + const struct ether_addr *ea; + + ea = ether_aton(arg); + if (ea == NULL) + errx(1, "%s: cannot parse bssid", arg); + memcpy(params.icp_bssid, ea->octet, IEEE80211_ADDR_LEN); + clone_setcallback(wlan_create); +} + +static +DECL_CMD_FUNC(set80211clone_wlanaddr, arg, d) +{ + const struct ether_addr *ea; + + ea = ether_aton(arg); + if (ea == NULL) + errx(1, "%s: cannot parse addres", arg); + memcpy(params.icp_macaddr, ea->octet, IEEE80211_ADDR_LEN); + params.icp_flags |= IEEE80211_CLONE_MACADDR; + clone_setcallback(wlan_create); +} + +static +DECL_CMD_FUNC(set80211clone_wlanmode, arg, d) +{ +#define iseq(a,b) (strncasecmp(a,b,sizeof(b)-1) == 0) + if (iseq(arg, "sta")) + params.icp_opmode = IEEE80211_M_STA; + else if (iseq(arg, "ahdemo") || iseq(arg, "adhoc-demo")) + params.icp_opmode = IEEE80211_M_AHDEMO; + else if (iseq(arg, "ibss") || iseq(arg, "adhoc")) + params.icp_opmode = IEEE80211_M_IBSS; + else if (iseq(arg, "ap") || iseq(arg, "host")) + params.icp_opmode = IEEE80211_M_HOSTAP; + else if (iseq(arg, "wds")) + params.icp_opmode = IEEE80211_M_WDS; + else if (iseq(arg, "monitor")) + params.icp_opmode = IEEE80211_M_MONITOR; + else + errx(1, "Don't know to create %s for %s", arg, name); + clone_setcallback(wlan_create); +#undef iseq +} + +static void +set80211clone_beacons(const char *val, int d, int s, const struct afswtch *rafp) +{ + /* NB: inverted sense */ + if (d) + params.icp_flags &= ~IEEE80211_CLONE_NOBEACONS; + else + params.icp_flags |= IEEE80211_CLONE_NOBEACONS; + clone_setcallback(wlan_create); +} + +static void +set80211clone_bssid(const char *val, int d, int s, const struct afswtch *rafp) +{ + if (d) + params.icp_flags |= IEEE80211_CLONE_BSSID; + else + params.icp_flags &= ~IEEE80211_CLONE_BSSID; + clone_setcallback(wlan_create); +} + +static void +set80211clone_wdslegacy(const char *val, int d, int s, const struct afswtch *rafp) +{ + if (d) + params.icp_flags |= IEEE80211_CLONE_WDSLEGACY; + else + params.icp_flags &= ~IEEE80211_CLONE_WDSLEGACY; + clone_setcallback(wlan_create); +} + static struct cmd ieee80211_cmds[] = { DEF_CMD_ARG("ssid", set80211ssid), DEF_CMD_ARG("nwid", set80211ssid), @@ -3339,6 +4386,8 @@ static struct cmd ieee80211_cmds[] = { DEF_CMD_ARG("roaming", set80211roaming), DEF_CMD("wme", 1, set80211wme), DEF_CMD("-wme", 0, set80211wme), + DEF_CMD("wmm", 1, set80211wme), + DEF_CMD("-wmm", 0, set80211wme), DEF_CMD("hidessid", 1, set80211hidessid), DEF_CMD("-hidessid", 0, set80211hidessid), DEF_CMD("apbridge", 1, set80211apbridge), @@ -3365,6 +4414,7 @@ static struct cmd ieee80211_cmds[] = { DEF_CMD("mac:open", IEEE80211_MACCMD_POLICY_OPEN, set80211maccmd), DEF_CMD("mac:allow", IEEE80211_MACCMD_POLICY_ALLOW, set80211maccmd), DEF_CMD("mac:deny", IEEE80211_MACCMD_POLICY_DENY, set80211maccmd), + DEF_CMD("mac:radius", IEEE80211_MACCMD_POLICY_RADIUS, set80211maccmd), DEF_CMD("mac:flush", IEEE80211_MACCMD_FLUSH, set80211maccmd), DEF_CMD("mac:detach", IEEE80211_MACCMD_DETACH, set80211maccmd), DEF_CMD_ARG("mac:add", set80211addmac), @@ -3381,13 +4431,13 @@ static struct cmd ieee80211_cmds[] = { DEF_CMD_ARG("bgscanidle", set80211bgscanidle), DEF_CMD_ARG("bgscanintvl", set80211bgscanintvl), DEF_CMD_ARG("scanvalid", set80211scanvalid), - DEF_CMD_ARG("roam:rssi11a", set80211roamrssi11a), - DEF_CMD_ARG("roam:rssi11b", set80211roamrssi11b), - DEF_CMD_ARG("roam:rssi11g", set80211roamrssi11g), - DEF_CMD_ARG("roam:rate11a", set80211roamrate11a), - DEF_CMD_ARG("roam:rate11b", set80211roamrate11b), - DEF_CMD_ARG("roam:rate11g", set80211roamrate11g), + DEF_CMD_ARG("roam:rssi", set80211roamrssi), + DEF_CMD_ARG("roam:rate", set80211roamrate), DEF_CMD_ARG("mcastrate", set80211mcastrate), + DEF_CMD_ARG("ucastrate", set80211ucastrate), + DEF_CMD_ARG("mgtrate", set80211mgtrate), + DEF_CMD_ARG("mgmtrate", set80211mgtrate), + DEF_CMD_ARG("maxretry", set80211maxretry), DEF_CMD_ARG("fragthreshold", set80211fragthreshold), DEF_CMD("burst", 1, set80211burst), DEF_CMD("-burst", 0, set80211burst), @@ -3414,10 +4464,27 @@ static struct cmd ieee80211_cmds[] = { DEF_CMD("-puren", 0, set80211puren), DEF_CMD("doth", 1, set80211doth), DEF_CMD("-doth", 0, set80211doth), + DEF_CMD("dfs", 1, set80211dfs), + DEF_CMD("-dfs", 0, set80211dfs), DEF_CMD("htcompat", 1, set80211htcompat), DEF_CMD("-htcompat", 0, set80211htcompat), + DEF_CMD("dwds", 1, set80211dwds), + DEF_CMD("-dwds", 0, set80211dwds), DEF_CMD("inact", 1, set80211inact), DEF_CMD("-inact", 0, set80211inact), + DEF_CMD("tsn", 1, set80211tsn), + DEF_CMD("-tsn", 0, set80211tsn), + DEF_CMD_ARG("regdomain", set80211regdomain), + DEF_CMD_ARG("country", set80211country), + DEF_CMD("indoor", 'I', set80211location), + DEF_CMD("-indoor", 'O', set80211location), + DEF_CMD("outdoor", 'O', set80211location), + DEF_CMD("-outdoor", 'I', set80211location), + DEF_CMD("anywhere", ' ', set80211location), + DEF_CMD("ecm", 1, set80211ecm), + DEF_CMD("-ecm", 0, set80211ecm), + DEF_CMD("dotd", 1, set80211dotd), + DEF_CMD("-dotd", 0, set80211dotd), DEF_CMD_ARG("htprotmode", set80211htprotmode), DEF_CMD("ht20", 1, set80211htconf), DEF_CMD("-ht20", 0, set80211htconf), @@ -3425,6 +4492,20 @@ static struct cmd ieee80211_cmds[] = { DEF_CMD("-ht40", 0, set80211htconf), DEF_CMD("ht", 3, set80211htconf), /* NB: 20+40 */ DEF_CMD("-ht", 0, set80211htconf), + /* XXX for testing */ + DEF_CMD_ARG("chanswitch", set80211chanswitch), + + /* vap cloning support */ + DEF_CLONE_CMD_ARG("wlanaddr", set80211clone_wlanaddr), + DEF_CLONE_CMD_ARG("wlanbssid", set80211clone_wlanbssid), + DEF_CLONE_CMD_ARG("wlandev", set80211clone_wlandev), + DEF_CLONE_CMD_ARG("wlanmode", set80211clone_wlanmode), + DEF_CLONE_CMD("beacons", 1, set80211clone_beacons), + DEF_CLONE_CMD("-beacons", 0, set80211clone_beacons), + DEF_CLONE_CMD("bssid", 1, set80211clone_bssid), + DEF_CLONE_CMD("-bssid", 0, set80211clone_bssid), + DEF_CLONE_CMD("wdslegacy", 1, set80211clone_wdslegacy), + DEF_CLONE_CMD("-wdslegacy", 0, set80211clone_wdslegacy), }; static struct afswtch af_ieee80211 = { .af_name = "af_ieee80211", diff --git a/sbin/ifconfig/ifmedia.c b/sbin/ifconfig/ifmedia.c index 947b961..04e2222 100644 --- a/sbin/ifconfig/ifmedia.c +++ b/sbin/ifconfig/ifmedia.c @@ -102,6 +102,11 @@ static struct ifmedia_type_to_subtype *get_toptype_ttos(int); static struct ifmedia_description *get_subtype_desc(int, struct ifmedia_type_to_subtype *ttos); +#define IFM_OPMODE(x) \ + ((x) & (IFM_IEEE80211_ADHOC | IFM_IEEE80211_HOSTAP | \ + IFM_IEEE80211_IBSS | IFM_IEEE80211_WDS | IFM_IEEE80211_MONITOR)) +#define IFM_IEEE80211_STA 0 + static void media_status(int s) { @@ -162,10 +167,13 @@ media_status(int s) break; case IFM_IEEE80211: - /* XXX: Different value for adhoc? */ - if (ifmr.ifm_status & IFM_ACTIVE) - printf("associated"); - else + if (ifmr.ifm_status & IFM_ACTIVE) { + /* NB: only sta mode associates */ + if (IFM_OPMODE(ifmr.ifm_active) == IFM_IEEE80211_STA) + printf("associated"); + else + printf("running"); + } else printf("no carrier"); break; } diff --git a/sbin/ifconfig/regdomain.c b/sbin/ifconfig/regdomain.c new file mode 100644 index 0000000..55c382e --- /dev/null +++ b/sbin/ifconfig/regdomain.c @@ -0,0 +1,636 @@ +/*- + * Copyright (c) 2008 Sam Leffler, Errno Consulting + * All rights reserved. + * + * 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 ``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 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. + */ +#ifndef lint +static const char rcsid[] = "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/errno.h> +#include <sys/mman.h> +#include <sys/sbuf.h> +#include <sys/stat.h> + +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <fcntl.h> +#include <err.h> +#include <unistd.h> + +#include <bsdxml.h> + +#include "regdomain.h" + +#include <net80211/_ieee80211.h> + +#define MAXLEVEL 20 + +struct mystate { + struct regdata *rdp; + struct regdomain *rd; /* current domain */ + struct netband *netband; /* current netband */ + struct freqband *freqband; /* current freqband */ + struct country *country; /* current country */ + netband_head *curband; /* current netband list */ + int level; + struct sbuf *sbuf[MAXLEVEL]; + int nident; +}; + +struct ident { + const void *id; + void *p; + enum { DOMAIN, COUNTRY, FREQBAND } type; +}; + +static void +start_element(void *data, const char *name, const char **attr) +{ +#define iseq(a,b) (strcasecmp(a,b) == 0) + struct mystate *mt; + const void *id, *ref, *mode; + int i; + + mt = data; + if (++mt->level == MAXLEVEL) { + /* XXX force parser to abort */ + return; + } + mt->sbuf[mt->level] = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND); + id = ref = mode = NULL; + for (i = 0; attr[i] != NULL; i += 2) { + if (iseq(attr[i], "id")) { + id = attr[i+1]; + } else if (iseq(attr[i], "ref")) { + ref = attr[i+1]; + } else if (iseq(attr[i], "mode")) { + mode = attr[i+1]; + } else + printf("%*.*s[%s = %s]\n", mt->level + 1, + mt->level + 1, "", attr[i], attr[i+1]); + } + if (iseq(name, "rd") && mt->rd == NULL) { + if (mt->country == NULL) { + mt->rd = calloc(1, sizeof(struct regdomain)); + mt->rd->name = strdup(id); + mt->nident++; + LIST_INSERT_HEAD(&mt->rdp->domains, mt->rd, next); + } else + mt->country->rd = (void *)strdup(ref); + return; + } + if (iseq(name, "defcc") && mt->rd != NULL) { + mt->rd->cc = (void *)strdup(ref); + return; + } + if (iseq(name, "netband") && mt->curband == NULL && mt->rd != NULL) { + if (mode == NULL) { + /* XXX complain */ + return; + } + if (iseq(mode, "11b")) + mt->curband = &mt->rd->bands_11b; + else if (iseq(mode, "11g")) + mt->curband = &mt->rd->bands_11g; + else if (iseq(mode, "11a")) + mt->curband = &mt->rd->bands_11a; + else if (iseq(mode, "11ng")) + mt->curband = &mt->rd->bands_11ng; + else if (iseq(mode, "11na")) + mt->curband = &mt->rd->bands_11na; + /* XXX else complain */ + return; + } + if (iseq(name, "band") && mt->netband == NULL) { + if (mt->curband == NULL) { + /* XXX complain */ + return; + } + mt->netband = calloc(1, sizeof(struct netband)); + LIST_INSERT_HEAD(mt->curband, mt->netband, next); + return; + } + if (iseq(name, "freqband") && mt->freqband == NULL && mt->netband != NULL) { + /* XXX handle inlines and merge into table? */ + if (mt->netband->band != NULL) { + /* XXX complain */ + } else + mt->netband->band = (void *)strdup(ref); + return; + } + + if (iseq(name, "country") && mt->country == NULL) { + mt->country = calloc(1, sizeof(struct country)); + mt->country->isoname = strdup(id); + mt->nident++; + LIST_INSERT_HEAD(&mt->rdp->countries, mt->country, next); + return; + } + + if (iseq(name, "freqband") && mt->freqband == NULL) { + mt->freqband = calloc(1, sizeof(struct freqband)); + mt->freqband->id = strdup(id); + mt->nident++; + LIST_INSERT_HEAD(&mt->rdp->freqbands, mt->freqband, next); + return; + } +#undef iseq +} + +static uint32_t +decode_flag(const char *p, int len) +{ +#define iseq(a,b) (strcasecmp(a,b) == 0) + static const struct { + const char *name; + int len; + uint32_t value; + } flags[] = { +#define FLAG(x) { #x, sizeof(#x), x } + FLAG(IEEE80211_CHAN_A), + FLAG(IEEE80211_CHAN_B), + FLAG(IEEE80211_CHAN_G), + FLAG(IEEE80211_CHAN_HT20), + FLAG(IEEE80211_CHAN_HT40), + FLAG(IEEE80211_CHAN_ST), + FLAG(IEEE80211_CHAN_TURBO), + FLAG(IEEE80211_CHAN_PASSIVE), + FLAG(IEEE80211_CHAN_DFS), + FLAG(IEEE80211_CHAN_CCK), + FLAG(IEEE80211_CHAN_OFDM), + FLAG(IEEE80211_CHAN_2GHZ), + FLAG(IEEE80211_CHAN_5GHZ), + FLAG(IEEE80211_CHAN_DYN), + FLAG(IEEE80211_CHAN_GFSK), + FLAG(IEEE80211_CHAN_GSM), + FLAG(IEEE80211_CHAN_STURBO), + FLAG(IEEE80211_CHAN_HALF), + FLAG(IEEE80211_CHAN_QUARTER), + FLAG(IEEE80211_CHAN_HT40U), + FLAG(IEEE80211_CHAN_HT40D), + FLAG(IEEE80211_CHAN_4MSXMIT), + FLAG(IEEE80211_CHAN_NOADHOC), + FLAG(IEEE80211_CHAN_NOHOSTAP), + FLAG(IEEE80211_CHAN_11D), + FLAG(IEEE80211_CHAN_FHSS), + FLAG(IEEE80211_CHAN_PUREG), + FLAG(IEEE80211_CHAN_108A), + FLAG(IEEE80211_CHAN_108G), +#undef FLAG + }; + int i; + + for (i = 0; i < sizeof(flags)/sizeof(flags[0]); i++) + if (len == flags[i].len && iseq(p, flags[i].name)) + return flags[i].value; + return 0; +#undef iseq +} + +static void +end_element(void *data, const char *name) +{ +#define iseq(a,b) (strcasecmp(a,b) == 0) + struct mystate *mt; + int len; + char *p; + + mt = data; + sbuf_finish(mt->sbuf[mt->level]); + p = sbuf_data(mt->sbuf[mt->level]); + len = sbuf_len(mt->sbuf[mt->level]); + + /* <freqband>...</freqband> */ + if (iseq(name, "freqstart") && mt->freqband != NULL) { + mt->freqband->freqStart = strtoul(p, NULL, 0); + goto done; + } + if (iseq(name, "freqend") && mt->freqband != NULL) { + mt->freqband->freqEnd = strtoul(p, NULL, 0); + goto done; + } + if (iseq(name, "chanwidth") && mt->freqband != NULL) { + mt->freqband->chanWidth = strtoul(p, NULL, 0); + goto done; + } + if (iseq(name, "chansep") && mt->freqband != NULL) { + mt->freqband->chanSep = strtoul(p, NULL, 0); + goto done; + } + if (iseq(name, "flags")) { + if (mt->freqband != NULL) + mt->freqband->flags |= decode_flag(p, len); + else if (mt->netband != NULL) + mt->netband->flags |= decode_flag(p, len); + else { + /* XXX complain */ + } + goto done; + } + + /* <rd> ... </rd> */ + if (iseq(name, "name") && mt->rd != NULL) { + mt->rd->name = strdup(p); + goto done; + } + if (iseq(name, "sku") && mt->rd != NULL) { + mt->rd->sku = strtoul(p, NULL, 0); + goto done; + } + if (iseq(name, "netband") && mt->rd != NULL) { + mt->curband = NULL; + goto done; + } + + /* <band> ... </band> */ + if (iseq(name, "freqband") && mt->netband != NULL) { + /* XXX handle inline freqbands */ + goto done; + } + if (iseq(name, "maxpower") && mt->netband != NULL) { + mt->netband->maxPower = strtoul(p, NULL, 0); + goto done; + } + if (iseq(name, "maxpowerdfs") && mt->netband != NULL) { + mt->netband->maxPowerDFS = strtoul(p, NULL, 0); + goto done; + } + + /* <country>...</country> */ + if (iseq(name, "isocc") && mt->country != NULL) { + mt->country->code = strtoul(p, NULL, 0); + goto done; + } + if (iseq(name, "name") && mt->country != NULL) { + mt->country->name = strdup(p); + goto done; + } + + if (len != 0) { + printf("Unexpected XML: name \"%s\" data \"%s\"\n", name, p); + /* XXX goto done? */ + } + /* </freqband> */ + if (iseq(name, "freqband") && mt->freqband != NULL) { + /* XXX must have start/end frequencies */ + /* XXX must have channel width/sep */ + mt->freqband = NULL; + goto done; + } + /* </rd> */ + if (iseq(name, "rd") && mt->rd != NULL) { + mt->rd = NULL; + goto done; + } + /* </band> */ + if (iseq(name, "band") && mt->netband != NULL) { + if (mt->netband->band == NULL) { + printf("No frequency band information at line %d\n", +#if 0 + XML_GetCurrentLineNumber(parser)); +#else + 0); +#endif + } + if (mt->netband->maxPower == 0) { + /* XXX complain */ + } + /* default max power w/ DFS to max power */ + if (mt->netband->maxPowerDFS == 0) + mt->netband->maxPowerDFS = mt->netband->maxPower; + mt->netband = NULL; + goto done; + } + /* </netband> */ + if (iseq(name, "netband") && mt->netband != NULL) { + mt->curband = NULL; + goto done; + } + /* </country> */ + if (iseq(name, "country") && mt->country != NULL) { + if (mt->country->code == 0) { + /* XXX must have iso cc */ + } + if (mt->country->name == NULL) { + /* XXX must have name */ + } + if (mt->country->rd == NULL) { + /* XXX? rd ref? */ + } + mt->country = NULL; + goto done; + } +done: + sbuf_delete(mt->sbuf[mt->level]); + mt->sbuf[mt->level--] = NULL; +#undef iseq +} + +static void +char_data(void *data, const XML_Char *s, int len) +{ + struct mystate *mt; + const char *b, *e; + + mt = data; + + b = s; + e = s + len-1; + for (; isspace(*b) && b < e; b++) + ; + for (; isspace(*e) && e > b; e++) + ; + if (e != b || (*b != '\0' && !isspace(*b))) + sbuf_bcat(mt->sbuf[mt->level], b, e-b+1); +} + +static void * +findid(struct regdata *rdp, const void *id, int type) +{ + struct ident *ip; + + for (ip = rdp->ident; ip->id != NULL; ip++) + if (ip->type == type && strcasecmp(ip->id, id) == 0) + return ip->p; + return NULL; +} + +/* + * Parse an regdomain XML configuration and build the internal representation. + */ +int +lib80211_regdomain_readconfig(struct regdata *rdp, const void *p, size_t len) +{ + XML_Parser parser; + struct mystate *mt; + struct regdomain *dp; + struct country *cp; + struct freqband *fp; + struct netband *nb; + const void *id; + int i; + + memset(rdp, 0, sizeof(struct regdata)); + mt = calloc(1, sizeof(struct mystate)); + if (mt == NULL) + return ENOMEM; + /* parse the XML input */ + mt->rdp = rdp; + parser = XML_ParserCreate(NULL); + XML_SetUserData(parser, mt); + XML_SetElementHandler(parser, start_element, end_element); + XML_SetCharacterDataHandler(parser, char_data); + if (XML_Parse(parser, p, len, 1) != XML_STATUS_OK) { + warnx("%s: %s at line %d", __func__, + XML_ErrorString(XML_GetErrorCode(parser)), + XML_GetCurrentLineNumber(parser)); + return -1; + } + XML_ParserFree(parser); + + /* setup the identifer table */ + rdp->ident = calloc(sizeof(struct ident), mt->nident + 1); + if (rdp->ident == NULL) + return ENOMEM; + free(mt); + i = 0; + LIST_FOREACH(dp, &rdp->domains, next) { + rdp->ident[i].id = dp->name; + rdp->ident[i].p = dp; + rdp->ident[i].type = DOMAIN; + i++; + } + LIST_FOREACH(fp, &rdp->freqbands, next) { + rdp->ident[i].id = fp->id; + rdp->ident[i].p = fp; + rdp->ident[i].type = FREQBAND; + i++; + } + LIST_FOREACH(cp, &rdp->countries, next) { + rdp->ident[i].id = cp->isoname; + rdp->ident[i].p = cp; + rdp->ident[i].type = COUNTRY; + i++; + } + + /* patch references */ + LIST_FOREACH(dp, &rdp->domains, next) { + if (dp->cc != NULL) { + id = dp->cc; + dp->cc = findid(rdp, id, COUNTRY); + free(__DECONST(char *, id)); + } + LIST_FOREACH(nb, &dp->bands_11b, next) + nb->band = findid(rdp, nb->band, FREQBAND); + LIST_FOREACH(nb, &dp->bands_11g, next) + nb->band = findid(rdp, nb->band, FREQBAND); + LIST_FOREACH(nb, &dp->bands_11a, next) + nb->band = findid(rdp, nb->band, FREQBAND); + LIST_FOREACH(nb, &dp->bands_11ng, next) + nb->band = findid(rdp, nb->band, FREQBAND); + LIST_FOREACH(nb, &dp->bands_11na, next) + nb->band = findid(rdp, nb->band, FREQBAND); + } + LIST_FOREACH(cp, &rdp->countries, next) { + id = cp->rd; + cp->rd = findid(rdp, id, DOMAIN); + free(__DECONST(char *, id)); + } + + return 0; +} + +static void +cleanup_bands(netband_head *head) +{ + struct netband *nb; + + for (;;) { + nb = LIST_FIRST(head); + if (nb == NULL) + break; + free(nb); + } +} + +/* + * Cleanup state/resources for a previously parsed regdomain database. + */ +void +lib80211_regdomain_cleanup(struct regdata *rdp) +{ + + free(rdp->ident); + rdp->ident = NULL; + for (;;) { + struct regdomain *dp = LIST_FIRST(&rdp->domains); + if (dp == NULL) + break; + LIST_REMOVE(dp, next); + cleanup_bands(&dp->bands_11b); + cleanup_bands(&dp->bands_11g); + cleanup_bands(&dp->bands_11a); + cleanup_bands(&dp->bands_11ng); + cleanup_bands(&dp->bands_11na); + if (dp->name != NULL) + free(__DECONST(char *, dp->name)); + } + for (;;) { + struct country *cp = LIST_FIRST(&rdp->countries); + if (cp == NULL) + break; + LIST_REMOVE(cp, next); + if (cp->name != NULL) + free(__DECONST(char *, cp->name)); + free(cp); + } + for (;;) { + struct freqband *fp = LIST_FIRST(&rdp->freqbands); + if (fp == NULL) + break; + LIST_REMOVE(fp, next); + free(fp); + } +} + +struct regdata * +lib80211_alloc_regdata(void) +{ + struct regdata *rdp; + struct stat sb; + void *xml; + int fd; + + rdp = calloc(1, sizeof(struct regdata)); + + fd = open(_PATH_REGDOMAIN, O_RDONLY); + if (fd < 0) { +#ifdef DEBUG + warn("%s: open(%s)", __func__, _PATH_REGDOMAIN); +#endif + free(rdp); + return NULL; + } + if (fstat(fd, &sb) < 0) { +#ifdef DEBUG + warn("%s: fstat(%s)", __func__, _PATH_REGDOMAIN); +#endif + close(fd); + free(rdp); + return NULL; + } + xml = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (xml == MAP_FAILED) { +#ifdef DEBUG + warn("%s: mmap", __func__); +#endif + close(fd); + free(rdp); + return NULL; + } + if (lib80211_regdomain_readconfig(rdp, xml, sb.st_size) != 0) { +#ifdef DEBUG + warn("%s: error reading regulatory database", __func__); +#endif + munmap(xml, sb.st_size); + close(fd); + free(rdp); + return NULL; + } + munmap(xml, sb.st_size); + close(fd); + + return rdp; +} + +void +lib80211_free_regdata(struct regdata *rdp) +{ + lib80211_regdomain_cleanup(rdp); + free(rdp); +} + +/* + * Lookup a regdomain by SKU. + */ +const struct regdomain * +lib80211_regdomain_findbysku(const struct regdata *rdp, enum RegdomainCode sku) +{ + const struct regdomain *dp; + + LIST_FOREACH(dp, &rdp->domains, next) { + if (dp->sku == sku) + return dp; + } + return NULL; +} + +/* + * Lookup a regdomain by name. + */ +const struct regdomain * +lib80211_regdomain_findbyname(const struct regdata *rdp, const char *name) +{ + const struct regdomain *dp; + + LIST_FOREACH(dp, &rdp->domains, next) { + if (strcasecmp(dp->name, name) == 0) + return dp; + } + return NULL; +} + +/* + * Lookup a country by ISO country code. + */ +const struct country * +lib80211_country_findbycc(const struct regdata *rdp, enum ISOCountryCode cc) +{ + const struct country *cp; + + LIST_FOREACH(cp, &rdp->countries, next) { + if (cp->code == cc) + return cp; + } + return NULL; +} + +/* + * Lookup a country by ISO/long name. + */ +const struct country * +lib80211_country_findbyname(const struct regdata *rdp, const char *name) +{ + const struct country *cp; + int len; + + len = strlen(name); + LIST_FOREACH(cp, &rdp->countries, next) { + if (strcasecmp(cp->isoname, name) == 0 || + strncasecmp(cp->name, name, len) == 0) + return cp; + } + return NULL; +} diff --git a/sbin/ifconfig/regdomain.h b/sbin/ifconfig/regdomain.h new file mode 100644 index 0000000..e7046a9 --- /dev/null +++ b/sbin/ifconfig/regdomain.h @@ -0,0 +1,112 @@ +/*- + * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting + * All rights reserved. + * + * 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 ``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 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 _LIB80211_H_ +#define _LIB80211_H_ + +#include <sys/cdefs.h> +#include <sys/queue.h> + +#include <net80211/ieee80211_regdomain.h> + +__BEGIN_DECLS + +struct freqband { + uint16_t freqStart; /* starting frequency (MHz) */ + uint16_t freqEnd; /* ending frequency (MHz) */ + uint8_t chanWidth; /* channel width (MHz) */ + uint8_t chanSep; /* channel sepaaration (MHz) */ + uint32_t flags; /* common operational constraints */ + + const void *id; + LIST_ENTRY(freqband) next; +}; + +struct netband { + const struct freqband *band; /* channel list description */ + uint8_t maxPower; /* regulatory cap on tx power (dBm) */ + uint8_t maxPowerDFS; /* regulatory cap w/ DFS (dBm) */ + uint32_t flags; /* net80211 channel flags */ + + LIST_ENTRY(netband) next; +}; +typedef LIST_HEAD(, netband) netband_head; + +struct country; + +struct regdomain { + enum RegdomainCode sku; /* regdomain code/SKU */ + const char *name; /* printable name */ + const struct country *cc; /* country code for 1-1/default map */ + + netband_head bands_11b; /* 11b operation */ + netband_head bands_11g; /* 11g operation */ + netband_head bands_11a; /* 11a operation */ + netband_head bands_11ng;/* 11ng operation */ + netband_head bands_11na;/* 11na operation */ + + LIST_ENTRY(regdomain) next; +}; + +struct country { + enum ISOCountryCode code; + const struct regdomain *rd; + const char* isoname; + const char* name; + + LIST_ENTRY(country) next; +}; + +struct ident; + +struct regdata { + LIST_HEAD(, country) countries; /* country code table */ + LIST_HEAD(, regdomain) domains; /* regulatory domains */ + LIST_HEAD(, freqband) freqbands; /* frequency band table */ + struct ident *ident; /* identifier table */ +}; + +#define _PATH_REGDOMAIN "/etc/regdomain.xml" + +struct regdata *lib80211_alloc_regdata(void); +void lib80211_free_regdata(struct regdata *); + +int lib80211_regdomain_readconfig(struct regdata *, const void *, size_t); +void lib80211_regdomain_cleanup(struct regdata *); + +const struct regdomain *lib80211_regdomain_findbysku(const struct regdata *, + enum RegdomainCode); +const struct regdomain *lib80211_regdomain_findbyname(const struct regdata *, + const char *); + +const struct country *lib80211_country_findbycc(const struct regdata *, + enum ISOCountryCode); +const struct country *lib80211_country_findbyname(const struct regdata *, + const char *); + +__END_DECLS + +#endif /* _LIB80211_H_ */ |